Sat Nov 18 16:46:01 1995 Ulrich Drepper <drepper@gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / libio / fileops.c
1 /* 
2 Copyright (C) 1993, 1995 Free Software Foundation
3
4 This file is part of the GNU IO Library.  This library is free
5 software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING.  If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 As a special exception, if you link this library with files
20 compiled with a GNU compiler to produce an executable, this does not cause
21 the resulting executable to be covered by the GNU General Public License.
22 This exception does not however invalidate any other reasons why
23 the executable file might be covered by the GNU General Public License. */
24
25 /*  written by Per Bothner (bothner@cygnus.com) */
26
27 #define _POSIX_SOURCE
28 #include "libioP.h"
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <errno.h>
34 #ifndef errno
35 extern int errno;
36 #endif
37
38 /* An fstream can be in at most one of put mode, get mode, or putback mode.
39    Putback mode is a variant of get mode.
40
41    In a filebuf, there is only one current position, instead of two
42    separate get and put pointers.  In get mode, the current posistion
43    is that of gptr(); in put mode that of pptr().
44
45    The position in the buffer that corresponds to the position
46    in external file system is file_ptr().
47    This is normally _IO_read_end, except in putback mode,
48    when it is _IO_save_end.
49    If the field _fb._offset is >= 0, it gives the offset in
50    the file as a whole corresponding to eGptr(). (?)
51
52    PUT MODE:
53    If a filebuf is in put mode, pbase() is non-NULL and equal to base().
54    Also, epptr() == ebuf().
55    Also, eback() == gptr() && gptr() == egptr().
56    The un-flushed character are those between pbase() and pptr().
57    GET MODE:
58    If a filebuf is in get or putback mode, eback() != egptr().
59    In get mode, the unread characters are between gptr() and egptr().
60    The OS file position corresponds to that of egptr().
61    PUTBACK MODE:
62    Putback mode is used to remember "excess" characters that have
63    been sputbackc'd in a separate putback buffer.
64    In putback mode, the get buffer points to the special putback buffer.
65    The unread characters are the characters between gptr() and egptr()
66    in the putback buffer, as well as the area between save_gptr()
67    and save_egptr(), which point into the original reserve buffer.
68    (The pointers save_gptr() and save_egptr() are the values
69    of gptr() and egptr() at the time putback mode was entered.)
70    The OS position corresponds to that of save_egptr().
71    
72    LINE BUFFERED OUTPUT:
73    During line buffered output, pbase()==base() && epptr()==base().
74    However, ptr() may be anywhere between base() and ebuf().
75    This forces a call to filebuf::overflow(int C) on every put.
76    If there is more space in the buffer, and C is not a '\n',
77    then C is inserted, and pptr() incremented.
78    
79    UNBUFFERED STREAMS:
80    If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
81 */
82
83 #define CLOSED_FILEBUF_FLAGS \
84   (_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET)
85
86
87 void
88 DEFUN(_IO_file_init, (fp),
89       register _IO_FILE *fp)
90 {
91   /* POSIX.1 allows another file handle to be used to change the position
92      of our file descriptor.  Hence we actually don't know the actual
93      position before we do the first fseek (and until a following fflush). */
94   fp->_offset = _IO_pos_BAD;
95   fp->_IO_file_flags |= CLOSED_FILEBUF_FLAGS;
96
97   _IO_link_in(fp);
98   fp->_fileno = -1;
99 }
100
101 int
102 DEFUN(_IO_file_close_it, (fp),
103       register _IO_FILE* fp)
104 {
105   int sync_status, close_status;
106   if (!_IO_file_is_open(fp))
107     return EOF;
108
109   sync_status = _IO_file_sync (fp);
110
111   _IO_unsave_markers(fp);
112
113   close_status = _IO_SYSCLOSE (fp);
114
115   /* Free buffer. */
116   _IO_setb(fp, NULL, NULL, 0);
117   _IO_setg(fp, NULL, NULL, NULL);
118   _IO_setp(fp, NULL, NULL);
119
120   _IO_un_link(fp);
121   fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
122   fp->_fileno = EOF;
123   fp->_offset = _IO_pos_BAD;
124
125   return close_status ? close_status : sync_status;
126 }
127
128 void
129 DEFUN(_IO_file_finish, (fp),
130       register _IO_FILE* fp)
131 {
132   if (_IO_file_is_open(fp))
133     {
134       _IO_do_flush (fp);
135       if (!(fp->_flags & _IO_DELETE_DONT_CLOSE))
136         _IO_SYSCLOSE (fp);
137     }
138   _IO_default_finish(fp);
139 }
140
141 _IO_FILE *
142 DEFUN(_IO_file_fopen, (fp, filename, mode),
143       register _IO_FILE *fp AND const char *filename AND const char *mode)
144 {
145   int oflags = 0, omode;
146   int read_write, fdesc;
147   int oprot = 0666;
148   if (_IO_file_is_open (fp))
149     return 0;
150   switch (*mode++) {
151   case 'r':
152     omode = O_RDONLY;
153     read_write = _IO_NO_WRITES;
154     break;
155   case 'w':
156     omode = O_WRONLY;
157     oflags = O_CREAT|O_TRUNC;
158     read_write = _IO_NO_READS;
159     break;
160   case 'a':
161     omode = O_WRONLY;
162     oflags = O_CREAT|O_APPEND;
163     read_write = _IO_NO_READS|_IO_IS_APPENDING;
164     break;
165   default:
166     errno = EINVAL;
167     return NULL;
168   }
169   if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
170     omode = O_RDWR;
171     read_write &= _IO_IS_APPENDING;
172   }
173   fdesc = open(filename, omode|oflags, oprot);
174   if (fdesc < 0)
175     return NULL;
176   fp->_fileno = fdesc;
177   _IO_mask_flags(fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
178   if (read_write & _IO_IS_APPENDING)
179     if (_IO_SEEKOFF (fp, (_IO_off_t)0, _IO_seek_end, _IOS_INPUT|_IOS_OUTPUT)
180         == _IO_pos_BAD)
181       return NULL;
182   _IO_link_in(fp);
183   return fp;
184 }
185
186 _IO_FILE*
187 DEFUN(_IO_file_attach, (fp, fd),
188       _IO_FILE *fp AND int fd)
189 {
190   if (_IO_file_is_open(fp))
191     return NULL;
192   fp->_fileno = fd;
193   fp->_flags &= ~(_IO_NO_READS+_IO_NO_WRITES);
194   fp->_flags |= _IO_DELETE_DONT_CLOSE;
195   fp->_offset = _IO_pos_BAD;
196   return fp;
197 }
198
199 _IO_FILE*
200 DEFUN(_IO_file_setbuf, (fp, p, len),
201       register _IO_FILE *fp AND char* p AND _IO_ssize_t len)
202 {
203     if (_IO_default_setbuf(fp, p, len) == NULL)
204         return NULL;
205
206     fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
207       = fp->_IO_buf_base;
208     _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
209
210     return fp;
211 }
212
213 /* Write TO_DO bytes from DATA to FP.
214    Then mark FP has having empty buffers. */
215
216 int
217 DEFUN(_IO_do_write, (fp, data, to_do),
218       register _IO_FILE *fp AND const char* data AND _IO_size_t to_do)
219 {
220   _IO_size_t count;
221   if (to_do == 0)
222     return 0;
223   if (fp->_flags & _IO_IS_APPENDING)
224     /* On a system without a proper O_APPEND implementation,
225        you would need to sys_seek(0, SEEK_END) here, but is
226        is not needed nor desirable for Unix- or Posix-like systems.
227        Instead, just indicate that offset (before and after) is
228        unpredictable. */
229     fp->_offset = _IO_pos_BAD;
230   else if (fp->_IO_read_end != fp->_IO_write_base)
231     { 
232       _IO_pos_t new_pos
233         = _IO_SYSSEEK(fp, fp->_IO_write_base - fp->_IO_read_end, 1);
234       if (new_pos == _IO_pos_BAD)
235         return EOF;
236       fp->_offset = new_pos;
237     }
238   count = _IO_SYSWRITE (fp, data, to_do);
239   if (fp->_cur_column)
240     fp->_cur_column = _IO_adjust_column(fp->_cur_column - 1, data, to_do) + 1;
241   _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
242   fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
243   fp->_IO_write_end = (fp->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED)) ? fp->_IO_buf_base
244     : fp->_IO_buf_end;
245   return count != to_do ? EOF : 0;
246 }
247
248 int
249 DEFUN(_IO_file_underflow, (fp),
250       register _IO_FILE *fp)
251 {
252   _IO_ssize_t count;
253 #if 0
254   /* SysV does not make this test; take it out for compatibility */
255   if (fp->_flags & _IO_EOF_SEEN)
256     return (EOF);
257 #endif
258
259   if (fp->_flags & _IO_NO_READS)
260     return EOF;
261   if (fp->_IO_read_ptr < fp->_IO_read_end)
262     return *(unsigned char*)fp->_IO_read_ptr;
263
264   if (fp->_IO_buf_base == NULL)
265     _IO_doallocbuf(fp);
266
267   /* Flush all line buffered files before reading. */
268   /* FIXME This can/should be moved to genops ?? */
269   if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
270     _IO_flush_all_linebuffered();
271
272   _IO_switch_to_get_mode(fp);
273
274   count = _IO_SYSREAD (fp, fp->_IO_buf_base,
275                        fp->_IO_buf_end - fp->_IO_buf_base);
276   if (count <= 0)
277     {
278       if (count == 0)
279         fp->_flags |= _IO_EOF_SEEN;
280       else
281         fp->_flags |= _IO_ERR_SEEN, count = 0;
282   }
283   fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
284   fp->_IO_read_end = fp->_IO_buf_base + count;
285   fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
286     = fp->_IO_buf_base;
287   if (count == 0)
288     return EOF;
289   if (fp->_offset != _IO_pos_BAD)
290     _IO_pos_adjust(fp->_offset, count);
291   return *(unsigned char*)fp->_IO_read_ptr;
292 }
293
294 int
295 DEFUN(_IO_file_overflow, (f, ch),
296       register _IO_FILE* f AND int ch)
297 {
298   if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
299     return EOF;
300   /* If currently reading or no buffer allocated. */
301   if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
302     {
303       /* Allocate a buffer if needed. */
304       if (f->_IO_write_base == 0)
305         {
306           _IO_doallocbuf(f);
307           _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
308         }
309       /* Otherwise must be currently reading. */
310       f->_IO_write_ptr = f->_IO_read_ptr;
311       f->_IO_write_base = f->_IO_write_ptr;
312       f->_IO_write_end = f->_IO_buf_end;
313       f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
314
315       if (f->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED))
316         f->_IO_write_end = f->_IO_write_ptr;
317       f->_flags |= _IO_CURRENTLY_PUTTING;
318     }
319   if (ch == EOF)
320     return _IO_do_flush(f);
321   if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
322     if (_IO_do_flush(f) == EOF)
323       return EOF;
324   *f->_IO_write_ptr++ = ch;
325   if ((f->_flags & _IO_UNBUFFERED)
326       || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
327     if (_IO_do_flush(f) == EOF)
328       return EOF;
329   return (unsigned char)ch;
330 }
331
332 int
333 DEFUN(_IO_file_sync, (fp),
334       register _IO_FILE* fp)
335 {
336   _IO_size_t delta;
337   /*    char* ptr = cur_ptr(); */
338   if (fp->_IO_write_ptr > fp->_IO_write_base)
339     if (_IO_do_flush(fp)) return EOF;
340   delta = fp->_IO_read_ptr - fp->_IO_read_end; 
341   if (delta != 0)
342     {
343 #ifdef TODO
344       if (_IO_in_backup(fp))
345         delta -= eGptr() - Gbase();
346 #endif
347       _IO_off_t new_pos = _IO_SYSSEEK (fp, delta, 1);
348       if (new_pos != (_IO_off_t)EOF)
349         fp->_IO_read_end = fp->_IO_read_ptr;
350 #ifdef ESPIPE
351       else if (errno == ESPIPE)
352         ; /* Ignore error from unseekable devices. */
353 #endif
354       else
355         return EOF;
356     }
357   fp->_offset = _IO_pos_BAD;
358   /* FIXME: Cleanup - can this be shared? */
359   /*    setg(base(), ptr, ptr); */
360   return 0;
361 }
362
363 _IO_pos_t
364 DEFUN(_IO_file_seekoff, (fp, offset, dir, mode),
365       register _IO_FILE *fp AND _IO_off_t offset AND int dir AND int mode)
366 {
367   _IO_pos_t result;
368   _IO_off_t delta, new_offset;
369   long count;
370
371   if (mode == 0)
372     dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
373
374   /* Flush unwritten characters.
375      (This may do an unneeded write if we seek within the buffer.
376      But to be able to switch to reading, we would need to set
377      egptr to ptr.  That can't be done in the current design,
378      which assumes file_ptr() is eGptr.  Anyway, since we probably
379      end up flushing when we close(), it doesn't make much difference.)
380      FIXME: simulate mem-papped files. */
381
382   if (fp->_IO_write_ptr > fp->_IO_write_base || _IO_in_put_mode(fp))
383     if (_IO_switch_to_get_mode(fp)) return EOF;
384
385   if (fp->_IO_buf_base == NULL)
386     {
387       _IO_doallocbuf(fp);
388       _IO_setp(fp, fp->_IO_buf_base, fp->_IO_buf_base);
389       _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
390     }
391
392   switch (dir)
393     {
394     case _IO_seek_cur:
395       /* Adjust for read-ahead (bytes is buffer). */
396       offset -= fp->_IO_read_end - fp->_IO_read_ptr;
397       if (fp->_offset == _IO_pos_BAD)
398         goto dumb;
399       /* Make offset absolute, assuming current pointer is file_ptr(). */
400       offset += _IO_pos_as_off(fp->_offset);
401
402       dir = _IO_seek_set;
403       break;
404     case _IO_seek_set:
405       break;
406     case _IO_seek_end:
407       {
408         struct stat st;
409         if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG(st.st_mode))
410           {
411             offset += st.st_size;
412             dir = _IO_seek_set;
413           }
414         else
415           goto dumb;
416       }
417     }
418   /* At this point, dir==_IO_seek_set. */
419
420   /* If destination is within current buffer, optimize: */
421   if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
422       && !_IO_in_backup (fp))
423     {
424       /* Offset relative to start of main get area. */
425       _IO_pos_t rel_offset = offset - fp->_offset
426         + (fp->_IO_read_end - fp->_IO_read_base);
427       if (rel_offset >= 0)
428         {
429 #if 0
430           if (_IO_in_backup(fp))
431             _IO_switch_to_main_get_area(fp);
432 #endif
433           if (rel_offset <= fp->_IO_read_end - fp->_IO_read_base)
434             {
435               _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base + rel_offset,
436                        fp->_IO_read_end);
437               _IO_setp(fp, fp->_IO_buf_base, fp->_IO_buf_base);
438               return offset;
439             }
440 #ifdef TODO
441             /* If we have streammarkers, seek forward by reading ahead. */
442             if (_IO_have_markers(fp))
443               {
444                 int to_skip = rel_offset
445                   - (fp->_IO_read_ptr - fp->_IO_read_base);
446                 if (ignore(to_skip) != to_skip)
447                   goto dumb;
448                 return offset;
449               }
450 #endif
451         }
452 #ifdef TODO
453       if (rel_offset < 0 && rel_offset >= Bbase() - Bptr())
454         {
455           if (!_IO_in_backup(fp))
456             _IO_switch_to_backup_area(fp);
457           gbump(fp->_IO_read_end + rel_offset - fp->_IO_read_ptr);
458           return offset;
459         }
460 #endif
461     }
462
463 #ifdef TODO
464   _IO_unsave_markers(fp);
465 #endif
466
467   if (fp->_flags & _IO_NO_READS)
468     goto dumb;
469
470   /* Try to seek to a block boundary, to improve kernel page management. */
471   new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
472   delta = offset - new_offset;
473   if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
474     {
475       new_offset = offset;
476       delta = 0;
477     }
478   result = _IO_SYSSEEK (fp, new_offset, 0);
479   if (result < 0)
480     return EOF;
481   if (delta == 0)
482     count = 0;
483   else
484     {
485       count = _IO_SYSREAD (fp, fp->_IO_buf_base,
486                            fp->_IO_buf_end - fp->_IO_buf_base);
487       if (count < delta)
488         {
489           /* We weren't allowed to read, but try to seek the remainder. */
490           offset = count == EOF ? delta : delta-count;
491           dir = _IO_seek_cur;
492           goto dumb;
493         }
494     }
495   _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base+delta, fp->_IO_buf_base+count);
496   _IO_setp(fp, fp->_IO_buf_base, fp->_IO_buf_base);
497   fp->_offset = result + count;
498   _IO_mask_flags(fp, 0, _IO_EOF_SEEN);
499   return offset;
500  dumb:
501
502   _IO_unsave_markers(fp);
503   result = _IO_SYSSEEK (fp, offset, dir);
504   if (result != EOF)
505     _IO_mask_flags(fp, 0, _IO_EOF_SEEN);
506   fp->_offset = result;
507   _IO_setg(fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
508   _IO_setp(fp, fp->_IO_buf_base, fp->_IO_buf_base);
509   return result;
510 }
511
512 _IO_ssize_t
513 DEFUN(_IO_file_read, (fp, buf, size),
514       register _IO_FILE* fp AND void* buf AND _IO_ssize_t size)
515 {
516   for (;;)
517     {
518       _IO_ssize_t count = _IO_read(fp->_fileno, buf, size);
519 #ifdef EINTR
520       if (count == -1 && errno == EINTR)
521         continue;
522 #endif
523       return count;
524     }
525 }
526
527 _IO_pos_t
528 DEFUN(_IO_file_seek, (fp, offset, dir),
529       _IO_FILE *fp AND _IO_off_t offset AND int dir)
530 {
531   return _IO_lseek(fp->_fileno, offset, dir);
532 }
533
534 int
535 DEFUN(_IO_file_stat, (fp, st),
536       _IO_FILE *fp AND void* st)
537 {
538   return _IO_fstat(fp->_fileno, (struct stat*)st);
539 }
540
541 int
542 DEFUN(_IO_file_close, (fp),
543       _IO_FILE* fp)
544 {
545   return _IO_close(fp->_fileno);
546 }
547
548 _IO_ssize_t
549 DEFUN(_IO_file_write, (f, data, n),
550       register _IO_FILE* f AND const void* data AND _IO_ssize_t n)
551 {
552   _IO_ssize_t to_do = n;
553   while (to_do > 0)
554     {
555       _IO_ssize_t count = _IO_write(f->_fileno, data, to_do);
556       if (count == EOF)
557         {
558 #ifdef EINTR
559           if (errno == EINTR)
560             continue;
561           else
562 #endif
563             {
564               f->_flags |= _IO_ERR_SEEN;
565               break;
566             }
567         }
568       to_do -= count;
569       data = (void*)((char*)data + count);
570     }
571   n -= to_do;
572   if (f->_offset >= 0)
573     f->_offset += n;
574   return n;
575 }
576
577 _IO_size_t
578 DEFUN(_IO_file_xsputn, (f, data, n),
579       _IO_FILE *f AND const void *data AND _IO_size_t n)
580 {
581   register const char *s = (char*) data;
582   _IO_size_t to_do = n;
583   int must_flush = 0;
584   _IO_size_t count;
585
586   if (n <= 0)
587     return 0;
588   /* This is an optimized implementation.
589      If the amount to be written straddles a block boundary
590      (or the filebuf is unbuffered), use sys_write directly. */
591
592   /* First figure out how much space is available in the buffer. */
593   count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
594   if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
595     {
596       count = f->_IO_buf_end - f->_IO_write_ptr;
597       if (count >= n)
598         { register const char *p;
599           for (p = s + n; p > s; )
600             {
601               if (*--p == '\n') {
602                 count = p - s + 1;
603                 must_flush = 1;
604                 break;
605               }
606             }
607         }
608     }
609   /* Then fill the buffer. */
610   if (count > 0)
611     {
612       if (count > to_do)
613         count = to_do;
614       if (count > 20) {
615         memcpy(f->_IO_write_ptr, s, count);
616         s += count;
617       }
618       else
619         {
620           register char *p = f->_IO_write_ptr;
621           register int i = (int)count;
622           while (--i >= 0) *p++ = *s++;
623         }
624       f->_IO_write_ptr += count;
625       to_do -= count;
626     }
627   if (to_do + must_flush > 0)
628     { _IO_size_t block_size, dont_write;
629       /* Next flush the (full) buffer. */
630       if (__overflow(f, EOF) == EOF)
631         return n - to_do;
632
633       /* Try to maintain alignment: write a whole number of blocks.
634          dont_write is what gets left over. */
635       block_size = f->_IO_buf_end - f->_IO_buf_base;
636       dont_write = block_size >= 128 ? to_do % block_size : 0;
637
638       count = to_do - dont_write;
639       if (_IO_do_write(f, s, count) == EOF)
640         return n - to_do;
641       to_do = dont_write;
642       
643       /* Now write out the remainder.  Normally, this will fit in the
644          buffer, but it's somewhat messier for line-buffered files,
645          so we let _IO_default_xsputn handle the general case. */
646       if (dont_write)
647         to_do -= _IO_default_xsputn(f, s+count, dont_write);
648     }
649   return n - to_do;
650 }
651
652 #if 0
653 /* Work in progress */
654 _IO_size_t
655 DEFUN(_IO_file_xsgetn, (fp, data, n),
656       _IO_FILE *fp AND void *data AND _IO_size_t n)
657 {
658   register _IO_size_t more = n;
659   register char *s = data;
660   for (;;)
661     {
662       _IO_ssize_t count = fp->_IO_read_end - fp->_IO_read_ptr; /* Data available. */
663       if (count > 0)
664         {
665           if (count > more)
666             count = more;
667           if (count > 20)
668             {
669               memcpy(s, fp->_IO_read_ptr, count);
670               s += count;
671               fp->_IO_read_ptr += count;
672             }
673           else if (count <= 0)
674             count = 0;
675           else
676             {
677               register char *p = fp->_IO_read_ptr;
678               register int i = (int)count;
679               while (--i >= 0) *s++ = *p++;
680               fp->_IO_read_ptr = p;
681             }
682             more -= count;
683         }
684 #if 0
685       if (! _IO_in put_mode (fp)
686           && ! _IO_have_markers (fp) && ! IO_have_backup (fp))
687         {
688           /* This is an optimization of _IO_file_underflow */
689           if (fp->_flags & _IO_NO_READS)
690             break;
691           /* If we're reading a lot of data, don't bother allocating
692              a buffer.  But if we're only reading a bit, perhaps we should ??*/
693           if (count <= 512 && fp->_IO_buf_base == NULL)
694             _IO_doallocbuf(fp);
695           if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
696             _IO_flush_all_linebuffered();
697
698           _IO_switch_to_get_mode(fp); ???;
699           count = _IO_SYSREAD (fp, s, more);
700           if (count <= 0)
701              {
702                if (count == 0)
703                  fp->_flags |= _IO_EOF_SEEN;
704                else
705                  fp->_flags |= _IO_ERR_SEEN, count = 0;
706              }
707           
708           s += count;
709           more -= count;
710         }
711 #endif
712       if (more == 0 || __underflow(fp) == EOF)
713         break;
714     }
715   return n - more;
716 }
717 #endif
718
719 struct _IO_jump_t _IO_file_jumps = {
720   JUMP_INIT_DUMMY,
721   JUMP_INIT(finish, _IO_file_finish),
722   JUMP_INIT(overflow, _IO_file_overflow),
723   JUMP_INIT(underflow, _IO_file_underflow),
724   JUMP_INIT(uflow, _IO_default_uflow),
725   JUMP_INIT(pbackfail, _IO_default_pbackfail),
726   JUMP_INIT(xsputn, _IO_file_xsputn),
727   JUMP_INIT(xsgetn, _IO_default_xsgetn),
728   JUMP_INIT(seekoff, _IO_file_seekoff),
729   JUMP_INIT(seekpos, _IO_default_seekpos),
730   JUMP_INIT(setbuf, _IO_file_setbuf),
731   JUMP_INIT(sync, _IO_file_sync),
732   JUMP_INIT(doallocate, _IO_file_doallocate),
733   JUMP_INIT(read, _IO_file_read),
734   JUMP_INIT(write, _IO_file_write),
735   JUMP_INIT(seek, _IO_file_seek),
736   JUMP_INIT(close, _IO_file_close),
737   JUMP_INIT(stat, _IO_file_stat)
738 };