code: plan9front

ref: e72da62915b09d5673b0c0179ba8dfe045aeb8c3
dir: /sys/src/cmd/uhtml.c/

View raw version
#include <u.h>
#include <libc.h>
#include <ctype.h>

int nbuf;
char buf[64*1024+1];
char *cset = nil;
char *whitespace = " \t\r\n";

void
usage(void)
{
	fprint(2, "%s [ -p ] [ -c charset ] [ file ]\n", argv0);
	exits("usage");
}

char*
attr(char *s, char *a)
{
	char *e, q;

	if((s = cistrstr(s, a)) == nil)
		return nil;
	s += strlen(a);
	while(*s && strchr(whitespace, *s))
		s++;
	if(*s++ != '=')
		return nil;
	while(*s && strchr(whitespace, *s))
		s++;
	q = 0;
	if(*s == '"' || *s == '\'')
		q = *s++;
	for(e = s; *e; e++){
		if(*e == q)
			break;
		if(isalnum(*e))
			continue;
		if(*e == '-' || *e == '_')
			continue;
		break;
	}
	if((e - s) > 1)
		return smprint("%.*s", (int)(e - s), s);
	return nil;
}

void
main(int argc, char *argv[])
{
	int n, q, pfd[2], pflag = 0;
	char *arg[4], *s, *g, *e, *p, *a, t;
	Rune r;

	ARGBEGIN {
	case 'c':
		cset = EARGF(usage());
		break;
	case 'p':
		pflag = 1;
		break;
	default:
		usage();
	} ARGEND;

	if(*argv){
		close(0);
		if(open(*argv, OREAD) != 0)
			sysfatal("open: %r");
	}
	nbuf = 0;
	while(nbuf < sizeof(buf)-1){
		if((n = read(0, buf + nbuf, sizeof(buf)-1-nbuf)) <= 0)
			break;
		nbuf += n;
		buf[nbuf] = 0;
	}

	p = buf;
	if(nbuf >= 3 && memcmp(p, "\xEF\xBB\xBF", 3)==0){
		p += 3;
		nbuf -= 3;
		cset = "utf";
		goto Found;
	}
	if(nbuf >= 2 && memcmp(p, "\xFE\xFF", 2) == 0){
		p += 2;
		nbuf -= 2;
		cset = "unicode-be";
		goto Found;
	}
	if(nbuf >= 2 && memcmp(p, "\xFF\xFE", 2) == 0){
		p += 2;
		nbuf -= 2;
		cset = "unicode-le";
		goto Found;
	}

	s = p;
	do {
		if((s = strchr(s, '<')) == nil)
			break;
		q = 0;
		g = ++s;
		e = buf+nbuf;
		while(s < e){
			if(*s == '=' && q == 0)
				q = '=';
			else if(*s == '\'' || *s == '"'){
				if(q == '=')
					q = *s;
				else if(q == *s)
					q = 0;
			}
			else if(*s == '>' && q != '\'' && q != '"'){
				e = s;
				break;
			}
			else if(q == '=' && strchr(whitespace, *s) == nil)
				q = 0;
			s++;
		}
		t = *e;
		*e = 0;
		if((a = attr(g, "encoding")) != nil || (a = attr(g, "charset")) != nil)
		if(cistrcmp(a, "utf") != 0 && cistrcmp(a, "utf-8") != 0){
			cset = a;
			*e = t;
			break;
		}
		*e = t;
		s = ++e;
	} while(t);

	s = p;
	while(s+UTFmax < p+nbuf){
		s += chartorune(&r, s);
		if(r == Runeerror){
			if(cset == nil)
				cset = "latin1";
			goto Found;
		}
	}
	cset = "utf";

Found:
	if(pflag){
		print("%s\n", cset);
		exits(0);
	}

	if(nbuf == 0){
		write(1, p, 0);
		exits(0);
	}

	if(pipe(pfd) < 0)
		sysfatal("pipe: %r");

	switch(rfork(RFFDG|RFREND|RFPROC)){
	case -1:
		sysfatal("fork: %r");
	case 0:
		dup(pfd[0], 0);
		close(pfd[0]);
		close(pfd[1]);

		arg[0] = "rc";
		arg[1] = "-c";
		arg[2] = smprint("{tcs -f %s || cat} | tcs -f html", cset);
		arg[3] = nil;
		exec("/bin/rc", arg);
	}

	dup(pfd[1], 1);
	close(pfd[0]);
	close(pfd[1]);

	while(nbuf > 0){
		if(write(1, p, nbuf) != nbuf)
			sysfatal("write: %r");
		p = buf;
		if((nbuf = read(0, p, sizeof(buf))) < 0)
			sysfatal("read: %r");
	}
	close(1);
	waitpid();
	exits(0);
}