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