xref: /plan9/sys/src/libhttpd/hio.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier #include <httpd.h>
480ee5cbfSDavid du Colombier 
580ee5cbfSDavid du Colombier static	char	hstates[] = "nrewE";
680ee5cbfSDavid du Colombier static	char	hxfers[] = " x";
780ee5cbfSDavid du Colombier 
880ee5cbfSDavid du Colombier int
980ee5cbfSDavid du Colombier hinit(Hio *h, int fd, int mode)
1080ee5cbfSDavid du Colombier {
1180ee5cbfSDavid du Colombier 	if(fd == -1 || mode != Hread && mode != Hwrite)
1280ee5cbfSDavid du Colombier 		return -1;
1380ee5cbfSDavid du Colombier 	h->hh = nil;
1480ee5cbfSDavid du Colombier 	h->fd = fd;
1580ee5cbfSDavid du Colombier 	h->seek = 0;
1680ee5cbfSDavid du Colombier 	h->state = mode;
1780ee5cbfSDavid du Colombier 	h->start = h->buf + 16;		/* leave space for chunk length */
1880ee5cbfSDavid du Colombier 	h->stop = h->pos = h->start;
1980ee5cbfSDavid du Colombier 	if(mode == Hread){
2080ee5cbfSDavid du Colombier 		h->bodylen = ~0UL;
2180ee5cbfSDavid du Colombier 		*h->pos = '\0';
2280ee5cbfSDavid du Colombier 	}else
2380ee5cbfSDavid du Colombier 		h->stop = h->start + Hsize;
2480ee5cbfSDavid du Colombier 	return 0;
2580ee5cbfSDavid du Colombier }
2680ee5cbfSDavid du Colombier 
2780ee5cbfSDavid du Colombier int
2880ee5cbfSDavid du Colombier hiserror(Hio *h)
2980ee5cbfSDavid du Colombier {
3080ee5cbfSDavid du Colombier 	return h->state == Herr;
3180ee5cbfSDavid du Colombier }
3280ee5cbfSDavid du Colombier 
3380ee5cbfSDavid du Colombier int
3480ee5cbfSDavid du Colombier hgetc(Hio *h)
3580ee5cbfSDavid du Colombier {
3680ee5cbfSDavid du Colombier 	uchar *p;
3780ee5cbfSDavid du Colombier 
3880ee5cbfSDavid du Colombier 	p = h->pos;
3980ee5cbfSDavid du Colombier 	if(p < h->stop){
4080ee5cbfSDavid du Colombier 		h->pos = p + 1;
4180ee5cbfSDavid du Colombier 		return *p;
4280ee5cbfSDavid du Colombier 	}
4380ee5cbfSDavid du Colombier 	p -= UTFmax;
4480ee5cbfSDavid du Colombier 	if(p < h->start)
4580ee5cbfSDavid du Colombier 		p = h->start;
4680ee5cbfSDavid du Colombier 	if(!hreadbuf(h, p) || h->pos == h->stop)
4780ee5cbfSDavid du Colombier 		return -1;
4880ee5cbfSDavid du Colombier 	return *h->pos++;
4980ee5cbfSDavid du Colombier }
5080ee5cbfSDavid du Colombier 
5180ee5cbfSDavid du Colombier int
5280ee5cbfSDavid du Colombier hungetc(Hio *h)
5380ee5cbfSDavid du Colombier {
5480ee5cbfSDavid du Colombier 	if(h->state == Hend)
5580ee5cbfSDavid du Colombier 		h->state = Hread;
5680ee5cbfSDavid du Colombier 	else if(h->state == Hread)
5780ee5cbfSDavid du Colombier 		h->pos--;
5880ee5cbfSDavid du Colombier 	if(h->pos < h->start || h->state != Hread){
5980ee5cbfSDavid du Colombier 		h->state = Herr;
6080ee5cbfSDavid du Colombier 		h->pos = h->stop;
6180ee5cbfSDavid du Colombier 		return -1;
6280ee5cbfSDavid du Colombier 	}
6380ee5cbfSDavid du Colombier 	return 0;
6480ee5cbfSDavid du Colombier }
6580ee5cbfSDavid du Colombier 
6680ee5cbfSDavid du Colombier /*
6780ee5cbfSDavid du Colombier  * fill the buffer, saving contents from vsave onwards.
6880ee5cbfSDavid du Colombier  * nothing is saved if vsave is nil.
6980ee5cbfSDavid du Colombier  * returns the beginning of the buffer.
7080ee5cbfSDavid du Colombier  *
7180ee5cbfSDavid du Colombier  * understands message body sizes and chunked transfer encoding
7280ee5cbfSDavid du Colombier  */
7380ee5cbfSDavid du Colombier void *
7480ee5cbfSDavid du Colombier hreadbuf(Hio *h, void *vsave)
7580ee5cbfSDavid du Colombier {
7680ee5cbfSDavid du Colombier 	Hio *hh;
7780ee5cbfSDavid du Colombier 	uchar *save;
7880ee5cbfSDavid du Colombier 	int c, in, cpy, dpos;
7980ee5cbfSDavid du Colombier 
8080ee5cbfSDavid du Colombier 	save = vsave;
8180ee5cbfSDavid du Colombier 	if(save && (save < h->start || save > h->stop)
8280ee5cbfSDavid du Colombier 	|| h->state != Hread && h->state != Hend){
8380ee5cbfSDavid du Colombier 		h->state = Herr;
8480ee5cbfSDavid du Colombier 		h->pos = h->stop;
8580ee5cbfSDavid du Colombier 		return nil;
8680ee5cbfSDavid du Colombier 	}
8780ee5cbfSDavid du Colombier 
8880ee5cbfSDavid du Colombier 	dpos = 0;
8980ee5cbfSDavid du Colombier 	if(save && h->pos > save)
9080ee5cbfSDavid du Colombier 		dpos = h->pos - save;
9180ee5cbfSDavid du Colombier 	cpy = 0;
9280ee5cbfSDavid du Colombier 	if(save){
9380ee5cbfSDavid du Colombier 		cpy = h->stop - save;
9480ee5cbfSDavid du Colombier 		memmove(h->start, save, cpy);
9580ee5cbfSDavid du Colombier 	}
9680ee5cbfSDavid du Colombier 	h->seek += h->stop - h->start - cpy;
9780ee5cbfSDavid du Colombier 	h->pos = h->start + dpos;
9880ee5cbfSDavid du Colombier 
9980ee5cbfSDavid du Colombier 	in = Hsize - cpy;
10080ee5cbfSDavid du Colombier 	if(h->state == Hend)
10180ee5cbfSDavid du Colombier 		in = 0;
10280ee5cbfSDavid du Colombier 	else if(in > h->bodylen)
10380ee5cbfSDavid du Colombier 		in = h->bodylen;
10480ee5cbfSDavid du Colombier 
10580ee5cbfSDavid du Colombier 	/*
10680ee5cbfSDavid du Colombier 	 * for chunked encoding, fill buffer,
10780ee5cbfSDavid du Colombier 	 * then read in new chunk length and wipe out that line
10880ee5cbfSDavid du Colombier 	 */
10980ee5cbfSDavid du Colombier 	hh = h->hh;
11080ee5cbfSDavid du Colombier 	if(hh != nil){
11180ee5cbfSDavid du Colombier 		if(!in && h->xferenc && h->state != Hend){
11280ee5cbfSDavid du Colombier 			if(h->xferenc == 2){
11380ee5cbfSDavid du Colombier 				c = hgetc(hh);
11480ee5cbfSDavid du Colombier 				if(c == '\r')
11580ee5cbfSDavid du Colombier 					c = hgetc(hh);
11680ee5cbfSDavid du Colombier 				if(c != '\n'){
11780ee5cbfSDavid du Colombier 					h->pos = h->stop;
11880ee5cbfSDavid du Colombier 					h->state = Herr;
11980ee5cbfSDavid du Colombier 					return nil;
12080ee5cbfSDavid du Colombier 				}
12180ee5cbfSDavid du Colombier 			}
12280ee5cbfSDavid du Colombier 			h->xferenc = 2;
12380ee5cbfSDavid du Colombier 			in = 0;
12480ee5cbfSDavid du Colombier 			while((c = hgetc(hh)) != '\n'){
12580ee5cbfSDavid du Colombier 				if(c >= '0' && c <= '9')
12680ee5cbfSDavid du Colombier 					c -= '0';
12780ee5cbfSDavid du Colombier 				else if(c >= 'a' && c <= 'f')
12880ee5cbfSDavid du Colombier 					c -= 'a' - 10;
12980ee5cbfSDavid du Colombier 				else if(c >= 'A' && c <= 'F')
13080ee5cbfSDavid du Colombier 					c -= 'A' - 10;
13180ee5cbfSDavid du Colombier 				else
13280ee5cbfSDavid du Colombier 					break;
13380ee5cbfSDavid du Colombier 				in = in * 16 + c;
13480ee5cbfSDavid du Colombier 			}
13580ee5cbfSDavid du Colombier 			while(c != '\n'){
13680ee5cbfSDavid du Colombier 				if(c < 0){
13780ee5cbfSDavid du Colombier 					h->pos = h->stop;
13880ee5cbfSDavid du Colombier 					h->state = Herr;
13980ee5cbfSDavid du Colombier 					return nil;
14080ee5cbfSDavid du Colombier 				}
14180ee5cbfSDavid du Colombier 				c = hgetc(hh);
14280ee5cbfSDavid du Colombier 			}
14380ee5cbfSDavid du Colombier 			h->bodylen = in;
14480ee5cbfSDavid du Colombier 
14580ee5cbfSDavid du Colombier 			in = Hsize - cpy;
14680ee5cbfSDavid du Colombier 			if(in > h->bodylen)
14780ee5cbfSDavid du Colombier 				in = h->bodylen;
14880ee5cbfSDavid du Colombier 		}
14980ee5cbfSDavid du Colombier 		if(in){
15080ee5cbfSDavid du Colombier 			while(hh->pos + in > hh->stop){
15180ee5cbfSDavid du Colombier 				if(hreadbuf(hh, hh->pos) == nil){
15280ee5cbfSDavid du Colombier 					h->pos = h->stop;
15380ee5cbfSDavid du Colombier 					h->state = Herr;
15480ee5cbfSDavid du Colombier 					return nil;
15580ee5cbfSDavid du Colombier 				}
15680ee5cbfSDavid du Colombier 			}
15780ee5cbfSDavid du Colombier 			memmove(h->start + cpy, hh->pos, in);
15880ee5cbfSDavid du Colombier 			hh->pos += in;
15980ee5cbfSDavid du Colombier 		}
16080ee5cbfSDavid du Colombier 	}else if(in && (in = read(h->fd, h->start + cpy, in)) < 0){
16180ee5cbfSDavid du Colombier 		h->state = Herr;
16280ee5cbfSDavid du Colombier 		h->pos = h->stop;
16380ee5cbfSDavid du Colombier 		return nil;
16480ee5cbfSDavid du Colombier 	}
16580ee5cbfSDavid du Colombier 	if(in == 0)
16680ee5cbfSDavid du Colombier 		h->state = Hend;
16780ee5cbfSDavid du Colombier 
16880ee5cbfSDavid du Colombier 	h->bodylen -= in;
16980ee5cbfSDavid du Colombier 
17080ee5cbfSDavid du Colombier 	h->stop = h->start + cpy + in;
17180ee5cbfSDavid du Colombier 	*h->stop = '\0';
17280ee5cbfSDavid du Colombier 	if(h->pos == h->stop)
17380ee5cbfSDavid du Colombier 		return nil;
17480ee5cbfSDavid du Colombier 	return h->start;
17580ee5cbfSDavid du Colombier }
17680ee5cbfSDavid du Colombier 
17780ee5cbfSDavid du Colombier int
17880ee5cbfSDavid du Colombier hbuflen(Hio *h, void *p)
17980ee5cbfSDavid du Colombier {
18080ee5cbfSDavid du Colombier 	return h->stop - (uchar*)p;
18180ee5cbfSDavid du Colombier }
18280ee5cbfSDavid du Colombier 
18380ee5cbfSDavid du Colombier /*
18480ee5cbfSDavid du Colombier  * prepare to receive a message body
18580ee5cbfSDavid du Colombier  * len is the content length (~0 => unspecified)
18680ee5cbfSDavid du Colombier  * te is the transfer encoding
18780ee5cbfSDavid du Colombier  * returns < 0 if setup failed
18880ee5cbfSDavid du Colombier  */
18980ee5cbfSDavid du Colombier Hio*
19080ee5cbfSDavid du Colombier hbodypush(Hio *hh, ulong len, HFields *te)
19180ee5cbfSDavid du Colombier {
19280ee5cbfSDavid du Colombier 	Hio *h;
19380ee5cbfSDavid du Colombier 	int xe;
19480ee5cbfSDavid du Colombier 
19580ee5cbfSDavid du Colombier 	if(hh->state != Hread)
19680ee5cbfSDavid du Colombier 		return nil;
19780ee5cbfSDavid du Colombier 	xe = 0;
19880ee5cbfSDavid du Colombier 	if(te != nil){
19980ee5cbfSDavid du Colombier 		if(te->params != nil || te->next != nil)
20080ee5cbfSDavid du Colombier 			return nil;
20180ee5cbfSDavid du Colombier 		if(cistrcmp(te->s, "chunked") == 0){
20280ee5cbfSDavid du Colombier 			xe = 1;
20380ee5cbfSDavid du Colombier 			len = 0;
2049a747e4fSDavid du Colombier 		}else if(cistrcmp(te->s, "identity") == 0){
20580ee5cbfSDavid du Colombier 			;
2069a747e4fSDavid du Colombier 		}else
20780ee5cbfSDavid du Colombier 			return nil;
20880ee5cbfSDavid du Colombier 	}
20980ee5cbfSDavid du Colombier 
21080ee5cbfSDavid du Colombier 	h = malloc(sizeof *h);
21180ee5cbfSDavid du Colombier 	if(h == nil)
21280ee5cbfSDavid du Colombier 		return nil;
21380ee5cbfSDavid du Colombier 
21480ee5cbfSDavid du Colombier 	h->hh = hh;
21580ee5cbfSDavid du Colombier 	h->fd = -1;
21680ee5cbfSDavid du Colombier 	h->seek = 0;
21780ee5cbfSDavid du Colombier 	h->state = Hread;
21880ee5cbfSDavid du Colombier 	h->xferenc = xe;
21980ee5cbfSDavid du Colombier 	h->start = h->buf + 16;		/* leave space for chunk length */
22080ee5cbfSDavid du Colombier 	h->stop = h->pos = h->start;
22180ee5cbfSDavid du Colombier 	*h->pos = '\0';
22280ee5cbfSDavid du Colombier 	h->bodylen = len;
22380ee5cbfSDavid du Colombier 	return h;
22480ee5cbfSDavid du Colombier }
22580ee5cbfSDavid du Colombier 
22680ee5cbfSDavid du Colombier /*
22780ee5cbfSDavid du Colombier  * dump the state of the io buffer into a string
22880ee5cbfSDavid du Colombier  */
22980ee5cbfSDavid du Colombier char *
23080ee5cbfSDavid du Colombier hunload(Hio *h)
23180ee5cbfSDavid du Colombier {
23280ee5cbfSDavid du Colombier 	uchar *p, *t, *stop, *buf;
23380ee5cbfSDavid du Colombier 	int ne, n, c;
23480ee5cbfSDavid du Colombier 
23580ee5cbfSDavid du Colombier 	stop = h->stop;
23680ee5cbfSDavid du Colombier 	ne = 0;
23780ee5cbfSDavid du Colombier 	for(p = h->pos; p < stop; p++){
23880ee5cbfSDavid du Colombier 		c = *p;
23980ee5cbfSDavid du Colombier 		if(c == 0x80)
24080ee5cbfSDavid du Colombier 			ne++;
24180ee5cbfSDavid du Colombier 	}
24280ee5cbfSDavid du Colombier 	p = h->pos;
24380ee5cbfSDavid du Colombier 
24480ee5cbfSDavid du Colombier 	n = (stop - p) + ne + 3;
24580ee5cbfSDavid du Colombier 	buf = mallocz(n, 1);
24680ee5cbfSDavid du Colombier 	if(buf == nil)
24780ee5cbfSDavid du Colombier 		return nil;
24880ee5cbfSDavid du Colombier 	buf[0] = hstates[h->state];
24980ee5cbfSDavid du Colombier 	buf[1] = hxfers[h->xferenc];
25080ee5cbfSDavid du Colombier 
25180ee5cbfSDavid du Colombier 	t = &buf[2];
25280ee5cbfSDavid du Colombier 	for(; p < stop; p++){
25380ee5cbfSDavid du Colombier 		c = *p;
25480ee5cbfSDavid du Colombier 		if(c == 0 || c == 0x80){
25580ee5cbfSDavid du Colombier 			*t++ = 0x80;
25680ee5cbfSDavid du Colombier 			if(c == 0x80)
25780ee5cbfSDavid du Colombier 				*t++ = 0x80;
25880ee5cbfSDavid du Colombier 		}else
25980ee5cbfSDavid du Colombier 			*t++ = c;
26080ee5cbfSDavid du Colombier 	}
26180ee5cbfSDavid du Colombier 	*t++ = '\0';
26280ee5cbfSDavid du Colombier 	if(t != buf + n)
26380ee5cbfSDavid du Colombier 		return nil;
26480ee5cbfSDavid du Colombier 	return (char*)buf;
26580ee5cbfSDavid du Colombier }
26680ee5cbfSDavid du Colombier 
26780ee5cbfSDavid du Colombier /*
26880ee5cbfSDavid du Colombier  * read the io buffer state from a string
26980ee5cbfSDavid du Colombier  */
27080ee5cbfSDavid du Colombier int
27180ee5cbfSDavid du Colombier hload(Hio *h, char *buf)
27280ee5cbfSDavid du Colombier {
27380ee5cbfSDavid du Colombier 	uchar *p, *t, *stop;
27480ee5cbfSDavid du Colombier 	char *s;
27580ee5cbfSDavid du Colombier 	int c;
27680ee5cbfSDavid du Colombier 
27780ee5cbfSDavid du Colombier 	s = strchr(hstates, buf[0]);
27880ee5cbfSDavid du Colombier 	if(s == nil)
27980ee5cbfSDavid du Colombier 		return 0;
28080ee5cbfSDavid du Colombier 	h->state = s - hstates;
28180ee5cbfSDavid du Colombier 
28280ee5cbfSDavid du Colombier 	s = strchr(hxfers, buf[1]);
28380ee5cbfSDavid du Colombier 	if(s == nil)
28480ee5cbfSDavid du Colombier 		return 0;
28580ee5cbfSDavid du Colombier 	h->xferenc = s - hxfers;
28680ee5cbfSDavid du Colombier 
28780ee5cbfSDavid du Colombier 	t = h->start;
28880ee5cbfSDavid du Colombier 	stop = t + Hsize;
28980ee5cbfSDavid du Colombier 	for(p = (uchar*)&buf[2]; c = *p; p++){
29080ee5cbfSDavid du Colombier 		if(c == 0x80){
29180ee5cbfSDavid du Colombier 			if(p[1] != 0x80)
29280ee5cbfSDavid du Colombier 				c = 0;
29380ee5cbfSDavid du Colombier 			else
2949a747e4fSDavid du Colombier 				p++;
29580ee5cbfSDavid du Colombier 		}
29680ee5cbfSDavid du Colombier 		*t++ = c;
29780ee5cbfSDavid du Colombier 		if(t >= stop)
29880ee5cbfSDavid du Colombier 			return 0;
29980ee5cbfSDavid du Colombier 	}
30080ee5cbfSDavid du Colombier 	*t = '\0';
30180ee5cbfSDavid du Colombier 	h->pos = h->start;
30280ee5cbfSDavid du Colombier 	h->stop = t;
30380ee5cbfSDavid du Colombier 	h->seek = 0;
30480ee5cbfSDavid du Colombier 	return 1;
30580ee5cbfSDavid du Colombier }
30680ee5cbfSDavid du Colombier 
30780ee5cbfSDavid du Colombier void
30880ee5cbfSDavid du Colombier hclose(Hio *h)
30980ee5cbfSDavid du Colombier {
31080ee5cbfSDavid du Colombier 	if(h->fd >= 0){
3119a747e4fSDavid du Colombier 		if(h->state == Hwrite)
31280ee5cbfSDavid du Colombier 			hxferenc(h, 0);
31380ee5cbfSDavid du Colombier 		close(h->fd);
31480ee5cbfSDavid du Colombier 	}
31580ee5cbfSDavid du Colombier 	h->stop = h->pos = nil;
31680ee5cbfSDavid du Colombier 	h->fd = -1;
31780ee5cbfSDavid du Colombier }
31880ee5cbfSDavid du Colombier 
31980ee5cbfSDavid du Colombier /*
32080ee5cbfSDavid du Colombier  * flush the buffer and possibly change encoding modes
32180ee5cbfSDavid du Colombier  */
32280ee5cbfSDavid du Colombier int
32380ee5cbfSDavid du Colombier hxferenc(Hio *h, int on)
32480ee5cbfSDavid du Colombier {
32580ee5cbfSDavid du Colombier 	if(h->xferenc && !on && h->pos != h->start)
32680ee5cbfSDavid du Colombier 		hflush(h);
32780ee5cbfSDavid du Colombier 	if(hflush(h) < 0)
32880ee5cbfSDavid du Colombier 		return -1;
32980ee5cbfSDavid du Colombier 	h->xferenc = !!on;
33080ee5cbfSDavid du Colombier 	return 0;
33180ee5cbfSDavid du Colombier }
33280ee5cbfSDavid du Colombier 
33380ee5cbfSDavid du Colombier int
33480ee5cbfSDavid du Colombier hputc(Hio *h, int c)
33580ee5cbfSDavid du Colombier {
33680ee5cbfSDavid du Colombier 	uchar *p;
33780ee5cbfSDavid du Colombier 
33880ee5cbfSDavid du Colombier 	p = h->pos;
33980ee5cbfSDavid du Colombier 	if(p < h->stop){
34080ee5cbfSDavid du Colombier 		h->pos = p + 1;
34180ee5cbfSDavid du Colombier 		return *p = c;
34280ee5cbfSDavid du Colombier 	}
34380ee5cbfSDavid du Colombier 	if(hflush(h) < 0)
34480ee5cbfSDavid du Colombier 		return -1;
34580ee5cbfSDavid du Colombier 	return *h->pos++ = c;
34680ee5cbfSDavid du Colombier }
34780ee5cbfSDavid du Colombier 
348*3ff48bf5SDavid du Colombier static int
3499a747e4fSDavid du Colombier fmthflush(Fmt *f)
3509a747e4fSDavid du Colombier {
3519a747e4fSDavid du Colombier 	Hio *h;
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier 	h = f->farg;
3549a747e4fSDavid du Colombier 	h->pos = f->to;
3559a747e4fSDavid du Colombier 	if(hflush(h) < 0)
3569a747e4fSDavid du Colombier 		return 0;
3579a747e4fSDavid du Colombier 	f->stop = h->stop;
3589a747e4fSDavid du Colombier 	f->to = h->pos;
3599a747e4fSDavid du Colombier 	f->start = h->pos;
3609a747e4fSDavid du Colombier 	return 1;
3619a747e4fSDavid du Colombier }
3629a747e4fSDavid du Colombier 
3639a747e4fSDavid du Colombier int
3649a747e4fSDavid du Colombier hvprint(Hio *h, char *fmt, va_list args)
3659a747e4fSDavid du Colombier {
3669a747e4fSDavid du Colombier 	int n;
3679a747e4fSDavid du Colombier 	Fmt f;
3689a747e4fSDavid du Colombier 
3699a747e4fSDavid du Colombier 	f.runes = 0;
3709a747e4fSDavid du Colombier 	f.stop = h->stop;
3719a747e4fSDavid du Colombier 	f.to = h->pos;
3729a747e4fSDavid du Colombier 	f.start = h->pos;
3739a747e4fSDavid du Colombier 	f.flush = fmthflush;
3749a747e4fSDavid du Colombier 	f.farg = h;
3759a747e4fSDavid du Colombier 	f.nfmt = 0;
3769a747e4fSDavid du Colombier 	f.args = args;
3779a747e4fSDavid du Colombier 	n = dofmt(&f, fmt);
3789a747e4fSDavid du Colombier 	h->pos = f.to;
3799a747e4fSDavid du Colombier 	return n;
3809a747e4fSDavid du Colombier }
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier int
38380ee5cbfSDavid du Colombier hprint(Hio *h, char *fmt, ...)
38480ee5cbfSDavid du Colombier {
38580ee5cbfSDavid du Colombier 	int n;
38680ee5cbfSDavid du Colombier 	va_list arg;
38780ee5cbfSDavid du Colombier 
38880ee5cbfSDavid du Colombier 	va_start(arg, fmt);
3899a747e4fSDavid du Colombier 	n = hvprint(h, fmt, arg);
39080ee5cbfSDavid du Colombier 	va_end(arg);
39180ee5cbfSDavid du Colombier 	return n;
39280ee5cbfSDavid du Colombier }
39380ee5cbfSDavid du Colombier 
39480ee5cbfSDavid du Colombier int
39580ee5cbfSDavid du Colombier hflush(Hio *h)
39680ee5cbfSDavid du Colombier {
39780ee5cbfSDavid du Colombier 	uchar *s;
39880ee5cbfSDavid du Colombier 	int w;
39980ee5cbfSDavid du Colombier 
40080ee5cbfSDavid du Colombier 	if(h->state != Hwrite){
40180ee5cbfSDavid du Colombier 		h->state = Herr;
40280ee5cbfSDavid du Colombier 		h->stop = h->pos;
40380ee5cbfSDavid du Colombier 		return -1;
40480ee5cbfSDavid du Colombier 	}
40580ee5cbfSDavid du Colombier 	s = h->start;
40680ee5cbfSDavid du Colombier 	w = h->pos - s;
40780ee5cbfSDavid du Colombier 	if(h->xferenc){
40880ee5cbfSDavid du Colombier 		*--s = '\n';
40980ee5cbfSDavid du Colombier 		*--s = '\r';
41080ee5cbfSDavid du Colombier 		do{
41180ee5cbfSDavid du Colombier 			*--s = "0123456789abcdef"[w & 0xf];
41280ee5cbfSDavid du Colombier 			w >>= 4;
41380ee5cbfSDavid du Colombier 		}while(w);
41480ee5cbfSDavid du Colombier 		h->pos[0] = '\r';
41580ee5cbfSDavid du Colombier 		h->pos[1] = '\n';
41680ee5cbfSDavid du Colombier 		w = &h->pos[2] - s;
41780ee5cbfSDavid du Colombier 	}
41880ee5cbfSDavid du Colombier 	if(write(h->fd, s, w) != w){
41980ee5cbfSDavid du Colombier 		h->state = Herr;
42080ee5cbfSDavid du Colombier 		h->stop = h->pos;
42180ee5cbfSDavid du Colombier 		return -1;
42280ee5cbfSDavid du Colombier 	}
42380ee5cbfSDavid du Colombier 	h->seek += w;
42480ee5cbfSDavid du Colombier 	h->pos = h->start;
42580ee5cbfSDavid du Colombier 	return 0;
42680ee5cbfSDavid du Colombier }
42780ee5cbfSDavid du Colombier 
42880ee5cbfSDavid du Colombier int
42980ee5cbfSDavid du Colombier hwrite(Hio *h, void *vbuf, int len)
43080ee5cbfSDavid du Colombier {
43180ee5cbfSDavid du Colombier 	uchar *pos, *buf;
43280ee5cbfSDavid du Colombier 	int n, m;
43380ee5cbfSDavid du Colombier 
43480ee5cbfSDavid du Colombier 	buf = vbuf;
43580ee5cbfSDavid du Colombier 	n = len;
43680ee5cbfSDavid du Colombier 	if(n < 0 || h->state != Hwrite){
43780ee5cbfSDavid du Colombier 		h->state = Herr;
43880ee5cbfSDavid du Colombier 		h->stop = h->pos;
43980ee5cbfSDavid du Colombier 		return -1;
44080ee5cbfSDavid du Colombier 	}
44180ee5cbfSDavid du Colombier 	pos = h->pos;
44280ee5cbfSDavid du Colombier 	if(pos + n >= h->stop){
44380ee5cbfSDavid du Colombier 		m = pos - h->start;
44480ee5cbfSDavid du Colombier 		if(m){
44580ee5cbfSDavid du Colombier 			m = Hsize - m;
44680ee5cbfSDavid du Colombier 			if(m){
44780ee5cbfSDavid du Colombier 				memmove(pos, buf, m);
44880ee5cbfSDavid du Colombier 				buf += m;
44980ee5cbfSDavid du Colombier 				n -= m;
45080ee5cbfSDavid du Colombier 			}
45180ee5cbfSDavid du Colombier 			if(write(h->fd, h->start, Hsize) != Hsize){
45280ee5cbfSDavid du Colombier 				h->state = Herr;
45380ee5cbfSDavid du Colombier 				h->stop = h->pos;
45480ee5cbfSDavid du Colombier 				return -1;
45580ee5cbfSDavid du Colombier 			}
45680ee5cbfSDavid du Colombier 			h->seek += Hsize;
45780ee5cbfSDavid du Colombier 		}
45880ee5cbfSDavid du Colombier 		m = n % Hsize;
45980ee5cbfSDavid du Colombier 		n -= m;
460b7b24591SDavid du Colombier 		if(n != 0 && write(h->fd, buf, n) != n){
46180ee5cbfSDavid du Colombier 			h->state = Herr;
46280ee5cbfSDavid du Colombier 			h->stop = h->pos;
46380ee5cbfSDavid du Colombier 			return -1;
46480ee5cbfSDavid du Colombier 		}
46580ee5cbfSDavid du Colombier 		h->seek += n;
46680ee5cbfSDavid du Colombier 		buf += n;
46780ee5cbfSDavid du Colombier 		pos = h->pos = h->start;
46880ee5cbfSDavid du Colombier 		n = m;
46980ee5cbfSDavid du Colombier 	}
47080ee5cbfSDavid du Colombier 	memmove(pos, buf, n);
47180ee5cbfSDavid du Colombier 	h->pos = pos + n;
47280ee5cbfSDavid du Colombier 	return len;
47380ee5cbfSDavid du Colombier }
474