1 /*	$Id: PosixFileDescr.c,v 1.12 2005/08/31 11:57:24 mva Exp $	*/
2 /*  Generalized access to POSIX-style file descriptors.
3     Copyright (C) 1997-2000, 2002, 2005  Michael van Acken
4 
5     This module is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public License
7     as published by the Free Software Foundation; either version 2 of
8     the License, or (at your option) any later version.
9 
10     This module is distributed in the hope that it will be useful, but
11     WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with OOC. If not, write to the Free Software Foundation,
17     59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <limits.h>
26 #include <string.h>
27 
28 #include <__oo2c.h>
29 #include <__config.h>
30 
31 #if TIME_WITH_SYS_TIME
32 # include <sys/time.h>
33 # include <time.h>
34 #else
35 # if HAVE_SYS_TIME_H
36 #  include <sys/time.h>
37 # else
38 #  include <time.h>
39 # endif
40 #endif
41 #if HAVE_SOCKET_H
42 #include <socket.h>
43 #endif
44 #if HAVE_SYSLIMITS_H
45 #include <syslimits.h>
46 #endif
47 #if HAVE_UNISTD_H
48 #include <unistd.h>
49 #elif HAVE_IO_H
50 #include <io.h>
51 #endif
52 
53 #ifdef __MINGW32__
54 #include <fcntl.h>
55 #endif
56 
57 #ifndef SSIZE_MAX
58 /* for every POSIX.1 system the macro SSIZE_MAX is the limit on the number of
59    bytes that can be read or written in a single operation; assume 2^15 if
60    there is no other information available */
61 #ifdef POSIX_SSIZE_MAX
62 #define SSIZE_MAX POSIX_SSIZE_MAX
63 #else
64 #define SSIZE_MAX 32768
65 #endif
66 #endif
67 
68 #define _MODULE_libc_ /* don't pull in declaration from the `libc' module */
69 #include <PosixFileDescr.d>
70 
71 /* keep track whether the file descriptors 0-2 refer to the standard IO
72    descriptors passed from the shell, or should be treated like any other
73    descriptor */
74 static int standard_io[3] = {1, 1, 1};
75 
76 static PosixFileDescr__ErrorContext PosixFileDescr__errorContext;
77 
78 
79 
80 /* function definitions */
81 
PosixFileDescr__ErrorContextDesc_GetTemplate(PosixFileDescr__ErrorContext context,Msg__Msg msg,Msg__LString templ,OOC_INT32 templ_0d)82 void PosixFileDescr__ErrorContextDesc_GetTemplate(PosixFileDescr__ErrorContext context, Msg__Msg msg, Msg__LString templ, OOC_INT32 templ_0d) {
83   /* super call to get first template string; this module adds no error
84      codes beyond the list given in Channel.Mod */
85   STATIC_TBCALL(Channel,ErrorContextDesc,GetTemplate,context,
86 		((Channel__ErrorContext)context, msg, templ, templ_0d));
87 
88   if (msg->attribList) {
89     Msg__Attribute attr;
90     OOC_CHAR16 eol[2] = {(OOC_CHAR16)CharClass__eol, (OOC_CHAR16)0};
91     OOC_CHAR16 str16[Msg__sizeAttrName+1];
92 
93     /*LongStrings__Append(eol, 2, templ, templ_0d);*/
94     attr = msg->attribList;
95     while (attr) {
96       LongStrings__Append(eol, 2, templ, templ_0d);
97       _copy_8to16((OOC_CHAR8*)attr->name, str16, strlen((char*)attr->name)+1);
98       LongStrings__Append(str16, Msg__sizeAttrName+1, templ, templ_0d);
99       _copy_8to16((OOC_CHAR8*)"=${", str16, Msg__sizeAttrName+1);
100       LongStrings__Append(str16, Msg__sizeAttrName+1, templ, templ_0d);
101       _copy_8to16((OOC_CHAR8*)attr->name, str16, Msg__sizeAttrName+1);
102       LongStrings__Append(str16, Msg__sizeAttrName+1, templ, templ_0d);
103       _copy_8to16((OOC_CHAR8*)"}", str16, Msg__sizeAttrName+1);
104       LongStrings__Append(str16, Msg__sizeAttrName+1, templ, templ_0d);
105       attr = attr->nextAttrib;
106     }
107   }
108 }
109 
get_error(Msg__Code code,int use_errno)110 static Msg__Msg get_error(Msg__Code code, int use_errno) {
111   Msg__Msg msg;
112 
113   msg = Msg__New((Msg__Context)PosixFileDescr__errorContext, code);
114   if (use_errno) {
115 #if HAVE_STRERROR
116     char *errstr = strerror(errno);
117     DYN_TBCALL(Msg,MsgDesc,SetStringAttrib,msg,
118 	       (msg, (const OOC_CHAR8*)"errstr", 7, (OOC_CHAR8*)errstr));
119 #endif
120     DYN_TBCALL(Msg,MsgDesc,SetIntAttrib,msg,
121 	       (msg, (const OOC_CHAR8*)"errno", 6, (OOC_INT32)errno));
122   }
123   return msg;
124 }
125 
adjust_pos(PosixFileDescr__Channel ch,int pos)126 static void adjust_pos(PosixFileDescr__Channel ch, int pos) {
127   if (ch->positionable && (ch->pos != pos)) {
128     (void)lseek(ch->fd, pos, SEEK_SET);
129     ch->pos = pos;
130   }
131 }
132 
write_error(void)133 static Msg__Msg write_error(void) {
134   if (errno == EBADF) {
135     return get_error(Channel__invalidChannel, 1);
136   } else if (errno == ENOSPC) {
137     return get_error(Channel__noRoom, 1);
138 #ifdef EDQUOT
139   } else if (errno == EDQUOT) {
140     return get_error(Channel__noRoom, 1);
141 #endif
142   } else {
143     return get_error(Channel__writeError, 1);
144   }
145 }
146 
read_error(void)147 static Msg__Msg read_error(void) {
148   if (errno == EBADF) {
149     return get_error(Channel__invalidChannel, 1);
150   } else {
151     return get_error(Channel__readError, 1);
152   }
153 }
154 
read_bytes(PosixFileDescr__Channel ch,OOC_INT32 pos,OOC_INT32 n,OOC_BYTE * x,OOC_INT32 * bytes_read)155 static Msg__Msg read_bytes(PosixFileDescr__Channel ch, OOC_INT32 pos, OOC_INT32 n,
156                            OOC_BYTE *x, OOC_INT32 *bytes_read) {
157 /* Reads only a single line for line buffered input from terminal; in this case
158    `n� should be large enough to hold a single line.  */
159   size_t size, acc;
160   ssize_t res;
161 
162   adjust_pos(ch, pos);
163   /* read bytes from stream; repeat until all have been read successfully */
164   acc = 0;
165   do {
166     /* make sure that no read request larger than the system limit is
167        issued */
168     size = n-acc;
169     if (size > SSIZE_MAX) size = SSIZE_MAX;
170     res = read(((PosixFileDescr__Channel)ch)->fd, x+acc, (ssize_t)size);
171     /* note: for a terminal in canonical input mode the above read will at most
172        return a single line, independent of the requested size; I hope that
173        SSIZE_MAX is larger than MAX_CANON... */
174     if (res >= 0) acc += res;
175   } while (((res == -1) && (errno == EINTR)) ||
176 	   ((res > 0) && (acc < (size_t)n) &&
177 	    (ch->buffering != PosixFileDescr__lineBuffer)));
178   *bytes_read = acc;
179   ch->pos += acc;
180 
181   /* check error condition */
182   if (res == -1) {
183     return read_error();
184   } else if ((res == 0) && (size != 0)) {
185     /* we tried to read behind the end of file */
186     return get_error(Channel__readAfterEnd, 0);
187   } else {
188     return Channel__done;
189   }
190 }
191 
write_bytes(PosixFileDescr__Channel ch,OOC_INT32 pos,OOC_INT32 n,const OOC_BYTE * x,OOC_INT32 * bytes_written)192 static Msg__Msg write_bytes(PosixFileDescr__Channel ch, OOC_INT32 pos, OOC_INT32 n,
193                             const OOC_BYTE *x, OOC_INT32 *bytes_written) {
194   size_t size, acc;
195   ssize_t res;
196 
197   adjust_pos(ch, pos);
198   /* write bytes to stream; repeat until all have been written successfully */
199   acc = 0;
200   do {
201     /* make sure that no write request larger than the system limit is
202        issued */
203     size = n-acc;
204     if (size > SSIZE_MAX) size = SSIZE_MAX;
205     res = write(((PosixFileDescr__Channel)ch)->fd, x+acc, size);
206     if (res >= 0) acc += res;
207   } while (((res == -1) && (errno == EINTR)) ||
208            ((res > 0) && (acc < (size_t)n)));
209   *bytes_written = acc;
210   ch->pos += acc;
211 
212   /* check error condition */
213   if (res == -1) {
214     return write_error();
215   } else {
216     return Channel__done;
217   }
218 }
219 
flush_buffer(PosixFileDescr__Channel ch)220 static Msg__Msg flush_buffer(PosixFileDescr__Channel ch) {
221   OOC_INT32 bytesWritten;
222 
223   if ((ch->buffering != PosixFileDescr__noBuffer) && ch->dirty) {
224     ch->dirty = 0;
225     return write_bytes(ch, ch->bufStart, ch->bufEnd - ch->bufStart,
226                        ch->buf, &bytesWritten);
227   } else {
228     return Channel__done;
229   }
230 }
231 
PosixFileDescr__InitReader(PosixFileDescr__Reader r,PosixFileDescr__Channel ch)232 void PosixFileDescr__InitReader(PosixFileDescr__Reader r, PosixFileDescr__Channel ch) {
233   r->base = (Channel__Channel)ch;
234   r->res = Channel__done;
235   r->bytesRead = -1;
236   r->positionable = ch->positionable;
237   r->pos = 0;
238   DYN_TBCALL(Channel,ChannelDesc,ClearError,ch,((Channel__Channel)ch));
239   if (!ch->positionable) {
240     ch->reader = r;
241   }
242 }
243 
PosixFileDescr__InitWriter(PosixFileDescr__Writer w,PosixFileDescr__Channel ch)244 void PosixFileDescr__InitWriter(PosixFileDescr__Writer w, PosixFileDescr__Channel ch) {
245   w->base = (Channel__Channel)ch;
246   w->res = Channel__done;
247   w->bytesWritten = -1;
248   w->positionable = ch->positionable && !ch->append;
249   w->pos = 0;
250   DYN_TBCALL(Channel,ChannelDesc,ClearError,ch,((Channel__Channel)ch));
251   if (!ch->positionable) {
252     ch->writer = w;
253   }
254 }
255 
256 
257 
PosixFileDescr__ReaderDesc_Pos(PosixFileDescr__Reader r)258 OOC_INT32 PosixFileDescr__ReaderDesc_Pos(PosixFileDescr__Reader r) {
259   if (r->positionable) {
260     return r->pos;
261   } else {
262     return Channel__noPosition;
263   }
264 }
265 
PosixFileDescr__ReaderDesc_Available(PosixFileDescr__Reader r)266 int PosixFileDescr__ReaderDesc_Available(PosixFileDescr__Reader r) {
267   struct stat stat_buf;
268   int res;
269   OOC_INT32 len;
270   PosixFileDescr__Channel ch = (PosixFileDescr__Channel)r->base;
271 
272   res = fstat(ch->fd, &stat_buf);
273 
274   if ((!r->base->open) || (res == -1)) {
275     /* error; assume that channel has been closed */
276     return -1;
277   } else if (S_ISREG(stat_buf.st_mode)) {
278     /* regular file; check position and size */
279     len = stat_buf.st_size;
280     if ((ch->buffering != PosixFileDescr__noBuffer) &&
281 	ch->dirty && (ch->bufEnd > len)) {
282       return len = ch->bufEnd;
283     }
284 
285     res = len - r->pos;
286     if (res < 0) {
287       /* a previous SetPos might have moved the reading position past the end
288          of the file; this is no error, put obviously no bytes can be read
289          there -- although this might change if the file is expanded */
290       return 0;
291     } else {
292       return res;
293     }
294   } else {
295 #ifndef __MINGW32__
296     /* something else, like terminal or socket */
297     fd_set set;
298     struct timeval timeout;
299 
300     timeout.tv_sec = 0;
301     timeout.tv_usec = 0;
302     FD_ZERO(&set);
303     FD_SET(ch->fd, &set);
304 
305     do {
306       res = select(FD_SETSIZE, (void*)&set, NULL, NULL, &timeout);
307     } while ((res == -1) && (errno == EINTR));
308 
309     /* res==-1: error; assume that channel has been closed
310        res== 0: no input availbale
311        res== 1: input available from channel */
312     return res;
313 #else
314     return 0;
315 #endif
316   }
317 }
318 
PosixFileDescr__ReaderDesc_SetPos(PosixFileDescr__Reader r,OOC_INT32 newPos)319 void PosixFileDescr__ReaderDesc_SetPos(PosixFileDescr__Reader r, OOC_INT32 newPos) {
320   if (r->res == Channel__done) {
321     if (!r->base->open) {
322       r->res = get_error(Channel__channelClosed, 0);
323     } else if ((r->positionable) && (newPos >= 0)) {
324       r->pos = newPos;
325     } else {
326       r->res = get_error(Channel__outOfRange, 0);
327     }
328   }
329 }
330 
PosixFileDescr__ReaderDesc_ReadByte(PosixFileDescr__Reader r,OOC_BYTE * x)331 void PosixFileDescr__ReaderDesc_ReadByte(PosixFileDescr__Reader r, OOC_BYTE *x) {
332   PosixFileDescr__Result res;
333   OOC_INT32 bytesRead;
334   PosixFileDescr__Channel ch = (PosixFileDescr__Channel)r->base;
335 
336   if (r->res == Channel__done) {
337     if (!r->base->open) {
338       r->res = get_error(Channel__channelClosed, 0);
339       r->bytesRead = 0;
340 
341     } else if (ch->buffering == PosixFileDescr__noBuffer) {
342       if (ch->dirty) {
343 	res = flush_buffer(ch);
344 	if (res != Channel__done) {
345 	  r->res = res;
346 	  r->bytesRead = 0;
347 	  return;
348 	}
349       }
350       res = read_bytes(ch, r->pos, 1, x, &r->bytesRead);
351       if (res != Channel__done) r->res = res;
352       r->pos += r->bytesRead;
353 
354     } else {
355       /* line or block buffering is enabled; check if the byte is currently in
356          the buffer; refill if it isn't */
357       if ((r->pos < ch->bufStart) || (ch->bufEnd <= r->pos)) {
358         /* requested byte isn't in buffer: flush and refill */
359         res = flush_buffer(ch);
360         if (res == Channel__done) {
361           res = read_bytes(ch, r->pos, ch->sizeBuffer, ch->buf, &bytesRead);
362           ch->bufStart = r->pos;
363           ch->bufEnd = r->pos + bytesRead;
364         }
365         if ((res != Channel__done) &&
366 	    ((res->code != Channel__readAfterEnd) || (bytesRead == 0))) {
367           /* reaching the end of the file is acceptable, all other errors have
368              to be reported back */
369           r->res = res;
370           r->bytesRead = 0;
371 	} else {
372 	  *x = ch->buf[0];
373 	  r->pos++;
374 	  r->bytesRead = 1;
375         }
376       } else {
377 	*x = ch->buf[r->pos - ch->bufStart];
378 	r->pos++;
379 	r->bytesRead = 1;
380       }
381     }
382   } else {
383     r->bytesRead = 0;
384   }
385 }
386 
PosixFileDescr__ReaderDesc_ReadBytes(PosixFileDescr__Reader r,OOC_BYTE * x,int x_0d,OOC_INT32 start,OOC_INT32 n)387 void PosixFileDescr__ReaderDesc_ReadBytes(PosixFileDescr__Reader r, OOC_BYTE* x, int x_0d, OOC_INT32 start, OOC_INT32 n) {
388   PosixFileDescr__Result res;
389   OOC_INT32 bytesRead, size;
390   PosixFileDescr__Channel ch = (PosixFileDescr__Channel)r->base;
391 
392   if (r->res == Channel__done) {
393     if (!r->base->open) {
394       r->res = get_error(Channel__channelClosed, 0);
395       r->bytesRead = 0;
396 
397     } else if (n == 0) {
398       /* ignore request for zero bytes, but only after we have made
399 	 sure that the channel is still open */
400 
401     } else if (ch->buffering == PosixFileDescr__noBuffer) {
402       if (ch->dirty) {
403 	res = flush_buffer(ch);
404 	if (res != Channel__done) {
405 	  r->res = res;
406 	  r->bytesRead = 0;
407 	  return;
408 	}
409       }
410       res = read_bytes(ch, r->pos, n, x+start, &r->bytesRead);
411       if (res != Channel__done) r->res = res;
412       r->pos += r->bytesRead;
413 
414     } else {
415       /* the following cases deal with block and line buffering */
416       if (ch->dirty && (ch->bufStart > r->pos)) {
417 	/* make sure that any holes we might notice are actually created for
418 	   the file descriptor */
419 	res = flush_buffer(ch);
420 	if (res != Channel__done) {
421 	  r->res = res;
422 	  r->bytesRead = 0;
423 	  return;
424 	}
425       }
426 
427       if ((ch->bufStart == ch->bufEnd) ||
428 	  (r->pos+n <= ch->bufStart) ||
429 	  (r->pos >= ch->bufEnd)) {
430 	/* the buffer is empty, or the whole requested interval is located in
431 	   front or behind the buffer */
432 
433 	if (ch->buffering == PosixFileDescr__lineBuffer) {
434  	  /* we are reading from a terminal in canonical input mode;
435 	     successively grab lines until the whole request is fulfilled */
436 	  r->bytesRead = 0;
437 	  do {  /* here holds: n>0, buffer is empty */
438 	    /* get a single line of input into the buffer */
439 	    res = read_bytes(ch, r->pos, ch->sizeBuffer, ch->buf, &bytesRead);
440 	    if (res != Channel__done) r->res = res;
441 	    ch->bufStart = r->pos;
442 	    ch->bufEnd = r->pos + bytesRead;
443 
444 	    /* copy available bytes from buffer to x+start */
445 	    size = n;
446 	    if (size > bytesRead) size = bytesRead;
447 	    (void)memcpy(x+start, ch->buf, size);
448 	    start += size;
449 	    n -= size;
450 	    r->bytesRead += size;
451 	    r->pos += size;
452 	  } while ((res == Channel__done) && (n > 0));
453 
454 	} else if (n >= ch->sizeBuffer) {
455 	  /* the requested block is larger than the buffer, so don't bother
456 	     filling it and transfer bytes directly to x+start */
457 	  res = read_bytes(ch, r->pos, n, x+start, &r->bytesRead);
458 	  if (res != Channel__done) r->res = res;
459 	  r->pos += r->bytesRead;
460 
461 	} else {
462 	  /* fill buffer with file contents starting at r->pos */
463 	  res = flush_buffer(ch);
464 	  if (res == Channel__done) {
465 	    res = read_bytes(ch, r->pos, ch->sizeBuffer, ch->buf, &bytesRead);
466 	    ch->bufStart = r->pos;
467 	    ch->bufEnd = r->pos + bytesRead;
468 
469 	    /* copy available bytes from buffer to x+start */
470 	    size = n;
471 	    if (size > bytesRead) size = bytesRead;
472 	    (void)memcpy(x+start, ch->buf, size);
473 	    r->bytesRead = size;
474 	    r->pos += size;
475 	  } else {
476 	    r->bytesRead = 0;
477 	  }
478 
479 	  if ((res != Channel__done) &&
480 	      ((res->code != Channel__readAfterEnd) || (r->bytesRead < n))) {
481 	    r->res = res;
482 	  }
483 	}
484 
485       } else {
486 	/* the intersection of the requested and the buffered file interval
487 	   isn't empty; first read the bytes in front of the buffer, then copy
488 	   the bytes from the buffer, and finally fill in the rest of the
489 	   request */
490 	if (r->pos < ch->bufStart) {
491 	  res = read_bytes(ch, r->pos, ch->bufStart - r->pos, x+start,
492 			   &r->bytesRead);
493 	  r->pos += r->bytesRead;
494 	  if (res != Channel__done) {
495 	    r->res = res;
496 	    return;
497 	  } else {
498 	    n -= r->bytesRead;
499 	  }
500 	} else {
501 	  r->bytesRead = 0;
502 	}
503 
504 	size = ch->bufEnd - r->pos;
505 	if (size > n) size = n;
506 	(void)memcpy(x + start + r->bytesRead,
507 		     ch->buf + r->pos - ch->bufStart, size);
508 	r->bytesRead += size;
509 	r->pos += size;
510 	n -= size;
511 
512 	if (n > 0) {
513 	  size = r->bytesRead;
514 	  PosixFileDescr__ReaderDesc_ReadBytes(r, x, x_0d, start+size, n);
515 	  r->bytesRead += size;
516 	}
517       }
518     }
519   } else {
520     r->bytesRead = 0;
521   }
522 }
523 
524 
525 
PosixFileDescr__WriterDesc_Pos(PosixFileDescr__Writer w)526 int PosixFileDescr__WriterDesc_Pos(PosixFileDescr__Writer w) {
527   if (w->positionable) {
528     return w->pos;
529   } else {
530     return Channel__noPosition;
531   }
532 }
533 
PosixFileDescr__WriterDesc_SetPos(PosixFileDescr__Writer w,OOC_INT32 newPos)534 void PosixFileDescr__WriterDesc_SetPos(PosixFileDescr__Writer w, OOC_INT32 newPos) {
535   if (w->res == Channel__done) {
536     if (!w->base->open) {
537       w->res = get_error(Channel__channelClosed, 0);
538     } else if ((w->positionable) && (newPos >= 0)) {
539       w->pos = newPos;
540     } else {
541       w->res = get_error(Channel__outOfRange, 0);
542     }
543   }
544 }
545 
PosixFileDescr__WriterDesc_WriteByte(PosixFileDescr__Writer w,OOC_BYTE x)546 void PosixFileDescr__WriterDesc_WriteByte(PosixFileDescr__Writer w, OOC_BYTE x) {
547   PosixFileDescr__Result res;
548   PosixFileDescr__Channel ch = (PosixFileDescr__Channel)w->base;
549 
550   if (w->res == Channel__done) {
551     if (!w->base->open) {
552       w->res = get_error(Channel__channelClosed, 0);
553       w->bytesWritten = 0;
554 
555     } else if (ch->buffering == PosixFileDescr__noBuffer) {
556       res = write_bytes(ch, w->pos, 1, &x, &w->bytesWritten);
557       if (res != Channel__done) w->res = res;
558       w->pos += w->bytesWritten;
559 
560     } else {                    /* buffering is enabled */
561       if (ch->dirty &&
562           ((w->pos < ch->bufStart) ||
563            (w->pos > ch->bufEnd) ||
564            ((w->pos == ch->bufEnd) &&
565             (ch->bufEnd - ch->bufStart == ch->sizeBuffer)))) {
566         /* current buffer is dirty, and the new byte can't be added to it;
567            flush it to make room for next output */
568         res = flush_buffer(ch);
569         if (res != Channel__done) {
570           w->res = res;
571           w->bytesWritten = 0;
572           return;
573         }
574       }
575 
576       if (!ch->dirty) {  /* convert buffer from read to write state */
577         ch->bufStart = w->pos;
578         ch->bufEnd = w->pos+1;
579 	ch->dirty = 1;
580       } else if (w->pos == ch->bufEnd) { /* add to end of buffer */
581         ch->bufEnd++;
582       }
583       ch->buf[w->pos - ch->bufStart] = x;
584       w->pos++;
585       w->bytesWritten = 1;
586 
587       if ((ch->buffering == PosixFileDescr__lineBuffer) &&
588           ((OOC_CHAR8)x == CharClass__eol)) {
589         res = flush_buffer(ch);
590         if (res) {
591           w->res = res;
592           w->bytesWritten = 0;
593         }
594       }
595     }
596   } else {
597     w->bytesWritten = 0;
598   }
599 }
600 
flush_lines(PosixFileDescr__Channel ch,OOC_INT32 start,OOC_INT32 end)601 static PosixFileDescr__Result flush_lines (PosixFileDescr__Channel ch,
602 					   OOC_INT32 start, OOC_INT32 end) {
603   OOC_INT32 i;
604 
605   i = start;
606   while ((i < end) && (ch->buf[i] != CharClass__eol)) {
607     i++;
608   }
609   if (i != end) {
610     return flush_buffer(ch);
611   } else {
612     return Channel__done;
613   }
614 }
615 
PosixFileDescr__WriterDesc_WriteBytes(PosixFileDescr__Writer w,const OOC_BYTE * x,int x_0d,OOC_INT32 start,OOC_INT32 n)616 void PosixFileDescr__WriterDesc_WriteBytes(PosixFileDescr__Writer w, const OOC_BYTE* x, int x_0d, OOC_INT32 start, OOC_INT32 n) {
617   PosixFileDescr__Result res;
618   OOC_INT32 size, s, e;
619   PosixFileDescr__Channel ch = (PosixFileDescr__Channel)w->base;
620 
621   if (w->res == Channel__done) {
622     if (!w->base->open) {
623       w->res = get_error(Channel__channelClosed, 0);
624       w->bytesWritten = 0;
625 
626     } else if (ch->buffering == PosixFileDescr__noBuffer) {
627       res = write_bytes(ch, w->pos, n, (const OOC_BYTE*)x+start,
628 			&w->bytesWritten);
629       if (res) w->res = res;
630       w->pos += w->bytesWritten;
631 
632     } else if (!ch->dirty ||
633                (w->pos+n <= ch->bufStart) ||
634                (w->pos > ch->bufEnd) ||
635                ((w->pos == ch->bufEnd) &&
636 		(ch->bufEnd - ch->bufStart == ch->sizeBuffer))) {
637       /* the buffer contains no written data, or the whole requested interval
638          is located in front or behind the buffer, or the buffer is full */
639       res = flush_buffer(ch);
640       if (res) {
641         w->res = res;
642         w->bytesWritten = 0;
643         return;
644       }
645 
646       if (n >= ch->sizeBuffer) {
647         /* the written block is larger than the buffer, so don't bother
648            filling it and transfer bytes directly from x+start*/
649         res = write_bytes(ch, w->pos, n, (const OOC_BYTE*)x+start,
650 			  &w->bytesWritten);
651         if (res) w->res = res;
652 	/* determine the intersection between buffer and write request */
653 	s = ch->bufStart;
654 	if (w->pos > s) s = w->pos;
655 	e = ch->bufEnd;
656 	if (w->pos + w->bytesWritten < e) e = w->pos + w->bytesWritten;
657 	if (s < e) {
658 	  /* someone was reading in the area we have just overwritten; update
659 	     buffer contents in intersection of buffer and write request
660 	     instead of invalidating the buffer */
661 	  (void)memcpy(ch->buf + (s - ch->bufStart),
662 		       x + start + (s - w->pos), e - s);
663 	}
664         w->pos += w->bytesWritten;
665 
666       } else {
667         /* copy bytes into buffer */
668         (void)memcpy(ch->buf, x+start, n);
669         ch->bufStart = w->pos;
670         ch->bufEnd = w->pos + n;
671         ch->dirty = 1;
672         w->bytesWritten = n;
673         w->pos += n;
674 	if (ch->buffering == PosixFileDescr__lineBuffer) {
675 	  res = flush_lines(ch, 0, n);
676 	  if (res) {
677 	    w->res = res;
678 	    w->bytesWritten = 0;
679 	  }
680 	}
681       }
682     } else {
683       /* the intersection of the written interval and the buffered file
684          interval isn't empty, or the new data extends the current buffer;
685          first write the bytes in front of the buffer, then extend the buffer,
686          and finally write the bytes after the buffer */
687       if (w->pos < ch->bufStart) {
688         res = write_bytes(ch, w->pos, ch->bufStart - w->pos,
689 			  (const OOC_BYTE*)x+start, &w->bytesWritten);
690         w->pos += w->bytesWritten;
691         if (res) {
692           w->res = res;
693           return;
694         } else {
695           n -= w->bytesWritten;
696         }
697       } else {
698         w->bytesWritten = 0;
699       }
700 
701       s = w->pos - ch->bufStart;
702       size = ch->sizeBuffer - s;
703       if (size > n) size = n;
704       (void)memcpy(ch->buf + s, x + start + w->bytesWritten, size);
705       if (w->pos + size > ch->bufEnd) {
706 	ch->bufEnd = w->pos + size;
707       }
708       w->bytesWritten += size;
709       w->pos += size;
710       n -= size;
711 
712       if (n > 0) {
713         size = w->bytesWritten;
714         PosixFileDescr__WriterDesc_WriteBytes(w, x, x_0d, start+size, n);
715 	if (w->res == Channel__done) w->bytesWritten += size;
716       } else if (ch->buffering == PosixFileDescr__lineBuffer) {
717 	res = flush_lines(ch, s, size);
718 	if (res) {
719 	  w->res = res;
720 	  w->bytesWritten = 0;
721 	}
722       }
723     }
724   } else {
725     w->bytesWritten = 0;
726   }
727 }
728 
729 
730 
PosixFileDescr__ChannelDesc_Length(PosixFileDescr__Channel ch)731 OOC_INT32 PosixFileDescr__ChannelDesc_Length(PosixFileDescr__Channel ch) {
732   int res;
733   struct stat stat_buf;
734   OOC_INT32 len;
735 
736   res = fstat(ch->fd, &stat_buf);
737   if (res == -1) {
738     return Channel__noLength;
739   } else {
740     len = stat_buf.st_size;
741     if ((ch->buffering != PosixFileDescr__noBuffer) &&
742 	ch->dirty && (ch->bufEnd > len)) {
743       return ch->bufEnd;
744     } else {
745       return len;
746     }
747   }
748 }
749 
750 /* define the day count of the Unix epoch (Jan 1 1970 00:00:00 GMT) for the
751    Time.TimeStamp format */
752 #define days_to_epoch 40587
753 #define secs_per_day 86400
754 
PosixFileDescr__ChannelDesc_GetModTime(PosixFileDescr__Channel ch,struct Time__TimeStamp * mtime,RT0__Struct mtime__tag)755 void PosixFileDescr__ChannelDesc_GetModTime(PosixFileDescr__Channel ch, struct Time__TimeStamp *mtime, RT0__Struct mtime__tag) {
756   int res;
757   struct stat stat_buf;
758 
759   res = fstat(ch->fd, &stat_buf);
760   if (res == -1) {
761     ch->res = get_error(Channel__noModTime, 0);
762   } else {
763     mtime->days = days_to_epoch + stat_buf.st_mtime / secs_per_day;
764     mtime->msecs = (stat_buf.st_mtime % secs_per_day) * 1000;
765 #if HAVE_ST_MTIME_USEC
766     mtime->msecs += (stat_buf.st_mtime_usec / 1000);
767 #endif
768     ch->res = Channel__done;
769   }
770 }
771 
PosixFileDescr__ChannelDesc_NewReader(PosixFileDescr__Channel ch)772 PosixFileDescr__Reader PosixFileDescr__ChannelDesc_NewReader(PosixFileDescr__Channel ch) {
773   PosixFileDescr__Reader r = NULL;
774 
775   if (!ch->open) {
776     ch->res = get_error(Channel__channelClosed, 0);
777   } else if (ch->readable) {
778     if (ch->positionable || (ch->reader == NULL)) {
779       r = RT0__NewObject(OOC_TYPE_DESCR(PosixFileDescr,ReaderDesc));
780       PosixFileDescr__InitReader (r, ch);
781     } else {
782       /* channel doesn't support multiple readers, so just return the
783          one previously created */
784       r = ch->reader;
785     }
786   } else {
787     ch->res = get_error(Channel__noReadAccess, 0);
788   }
789 
790   return r;
791 }
792 
PosixFileDescr__ChannelDesc_NewWriter(PosixFileDescr__Channel ch)793 PosixFileDescr__Writer PosixFileDescr__ChannelDesc_NewWriter(PosixFileDescr__Channel ch) {
794   PosixFileDescr__Writer w = NULL;
795 
796   if (!ch->open) {
797     ch->res = get_error(Channel__channelClosed, 0);
798   } else if (ch->writable) {
799     if (ch->positionable || (ch->writer == NULL)) {
800       w = RT0__NewObject(OOC_TYPE_DESCR(PosixFileDescr,WriterDesc));
801       PosixFileDescr__InitWriter (w, ch);
802     } else {
803       /* channel doesn't support multiple writers, so just return the
804          one previously created */
805       w = ch->writer;
806     }
807   } else {
808     ch->res = get_error(Channel__noWriteAccess, 0);
809   }
810 
811   return w;
812 }
813 
PosixFileDescr__ChannelDesc_Flush(PosixFileDescr__Channel ch)814 void PosixFileDescr__ChannelDesc_Flush(PosixFileDescr__Channel ch) {
815   ch->res = flush_buffer(ch);
816   if (ch->buffering == PosixFileDescr__blockBuffer) {
817     /* invalidate whole buffer to force next read operation to get the
818        data from the OS; of course this won�t work for unbuffered input or
819        when reading lines from a terminal in canonical input mode */
820     ch->bufEnd = ch->bufStart;
821   }
822 }
823 
PosixFileDescr__ChannelDesc_Close(PosixFileDescr__Channel ch)824 void PosixFileDescr__ChannelDesc_Close(PosixFileDescr__Channel ch) {
825   int res;
826 
827   /* flush the channel; this may be an upcall */
828   DYN_TBCALL(Channel,ChannelDesc,Flush,ch,((Channel__Channel)ch));
829 
830   /* close the file descriptor; try again if the primitive is
831      interrupted by signal */
832   do {
833     res = close(ch->fd);
834   } while ((res == -1) && (errno == EINTR));
835   if (ch->fd <= PosixFileDescr__stderrFileno) {
836     /* this fd isn't used for standard IO anymore */
837     standard_io[ch->fd] = 0;
838   }
839   ch->fd = -1;
840 
841   /* only put a close error into ch->res if the flush succeeded;
842      otherwise keep the old error indication */
843   if ((res == -1) && (ch->res == Channel__done)) {
844     if (errno == EBADF) {
845       ch->res = get_error(Channel__invalidChannel, 1);
846     } else if (errno == ENOSPC) {
847       ch->res = get_error(Channel__noRoom, 1);
848 #ifdef EDQUOT
849     } else if (errno == EDQUOT) {
850       ch->res = get_error(Channel__noRoom, 1);
851 #endif
852     } else {
853       ch->res = get_error(Channel__writeError, 1);
854     }
855   }
856 
857   /* free buffer */
858   if (ch->buf) {
859     RT0__FreeBlock(ch->buf);
860     ch->buf = NULL;
861   }
862 
863   /* mark channel as closed */
864   ch->open = 0;
865 }
866 
PosixFileDescr__Init(PosixFileDescr__Channel ch,PosixFileDescr__FileDescriptor fd,OOC_INT8 mode)867 void PosixFileDescr__Init(PosixFileDescr__Channel ch, PosixFileDescr__FileDescriptor fd, OOC_INT8 mode) {
868 /* Initializes channel `ch' to use file descriptor `fd' and access rights
869    `mode'.  If `fd' is a file, block buffering is enabled; if it's a terminal,
870    output is line buffered; otherwise no buffering is applied.
871    The standard file descriptors that were passed from the shell are handled
872    specially: positioning is disabled, and stderr is never buffered.  */
873   struct stat stat_buf;
874   int size;
875 
876   ch->fd = fd;
877   ch->pos = lseek(fd, 0, SEEK_CUR);
878   ch->positionable = (ch->pos != -1);
879   ch->append = 0;
880   ch->dirty = 0;
881   ch->bufStart = 0;
882   ch->bufEnd = 0;
883   ch->reader = NULL;
884   ch->writer = NULL;
885 
886   /* assume that this call never fails; otherwise someone handed us a bad file
887      descriptor, which is forbidden :-) */
888   (void)fstat(fd, &stat_buf);
889 
890   /* decide which buffering to use, get memory for buffer */
891   if (isatty(fd)) {
892     /* do line buffering for anything that is connected to a terminal;
893        canonical input mode is assumed when reading from a terminal; likewise
894        it is assumed that positioning is not possible */
895     ch->buffering = PosixFileDescr__lineBuffer;
896   } else if (S_ISREG(stat_buf.st_mode)) {
897     /* files are buffered on a per block basis; this can only work for files,
898        since other input types would block when filling the buffer */
899     ch->buffering = PosixFileDescr__blockBuffer;
900   } else {  /* the conservative approach: don't buffer unknown fd */
901     ch->buffering = PosixFileDescr__noBuffer;
902   }
903 
904   /* handle standard file descriptors: no positioning and don't buffer
905      stderr */
906   if (((fd == PosixFileDescr__stdoutFileno) ||
907        (fd == PosixFileDescr__stdinFileno)) && standard_io[fd]) {
908     ch->positionable = 0;
909   } else if ((fd == PosixFileDescr__stderrFileno) && standard_io[fd]) {
910     ch->positionable = 0;
911     ch->buffering = PosixFileDescr__noBuffer;
912   }
913 
914   if (ch->buffering != PosixFileDescr__noBuffer) {
915 #if HAVE_ST_BLKSIZE
916     size = stat_buf.st_blksize;
917     if (size < 1024) {  /* impose a lower and upper limit on block size */
918       size = 1024;
919     } else if (size > 8192) {
920       size = 8192;
921     }
922 #else
923     size = 2048;
924 #endif
925     /* in any case `size' should be sufficiently large compared with
926        MAX_CANON */
927     ch->buf = RT0__NewBlock(size);
928     ch->sizeBuffer = size;
929   } else {
930     ch->buf = NULL;
931     ch->sizeBuffer = 0;
932   }
933 
934   DYN_TBCALL(Channel,ChannelDesc,ClearError,ch,((Channel__Channel)ch));
935   ch->readable = (mode == PosixFileDescr__readOnly) || (mode == PosixFileDescr__readWrite);
936   ch->writable = (mode == PosixFileDescr__writeOnly) || (mode == PosixFileDescr__readWrite);
937   ch->open = 1;
938 }
939 
PosixFileDescr__Truncate(PosixFileDescr__Writer w,int newLength)940 void PosixFileDescr__Truncate(PosixFileDescr__Writer w, int newLength) {
941 #if HAVE_FTRUNCATE
942   int res;
943 
944   if (w->res == Channel__done) {
945     if (!w->base->open) {
946       w->res = get_error(Channel__channelClosed, 0);
947     } else {
948       PosixFileDescr__Channel ch = (PosixFileDescr__Channel)w->base;
949       do {
950 	/* mh, ftruncate is neither ANSI nor POSIX; if there is a system out
951            there that doesn't support it we need to extend configure to check
952 	   for its presence and provide alternative code... --mva */
953         res = ftruncate(ch->fd, newLength);
954       } while ((res == -1) && (errno == EINTR));
955 
956       if (res == -1) {
957         w->res = write_error();
958       } else if (ch->bufEnd > newLength) {
959         /* truncate buffer */
960         if (ch->bufStart >= newLength) {
961           ch->bufEnd = ch->bufStart; /* empty interval means empty buffer */
962         } else {
963           ch->bufEnd = newLength;
964         }
965       }
966     }
967   }
968 #else
969   /* ftruncate not in MINGW32 API. Return error. */
970   w->res = get_error(Channel__invalidChannel, 0);
971 #endif
972 }
973 
OOC_PosixFileDescr_init(void)974 void OOC_PosixFileDescr_init(void) {
975   PosixFileDescr__errorContext = RT0__NewObject(OOC_TYPE_DESCR(PosixFileDescr,ErrorContextDesc));
976   Msg__InitContext((Msg__Context)PosixFileDescr__errorContext,
977 		   (const OOC_CHAR8*)"OOC:Core:PosixFileDescr", 24);
978 #ifdef O_BINARY
979   /* set standard I/O channels to binary mode */
980   setmode(fileno(stdin), O_BINARY);
981   setmode(fileno(stdout), O_BINARY);
982   setmode(fileno(stderr), O_BINARY);
983 #endif
984 }
985 
OOC_PosixFileDescr_destroy(void)986 void OOC_PosixFileDescr_destroy(void) {
987   /* FIXME... if we ever to module unloading  */
988 }
989