/*
 * File: fsys.c
 *  Contents: close, exit, getenv, open, read, reads, remove, rename, [save],
 *   seek, stop, [system], where, write, writes, [getch, getche, kbhit]
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"

#if MICROSOFT || SCO_XENIX
#define BadCode
#endif					/* MICROSOFT || SCO_XENIX */

#ifdef XENIX_386
#define register
#endif					/* XENIX_386 */

#if MACINTOSH
#if MPW
#include <Files.h>
#include <FCntl.h>
#include <IOCtl.h>
#define isatty(fd) (!ioctl((fd), FIOINTERACTIVE))
#define fflush(f) 0
#endif					/* MPW */
#endif					/* MACINTOSH */

/*
 * close(f) - close file f.
 */

FncDcl(close,1)
   {
   FILE *f;

   /*
    * Arg1 must be a file.
    */
   if (Arg1.dword != D_File) 
      RunErr(105, &Arg1);

   /*
    * Close Arg1, using fclose or pclose as appropriate.
    */

#if UNIX || VMS
   if (BlkLoc(Arg1)->file.status & Fs_Pipe) {
      BlkLoc(Arg1)->file.status = 0;
      MakeInt((long)((pclose(BlkLoc(Arg1)->file.fd) >> 8) & 0377), &Arg0);
      Return;
      }
   else
#endif					/* UNIX || VMS */

      f = BlkLoc(Arg1)->file.fd;

   fclose(f);
   BlkLoc(Arg1)->file.status = 0;

   /*
    * Return the closed file.
    */
   Arg0 = Arg1;
   Return;
   }

/*
 * exit(status) - exit process with specified status, defaults to 0.
 */

FncDcl(exit,1)
   {
   if (defshort(&Arg1, NormalExit) == Error) 
      RunErr(0, NULL);
   c_exit((int)IntVal(Arg1));
   }

/*
 * getenv(s) - return contents of environment variable s
 */

FncDcl(getenv,1)
   {

#ifndef EnvVars
   RunErr(-121, NULL);
#else					/* EnvVars */

   register char *p;
   register word len;
   char sbuf[256];


   /*
    * Make a C-style string out of Arg1
    */
   switch (cvstr(&Arg1, sbuf)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg1, sbuf);
         break;

      default:
         RunErr(103, &Arg1);
      }

   if ((p = getenv(StrLoc(Arg1))) != NULL) {	/* get environment variable */
      len = strlen(p);
      if (strreq(len) == Error) 
         RunErr(0, NULL);
      StrLen(Arg0) = len;
      StrLoc(Arg0) = alcstr(p, len);
      Return;
      }
   else 				/* fail if not in environment */
      Fail;
#endif					/* EnvVars */
   }

/*
 * open(s1,s2,s3) - open file s1 with mode s2 and attributes s3.
 */
FncDcl(open,3)
   {
   register word slen;
   register int i;
   register char *s;
   int status;
   char mode[4];
   extern FILE *fopen();
   char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];
   char *openstring;
   FILE *f;

#ifdef OpenAttributes
   char sbuf3[MaxCvtLen];
   char *attrstring;
#endif					/* OpenAttributes */

/*
 * The following code is operating-system dependent [@fsys.01].  Make
 *  declarations as needed for opening files.
 */

#if PORT
Deliberate Syntax Error
#endif                                  /* PORT */

#if AMIGA || MACINTOSH
   /* nothing is needed */
#endif                                  /* AMIGA || MACINTOSH */

#if ATARI_ST || HIGHC_386 || MSDOS || OS2
   char untranslated;
#endif                                  /* ATARI_ST || HIGHC_386 ... */

#if MACINTOSH
#if LSC
   char untranslated;
#endif					/* LSC */
#endif					/* MACINTOSH */

#if MVS || VM
   char untranslated;
#if SASC
#include <lcio.h>
#endif					/* SASC */
#endif                                  /* MVS || VM */

#if UNIX || VMS
   extern FILE *popen();
#endif                                  /* MACINTOSH || UNIX || VMS */

