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