/* Copyright 1996 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* stdio.c: ANSI draft (X3J11 Oct 86) library code, section 4.9 */ /* Copyright (C) Codemist Ltd, 1988 */ /* Copyright (C) Acorn Computers Ltd., 1988 */ /* version 0.17e */ /* Incorporate _sys_tmpnam_ idea from WGD */ /* __pos of a file is now a long, and we avoid warnings by two casts */ /* of this to (int) in the calls to _sys_seek(). */ /* N.B. I am trying to factor out machine dependence via calls to */ /* routines like _sys_read_ which can be implemented as _osgbpb or */ /* NIOP as required. This file SHOULD therefore be machine independent */ /* the #include <stdio.h> imports macros getc/putc etc. Note that we must keep the two files in step (more details in ctype.c). */ #define __system_io 1 /* makes stdio.h declare more */ #include "hostsys.h" /* _sys_alloc() etc */ #include <stdio.h> /* macros for putc, getc, putchar, getchar */ #include <string.h> /* for memcpy etc */ #include <stddef.h> /* for size_t etc */ #include <stdlib.h> /* for free() */ #include <time.h> /* for time() for tmpfil() */ #include <errno.h> #include "kernel.h" /* debug */ #include "hostsys.h" extern char *_kernel_getmessage(char *msg, char *tag); extern int _fprintf_lf(FILE *fp, const char *fmt, ...); extern int _sprintf_lf(char *buff, const char *fmt, ...); /* HIDDEN EXPORTS */ int __dup(int new, int old); int _fisatty(FILE *stream); int __backspace(FILE *stream); /* strict right inverse of getc() */ #ifndef _SYS_OPEN /* Temp feature to cope with upgrade path of compiler */ #define _SYS_OPEN SYS_OPEN #endif #ifndef EDOM #define EDOM 1 #endif #define NO_DEBUG /* in the shared library world, __iob and _errno are generated by s.clib, */ /* in order to make it easier to keep their position fixed */ /*FILE __iob[_SYS_OPEN];*/ /* Note that the real name for errno is _errno, and the macro errno is */ /* defined in errno.h */ /*volatile int errno;*/ typedef struct __extradata { /* * BODGE BODGE BODGE BODGE * sscanf needs to know the size of this structure, so if you change * it, alter sscanf's view of it. */ unsigned char __lilbuf[2]; /* single byte buffer for them that want it */ /* plus an unget char is put in __lilbuf[1] */ long _lspos; /* what __pos should be (set after lazy seek) */ unsigned char *__extent; /* extent of writes into the current buffer */ int __buflim; /* used size of buffer */ int __savedicnt; /* after unget contains old icnt */ int __savedocnt; /* after unget contains old ocnt */ } _extradata, *_extradatap; static _extradatap _extra; #define EXTENT(stream) ((stream)->__extrap->__extent > (stream)->__ptr \ ? (stream)->__extrap->__extent : (stream)->__ptr) /* Explanation of the _IOxxx bits: */ /* IONOWRITES says that the file is positioned at the end of the current */ /* buffer (as when performing sequential reads), while IODIRTIED */ /* indicates that its position is at the start of the current buffer (as */ /* in the usual case when writing a file). When the relevant bit is not */ /* set and a transfer is needed _sys_seek() is used to reposition the */ /* file. Now extra bit _IOSEEK indicating that repositioning of the file */ /* is required before performing I/O */ /* Extra bit _IOLAZY added to indicate that a seek has been requested */ /* but has been deferred until the next fill or flush buffer */ /* I use IONOWRITES and IONOREADS to check the restriction */ /* that read and write operations must be separated by fseek/fflush. */ /* N.B. bits up to (and including 0xfff) in <stdio.h>, as below: */ #ifdef never #define _IOREAD 0x00000001 /* open for input */ #define _IOWRITE 0x00000002 /* open for output */ #define _IOBIN 0x00000004 /* binary stream */ #define _IOSTRG 0x00000008 /* string stream */ #define _IOSEEK 0x00000010 /* physical seek required before IO */ #define _IOLAZY 0x00000020 /* possible seek pending */ #define _IOEOF 0x00000040 /* end-of-file reached */ #define _IOERR 0x00000080 /* error occurred on stream */ #define _IOFBF 0x00000100 /* fully buffered IO */ #define _IOLBF 0x00000200 /* line buffered IO */ #define _IONBF 0x00000400 /* unbuffered IO */ #define _IOSBF 0x00000800 /* system allocated buffer */ #define _IOAPPEND 0x00008000 /* must seek to eof before write */ #endif #define _IONOWRITES 0x00001000 /* last op was a read */ #define _IONOREADS 0x00002000 /* last op was a write */ #define _IOPEOF 0x00004000 /* 'pending' EOF */ #define _IODIRTIED 0x00010000 /* buffer has been written to */ #define _IOBUFREAD 0x00020000 /* indicates that the buffer being */ /* written was read from file, so a */ /* seek to start of buffer is required*/ #define _IOADFSBUG 0x00040000 /* indicates that the underlying */ /* system buffer was last written to */ /* and an ensure should be done if */ /* a seek is about to happen before */ /* the next read is performed */ #define _IOUNGET 0x00080000 /* last op was an unget */ #define _IOSHARED 0x00100000 /* 2 streams share the same file */ #define _IOUSERFILE 0x00200000 /* set if should be closed by user */ /* terminateio in a module */ #define _IOACTIVE 0x00400000 /* some IO operation already performed*/ #define _IODEL 0xad800000 /* for safety check 9 bits */ #define _IODELMSK 0xff800000 /* first functions for macros in <stdio.h> */ int (fgetc)(FILE *stream) { return getc(stream); } int (fputc)(int ch, FILE *stream) { return putc(ch, stream); } int (getc)(FILE *stream) { return getc(stream); } int (getchar)() { return getchar(); } int (putc)(int ch, FILE *stream) { return putc(ch, stream); } int (putchar)(int ch) { return putchar(ch); } int (feof)(FILE *stream) { return feof(stream); } int (ferror)(FILE *stream) { return ferror(stream); } #define STDOUT stderr #ifdef DEBUG #define dbmsg(m, m2) if (stream > STDOUT || stream == stdin) {int last ;int iw; char v[128]; \ sprintf(v, m, m2); \ for(iw=0;v[iw];_kernel_oswrch(last=v[iw++])); \ if (last = 10)_kernel_oswrch(13);} #define dbmsg_noNL(m, m2) if (stream > STDOUT || stream == stdin) {int last ;int iw; char v[128]; \ sprintf(v, m, m2); \ for(iw=0;v[iw];_kernel_oswrch(last=v[iw++]));} #else #define dbmsg(m, m2) #define dbmsg_noNL(m, m2) #endif /* put this here too */ void clearerr(FILE *stream) { /* we should do more in 'clearerr' resetting __pos __ptr and _cnt */ if (!stream) return; stream->__flag &= ~(_IOEOF+_IOERR+_IOPEOF); } #define seterr(stream) ((stream)->__flag |= _IOERR, \ (stream)->__icnt = (stream)->__ocnt = 0) int setvbuf(FILE *stream, char *buf, int type, size_t size) { int flags = stream -> __flag; unsigned char *ubuf = (unsigned char *) buf; /* costs nothing */ if ((!(flags & _IOREAD+_IOWRITE)) || (flags & _IOACTIVE)) return 1; /* failure - not open || already active */ switch (type) { default: return 1; /* failure */ case _IONBF: ubuf = stream->__extrap->__lilbuf; size = 1; break; case _IOLBF: case _IOFBF: if (size-1 >= 0xffffff) return 1; /* unsigned! */ break; } stream->__ptr = stream->__base = ubuf; stream->__bufsiz = size; stream->__flag = (stream->__flag & ~(_IOSBF+_IOFBF+_IOLBF+_IONBF)) | type; return 0; /* success */ } void setbuf(FILE *stream, char *buf) { (void) setvbuf(stream, buf, (buf!=0 ? _IOFBF : _IONBF), BUFSIZ); } int __backspace(FILE *stream) { if (!(stream->__flag & _IOEOF) && (stream->__ptr > stream->__base)) { ++(stream->__icnt); --(stream->__ptr); return 0; } return EOF; } int ungetc(int c,FILE *stream) { /* made into a fn to evaluate each arg once. */ if (c==EOF || (stream->__flag & (_IOUNGET+_IONOREADS))) return EOF; /* put char into unget buffer */ stream->__extrap->__lilbuf[1] = c; stream->__flag = (stream->__flag | _IOUNGET | _IONOWRITES | _IOACTIVE) & ~(_IOEOF); stream->__extrap->__savedocnt = stream->__ocnt; stream->__extrap->__savedicnt = stream->__icnt; stream->__icnt = stream->__ocnt = 0; return c; } static int _writebuf(unsigned char *buf, int len, FILE *stream) { int w; FILEHANDLE fh = stream->__file; int flag = stream->__flag; if (flag & _IOSHARED) /* this is really gross */ { flag |= _IOSEEK; stream->__pos = _sys_flen(fh); dbmsg("_IOSHARED so zoom to end %d\n", (int)stream->__pos); } if (flag & _IOSEEK+_IOBUFREAD) { dbmsg("_writebuf seeking to %d\n", (int)stream->__pos); if (_sys_seek(fh, (int)stream->__pos) < 0) { seterr(stream); return EOF; } stream->__flag = (flag &= ~(_IOSEEK+_IOBUFREAD)); } dbmsg_noNL("_writebuf pop goes the stoat %i, ", fh); dbmsg_noNL("%X, ", buf); dbmsg_noNL("%d, ", len); dbmsg("%X\n", flag); w = _sys_write(fh, buf, len, flag); stream->__flag |= _IOADFSBUG; dbmsg("_sys_write_ returned %d\n", w); stream->__pos += len - (w & 0x7fffffffL); if (w!=0) /* AM: was (w<0) but trap unwritten chars as error too */ { seterr(stream); return(EOF); } dbmsg("filelen = %d\n",_sys_flen(fh)); /* length of this file */ return 0; } static int _fflush(FILE *stream) { unsigned char *buff = stream->__base; unsigned char *extent = EXTENT(stream); dbmsg("%s\n", "_fflush"); stream->__flag &= ~_IOUNGET; if ((stream->__flag & _IOREAD+_IOWRITE) == _IOREAD) return 0; if ((stream->__flag & _IOERR+_IOWRITE) != _IOWRITE) return EOF; /* N.B. really more to do here for ANSI input stream */ if (stream->__flag & _IODIRTIED) { /* only write if dirty buffer - this avoids problems with writing to a file opened in append (or read+write) mode when only input has been done since the last fflush/fseek. */ dbmsg("%s\n", "dirty buffer"); if (extent != buff) /* something to do */ { dbmsg("%s\n", "call _writebuf"); if (_writebuf(buff, extent - buff, stream)) return EOF; } stream->__ptr = stream->__extrap->__extent = extent = buff; /* the next line forces a call to __filbuf/__flsbuf on next putc/getc - */ /* this is necessary since change of direction may happen. */ stream->__ocnt = 0; stream->__flag &= ~_IODIRTIED; dbmsg("%s\n", "clear IODIRTIED"); } else dbmsg("%s\n", "not a dirty buffer"); /* now do quick frig to fix I/O streams: essentially fseek(s, 0, SEEK_CUR). if ((stream->__flag & (_IOREAD+_IOWRITE)) == (_IOREAD+_IOWRITE)) { stream->__flag = stream->__flag & ~(_IOEOF|_IONOWRITES|_IOREADS|_IOPEOF) |_IOSEEK; stream->__icnt = stream->__ocnt = 0; stream->__pos += (extent - buff); stream->__ptr = buff; } */ return 0; } static void _deferredlazyseek(FILE *stream) { dbmsg("deferredlazyseek to %d\n", (int)stream->__extrap->_lspos); /* only here because of a seek */ stream->__flag &= ~_IOLAZY; if (stream->__pos != stream->__extrap->_lspos) { _fflush(stream); /* clear EOF condition */ dbmsg("%s\n", "clear IODIRTIED"); stream->__flag = stream->__flag & ~(_IONOWRITES | _IONOREADS) | _IOSEEK; stream->__pos = stream->__extrap->_lspos; stream->__ptr = stream->__extrap->__extent = stream->__base; } else dbmsg("%s\n", ".....already there"); stream->__flag &= ~(_IOEOF | _IOPEOF); } extern int __flsbuf(int ch, FILE *stream) { int flag; dbmsg_noNL("%s ", "__flsbuf"); dbmsg("%c\n", ch); stream->__flag = (stream->__flag & ~_IOUNGET) | _IOACTIVE; if (stream->__flag & _IOLAZY) _deferredlazyseek(stream); if ((stream->__ocnt < -1) && !(stream->__flag & _IOLBF)) { /* buffer ~empty, sequence of events that lead to here are : */ /* put seek get seek put */ dbmsg("flushbuf negative ocnt = %d\n", stream->__ocnt); stream->__ocnt = (-stream->__ocnt) - 2; stream->__flag |= (_IODIRTIED+_IONOREADS); /* we are writing */ return *(stream->__ptr)++ = (ch); } flag = stream->__flag; if ((flag & _IOERR+_IOSTRG+_IOWRITE+_IONOWRITES) != _IOWRITE) { seterr(stream); dbmsg("%s\n", "!= _IOWRITE"); return EOF; } /* the next conditional code is ACN's view of that APPEND means seek to */ /* EOF after EVERY fflush, not just initially. Hmm, ANSI really should */ /* clarify - the problem is perhaps that we wish to seek to EOF after */ /* fflush after read, but not after fflush after write? */ /* The line "if ((flag & (_IOSEEK+_IOAPPEND)) == (_IOSEEK+_IOAPPEND))" */ /* which PH10 suggested does not work, try the sequence: */ /* create file with something in it; close file; open the file (append mode)*/ /* seek to start; write a char; THE CHAR WRITTEN IS NOT WRITTEN TO EOF */ /* The following line has been reinstated cos it works. */ if ((flag & (_IONOREADS+_IOSEEK+_IOAPPEND)) == _IOAPPEND) /* Will somebody please help ACN remember/understand what was going on here!*/ { /* first write to APPEND file after FFLUSH, but not FSEEK nor */ /* fopen (does its own FSEEK) */ fseek(stream, 0L, SEEK_END); if (stream->__flag & _IOLAZY) _deferredlazyseek(stream); flag = stream->__flag; } dbmsg("%s\n", "set IODIRTIED"); stream->__flag = (flag |= (_IODIRTIED+_IONOREADS)); /* we are writing */ if (stream->__base == NULL) { if (_sys_istty(stream)) { /* terminal - unbuffered */ stream->__ptr = stream->__base = stream->__extrap->__lilbuf; stream->__bufsiz = 1; stream->__flag = (flag |= _IONBF); } else { /* allocate default system buffer */ stream->__ptr = stream->__base = _sys_alloc(stream->__bufsiz); stream->__flag |= (flag |= _IOSBF); if ((flag & _IOLBF+_IOFBF) == 0) stream->__flag |= (flag |= _IOFBF); } } if (flag & _IOFBF) /* system or user buffer */ { unsigned char *buff = stream->__base; int count = EXTENT(stream) - buff; if (count != 0) { if (_writebuf(buff, count, stream)) return EOF; } stream->__ptr = stream->__extrap->__extent = buff+1; stream->__ocnt = stream->__bufsiz - 1; stream->__extrap->__buflim = stream->__bufsiz; return (*buff = ch); } else /* no buffer (i.e. 1 char private one) or line buffer */ { unsigned char *buff = stream->__base; int count; *stream->__ptr++ = ch; /* always room */ count = EXTENT(stream) - buff; stream->__extrap->__buflim = stream->__bufsiz; if ((flag & _IONBF) || (unsigned char)ch == '\n' || count >= stream->__bufsiz) { stream->__ptr = stream->__extrap->__extent = buff; stream->__ocnt = 0; /* 2^31 is big but finite */ return _writebuf(buff, count, stream) ? EOF : (unsigned char)ch; } return (unsigned char)ch; } } extern int __filbuf(FILE *stream) { int w; unsigned char *buff; FILEHANDLE fh; int request; dbmsg("%s\n", "__filbuf"); stream->__flag |= _IOACTIVE; if (stream->__flag & _IOUNGET) { stream->__icnt = stream->__extrap->__savedicnt; stream->__ocnt = stream->__extrap->__savedocnt; stream->__flag &= ~_IOUNGET; return stream->__extrap->__lilbuf[1]; } if (stream->__flag & _IOLAZY) _deferredlazyseek(stream); /* note that sscanf (q.v.) requires this next line to yield EOF */ if ((stream->__flag & (_IOEOF+_IOERR+_IOSTRG+_IOREAD+_IOPEOF+_IONOREADS)) != _IOREAD) { stream->__icnt = 0; /* 2^31 is big but finite */ if (stream->__flag & _IOEOF+_IOPEOF+_IOSTRG) /* writing ok after EOF read according to ansi */ stream->__flag = stream->__flag & ~(_IONOWRITES+_IOPEOF) | _IOEOF; else seterr(stream); return(EOF); } if ((stream->__flag & _IOSBF+_IONBF+_IODIRTIED) == 0) if ((stream->__base == NULL) && ((stream->__flag & _IODIRTIED) == 0)) { /* allocate default system buffer */ stream->__ptr = stream->__base = _sys_alloc(stream->__bufsiz); stream->__flag |= _IOSBF; if ((stream->__flag & _IOLBF+_IOFBF) == 0) stream->__flag |= _IOFBF; } if (stream->__icnt < -1) { /* buffer ~empty, came here cos seek wanted to to tidy up flags etc */ dbmsg("fillbuf negative icnt = %d\n", stream->__icnt); stream->__icnt = (-stream->__icnt) - 2; stream->__flag |= _IONOWRITES; /* we are reading */ return *(stream->__ptr)++; } fh = stream->__file; if (stream->__flag & _IOSEEK) { if (stream->__flag & _IODIRTIED) _fflush(stream); else { dbmsg("fillbuf seeking to %d\n", (int)stream->__pos); if (stream->__flag & _IOADFSBUG) { _sys_ensure(fh); stream->__flag &= ~_IOADFSBUG; } if (_sys_seek(fh, (int)stream->__pos) < 0) { seterr(stream); return EOF; } } } stream->__flag |= _IONOWRITES; /* we are reading */ if (stream->__flag & _IODIRTIED) { int extent = (int) (EXTENT(stream) - stream->__base); request = stream->__bufsiz - extent; if (request == 0) { _fflush(stream); request = stream->__bufsiz; } else { dbmsg("fillbuf flag %X\n", stream->__flag); if ((stream->__flag & (_IOBUFREAD+_IOSEEK)) == 0) { dbmsg("fillbuf dirty buffer, read into end,seeking to %d\n",(int) stream->__pos+extent); if (stream->__flag & _IOADFSBUG) { _sys_ensure(fh); stream->__flag &= ~_IOADFSBUG; } if (_sys_seek(fh, (int) stream->__pos + extent) < 0) { seterr(stream); return EOF; } else stream->__flag |= _IOBUFREAD; } } buff = stream->__base + (stream->__bufsiz - request); dbmsg_noNL("fillbuf not splatting out writ part of file request %d ", request); dbmsg("at buff %X\n", (int)buff); } else { request = stream->__bufsiz; buff = stream->__base; stream->__pos += stream->__ptr - buff; /* add buf size for ftell() */ } stream->__flag &= ~_IOSEEK; dbmsg("READING FROM FILE REQUEST = %d\n", request); dbmsg("filelen = %d\n",_sys_flen(fh)); /* length of this file */ stream->__icnt = 0; w = _sys_read(fh, buff, request, stream->__flag); stream->__flag &= ~_IOADFSBUG; dbmsg("_sys_read_ returned %d\n", w); if (w<0) { if (w == _kernel_ERROR) { seterr(stream); return EOF; } /* this deals with operating systems with 'early' eof */ stream->__flag |= _IOPEOF; w = w & 0x7fffffff; } w = request - w; dbmsg("read %d bytes\n", w); stream->__extrap->__extent = buff + w; stream->__extrap->__buflim = stream->__bufsiz; if (w==0) /* this deals with operating systems with 'late' eof */ { stream->__flag |= _IOEOF; /* is this case independent? */ stream->__flag &= ~_IONOWRITES; /* writing OK after EOF read */ stream->__icnt = 0; stream->__ptr = buff; /* just for fun - NB affects ftell() - check */ return(EOF); } else { stream->__icnt = w-1; stream->__ptr = buff+1; stream->__flag |= _IOBUFREAD; return(buff[0]); } } static int _fillb2(FILE *stream) { if (__filbuf(stream) == EOF) return EOF; stream->__icnt++; stream->__ptr--; return 0; } int fclose(FILE *stream) { /* MUST be callable on a closed file - if stream clr then no-op. */ FILEHANDLE fh; unsigned char *buff; int flag; _extradatap extrap; int res = 0; /* Deal with programs calling fclose(0), ECN 21-09-93 */ if (!stream) return EOF; fh = stream->__file; buff = stream->__base; flag = stream->__flag; extrap = stream->__extrap; if (!(flag & _IOREAD+_IOWRITE)) return EOF; /* already closed */ if (!(flag & _IOSTRG)) /* from _fopen_string */ { int fd = _SYS_OPEN; res = fflush(stream); if (flag & _IOSHARED) { for (fd = 0; fd < _SYS_OPEN; ++fd) { FILE *f = &__iob[fd]; if ((f != stream) && (f->__flag & _IOREAD+_IOWRITE) && /* f is open */ (f->__file == fh) && (f->__flag & _IOSHARED)) { f->__flag &= ~_IOSHARED; /* no longer shared */ stream->__flag &= ~_IOSHARED; break; } } } /* Assert: fd != SYS_OPEN => fh is shared and mustn't be closed */ if (fd == _SYS_OPEN && _sys_close(fh) < 0) res = EOF; /* close real file */ if (flag & _IOSBF) free(buff); /* free buffer if system */ if ((flag & _IODELMSK) == _IODEL) { char name[L_tmpnam]; _sys_tmpnam_(name, stream->__signature); if (remove(name) != 0) res = EOF; /* delete the file if pos */ } } memclr(stream->__extrap, sizeof(_extradata)); memclr(stream, sizeof(FILE)); stream->__extrap = extrap; return res; } FILE *freopen(const char *name, const char *mode, FILE *iob) { /* The use of modes "r+", "w+" and "a+" is not fully thought out */ /* yet, in that calls to __flsbuf may write back stuff that was */ /* loaded by __filbuf and thereby corrupt the file. */ /* This is now just about fixed given the ANSI restriction that */ /* calls to getc/putc must be fflush/fseek separated. */ FILEHANDLE fh; int flag, openmode; /* nasty magic numbers for openmode */ fclose(iob); switch (*mode++) { default: return(NULL); /* mode is incorrect */ case 'r': flag = _IOREAD; openmode = OPEN_R; break; case 'w': flag = _IOWRITE; openmode = OPEN_W; break; case 'a': flag = _IOWRITE | _IOAPPEND; openmode = OPEN_A; break; } for (;;) { switch (*mode++) { case '+': flag |= _IOREAD+_IOWRITE, openmode |= OPEN_PLUS; continue; case 'b': flag |= _IOBIN, openmode |= OPEN_B; continue; } if (*(mode-1) == 't') openmode |= OPEN_T; break; } if ((fh = _sys_open(name, openmode)) == NONHANDLE) return NULL; if (_kernel_client_is_module()) flag |= _IOUSERFILE; iob->__ptr = iob->__base = NULL; iob->__bufsiz = BUFSIZ; iob->__flag = flag; iob->__file = fh; if (openmode & OPEN_A) fseek(iob, 0L, SEEK_END); /* a or a+ */ return iob; } FILE *fopen(const char *name, const char *mode) { int i; for (i=3; i<_SYS_OPEN; i++) { FILE *stream = &__iob[i]; if (!(stream->__flag & _IOREAD+_IOWRITE)) /* if not open then try it */ return (freopen(name, mode, stream)); } return 0; /* no more i/o channels allowed for */ } FILEHANDLE __dup(int new, int old) { FILE *s_new, *s_old; (void) fclose(&__iob[new]); s_new = &__iob[new]; s_old = &__iob[old]; s_old->__flag |= _IOSHARED; s_new->__flag = s_old->__flag & (_IOREAD+_IOWRITE+_IOAPPEND+_IOSHARED+ _IOSBF+_IOFBF+_IOLBF+_IONBF); s_new->__file = s_old->__file; return s_new->__file; } FILE *_fopen_string_file(const char *data, int length) { /* open a file that will read data from the given string argument */ /* The declaration of this function in #include "hostsys.h" suggests */ /* that this function is of type (void *), so this definition will lead */ /* to a warning message. */ int i; for (i=3; i<_SYS_OPEN; i++) { FILE *stream = &__iob[i]; if (!(stream->__flag & _IOREAD+_IOWRITE)) /* if not open then try it */ { fclose(stream); stream->__flag = _IOSTRG+_IOREAD+_IOACTIVE; stream->__ptr = stream->__base = (unsigned char *)data; stream->__icnt = length; return stream; } } return 0; /* no more i/o channels allowed for */ } int _fisatty(FILE *stream) /* not in ANSI, but related needed for ML */ { if ((stream->__flag & _IOREAD) && _sys_istty(stream)) return 1; return 0; } /* initialisation/termination code... */ void _initio(char *f1,char *f2,char *f3) { int i; char v[128]; memclr(__iob, _SYS_OPEN*sizeof(FILE)); /* _extra = calloc(_SYS_OPEN, sizeof(_extradata)); */ _extra = _sys_alloc(_SYS_OPEN * sizeof(_extradata)); memclr(_extra, _SYS_OPEN * sizeof(_extradata)); for (i=0; i<_SYS_OPEN; i++) __iob[i].__extrap = _extra+i; /* In the next lines DO NOT use standard I/O for error msgs (not open yet) Moreover, open in this order so we do not create/overwrite output if input does not exist. */ if (freopen(f3, "w", stderr) == 0) _sprintf_lf(v,_kernel_getmessage("Couldn't write %s", "C46"), f3), _sys_msg(v), exit(1); if (freopen(f1, "r", stdin) == 0) _sprintf_lf(v,_kernel_getmessage("Couldn't read %s", "C47"), f1), _sys_msg(v), exit(1); if (freopen(f2, "w", stdout) == 0) _sprintf_lf(v,_kernel_getmessage("Couldn't write %s", "C46"), f2), _sys_msg(v), exit(1); } void _terminateio() { int i; int closeall = !_kernel_client_is_module(); for (i=3; i<_SYS_OPEN; i++) { FILE *f = &__iob[i]; if (closeall || (f->__flag & _IOUSERFILE)) fclose(f); } /* for cowardice do stdin, stdout, stderr last (in that order) */ for (i=0; i<3; i++) { FILE *f = &__iob[i]; if (closeall || (f->__flag & _IOUSERFILE)) { fclose(f); if (!closeall) freopen(TTYFILENAME, (f == stdin ? "r" : "w"), f); } } if (_extra) { free(_extra); _extra = 0; } } /* now the less machine dependent functions ... */ char *fgets(char *s, int n, FILE *stream) { char *a = s; if (n <= 1) return NULL; /* best of a bad deal */ do { int ch = getc(stream); if (ch == EOF) /* error or EOF */ { if (s == a) return NULL; /* no chars -> leave */ if (ferror(stdin)) a = NULL; break; /* add NULL even if ferror(), spec says 'indeterminate' */ } if ((*s++ = ch) == '\n') break; } while (--n > 1); *s = 0; return a; } char *gets(char *s) { char *a = s; for (;;) { int ch = getc(stdin); if (ch == EOF) /* error or EOF */ { if (s == a) return NULL; /* no chars -> leave */ if (ferror(stdin)) a = NULL; break; /* add NULL even if ferror(), spec says 'indeterminate' */ } if (ch == '\n') break; *s++ = ch; } *s = 0; return a; } int fputs(const char *s, FILE *stream) { char c; while((c = *s++) != 0) if(putc(c, stream)==EOF) return(EOF); return(0); } int puts(const char *s) { char c; while ((c = *s++) != 0) if (putchar(c) == EOF) return EOF; return putchar('\n'); } /* _read improved to use __filbuf and block moves. Optimisation to memcpy too if word move. Still possible improvments avoiding copy but I don't want to do these yet because of interactions (e.g. __pos of a file). N.B. _read is not far from unix 'read' */ static int _read(char *ptr, int nbytes, FILE *stream) { int i = nbytes; dbmsg("%s\n", "_read"); if (i == 0) return 0; if (stream->__flag & _IOUNGET) { *ptr++ = __filbuf(stream); i--; } if (stream->__flag & _IOLAZY) _deferredlazyseek(stream); do { dbmsg("in _read, icnt = %d\n", stream->__icnt); if (i <= stream->__icnt) { memcpy(ptr, stream->__ptr, i); stream->__icnt -= i; stream->__ptr += i; return nbytes; } else if (stream->__icnt >= 0) { memcpy(ptr, stream->__ptr, stream->__icnt); ptr += stream->__icnt, i -= stream->__icnt; stream->__ptr += stream->__icnt, stream->__icnt = -1; /* for __pos */ } else stream->__icnt--; } while (_fillb2(stream) != EOF); return nbytes-i; /* for (i=0; i<nbytes; i++) { if ((ch = getc(stream)) == EOF) return i; *ptr++ = ch; } return nbytes; */ } size_t fread(void *ptr, size_t itemsize, size_t count, FILE *stream) { /* ANSI spec says EOF and ERR treated the same as far as fread * is concerned and that the number of WHOLE items read is returned. */ dbmsg("fread %d\n", count); return itemsize == 0 ? 0 /* slight ansi irrationality */ : _read(ptr, itemsize*count, stream) / itemsize; } static int _write(const char *ptr, int nbytes, FILE *stream) { int i; for(i=0; i<nbytes; i++) if (putc(*ptr++, stream) == EOF) return 0; /* H&S say 0 on error */ return nbytes; } size_t fwrite(const void *ptr, size_t itemsize, size_t count, FILE *stream) { /* The comments made about fread apply here too */ dbmsg("fwrite %d\n", count); return itemsize == 0 ? count : _write(ptr, itemsize*count, stream) / itemsize; } /* back to machine dependent functions */ #define _ftell(stream) ( (stream)->__flag & _IOLAZY \ ? (stream)->__extrap->_lspos \ : (stream)->__pos + (stream)->__ptr - (stream)->__base) long int ftell(FILE *stream) { long pos; if (!(stream->__flag & _IOREAD+_IOWRITE)) /* already closed */ { errno = EDOM; return -1L; } pos = _ftell(stream); if (stream->__flag & _IOUNGET && pos > 0) --pos; return pos; } /* The treatment of files that can be written to seems complicated in fseek */ int fseek(FILE *stream, long int offset, int whence) { FILEHANDLE fh = stream->__file; int flag = stream->__flag; dbmsg_noNL("%s ", "SEEK ENTRY"); if (!(flag & _IOREAD+_IOWRITE+_IOSHARED) || _sys_istty(stream)) return(2); /* fseek impossible */ switch(whence) { case SEEK_SET: break; /* relative to file start */ case SEEK_CUR: offset += ftell(stream); /* relative seek */ break; case SEEK_END: { long int filelen, filepos; filelen = _sys_flen(fh); /* length of this file */ dbmsg("filelen in seek = %d\n", (int)filelen); if (filelen<0) /* failed to read length */ { seterr(stream); return 1; } filepos = stream->__pos + EXTENT(stream) - stream->__base; if (stream->__flag & _IOLAZY && filepos < stream->__extrap->_lspos) filepos = stream->__extrap->_lspos; if (filepos>filelen) /* only possible on write */ filelen = filepos; /* allow for stuff buffered */ offset += filelen; /* relative to end of file */ } break; default: seterr(stream); return(2); /* illegal operation code */ } if (offset < 0) { seterr(stream); return 2; } /* fseek impossible */ if ((flag & _IONOREADS) && stream->__extrap->__extent < stream->__ptr) stream->__extrap->__extent = stream->__ptr; dbmsg_noNL("%s ", "SEEK"); dbmsg_noNL("__pos %d", (int)stream->__pos); dbmsg_noNL(" offset %d", (int)offset); dbmsg_noNL(" buflim %d", stream->__extrap->__buflim); dbmsg_noNL(" __ptr %X", (int)stream->__ptr); dbmsg_noNL(" __icnt %d", stream->__icnt); dbmsg_noNL(" __ocnt %d", stream->__ocnt); dbmsg_noNL(" __base %X", (int)stream->__base); if (offset < stream->__pos || offset > stream->__pos + EXTENT(stream) - stream->__base || offset >= stream->__pos + stream->__extrap->__buflim) { /* outside buffer */ dbmsg("%s\n", " outside buffer"); flag |= _IOLAZY; stream->__icnt = stream->__ocnt = 0; stream->__extrap->_lspos = offset; } else { /* inside buffer */ dbmsg("%s\n", " inside buffer"); offset -= stream->__pos; if (flag & _IOWRITE) stream->__ocnt = -(stream->__extrap->__buflim - (int)offset); if (flag & _IOREAD) stream->__icnt = -((int)(EXTENT(stream) - stream->__base) - (int)offset); stream->__ptr = stream->__base + offset; flag &= ~_IOLAZY; } dbmsg_noNL("AFTER SEEK __ptr %X", (int)stream->__ptr); dbmsg_noNL(" __icnt %d", stream->__icnt); dbmsg(" __ocnt %d\n", stream->__ocnt); stream->__flag = flag & ~(_IOEOF+_IONOWRITES+_IONOREADS+_IOUNGET); return 0; } static int _do_fflush(FILE *stream) { /* ignore the effect of a previous unget on the file position indicator by using _ftell rather than ftell */ if (stream->__flag & _IOREAD+_IOWRITE) /* not open */ { long offset = _ftell(stream); int res; dbmsg("%s\n", "fflush"); if (stream->__flag & _IOLAZY) _deferredlazyseek(stream); stream->__flag &= ~(_IONOREADS+_IONOWRITES); res =_fflush(stream); fseek(stream, offset, SEEK_SET); return res; } else return 0; } int fflush(FILE *stream) { /* new definition (ANSI May 88) says that if stream == NULL, apply fflush to all applicable streams */ int res = 0; if (stream != NULL) res =_do_fflush(stream); else { int i; for (i=0; i<_SYS_OPEN; i++) if (_do_fflush(&__iob[i]) != 0) res = EOF; } return res; } void rewind(FILE *stream) { fseek(stream, 0L, SEEK_SET); clearerr(stream); } /* the following routines need to become the main entry I suppose */ int fgetpos(FILE *stream, fpos_t *pos) { pos->__lo = ftell(stream); return 0; } int fsetpos(FILE *stream, const fpos_t *pos) { int res = fseek(stream, pos->__lo, SEEK_SET); if (res) errno = EDOM; return res; } static char _tmp_file_name[L_tmpnam] = ""; static int _tmp_file_sig; char *tmpnam(char *a) { /* Obtaining an unique name is tolerably nasty - what I do here is */ /* derive the name (via _sys_tmpnam_()) */ /* from an integer that is constructed out of a serial number combined */ /* with the current clock setting. An effect of this is that the file */ /* name can be reconstructed from a 32-bit integer for when I want to */ /* delete the file. The serial number is held in zero page and is */ /* returned and incremented by __counter(). This ensures uniqueness */ /* if tmpnam is called from two applications within the same second. */ _kernel_osfile_block fb; if (a==NULL) a = _tmp_file_name; do { _tmp_file_sig = ((int)time(NULL) << 8) | __counter(); _sys_tmpnam_(a, _tmp_file_sig); } while (_kernel_osfile(17, a, &fb) != 0); return a; } char *__old_tmpnam(char *a) { return tmpnam(a); } FILE *tmpfile() { char name[L_tmpnam]; FILE *f; f = fopen(tmpnam(name), "w+b"); if (f) { f->__flag |= _IODEL; f->__signature = _tmp_file_sig; } return f; } void perror(const char *s) { char b[256]; if (s != 0 && *s != 0) fprintf(stderr, "%s: ", s); fprintf(stderr, "%s\n", _strerror(errno, b)); } /* end of stdio.c */