/*
 * End of operating-system specific code.
 */


   /*
    * Arg1 must be a string and a C string copy of it is also needed.
    *  Make it a string if it is not one; make a C string if Arg1 is
    *  a string.
    */
   switch (cvstr(&Arg1, sbuf1)) {

      case Cvt:
         openstring = StrLoc(Arg1);
         if (strreq(StrLen(Arg1)) == Error)
            RunErr(0, NULL);
         StrLoc(Arg1) = alcstr(StrLoc(Arg1), StrLen(Arg1));
         break;

      case NoCvt:
         tended[1] = Arg1;
         ntended = 1;
         qtos(&tended[1], sbuf1);
         openstring = StrLoc(tended[1]);
         break;

      default:
         RunErr(103, &Arg1);
      }
   /*
    * s2 defaults to "r".
    */
   if (defstr(&Arg2, sbuf2, &letr) == Error)
      RunErr(0, NULL);

#ifdef OpenAttributes
   /*
    * Convert s3 to a string, defaulting to "".
    */
   ntended++;
   tended[ntended] = Arg3;
   if (ChkNull(tended[ntended]))
      tended[ntended] = emptystr;
   switch (cvstr(&tended[ntended], sbuf3)) {

      case Cvt:
         attrstring = StrLoc(Arg3);
         if (strreq(StrLen(Arg3)) == Error)
            RunErr(0, NULL);
         StrLoc(Arg3) = alcstr(StrLoc(Arg3), StrLen(Arg3));
         break;

      case NoCvt:
         qtos(&tended[ntended], sbuf3);
         attrstring = StrLoc(tended[ntended]);
         break;

      default:
         RunErr(103, &Arg3);
      }
#endif                                  /* OpenAttributes */

   if (blkreq((word)sizeof(struct b_file)) == Error)
      RunErr(0, NULL);
   status = 0;

/*
 * The following code is operating-system dependent [@fsys.02].  Provide
 *  declaration for untranslated line-termination mode, if supported.
 */

#if PORT
   /* nothing to do */
Deliberate Syntax Error
#endif                                  /* PORT */

#if AMIGA
   /* translated mode could be supported, but is not now */
#endif                                  /* AMIGA */

#if ATARI_ST || HIGHC_386 || MSDOS || MVS || OS2 || VM
   untranslated = 0;
#endif                                  /* ATARI_ST || HIGHC_386 ... */

#if MACINTOSH
#if LSC
   untranslated = 0;
#endif					/* LSC */
#endif					/* MACINTOSH */

#if UNIX || VMS
   /* nothing to do */
#endif                                  /* UNIX || VMS */

/*
 * End of operating-system specific code.
 */

   /*
    * Scan Arg2, setting appropriate bits in status.  Produce a run-time error
    *  if an unknown character is encountered.
    */
   s = StrLoc(Arg2);
   slen = StrLen(Arg2);
   for (i = 0; i < slen; i++) {
      switch (*s++) {
         case 'a':
         case 'A':
            status |= Fs_Write|Fs_Append;
            continue;
         case 'b':
         case 'B':
            status |= Fs_Read|Fs_Write;
            continue;
         case 'c':
         case 'C':
            status |= Fs_Create|Fs_Write;
            continue;
         case 'r':
         case 'R':
            status |= Fs_Read;
            continue;
         case 'w':
         case 'W':
            status |= Fs_Write;
            continue;

/*
 * The following code is operating-system dependent [@fsys.03].  Handle
 * untranslated line-terminator mode and pipes, if supported.
 */

#if PORT
         case 't':
         case 'T':
         case 'u':
         case 'U':
            continue;			/* no-op */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA 
         case 't':
         case 'T':
         case 'u':
         case 'U':
            continue;			/* no-op */
#endif					/* AMIGA */

#if ATARI_ST || HIGHC_386 || MSDOS || OS2 || SASC
         case 't':
         case 'T':
            untranslated = 0;

#ifdef RecordIO
            status &= ~Fs_Record;
#endif					/* RecordIO */

            continue;
         case 'u':
         case 'U':
            untranslated = 1;

#ifdef RecordIO
            status &= ~Fs_Record;
#endif					/* RecordIO */

            continue;
#endif					/* ATARI_ST || HIGHC_386 || ... */

#ifdef RecordIO
         case 's':
         case 'S':
            untranslated = 1;
            status |= Fs_Record;
            continue;
#endif                                  /* RecordIO */

#if MACINTOSH
#if LSC
         case 't':
         case 'T':
            untranslated = 0;
            continue;
         case 'u':
         case 'U':
            untranslated = 1;
            continue;
#endif					/* LSC */
#endif					/* MACINTOSH */

#if UNIX || VMS
         case 't':
         case 'T':
         case 'u':
         case 'U':
            continue;			/* no-op */
         case 'p':
         case 'P':
            status |= Fs_Pipe;
            continue;
#endif					/* UNIX || VMS */

/*
 * End of operating-system specific code.
 */

         default:
            RunErr(209, &Arg2);
         }
      }

   /*
    * Construct a mode field for fopen/popen.
    */
   mode[0] = '\0';
   mode[1] = '\0';
   mode[2] = '\0';
   mode[3] = '\0';

   if ((status & (Fs_Read|Fs_Write)) == 0)   /* default: read only */
      status |= Fs_Read;
   if (status & Fs_Create)
      mode[0] = 'w';
   else if (status & Fs_Append)
      mode[0] = 'a';
   else if (status & Fs_Read)
      mode[0] = 'r';
   else
      mode[0] = 'w';

