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