stdio 36.4 KB
Newer Older
Neil Turton's avatar
Neil Turton committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/* 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 */
Kevin Bracey's avatar
Kevin Bracey committed
44
#include "hostsys.h"
Neil Turton's avatar
Neil Turton committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

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;
Kevin Bracey's avatar
Kevin Bracey committed
231
        stream->__pos = _sys_flen(fh);
Neil Turton's avatar
Neil Turton committed
232 233 234 235 236
dbmsg("_IOSHARED so zoom to end %d\n", (int)stream->__pos);
    }
    if (flag & _IOSEEK+_IOBUFREAD)
    {
dbmsg("_writebuf seeking to %d\n", (int)stream->__pos);
Kevin Bracey's avatar
Kevin Bracey committed
237
        if (_sys_seek(fh, (int)stream->__pos) < 0)
Neil Turton's avatar
Neil Turton committed
238 239 240 241 242 243 244 245 246
        {   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);
Kevin Bracey's avatar
Kevin Bracey committed
247
    w = _sys_write(fh, buf, len, flag);
Neil Turton's avatar
Neil Turton committed
248 249 250 251 252 253 254
    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);
    }
Kevin Bracey's avatar
Kevin Bracey committed
255
dbmsg("filelen = %d\n",_sys_flen(fh));           /* length of this file      */
Neil Turton's avatar
Neil Turton committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    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)
Kevin Bracey's avatar
Kevin Bracey committed
362
    {   if (_sys_istty(stream)) {         /* terminal - unbuffered  */
Neil Turton's avatar
Neil Turton committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
            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) {
Kevin Bracey's avatar
Kevin Bracey committed
450
          _sys_ensure(fh);
Neil Turton's avatar
Neil Turton committed
451 452
          stream->__flag &= ~_IOADFSBUG;
        }
Kevin Bracey's avatar
Kevin Bracey committed
453
        if (_sys_seek(fh, (int)stream->__pos) < 0)
Neil Turton's avatar
Neil Turton committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
        {   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) {
Kevin Bracey's avatar
Kevin Bracey committed
473
            _sys_ensure(fh);
Neil Turton's avatar
Neil Turton committed
474 475
            stream->__flag &= ~_IOADFSBUG;
          }
Kevin Bracey's avatar
Kevin Bracey committed
476
          if (_sys_seek(fh, (int) stream->__pos + extent) < 0)
Neil Turton's avatar
Neil Turton committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
          {   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);
Kevin Bracey's avatar
Kevin Bracey committed
494
dbmsg("filelen = %d\n",_sys_flen(fh));             /* length of this file */
Neil Turton's avatar
Neil Turton committed
495
    stream->__icnt = 0;
Kevin Bracey's avatar
Kevin Bracey committed
496
    w = _sys_read(fh, buff, request, stream->__flag);
Neil Turton's avatar
Neil Turton committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
    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 &&
Kevin Bracey's avatar
Kevin Bracey committed
563
            _sys_close(fh) < 0) res = EOF;          /* close real file */
Neil Turton's avatar
Neil Turton committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
        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 */
Kevin Bracey's avatar
Kevin Bracey committed
589 590
        case 'r': flag = _IOREAD;  openmode = OPEN_R; break;
        case 'w': flag = _IOWRITE; openmode = OPEN_W; break;
Neil Turton's avatar
Neil Turton committed
591
        case 'a': flag = _IOWRITE | _IOAPPEND;
Kevin Bracey's avatar
Kevin Bracey committed
592
                                   openmode = OPEN_A; break;
Neil Turton's avatar
Neil Turton committed
593 594 595 596
    }
    for (;;)
    {   switch (*mode++)
        {
Kevin Bracey's avatar
Kevin Bracey committed
597
    case '+':   flag |= _IOREAD+_IOWRITE, openmode |= OPEN_PLUS;
Neil Turton's avatar
Neil Turton committed
598
                continue;
Kevin Bracey's avatar
Kevin Bracey committed
599
    case 'b':   flag |= _IOBIN, openmode |= OPEN_B;
Neil Turton's avatar
Neil Turton committed
600 601
                continue;
        }
Kevin Bracey's avatar
Kevin Bracey committed
602
        if (*(mode-1) == 't') openmode |= OPEN_T;
Neil Turton's avatar
Neil Turton committed
603 604 605 606 607 608 609
        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;
Kevin Bracey's avatar
Kevin Bracey committed
610
    if (openmode & OPEN_A) fseek(iob, 0L, SEEK_END);  /* a or a+             */
Neil Turton's avatar
Neil Turton committed
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    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 */
Kevin Bracey's avatar
Kevin Bracey committed
658
{   if ((stream->__flag & _IOREAD) && _sys_istty(stream)) return 1;
Neil Turton's avatar
Neil Turton committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
    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");

Kevin Bracey's avatar
Kevin Bracey committed
844
    if (!(flag & _IOREAD+_IOWRITE+_IOSHARED) || _sys_istty(stream))
Neil Turton's avatar
Neil Turton committed
845 846 847 848 849 850 851 852 853 854 855
        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;
Kevin Bracey's avatar
Kevin Bracey committed
856
            filelen = _sys_flen(fh);           /* length of this file      */
Neil Turton's avatar
Neil Turton committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
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;
}

Kevin Bracey's avatar
Kevin Bracey committed
1004 1005 1006 1007 1008 1009
void perror(const char *s)
{   char b[256];
    if (s != 0 && *s != 0) fprintf(stderr, "%s: ", s);
    fprintf(stderr, "%s\n", _strerror(errno, b));
}

Neil Turton's avatar
Neil Turton committed
1010
/* end of stdio.c */