/*
 * The following code is operating-system dependent [@fsys.04].  Handle open
 *  modes.
 */

#if PORT
   if ((status & (Fs_Read|Fs_Write)) == (Fs_Read|Fs_Write))
      mode[1] = '+';
Deliberate Syntax Error
#endif                                  /* PORT */

#if AMIGA || UNIX || VMS
   if ((status & (Fs_Read|Fs_Write)) == (Fs_Read|Fs_Write))
      mode[1] = '+';
#endif                                  /* AMIGA || UNIX || VMS */

#if ATARI_ST
   if ((status & (Fs_Read|Fs_Write)) == (Fs_Read|Fs_Write)) {
      mode[1] = '+';
      mode[2] = untranslated ? 'b' : 'a';
      }
   else mode[1] = untranslated ? 'b' : 'a';
#endif                                  /* ATARI_ST */

#if HIGHC_386 || OS2
   if ((status & (Fs_Read|Fs_Write)) == (Fs_Read|Fs_Write)) {
      mode[1] = '+';
      mode[2] = untranslated ? 'b' : 't';
      }
   else mode[1] = untranslated ? 'b' : 't';
#endif                                  /* HIGHC_386 || OS2 */

#if MACINTOSH
#if LSC
   untranslated = 0;
#endif					/* LSC */
#endif					/* MACINTOSH */

#if MVS || VM
   if ((status & (Fs_Read|Fs_Write)) == (Fs_Read|Fs_Write)) {
      mode[1] = '+';
      mode[2] = untranslated ? 'b' : 0;
      }
   else mode[1] = untranslated ? 'b' : 0;
#endif                                  /* MVS || VM */

/*
 * End of operating-system specific code.
 */

   /*
    * Open the file with fopen or popen.
    */

#ifdef OpenAttributes
#if SASC
#ifdef RecordIO
      f = afopen(openstring, mode, status & Fs_Record ? "seq" : "",
                 attrstring);
#else					/* RecordIO */
      f = afopen(openstring, mode, "", attrstring);
#endif                                  /* RecordIO */
#endif                                  /* SASC */

#else                                   /* OpenAttributes */

#if UNIX || VMS
   if (status & Fs_Pipe) {
      if (status != (Fs_Read|Fs_Pipe) && status != (Fs_Write|Fs_Pipe))
         RunErr(209, &Arg2);
      f = popen(openstring, mode);
      }
   else
#endif                                  /* UNIX || VMS */

      f = fopen(openstring, mode);
