1 /*
2  * Copyright (C) 1998,1999 Uwe Ohse
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * As a special exception this source may be used as part of the
19  * SRS project by CORE/Computer Service Langenbach
20  * regardless of the copyright they choose.
21  *
22  * Contact: uwe@ohse.de
23  */
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "timselsysdep.h"
29 #include "uostr.h"
30 #include "uoio.h"
31 
32 #define READCONST 8192
33 #define SAMEBLOCK(move,start) (((size_t) ((move)-(start)))<READCONST)
34 
35 #define ULEFT(u) (u->buflen-(u->rstart-u->buf))
36 
37 void
uoio_destroy(uoio_t * u)38 uoio_destroy (uoio_t * u)
39 {
40 	if (u->buf)
41 		free (u->buf);
42 }
43 
44 static int
handle_timeout(uoio_t * u,int w)45 handle_timeout(uoio_t *u, int w)
46 {
47 	fd_set set;
48 	time_t start=0;
49 	int x;
50 	struct timeval tv;
51 	while (1) {
52 		tv.tv_usec=0;
53 		if (!start) {
54 			start=time(0);
55 			tv.tv_sec=u->timeout;
56 		} else
57 			tv.tv_sec=u->timeout-(time(0)-start);
58 		FD_ZERO(&set);
59 		FD_SET(u->fd,&set);
60 		x=select(u->fd + 1,w ? 0 : &set, w ? &set : 0,0,&tv);
61 		if (x==-1 && errno==EINTR) continue;
62 		if (x==-1) return -1;
63 		if (x==0) { errno=ETIMEDOUT; return -1; }
64 		return 0;
65 	}
66 }
67 
68 #define DO_DISQUALIFY_CONST(from,to) \
69 	union DISQUALIFY {const char *ccs; char *cs;} disqua; \
70 	disqua.ccs=from;to=disqua.cs;
71 
72 static int
uoio_do_write(uoio_t * u,const char * robuf,size_t len)73 uoio_do_write(uoio_t *u,const char *robuf,size_t len)
74 {
75 	char *buf;
76 
77 	DO_DISQUALIFY_CONST(robuf,buf);
78 
79 	if (u->oerr) {
80 		errno=u->oerr;
81 		return -1;
82 	}
83 	while(len) {
84 		ssize_t r;
85 		if (u->timeout) {
86 			if (handle_timeout(u,1))
87 				return -1;
88 		}
89 		r=u->op.w(u->fd,buf,len);
90 		if (r==-1) {
91 			if (errno==EINTR)
92 				continue;
93 			u->oerr=errno;
94 			return -1; /* maybe data loss */
95 		}
96 		buf+=r;
97 		len-=r;
98 	}
99 	return 0;
100 }
101 
102 static inline int
uoio_do_writev(uoio_t * u,struct iovec * iov,int count)103 uoio_do_writev(uoio_t *u,struct iovec *iov,int count)
104 {
105 	if (u->oerr) {
106 		errno=u->oerr;
107 		return -1;
108 	}
109 	do {
110 		ssize_t r;
111 		if (u->timeout) {
112 			if (handle_timeout(u,1))
113 				return -1;
114 		}
115 		r=u->opv(u->fd,iov,count);
116 		if (r==-1) {
117 			if (errno==EINTR)
118 				continue;
119 			u->oerr=errno;
120 			return -1; /* maybe data loss */
121 		}
122 		while (count && r) {
123 			if (r>=(ssize_t) iov[0].iov_len) {
124 				r-=iov[0].iov_len;
125 				iov[0].iov_len=0;
126 				count--;
127 				iov++;
128 			} else {
129 				iov[0].iov_len-=r;
130 				r=0;
131 			}
132 		}
133 	} while (count);
134 	return 0;
135 }
136 
137 ssize_t
uoio_flush(uoio_t * u)138 uoio_flush(uoio_t *u)
139 {
140 	ssize_t r;
141 	if (u->oerr) {
142 		errno=u->oerr;
143 		return -1;
144 	}
145 	if (!u->buf || u->buflen==0)
146 		return 0;
147 	r=uoio_do_write(u,(const char *)u->buf,u->buflen);
148 	if (r<0)
149 		return r;
150 	u->buflen=0;
151 	return 0;
152 }
153 
154 #define WBUF 4096
155 ssize_t
uoio_write_mem(uoio_t * u,const void * buf,size_t len)156 uoio_write_mem(uoio_t *u, const void *buf,size_t len)
157 {
158 	if (!u->buf) {
159 		u->buf=(char *) malloc(WBUF);
160 		if (u->buf) {
161 			u->bufsize=WBUF;
162 		} else {
163 			/* uh ... */
164 			ssize_t r;
165 			r=uoio_do_write(u,(const char *)buf,len);
166 			if (r<0)
167 				return r;
168 			return len;
169 		}
170 	}
171 	/* can we put it into our buffer? */
172 	if (len+u->buflen<=u->bufsize) {
173 		size_t done=0;
174 		done=u->bufsize-u->buflen;
175 		memcpy(u->buf+u->buflen,buf,len);
176 		u->buflen+=len;
177 		if (u->buflen==u->bufsize) {
178 			ssize_t r;
179 			r=uoio_do_write(u,u->buf,u->buflen);
180 			if (r)
181 				return r;
182 			u->buflen=0;
183 		}
184 		return len;
185 	}
186 	/* ok, too much */
187 	if (u->buflen==0) {
188 		/* easy case */
189 		ssize_t r;
190 		r=uoio_do_write(u,(const char *)buf,len);
191 		if (r<0)
192 			return r;
193 		return len;
194 	}
195 	if (u->opv) {
196 		/* writev the whole garbage */
197 		ssize_t r;
198 		struct iovec iov[2];
199 		char *wbuf;
200 		DO_DISQUALIFY_CONST((const char *)buf,wbuf);
201 		iov[0].iov_base=u->buf;
202 		iov[0].iov_len=u->buflen;
203 		iov[1].iov_base=wbuf;
204 		iov[1].iov_len=len;
205 		r=uoio_do_writev(u,iov,2);
206 		u->buflen=0;
207 		if (r<0)
208 			return r;
209 		return len;
210 	}
211 	/* if it's too much for the rest of this buffer and
212 	 * too much for the next buffer then do it the hard
213 	 * way.
214 	 */
215 	if (len>(u->bufsize-u->buflen)+u->bufsize) {
216 		ssize_t r;
217 		r=uoio_do_write(u,(const char *)u->buf,u->buflen);
218 		if (r<0)
219 			return r;
220 		u->buflen=0;
221 		r=uoio_do_write(u,(const char *)buf,len);
222 		if (r<0)
223 			return r;
224 		return len;
225 	}
226 	/* copy as much as we can into the buffer */
227 	{
228 		size_t done;
229 		ssize_t r;
230 
231 		done=u->bufsize-u->buflen;
232 		if (done)
233 			memcpy(u->buf+u->buflen,buf,done);
234 		r=uoio_do_write(u,u->buf,u->bufsize);
235 		if (r<0)
236 			return r;
237 		memcpy(u->buf,((const char *)buf)+done,len-done);
238 		u->buflen=len-done;
239 		return len;
240 	}
241 }
242 
243 ssize_t
uoio_write_cstr(uoio_t * u,const char * s)244 uoio_write_cstr(uoio_t *u,const char *s)
245 {
246 	return uoio_write_mem(u,s,strlen(s));
247 }
248 
249 ssize_t
uoio_write_char(uoio_t * u,char c)250 uoio_write_char(uoio_t *u,char c)
251 {
252 	/* bye bye performance */
253 	return uoio_write_mem(u,&c,1);
254 }
255 
256 ssize_t
uoio_write_uostr(uoio_t * u,uostr_t * s)257 uoio_write_uostr(uoio_t *u,uostr_t *s)
258 {
259 	return uoio_write_mem(u,s->data,s->len);
260 }
261 
262 ssize_t
uoio_getmem(uoio_t * u,void * vs,size_t len)263 uoio_getmem (uoio_t * u, void *vs, size_t len)
264 {
265 	char *s=(char *) vs;
266 	size_t gotlen=0;
267 	while (1) {
268 		ssize_t r;
269 		if (u->rstart) {
270 			while (len && u->rstart) {
271 				*s++=*u->rstart;
272 				u->rstart++;
273 				gotlen++;
274 				len--;
275 				if (u->rstart == u->buf + u->buflen) {
276 					u->buflen=0;
277 					u->rstart = 0;
278 				}
279 			}
280 			if (!len) return gotlen; /* finished */
281 		}
282 		if (u->eof) return gotlen;
283 		if (!u->buf) {
284 			u->rstart = u->buf = (char *) malloc (READCONST);
285 			if (!u->buf) {
286 				errno = ENOMEM;
287 				return -1;
288 			}
289 			u->bufsize = READCONST;
290 		}
291 		/* read something */
292 		if (u->timeout) {
293 			if (handle_timeout(u,0))
294 				return -1;
295 		}
296 		while (1) {
297 			r = u->op.r (u->fd, u->buf + u->buflen, u->bufsize-u->buflen);
298 			if (r < 0 && errno == EINTR)
299 				continue;
300 			if (r < 0)
301 				return -1;
302 			break;
303 		}
304 		if (r == 0) {
305 			u->eof = 1;
306 			u->rstart=0;
307 		} else
308 			u->rstart=u->buf;
309 		u->buflen += r;
310 	}
311 }
312 ssize_t
uoio_getchar(uoio_t * u,char * s)313 uoio_getchar (uoio_t * u, char *s)
314 {
315 	while (1) {
316 		ssize_t r;
317 		if (u->rstart) {
318 			*s=*u->rstart;
319 			u->rstart++;
320 			if (u->rstart == u->buf + u->buflen) {
321 				u->buflen=0;
322 				u->rstart = 0;
323 			}
324 			return 1;
325 		}
326 		if (u->eof) return 0;
327 		if (!u->buf) {
328 			u->rstart = u->buf = (char *)malloc (READCONST);
329 			if (!u->buf) {
330 				errno = ENOMEM;
331 				return -1;
332 			}
333 			u->bufsize = READCONST;
334 		}
335 		/* read something */
336 		if (u->timeout) {
337 			if (handle_timeout(u,0))
338 				return -1;
339 		}
340 		while (1) {
341 			r = u->op.r (u->fd, u->buf + u->buflen, u->bufsize-u->buflen);
342 			if (r < 0 && errno == EINTR)
343 				continue;
344 			if (r < 0)
345 				return -1;
346 			break;
347 		}
348 		if (r == 0) {
349 			u->eof = 1;
350 			u->rstart=0;
351 		} else {
352 			u->buflen += r;
353 			u->rstart=u->buf;
354 		}
355 	}
356 }
357 ssize_t
uoio_getdelim_zc(uoio_t * u,char ** s,int delim)358 uoio_getdelim_zc (uoio_t * u, char **s, int delim)
359 {
360 	while (1) {
361 		if (u->rstart) {
362 			char *p;
363 			char *e;
364 			for (p=u->rstart, e=u->buf+u->buflen; p!=e && *p!=delim; p++) /* nothing */;
365 			if (p!=e) {
366 				ssize_t ret;
367 				/* found delim in unread part of the buffer */
368 				*s = u->rstart;
369 				ret = p - u->rstart + 1;
370 				if (p >= u->buf + u->buflen -1) {
371 					u->rstart = 0;
372 					u->buflen = 0;
373 				} else
374 					u->rstart = p + 1;
375 /* write(1,*s,ret); */
376 				return ret;
377 			}
378 			/* there is something in the buffer, but not enough.
379 			 * shuffle around if on the first block.
380 			 */
381 			if (u->rstart != u->buf && !SAMEBLOCK(u->rstart,u->buf)) {
382 /* write(1,*s,ret); */
383 				size_t l = ULEFT (u);
384 				if (u->buf+l>=u->rstart)
385 					memcpy (u->buf, u->rstart, l);
386 				else
387 					memmove (u->buf, u->rstart, l);
388 				u->rstart=u->buf;
389 				u->buflen = l;
390 			}
391 			/* now get something more ... */
392 
393 			/* if buffer is full get a new block */
394 			/* if buffer is nearly full get a new block plus something */
395 			if (u->bufsize - u->buflen < 1024) {
396 				size_t l;
397 				size_t o;
398 				l = u->bufsize + READCONST;
399 				o = (u->rstart - u->buf);
400 				p = (char *) realloc (u->buf, l);
401 				if (!p) {
402 					errno = ENOMEM;
403 					return -1;
404 				}
405 				u->bufsize = l;
406 				u->buf = p;
407 				u->rstart = p + o;
408 			}
409 			/* ok, enough place to read something */
410 		} else if (!u->buf) {
411 			u->rstart = u->buf = (char *) malloc (READCONST);
412 			if (!u->buf) {
413 				errno = ENOMEM;
414 				return -1;
415 			}
416 			u->bufsize = READCONST;
417 		}
418 		if (u->eof) {
419 			/* we didn't find the character. return the rest of the buffer */
420 			if (!u->rstart) {
421 				*s = NULL;
422 				return 0;
423 			} else {
424 				size_t l = ULEFT (u);
425 				*s = u->rstart;
426 				u->rstart = 0;
427 				return l;
428 			}
429 		}
430 		/* read something */
431 		{
432 			ssize_t r;
433 			if (u->timeout) {
434 				if (handle_timeout(u,0))
435 					return -1;
436 			}
437 			while (1) {
438 				r = u->op.r (u->fd, u->buf + u->buflen, u->bufsize-u->buflen);
439 				if (r < 0 && errno == EINTR)
440 					continue;
441 				if (r < 0)
442 					return -1;
443 				break;
444 			}
445 			if (r == 0)
446 				u->eof = 1;
447 			u->buflen += r;
448 			if (!u->rstart) u->rstart=u->buf;
449 		}
450 	}
451 	/* not reached */
452 }
453 
454 
455 void
uoio_assign_r(uoio_t * u,int fd,ssize_t (* op)(int,void *,size_t),ssize_t (* opv)(int fd,const struct iovec * vector,int count))456 uoio_assign_r(uoio_t *u,int fd,
457         ssize_t (*op)(int,void *,size_t),
458 	ssize_t (*opv)(int fd, const struct iovec * vector, int count))
459 {
460 	memset(u,0,sizeof(*u));
461 	u->fd = fd;
462 	u->op.r = op;
463 	u->opv = opv;
464 	u->buf = 0;
465 	u->rstart = 0;
466 	u->buflen = 0;
467 	u->bufsize = 0;
468 	u->eof = 0;
469 	u->oerr = 0;
470 	u->timeout = 0;
471 }
472 
473 
474 void
uoio_assign_w(uoio_t * u,int fd,ssize_t (* op)(int,const void *,size_t),ssize_t (* opv)(int fd,const struct iovec * vector,int count))475 uoio_assign_w(uoio_t *u,int fd,
476         ssize_t (*op)(int,const void *,size_t),
477 	ssize_t (*opv)(int fd, const struct iovec * vector, int count))
478 {
479 	memset(u,0,sizeof(*u));
480 	u->fd = fd;
481 	u->op.w = op;
482 	u->opv = opv;
483 	u->buf = 0;
484 	u->rstart = 0;
485 	u->buflen = 0;
486 	u->bufsize = 0;
487 	u->eof = 0;
488 	u->oerr = 0;
489 	u->timeout = 0;
490 }
491