1 /*
2 * Copyright (C) 2006-2008 Claus-Justus Heine
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, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 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; see the file COPYING. If not, write to the
16 Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 * This file implements setpos()/getpos() facilities for non-seekable
19 * files by generating a secondary dynamic file buffering of arbitrary
20 * size on top of stdio.
21 */
22
23 /* As a special exception the author of this files permits the
24 * distribution of this software with Geomview. As long as this file
25 * is distributed with Geomview, the conditions of the GNU Lesser
26 * Public Licence apply. See the file "COPYING" in the top-level
27 * directory.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #define _FILE_OFFSET_BITS 64
35 #define _LARGEFILE64_SOURCE
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42 #include <errno.h>
43
44 #if HAVE_FCNTL_H && HAVE_FCNTL
45 # include <fcntl.h>
46
47 static const int o_nonblock =
48 #ifdef O_NONBLOCK
49 O_NONBLOCK |
50 #endif
51 #ifdef O_NDELAY
52 O_NDELAY |
53 #endif
54 #ifdef FNONBLK
55 FNONBLK |
56 #endif
57 #ifdef FNDELAY
58 FNDELAY |
59 #endif
60 0;
61
62 #if !defined(O_NONBLOCK) && !defined(O_NDELAY) \
63 && !defined(FNONBLK) && !defined(FNDELAY)
64 # error Do not know how to achieve non-blocking IO
65 #endif
66
67 #endif
68
69 #if HAVE_OFF64_T && HAVE_LSEEK64
70 # define HAVE_SOME_LSEEK 1
71 typedef off64_t offset_t;
72 # define LSEEK lseek64
73 # ifndef HAVE_DECL_LSEEK64
74 extern off64_t lseek64(int fd, off64_t offset, int whence);
75 # endif
76 #elif HAVE_LOFF_T && HAVE_LLSEEK
77 # define HAVE_SOME_LSEEK 1
78 typedef loff_t offset_t;
79 # define LSEEK llseek
80 # ifndef HAVE_DECL_LLSEEK
81 loff_t llseek(int fd, loff_t offset, int whence);
82 # endif
83 #elif HAVE_OFF_T && HAVE_LSEEK
84 # define HAVE_SOME_LSEEK 1
85 typedef off_t offset_t;
86 # define LSEEK lseek
87 # ifndef HAVE_DECL_LSEEK
88 off_t lseek(int fd, off_t offset, int whence);
89 # endif
90 #endif
91
92 #define POSIX_SHORTCUT (HAVE_READ && HAVE_SOME_LSEEK)
93
94 #include "iobuffer.h"
95
96 #ifndef DEBUG
97 # define DEBUG 0
98 #endif
99
100 #ifndef min
101 # define min(a, b) (((a) <= (b)) ? (a) : (b))
102 #endif
103
104 #ifndef BUFSIZ
105 # define BUFFER_SIZE 8192
106 #else
107 # define BUFFER_SIZE BUFSIZ
108 #endif
109
110 typedef struct IOBuffer
111 {
112 struct IOBuffer *next;
113 char buffer[BUFFER_SIZE];
114 } IOBuffer;
115
116 typedef struct IOBufferList
117 {
118 IOBuffer *buf_head; /**< Head of linked list of buffers */
119 IOBuffer *buf_tail; /**< Pointer to tail of buffer list */
120 IOBuffer *buf_ptr; /**< Pointer to current buffer */
121 size_t buf_pos; /**< Byte pos into buf_ptr */
122 size_t tail_size; /**< Size used inside *buf_ptr */
123 size_t tot_pos;
124 size_t tot_size;
125 } IOBLIST;
126
127 struct IOBFILE
128 {
129 FILE *istream; /**< The underlying stdio FILE */
130 IOBLIST ioblist;
131 IOBLIST ioblist_mark;
132 int can_seek:1;
133 int mark_wrap:1; /**< Set when the buffer no longer covers the
134 position of the mark. */
135 int mark_set:1;
136 int eof:2;
137 int ungetc;
138 fpos_t stdiomark; /**< stdio mark interface */
139 #if POSIX_SHORTCUT
140 offset_t posixmark; /**< using read(), write() and a flavour of lseek() */
141 #endif
142 size_t mark_pos; /**< Offset into our buffer structures (pipe, tty) */
143 int mark_ungetc; /**< Copy of what ungetc was when setmark() was called
144 */
145 int fd;
146 #if HAVE_FCNTL
147 int fflags;
148 #endif
149 #if DEBUG
150 size_t read_count;
151 #endif
152 };
153
154 #if DEBUG
155 static int n_total_buffers;
156 static int max_total_buffers;
157 static int n_fopen;
158 static int max_fopen;
159 static int n_fileopen;
160 static int max_fileopen;
161 static int n_popen;
162 static int max_popen;
163 #endif
164
iob_release_buffer(IOBLIST * ioblist)165 static void iob_release_buffer(IOBLIST *ioblist)
166 {
167 IOBuffer *iob, *prev;
168
169 prev = ioblist->buf_head;
170 iob = prev->next;
171 prev->next = NULL;
172 while (iob) {
173 prev = iob;
174 iob = iob->next;
175 free(prev);
176 #if DEBUG
177 n_total_buffers --;
178 #endif
179 }
180 memset(ioblist, 0, sizeof(*ioblist));
181 }
182
iob_init_buffer(IOBLIST * ioblist)183 static void iob_init_buffer(IOBLIST *ioblist)
184 {
185 ioblist->buf_head = malloc(sizeof(IOBuffer));
186 #if DEBUG
187 n_total_buffers ++;
188 if (n_total_buffers > max_total_buffers) {
189 max_total_buffers = n_total_buffers;
190 fprintf(stderr, "MAX BUFFERS: %d\n", max_total_buffers);
191 }
192 #endif
193 ioblist->buf_head->next = ioblist->buf_head;
194
195 ioblist->buf_ptr = ioblist->buf_head;
196 ioblist->buf_tail = ioblist->buf_head;
197
198 ioblist->tot_pos = 0;
199 ioblist->tot_size = 0;
200 ioblist->tail_size = 0;
201 ioblist->buf_pos = 0;
202 }
203
iob_copy_buffer(IOBLIST * to,IOBLIST * from)204 static void iob_copy_buffer(IOBLIST *to, IOBLIST *from)
205 {
206 IOBuffer *iob;
207
208 iob_init_buffer(to);
209 for (iob = from->buf_head; iob->next != from->buf_head; iob = iob->next) {
210 if (iob == from->buf_ptr) {
211 to->buf_ptr = to->buf_tail;
212 }
213 memcpy(to->buf_tail->buffer, iob->buffer, BUFFER_SIZE);
214 to->buf_tail->next = malloc(sizeof(IOBuffer));
215 #if DEBUG
216 n_total_buffers ++;
217 if (n_total_buffers > max_total_buffers) {
218 max_total_buffers = n_total_buffers;
219 fprintf(stderr, "MAX BUFFERS: %d\n", max_total_buffers);
220 }
221 #endif
222 to->buf_tail = to->buf_tail->next;
223 to->buf_tail->next = to->buf_head;
224 }
225 to->tot_pos = from->tot_pos;
226 to->tot_size = from->tot_size;
227 to->tail_size = from->tail_size;
228 to->buf_pos = from->buf_pos;
229 }
230
iobfile(IOBFILE * iobf)231 FILE *iobfile(IOBFILE *iobf)
232 {
233 return iobf ? iobf->istream : NULL;
234 }
235
iobfileno(IOBFILE * iobf)236 int iobfileno(IOBFILE *iobf)
237 {
238 return iobf ? iobf->fd : -1;
239 }
240
iobfileopen(FILE * istream)241 IOBFILE *iobfileopen(FILE *istream)
242 {
243 IOBFILE *iobf;
244
245 if (istream == NULL) {
246 return NULL;
247 }
248
249 iobf = calloc(1, sizeof(IOBFILE));
250 iobf->istream = istream;
251 iobf->fd = fileno(istream);
252 iobf->ungetc = EOF;
253
254 if (iobf->fd >= 0) {
255 /* Determine whether we have file positioning support */
256 if (lseek(iobf->fd, 0, SEEK_CUR) != -1 && !isatty(iobf->fd)) {
257 iobf->can_seek = -1;
258 }
259
260 /* If we have read(2) and write(2), then we disable stdio
261 * completely and use the system calls directly.
262 */
263 #if SETVBUF_REVERSED
264 setvbuf(istream, _IONBF, NULL, 0);
265 #else
266 setvbuf(istream, NULL, _IONBF, 0);
267 #endif
268 #if HAVE_FCNTL
269 iobf->fflags = fcntl(iobf->fd, F_GETFL);
270 if (iobf->fflags != -1 && (iobf->fflags & o_nonblock)) {
271 iobf->fflags &= ~o_nonblock;
272 if (fcntl(iobf->fd, F_SETFL, iobf->fflags) < 0) {
273 fprintf(stderr, "iobfileopen(): unable to clear O_NONBLOCK: \"%s\"\n",
274 strerror(errno));
275 }
276 }
277 } else {
278 iobf->fflags = -1;
279 #endif
280 }
281
282 iob_init_buffer(&iobf->ioblist);
283
284 iobf->ungetc = EOF;
285
286 #if DEBUG
287 n_fileopen++;
288 if (n_fileopen > max_fileopen) {
289 max_fileopen = n_fileopen;
290 fprintf(stderr, "max fileopen: %d\n", max_fileopen);
291 }
292 #endif
293
294 return iobf;
295 }
296
iobfopen(const char * name,const char * mode)297 IOBFILE *iobfopen(const char *name, const char *mode)
298 {
299 FILE *stream;
300
301 if (strchr(mode, 'a') != NULL || strchr(mode, 'w') != NULL) {
302 fprintf(stderr, "iobfopen(): Write mode is unsupported\n");
303 return NULL;
304 }
305 stream = fopen(name, mode);
306 if (stream == NULL)
307 return NULL;
308
309 #if DEBUG
310 n_fopen++;
311 if (n_fopen > max_fopen) {
312 max_fopen = n_fopen;
313 fprintf(stderr, "max fopen: %d\n", max_fopen);
314 }
315 #endif
316
317 return iobfileopen(stream);
318 }
319
iobfileclose(IOBFILE * iobf)320 int iobfileclose(IOBFILE *iobf)
321 {
322 iob_release_buffer(&iobf->ioblist);
323 if (iobf->ioblist_mark.buf_head) {
324 iob_release_buffer(&iobf->ioblist_mark);
325 }
326 free(iobf);
327
328 #if DEBUG
329 n_fileopen--;
330 #endif
331
332 return 0;
333 }
334
iobfclose(IOBFILE * iobf)335 int iobfclose(IOBFILE *iobf)
336 {
337 int result;
338
339 result = fclose(iobf->istream);
340
341 (void)iobfileclose(iobf);
342
343 #if DEBUG
344 n_fopen--;
345 #endif
346
347 return result;
348 }
349
350 #if HAVE_POPEN
iobpopen(const char * cmd,const char * mode)351 IOBFILE *iobpopen(const char *cmd, const char *mode)
352 {
353 FILE *stream;
354
355 if (strchr(mode, 'a') != NULL || strchr(mode, 'w') != NULL) {
356 fprintf(stderr, "iobfopen(): Write mode is unsupported\n");
357 return NULL;
358 }
359 stream = popen(cmd, mode);
360 if (stream == NULL)
361 return NULL;
362
363 #if DEBUG
364 n_popen++;
365 if (n_popen > max_popen) {
366 max_popen = n_popen;
367 fprintf(stderr, "max popen: %d\n", max_popen);
368 }
369 #endif
370
371 return iobfileopen(stream);
372 }
373
iobpclose(IOBFILE * iobf)374 int iobpclose(IOBFILE *iobf)
375 {
376 int result;
377
378 result = pclose(iobf->istream);
379
380 (void)iobfileclose(iobf);
381
382 #if DEBUG
383 n_popen--;
384 #endif
385
386 return result;
387 }
388 #endif
389
iobftell(IOBFILE * iobf)390 long iobftell(IOBFILE *iobf)
391 {
392 long pos;
393
394 if (!iobf->can_seek) {
395 return ~0L;
396 }
397 #if POSIX_SHORTCUT
398 pos = (long)LSEEK(iobf->fd, 0, SEEK_CUR);
399 #else
400 pos = ftell(iobf->istream);
401 #endif
402
403 return pos < 0
404 ? pos
405 : (long)(pos - (iobf->ioblist.tot_size - iobf->ioblist.tot_pos));
406 }
407
iobfseek(IOBFILE * iobf,long offset,int whence)408 int iobfseek(IOBFILE *iobf, long offset, int whence)
409 {
410 if (iobf->can_seek) {
411 #if POSIX_SHORTCUT
412 if ((long)LSEEK(iobf->fd, (offset_t)offset, whence) < 0) {
413 return -1;
414 }
415 #else
416 if (fseek(iobf->istream, offset, whence) != 0) {
417 return -1;
418 }
419 #endif
420 iob_release_buffer(&iobf->ioblist);
421 iob_init_buffer(&iobf->ioblist);
422 return 0;
423 }
424 return -1;
425 }
426
iobfgets(char * s,int size,IOBFILE * iobf)427 char *iobfgets(char *s, int size, IOBFILE *iobf)
428 {
429 char *p = s;
430 int c = 0;
431
432 while (--size) {
433 *p++ = c = iobfgetc(iobf);
434 if (c == '\n' || c == EOF) {
435 break;
436 }
437 }
438 if (c == EOF) {
439 p--;
440 }
441 *p = '\0';
442 if (s == p && size) {
443 return NULL;
444 } else {
445 return s;
446 }
447 }
448
449 /* Copy the buffer contents between tot_pos and tot_pos - size t
450 * ptr
451 */
iobfgetbuffer(IOBFILE * iobf,void * ptr,size_t size,int direction)452 size_t iobfgetbuffer(IOBFILE *iobf, void *ptr, size_t size, int direction)
453 {
454 IOBLIST *ioblist = &iobf->ioblist;
455 IOBuffer *iob;
456 int skip, i;
457 size_t offset, cpsz, rval, tot_space = ioblist->tot_size - ioblist->tot_pos;
458 char *buf = ptr;
459
460 if (iobf->ungetc != EOF) {
461 ++tot_space;
462 }
463
464 if (ptr == NULL) {
465 return direction < 0 ? ioblist->tot_pos : tot_space;
466 }
467
468 if (direction < 0) {
469 rval = size = min(size, ioblist->tot_pos);
470 skip = (ioblist->tot_pos - size) / BUFFER_SIZE;
471 for (i = 0, iob = ioblist->buf_head; i < skip; iob = iob->next, i++);
472 offset = (ioblist->tot_pos - size) % BUFFER_SIZE;
473 cpsz = min(size, BUFFER_SIZE - offset);
474 memcpy(buf, iob->buffer + offset, cpsz);
475 buf += cpsz;
476 size -= cpsz;
477 while (size) {
478 iob = iob->next;
479 cpsz = min(size, BUFFER_SIZE);
480 memcpy(buf, iob->buffer, cpsz);
481 buf += cpsz;
482 size -= cpsz;
483 }
484 } else {
485 rval = size = min(size, tot_space);
486 if (size > 0 && iobf->ungetc != EOF) {
487 *buf++ = iobf->ungetc;
488 --size;
489 }
490 iob = ioblist->buf_ptr;
491 offset = ioblist->buf_pos;
492 cpsz = min(size, BUFFER_SIZE - offset);
493 memcpy(buf, iob->buffer + offset, cpsz);
494 size -= cpsz;
495 buf += cpsz;
496 while (size) {
497 iob = iob->next;
498 cpsz = min(size, BUFFER_SIZE);
499 memcpy(buf, iob->buffer, cpsz);
500 buf += cpsz;
501 size -= cpsz;
502 }
503 }
504 return rval;
505 }
506
507 static size_t
iobfread_buffer(void * ptr,size_t size,IOBFILE * iobf)508 iobfread_buffer(void *ptr, size_t size, IOBFILE *iobf)
509 {
510 IOBLIST *ioblist = &iobf->ioblist;
511 size_t tot_space, rq_sz, rq_sz_pos, rd_sz;
512 char *buf = ptr;
513
514 tot_space = ioblist->tot_size - ioblist->tot_pos;
515 if (iobf->ungetc != EOF) {
516 ++tot_space;
517 }
518 rq_sz = min(size, tot_space);
519
520 if (rq_sz == 0) {
521 return 0;
522 }
523
524 rd_sz = 0;
525
526 if (iobf->ungetc != EOF) {
527 buf[0] = iobf->ungetc & 0xff;
528 iobf->ungetc = EOF;
529 ++buf;
530 ++rd_sz;
531 --rq_sz;
532 #if DEBUG
533 --iobf->read_count;
534 #endif
535 }
536
537 while (rq_sz) {
538 rq_sz_pos = min(rq_sz, BUFFER_SIZE - ioblist->buf_pos);
539 memcpy(buf, ioblist->buf_ptr->buffer + ioblist->buf_pos, rq_sz_pos);
540 ioblist->buf_pos += rq_sz_pos;
541 ioblist->tot_pos += rq_sz_pos;
542 buf += rq_sz_pos;
543 rd_sz += rq_sz_pos;
544 rq_sz -= rq_sz_pos;
545 if (ioblist->buf_pos == BUFFER_SIZE &&
546 ioblist->buf_ptr != ioblist->buf_tail) {
547 /* advance to next buffer */
548 ioblist->buf_ptr = ioblist->buf_ptr->next;
549 ioblist->buf_pos = 0;
550 if ((!iobf->mark_set || iobf->can_seek) &&
551 ioblist->buf_head->next->next != ioblist->buf_head) {
552 /* Release buffers no longer needed. */
553 ioblist->buf_tail->next = ioblist->buf_head->next;
554 free(ioblist->buf_head);
555 #if DEBUG
556 n_total_buffers --;
557 #endif
558 ioblist->buf_head = ioblist->buf_tail->next;
559 ioblist->tot_pos -= BUFFER_SIZE;
560 ioblist->tot_size -= BUFFER_SIZE;
561 }
562 }
563 }
564 return rd_sz;
565 }
566
iob_check_space(IOBFILE * iobf)567 static void iob_check_space(IOBFILE *iobf)
568 {
569 IOBLIST *ioblist = &iobf->ioblist;
570
571 if (ioblist->tail_size < BUFFER_SIZE) {
572 return;
573 }
574
575 if ((!iobf->can_seek && iobf->mark_set) ||
576 ioblist->buf_head->next == ioblist->buf_head) {
577 /* allocate up to two buffers in normal operation and as many as
578 * needed for files without seek capabilities.
579 */
580 ioblist->buf_tail->next = malloc(sizeof(IOBuffer));
581 #if DEBUG
582 n_total_buffers ++;
583 if (n_total_buffers > max_total_buffers) {
584 max_total_buffers = n_total_buffers;
585 fprintf(stderr, "MAX BUFFERS: %d\n", max_total_buffers);
586 }
587 #endif
588 ioblist->buf_tail = ioblist->buf_tail->next;
589 ioblist->buf_tail->next = ioblist->buf_head;
590 ioblist->tail_size = 0;
591 } else {
592 /* rotate list */
593 ioblist->buf_tail = ioblist->buf_tail->next;
594 ioblist->buf_head = ioblist->buf_head->next;
595 ioblist->tot_pos -= BUFFER_SIZE;
596 ioblist->tot_size -= BUFFER_SIZE;
597 ioblist->tail_size = 0;
598 iobf->mark_wrap = ~0;
599 }
600 }
601
602 /* Flush the buffer as much as possible, but we have to be careful not
603 * to discard data if tot_pos < tot_size. In this case we discard all
604 * data < tot_pos and leave the rest as is.
605 */
iob_flush_buffer(IOBLIST * ioblist)606 static void iob_flush_buffer(IOBLIST *ioblist)
607 {
608 while (ioblist->buf_head != ioblist->buf_ptr) {
609 ioblist->buf_tail->next = ioblist->buf_head->next;
610 free(ioblist->buf_head);
611 #if DEBUG
612 n_total_buffers --;
613 #endif
614 ioblist->buf_head = ioblist->buf_tail->next;
615 ioblist->tot_pos -= BUFFER_SIZE;
616 ioblist->tot_size -= BUFFER_SIZE;
617 }
618 /* Check for the special case where we have one and only one buffer
619 * and tot_pos points to the very end of it. In this case, discard
620 * this buffer, too. The other possibility would be to allocate a
621 * second buffer.
622 */
623 if (ioblist->buf_head == ioblist->buf_head->next &&
624 ioblist->tot_pos == BUFFER_SIZE) {
625 #if 1
626 ioblist->tot_pos =
627 ioblist->tot_size =
628 ioblist->buf_pos =
629 ioblist->tail_size = 0;
630 #else
631 ioblist->buf_tail->next = malloc(sizeof(IOBuffer));
632 #if DEBUG
633 n_total_buffers ++;
634 if (n_total_buffers > max_total_buffers) {
635 max_total_buffers = n_total_buffers;
636 fprintf(stderr, "MAX BUFFERS: %d\n", max_total_buffers);
637 }
638 #endif
639 ioblist->buf_tail = ioblist->buf_tail->next;
640 ioblist->buf_tail->next = ioblist->buf_head;
641 ioblist->tail_size =
642 ioblist->buf_pos = 0;
643 #endif
644 }
645 }
646
iobfrewind(IOBFILE * iobf)647 void iobfrewind(IOBFILE *iobf)
648 {
649 rewind(iobf->istream);
650 #if 0
651 if (1 || iobf->can_seek) {
652 iob_release_buffer(&iobf->ioblist);
653 iob_init_buffer(&iobf->ioblist);
654 } else {
655 iobf->ioblist.tot_pos =
656 iobf->ioblist.buf_pos = 0;
657 iobf->ioblist.buf_ptr =
658 iobf->ioblist.buf_head;
659 }
660 #else
661 /* The fastest way is: simply move the position pointer to end of
662 buffer */
663 iobf->ioblist.tot_pos = iobf->ioblist.tot_size;
664 iobf->ioblist.buf_pos = iobf->ioblist.tail_size;
665 #endif
666 if (iobf->ioblist_mark.buf_head) {
667 iob_release_buffer(&iobf->ioblist_mark);
668 }
669 iobf->mark_set = 0;
670 iobf->mark_wrap = 0;
671 iobf->mark_pos = ~0;
672 memset(&iobf->stdiomark, ~0, sizeof(iobf->stdiomark));
673 iobf->posixmark = -1;
674
675 /* Clear status flags */
676 iobf->ungetc = EOF;
677 iobf->eof = 0;
678 }
679
iobfread(void * ptr,size_t size,size_t nmemb,IOBFILE * iobf)680 size_t iobfread(void *ptr, size_t size, size_t nmemb, IOBFILE *iobf)
681 {
682 IOBLIST *ioblist = &iobf->ioblist;
683 size_t rq_size = size * nmemb, rd_size, rd_tot;
684 ssize_t tail_rd;
685 char *buf = ptr;
686 #if HAVE_FCNTL
687 int first = 1;
688 int fcntl_err = 0;
689 #endif
690 int cnt;
691
692 if (size*nmemb == 0) {
693 return 0;
694 }
695
696 rd_tot = 0;
697 tail_rd = ~0;
698 cnt = 0;
699 do {
700 ++cnt;
701 rd_size = iobfread_buffer(buf, rq_size, iobf);
702 rq_size -= rd_size;
703 rd_tot += rd_size;
704 buf += rd_size;
705 if (iobf->eof && rq_size) {
706 iobf->eof = -1;
707 break;
708 }
709 if (tail_rd && rq_size && !iobf->eof) {
710 ssize_t tail_space;
711
712 iob_check_space(iobf);
713 tail_space = BUFFER_SIZE - ioblist->tail_size;
714 #if HAVE_FCNTL
715 if (!iobf->can_seek) {
716 if (first && iobf->fflags != -1) {
717 fcntl_err = fcntl(iobf->fd, F_SETFL, iobf->fflags | o_nonblock);
718 }
719 if (!first || (iobf->fd >= 0 && iobf->fflags == -1) || fcntl_err) {
720 tail_space = min(tail_space, (ssize_t)rq_size);
721 }
722 }
723 #else
724 if (!iobf->can_seek && iobf->fd >= 0)
725 tail_space = min(tail_space, rq_size);
726 #endif
727 #if POSIX_SHORTCUT
728 if (iobf->fd >= 0) {
729 tail_rd = read(iobf->fd,
730 ioblist->buf_tail->buffer + ioblist->tail_size,
731 tail_space);
732 if (tail_rd < 0) {
733 if (errno == EAGAIN) {
734 tail_rd = 0; /* probably a pipe */
735 } else {
736 tail_rd = 0; /* maybe do something else in this case */
737 }
738 } else {
739 /* EOF is indicated by returning 0 for a request size > 0 */
740 if (tail_rd == 0 && tail_space > 0) {
741 iobf->eof = 1; /* eof is a counter! */
742 }
743 }
744 ioblist->tail_size += tail_rd;
745 ioblist->tot_size += tail_rd;
746 } else {
747 #endif
748 tail_rd = fread(ioblist->buf_tail->buffer + ioblist->tail_size,
749 1, tail_space, iobf->istream);
750
751 ioblist->tail_size += tail_rd;
752 ioblist->tot_size += tail_rd;
753 if (tail_rd < tail_space && feof(iobf->istream)) {
754 iobf->eof = 1;
755 }
756 }
757 #if HAVE_FCNTL
758 if (!iobf->can_seek && first && iobf->fflags != -1 && !fcntl_err) {
759 first = 0;
760 clearerr(iobf->istream);
761 if ((fcntl_err = fcntl(iobf->fd, F_SETFL, iobf->fflags))
762 < 0) {
763 fprintf(stderr, "iobfread(): unable to clear O_NONBLOCK: \"%s\"\n",
764 strerror(errno));
765 }
766 if (tail_rd == 0 && rq_size) {
767 tail_rd = ~0; /* retry with blocking IO */
768 iobf->eof = 0;
769 }
770 }
771 #endif
772 }
773 } while (tail_rd && rq_size);
774 #if DEBUG
775 iobf->read_count += rd_tot;
776 #endif
777 return rd_tot / size;
778 }
779
iobfgetc(IOBFILE * iobf)780 int iobfgetc(IOBFILE *iobf)
781 {
782 int c = EOF;
783 unsigned char c_char;
784
785 if (iobf->eof != -1 && iobfread(&c_char, 1, 1, iobf) == 1) {
786 c = c_char;
787 }
788
789 return c;
790 }
791
iobfsetmark(IOBFILE * iobf)792 int iobfsetmark(IOBFILE *iobf)
793 {
794 IOBLIST *ioblist = &iobf->ioblist;
795 int result = 0;
796
797 if (iobf->mark_set) {
798 iobfclearmark(iobf);
799 }
800
801 /* FIXME: generate error if EOF condition is set? */
802 if (iobf->eof == -1) {
803 return -1;
804 }
805
806 iob_flush_buffer(ioblist);
807
808 iobf->mark_set = ~0;
809 iobf->mark_wrap = 0;
810 iobf->mark_pos = ioblist->tot_pos;
811 iobf->mark_ungetc = iobf->ungetc;
812
813 if (iobf->can_seek) {
814 #if POSIX_SHORTCUT
815 if ((iobf->posixmark = LSEEK(iobf->fd, 0, SEEK_CUR)) < 0) {
816 result = -1;
817 }
818 #else
819 result = fgetpos(iobf->istream, &iobf->stdiomark);
820 #endif
821 iob_copy_buffer(&iobf->ioblist_mark, &iobf->ioblist);
822 }
823
824 return result;
825 }
826
iobfseekmark(IOBFILE * iobf)827 int iobfseekmark(IOBFILE *iobf)
828 {
829 IOBLIST *ioblist;
830
831 if (!iobf->mark_set) {
832 return -1;
833 }
834
835 if (iobf->mark_wrap) { /* implies can_seek */
836 #if POSIX_SHORTCUT
837 if (LSEEK(iobf->fd, iobf->posixmark, SEEK_SET) != iobf->posixmark) {
838 return -1;
839 }
840 #else
841 if (fsetpos(iobf->istream, &iobf->stdiomark) != 0) {
842 return -1;
843 }
844 #endif
845 iob_release_buffer(&iobf->ioblist);
846 iob_copy_buffer(&iobf->ioblist, &iobf->ioblist_mark);
847 iobf->mark_wrap = 0;
848 }
849
850 ioblist = &iobf->ioblist;
851
852 ioblist->buf_ptr = ioblist->buf_head;
853 ioblist->tot_pos = iobf->mark_pos;
854 ioblist->buf_pos = iobf->mark_pos % BUFFER_SIZE;
855
856 iobf->ungetc = iobf->mark_ungetc;
857
858 /* Clear status flags on success */
859 if (iobf->eof == -1) {
860 iobf->eof = 1;
861 }
862 #if DEBUG
863 iobf->read_count = ioblist->tot_pos;
864 #endif
865 return 0;
866 }
867
iobfclearmark(IOBFILE * iobf)868 int iobfclearmark(IOBFILE *iobf)
869 {
870 if (!iobf->mark_set) {
871 return -1;
872 }
873
874 iobf->mark_set = 0;
875 iobf->mark_wrap = 0;
876
877 if (iobf->ioblist_mark.buf_head != NULL) {
878 iob_release_buffer(&iobf->ioblist_mark);
879 }
880
881 return 0;
882 }
883
iobfungetc(int c,IOBFILE * iobf)884 int iobfungetc(int c, IOBFILE *iobf)
885 {
886 if (c == EOF) { /* ??? */
887 iobf->ungetc = EOF;
888 } else {
889 iobf->ungetc = c & 0xff;
890 if (iobf->eof == -1){
891 iobf->eof = 1;
892 }
893 }
894 return c;
895 }
896
iobfeof(IOBFILE * iobf)897 int iobfeof(IOBFILE *iobf)
898 {
899 if (iobf->ungetc != EOF) {
900 return 0;
901 } else if (iobf->ioblist.tot_pos < iobf->ioblist.tot_size) {
902 return 0;
903 } else if (iobf->eof == -1) {
904 if (POSIX_SHORTCUT && iobf->fd >= 0) {
905 return 1;
906 } else if (feof(iobf->istream)) {
907 return 1;
908 }
909 iobf->eof = 0;
910 return 0;
911 } else {
912 return 0;
913 }
914 }
915
916 /*
917 * Local Variables: ***
918 * c-basic-offset: 2 ***
919 * End: ***
920 */
921