#endif                                  /* OpenAttributes */

   /*
    * Fail if the file cannot be opened.
    */
   if (f == NULL)
      Fail;

   /*
    * Return the resulting file value.
    */
   Arg0.dword = D_File;
   BlkLoc(Arg0) = (union block *) alcfile(f, status, &Arg1);
   ntended = 0;
   Return;
   }

/*
 * read(f) - read line on file f.
 */
FncDcl(read,1)
   {
   register word slen, rlen;
   register char *sp;
   int status;
   static char sbuf[MaxReadStr];
   FILE *f;

   /*
    * Default Arg1 to &input.
    */
   if (deffile(&Arg1, &input) == Error) 
      RunErr(0, NULL);

   /*
    * Get a pointer to the file and be sure that it is open for reading.
    */
   f = BlkLoc(Arg1)->file.fd;
   status = (int)BlkLoc(Arg1)->file.status;
   if ((status & Fs_Read) == 0) 
      RunErr(212, &Arg1);

#ifdef StandardLib
   if (status & Fs_Writing) {
      fseek(f, 0L, SEEK_CUR);
      BlkLoc(Arg1)->file.status &= ~Fs_Writing;
      }
   BlkLoc(Arg1)->file.status |= Fs_Reading;
#endif					/* StandardLib */

   /*
    * Use getstrg to read a line from the file, failing if getstrg
    *  encounters end of file. [[ What about -2?]]
    */
   StrLen(Arg0) = 0;
   do {

#ifdef RecordIO
      if ((slen = (status & Fs_Record ? getrec(sbuf, MaxReadStr, f) :
                                        getstrg(sbuf, MaxReadStr, f)))
          == -1) Fail;
#else					/* RecordIO */
      if ((slen = getstrg(sbuf,MaxReadStr,f)) == -1)
         Fail;
#endif                                  /* RecordIO */

      /*
       * Allocate the string read and make Arg0 a descriptor for it.
       */
      rlen = slen < 0 ? (word)MaxReadStr : slen;
      if (strreq(rlen) == Error) 
         RunErr(0, NULL);
      sp = alcstr(sbuf,rlen);
      if (StrLen(Arg0) == 0)
         StrLoc(Arg0) = sp;
      StrLen(Arg0) += rlen;
      } while (slen < 0);
   Return;
   }

/*
 * reads(f,i) - read i characters on file f.
 */
FncDcl(reads,2)
   {
   register word cnt;
   long tally;
   int status;
   FILE *f;

   /*
    * Arg1 defaults to &input and Arg2 defaults to 1 (character).
    */
   if ((deffile(&Arg1, &input) == Error) ||
       (defshort(&Arg2, 1) == Error)) 
      RunErr(0, NULL);

   /*
    * Get a pointer to the file and be sure that it is open for reading.
    */
   f = BlkLoc(Arg1)->file.fd;
   status = (int)BlkLoc(Arg1)->file.status;
   if ((status & Fs_Read) == 0) 
      RunErr(212, &Arg1);

#ifdef StandardLib
   if (status & Fs_Writing) {
      fseek(f, 0L, SEEK_CUR);
      BlkLoc(Arg1)->file.status &= ~Fs_Writing;
      }
   BlkLoc(Arg1)->file.status |= Fs_Reading;
#endif					/* StandardLib */

   /*
    * Be sure that a positive number of bytes is to be read.
    */
   if ((cnt = IntVal(Arg2)) <= 0) 
      RunErr(205, &Arg2);

   /*
    * Ensure that enough space for the string exists and read it directly
    *  into the string space.  (By reading directly into the string space,
    *  no arbitrary restrictions are placed on the size of the string that
    *  can be read.)  Make Arg0 a descriptor for the string and return it.
    */
   if (strreq(cnt) == Error) 
      RunErr(0, NULL);
   if (strfree + cnt > strend)
      syserr("reads allocation botch");
   StrLoc(Arg0) = strfree;

#if AMIGA
   /*
    * The following code is special for Lattice 4.0 -- it was different
    *  for Lattice 3.10.  It probably won't work correctly with other
    *  C compilers.
    */
   if (IsInteractive(_ufbs[fileno(f)].ufbfh)) {
      if ((cnt = read(fileno(f),StrLoc(Arg0),cnt)) <= 0)
         Fail;
      StrLen(Arg0) = cnt;
      alcstr(NULL, cnt);
      Return;
      }
#endif					/* AMIGA */

   tally = longread(StrLoc(Arg0),sizeof(char),cnt,f);
   if (tally == 0)
      Fail;
   StrLen(Arg0) = tally;
   alcstr(NULL, (word)tally);
   Return;
   }

/*
 * remove(s) - remove the file named s.
 */

FncDcl(remove,1)
   {
   char sbuf[MaxCvtLen];

   /*
    * Make a C-style string out of Arg1
    */
   switch (cvstr(&Arg1, sbuf)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg1, sbuf);
         break;

      default:
         RunErr(103, &Arg1);
      }
   if (unlink(StrLoc(Arg1)) != 0)
      Fail;
   Arg0 = nulldesc;
   Return;
   }

/*
 * rename(s1,s2) - rename the file named s1 to have the name s2.
 */

FncDcl(rename,2)
   {
   char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];

   /*
    * Make a C-style string out of Arg1
    */
   switch (cvstr(&Arg1, sbuf1)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg1, sbuf1);
         break;

      default:
         RunErr(103, &Arg1);
      }

   /*
    * Make a C-style string out of Arg2
    */
   switch (cvstr(&Arg2, sbuf2)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg2, sbuf2);
         break;

      default:
         RunErr(103, &Arg2);
      }

/*
 * The following code is operating-system dependent [@fsys.05].  Rename the
 *  file, and fail if unsuccessful.
 */

#if PORT
   /* need something */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || ATARI_ST || HIGHC_386 || MACINTOSH || MSDOS || MVS || OS2 || VM || VMS
   {
   if (rename(StrLoc(Arg1),StrLoc(Arg2)) != 0)
      Fail;
   }
#endif					/* AMIGA || ATARI_ST ... */

#if UNIX
   if (link(StrLoc(Arg1),StrLoc(Arg2)) != 0)
      Fail;
   if (unlink(StrLoc(Arg1)) != 0) {
      unlink(StrLoc(Arg2));	/* try to undo partial rename */
      Fail;
      }
#endif					/* UNIX */

/*
 * End of operating-system specific code.
 */

   Arg0 = nulldesc;
   Return;
   }

#ifdef ExecImages
/*
 * save(s) - save the run-time system in file s
 */

FncDcl(save,1)
   {
   char sbuf[MaxCvtLen];
   int f, fsz;

   dumped = 1;

   /* if (ChkNull(Arg1)) { abort(); } */

   /*
    * Make a C-style string out of Arg1.
    */
   switch (cvstr(&Arg1, sbuf)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg1, sbuf);
         break;

      default:
         RunErr(103, &Arg1);
      }


   /*
    * Open the file for the executable image.
    */
   f = creat(StrLoc(Arg1), 0777);
   if (f == -1)
      Fail;
   fsz = wrtexec(f);
   /*
    * It happens that most wrtexecs don't check the system call return
    *  codes and thus they'll never return -1.  Nonetheless...
    */
   if (fsz == -1)
      Fail;
   /*
    * Return the size of the data space.
    */
   MakeInt(fsz, &Arg0);
   Return;
   }

#endif					/* ExecImages */

/*
 * seek(file,position) - seek to byte byte position in file.
 */

FncDcl(seek,2)
   {
   long l1;
   FILE *fd;

   if (Arg1.dword != D_File) 
      RunErr(-105, NULL);

   if (defint(&Arg2, &l1, 1L) == Error)
      RunErr(0, NULL);

   fd = BlkLoc(Arg1)->file.fd;

   if (BlkLoc(Arg1)->file.status == 0)
      Fail;
    if (l1 > 0) {

#ifdef StandardLib
       if (fseek(fd, l1 - 1, SEEK_SET) == -1)
#else					/* StandardLib */
       if (fseek(fd, l1 - 1, 0) == -1)
#endif					/* StandardLib */

          Fail;
       }
    else {

#ifdef StandardLib
       if (fseek(fd, l1, SEEK_END) == -1)
#else					/* StandardLib */
       if (fseek(fd, l1, 2) == -1)
#endif					/* StandardLib */
          Fail;
       }

#ifdef StandardLib
    BlkLoc(Arg1)->file.status &= ~(Fs_Reading | Fs_Writing);
#endif					/* StandardLib */

   Arg0 = Arg1;
   Return;
   }

/*
 * stop(a,b,...) - write arguments (starting on error output) and stop.
 */

FncDclV(stop)
    {
   register word n;
   char sbuf[MaxCvtLen];
   FILE *f;

#ifdef BadCode
   struct descrip temp;
#endif					/* BadCode */

   f = stderr;
   ntended = 1;
   /*
    * Loop through arguments.
    */

   for (n = 1; n <= nargs; n++) {

#ifdef BadCode 
      temp = Arg(n);			/* workaround for Microsoft C bug */
      tended[1] = temp;
#else					/* BadCode */
      tended[1] = Arg(n);
#endif					/* BadCode */

      if (tended[1].dword == D_File) {
         if (n > 1)
            putc('\n', f);
         if ((BlkLoc(tended[1])->file.status & Fs_Write) == 0) 
            RunErr(213, &tended[1]);
         f = BlkLoc(tended[1])->file.fd;

#ifdef StandardLib
         if (BlkLoc(tended[1])->file.status & Fs_Reading) {
            fseek(f, 0L, SEEK_CUR);
            BlkLoc(tended[1])->file.status &= ~Fs_Reading;
            }
         BlkLoc(tended[1])->file.status |= Fs_Writing;
#endif					/* StandardLib */
         }
      else {

         if (n == 1 && (k_output.status & Fs_Write) == 0)
            RunErr(-213, NULL);

#ifdef StandardLib
         if (n == 1) {
            if (k_output.status & Fs_Reading) {
               fseek(f, 0L, SEEK_CUR);
               k_output.status &= ~Fs_Reading;
               }
            k_output.status |= Fs_Writing;
         }
#endif					/* StandardLib */

         if (ChkNull(tended[1]))
            tended[1] = emptystr;
         if (cvstr(&tended[1], sbuf) == CvtFail) 
            RunErr(109, &tended[1]);
         putstr(f, &tended[1]);
         }
      }

   putc('\n', f);
   fflush(f);
   c_exit(ErrorExit);
   }

#ifdef SystemFnc
/*
 * system(s) - execute string s as a system command.
 */

FncDcl(system,1)
   {
   char sbuf[MaxCvtLen];
   char *systemstring;

   /*
    * Make a C-style string out of Arg1
    */
   switch (cvstr(&Arg1, sbuf)) {

      case Cvt:   /* Already converted to a C-style string */
         break;

      case NoCvt:
         qtos(&Arg1, sbuf);
         break;

      default:
         RunErr(103, &Arg1);
      }
      systemstring = StrLoc(Arg1);

   /*
    * Pass the C string to the system() function and return the exit code
    *  of the command as the result of system().
    */

/*
 * The following code is operating-system dependent [@fsys.06].  Perform system
 *  call.  Should not get here unless system(s) is supported.
 */

#if PORT
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || MSDOS || OS2 || UNIX
   MakeInt((long)((system(systemstring) >> 8) & 0377), &Arg0);
#endif					/* AMIGA || MSDOS || ... */

#if ATARI_ST || VMS
   MakeInt(system(systemstring), &Arg0);
#endif					/* ATARI_ST || VMS */

#if HIGHC_386 || MACINTOSH
   /* Should not get here */
#endif					/* HIGHC_386 */

#if MVS || VM
#if SASC && MVS
   {
      char *wprefix;
      wprefix = malloc(strlen(systemstring)+5);
                     /* hope this will do no harm... */
      sprintf(wprefix,"tso:%s",systemstring);
      MakeInt((long)system(wprefix), &Arg0);
      free(wprefix);
   }
#else					/* SASC && MVS */
   MakeInt((long)system(systemstring), &Arg0);
#endif					/* SASC && MVS */
#endif                                  /* MVS || VM */

/*
 * End of operating-system specific code.
 */
   Return;
   }

#endif					/* SystemFnc */
/*
 * where(file) - return current offset position in file.
 */

FncDcl(where,1)
   {
   FILE *fd;
   long ftell();
   long pos;

   if (Arg1.dword != D_File) 
      RunErr(-105, NULL);

   fd = BlkLoc(Arg1)->file.fd;

   if ((BlkLoc(Arg1)->file.status == 0))
      Fail;

#ifdef StandardLib
   MakeInt(pos = ftell(fd) + 1, &Arg0);
   if (pos == 0)
      Fail;  /* may only be effective on ANSI systems */
#else					/* StandardLib */
   MakeInt(ftell(fd) + 1, &Arg0);
#endif					/* StandardLib */

   Return;
   }

/*
 * write(a,b,...) - write arguments.
 */
FncDclV(write)
   {
   register word n;
   char sbuf[MaxCvtLen];
   FILE *f;

#ifdef RecordIO
   word status = k_output.status;
#endif					/* RecordIO */

#ifdef BadCode
   struct descrip temp;
#endif					/* BadCode */

   f = stdout;
   ntended = 1;
   tended[1] = emptystr;

   /*
    * Loop through the arguments.
    */
   for (n = 1; n <= nargs; n++) {

#ifdef BadCode
      temp = Arg(n);			/* workaround for Microsoft bug */
      tended[1] = temp;
#else					/* BadCode */
      tended[1] = Arg(n);
#endif					/* BadCode */

      if (tended[1].dword == D_File)	{	/* Current argument is a file */
         /*
          * If this is not the first argument, output a newline to the current
          *  file and flush it.
          */
         if (n > 1) {

#ifdef RecordIO
            if (status & Fs_Record)
               flushrec(f);
            else
#endif					/* RecordIO */

            putc('\n', f);
            fflush(f);
            }
         /*
          * Switch the current file to the file named by the current argument
          *  providing it is a file.  tended[1] is made to be a empty string to
          *  avoid a special case.
          */
         if ((BlkLoc(tended[1])->file.status & Fs_Write) == 0) 
            RunErr(213, &tended[1]);
         f = BlkLoc(tended[1])->file.fd;

#ifdef StandardLib
         if (BlkLoc(tended[1])->file.status & Fs_Reading) {
            fseek(f, 0L, SEEK_CUR);
            BlkLoc(tended[1])->file.status &= ~Fs_Reading;
            }
         BlkLoc(tended[1])->file.status |= Fs_Writing;
#endif					/* StandardLib */

#ifdef RecordIO
         status = BlkLoc(tended[1])->file.status;
#endif					/* RecordIO */

         tended[1] = emptystr;
         }
      else {	/* Current argument is a string */
         /*
          * On first argument, check to be sure that &output is open
          *  for output.
          */
         if (n == 1 && (k_output.status & Fs_Write) == 0)
            RunErr(-213, NULL);

#ifdef StandardLib
         if (n == 1) {
            if (k_output.status & Fs_Reading) {
               fseek(f, 0L, SEEK_CUR);
               k_output.status &= ~Fs_Reading;
               }
            k_output.status |= Fs_Writing;
         }
#endif					/* StandardLib */

         /*
          * Convert the argument to a string, defaulting to a empty string.
          */
         if (ChkNull(tended[1]))
            tended[1] = emptystr;
         if (cvstr(&tended[1], sbuf) == CvtFail) 
            RunErr(109, &tended[1]);

         /*
          * Output the string.
          */

#ifdef RecordIO
         if ((status & Fs_Record ? putrec(f, &tended[1]) :
                                   putstr(f, &tended[1])) == Failure)
#else					/* RecordIO */
         if (putstr(f, &tended[1]) == Failure)
#endif					/* RecordIO */
            RunErr(-214, NULL);
         }
      }
   /*
    * Append a newline to the file and flush it.
    */

#ifdef RecordIO
   if (status & Fs_Record)
      flushrec(f);
   else
#endif					/* RecordIO */

   putc('\n', f);
   if (ferror(f))
      RunErr(-214, NULL);

   fflush(f);

   /*
    * Return the last argument.
    */
   ntended = 0;
   Arg(0) = Arg(n - 1);
   Return;
   }

/*
 * writes(a,b,...) - write arguments without newline terminator.
 */

FncDclV(writes)
   {
   register word n;
   char sbuf[MaxCvtLen];
   FILE *f;

#ifdef BadCode
   struct descrip temp;
#endif					/* BadCode */

   f = stdout;
   ntended = 1;
   tended[1] = emptystr;

   /*
    * Loop through the arguments.
    */
   for (n = 1; n <= nargs; n++) {

#ifdef BadCode
      temp = Arg(n);			/* workaround for Microsoft bug */
      tended[1] = temp;
#else					/* BadCode */
      tended[1] = Arg(n);
#endif					/* BadCode */

      if (tended[1].dword == D_File)	{	/* Current argument is a file */
         /*
          * Switch the current file to the file named by the current argument
          *  providing it is a file.  tended[1] is made to be a empty string to
          *  avoid a special case.
          */
         if ((BlkLoc(tended[1])->file.status & Fs_Write) == 0) 
            RunErr(213, &tended[1]);
         f = BlkLoc(tended[1])->file.fd;

#ifdef StandardLib
         if (BlkLoc(tended[1])->file.status & Fs_Reading) {
            fseek(f, 0L, SEEK_CUR);
            BlkLoc(tended[1])->file.status &= ~Fs_Reading;
            }
         BlkLoc(tended[1])->file.status |= Fs_Writing;
#endif					/* StandardLib */

         tended[1] = emptystr;
         }
      else {	/* Current argument is a string */
         /*
          * On first argument, check to be sure that &output is open
          *  for output.
          */
         if (n == 1 && (k_output.status & Fs_Write) == 0) 
            RunErr(-213, NULL);

#ifdef StandardLib
         if (n == 1) {
            if (k_output.status & Fs_Reading) {
               fseek(f, 0L, SEEK_CUR);
               k_output.status &= ~Fs_Reading;
               }
            k_output.status |= Fs_Writing;
         }
#endif					/* StandardLib */

         /*
          * Convert the argument to a string, defaulting to a empty string.
          */
         if (ChkNull(tended[1]))
            tended[1] = emptystr;
         if (cvstr(&tended[1], sbuf) == CvtFail)
            RunErr(109, &tended[1]);
         /*
          * Output the string and flush the file.
          */
         if (putstr(f, &tended[1]) == Failure)
            RunErr(-214, NULL);

#if !MVS && !VM         /* forces record break on the 370! */
         fflush(f);
#endif					/* !MVS && !VM */

         }
      }
   /*
    * Return the last argument.
    */
   ntended = 0;
   Arg(0) = Arg(n - 1);
   Return;
   }

#ifdef KeyboardFncs
/*
 * getch() - return a character from console.
 */

FncDcl(getch,0)
   {
   unsigned char c;
   int i;
   i = getch();
   if (i<0)
      Fail;
   if (strreq((word)1) == Error)
      RunErr(0, NULL);
   c = (unsigned char) i;
   StrLoc(Arg0) = alcstr((char *)&c,(word)1);
   StrLen(Arg0) = 1;
   Return;
   }

/*
 * getche() -- return a character from console with echo.
 */

FncDcl(getche,0)
   {
   unsigned char c;
   int i;
   i = getche();
   if (i<0)
      Fail;
   if (strreq((word)1) == Error)
      RunErr(0, NULL);
   c = (unsigned char) i;
   StrLoc(Arg0) = alcstr((char *)&c,(word)1);
   StrLen(Arg0) = 1;
   Return;
   }

/*
 * kbhit() -- Check to see if there is a keyboard character waiting to
 *  be read.
 */

FncDcl(kbhit,0)
   {
   if (kbhit()) {
      Arg0 = nulldesc;
      Return;
      }
   else Fail;
   }
#endif					/* KeyboardFncs */
