home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume26 / calc / part05 / file.c < prev    next >
C/C++ Source or Header  |  1992-05-09  |  10KB  |  528 lines

  1. /*
  2.  * Copyright (c) 1992 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * File I/O routines callable by users.
  7.  */
  8.  
  9. #include "stdarg.h"
  10. #include "calc.h"
  11.  
  12.  
  13. #define    READSIZE    1024    /* buffer size for reading */
  14.  
  15. /*
  16.  * Definition of opened files.
  17.  */
  18. typedef struct {
  19.     FILEID id;        /* id to identify this file */
  20.     FILE *fp;        /* real file structure for I/O */
  21.     char *name;        /* file name */
  22.     BOOL reading;        /* TRUE if opened for reading */
  23.     BOOL writing;        /* TRUE if opened for writing */
  24.     char *mode;        /* open mode */
  25. } FILEIO;
  26.  
  27.  
  28. /*
  29.  * Table of opened files.
  30.  * The first three entries always correspond to stdin, stdout, and stderr,
  31.  * and cannot be closed.  Their file ids are always 0, 1, and 2.
  32.  */
  33. static FILEIO files[MAXFILES] = {
  34.     FILEID_STDIN,  stdin,  "(stdin)",  TRUE, FALSE, "reading",
  35.     FILEID_STDOUT, stdout, "(stdout)", FALSE, TRUE, "writing",
  36.     FILEID_STDERR, stderr, "(stderr)", FALSE, TRUE, "writing"
  37. };
  38.  
  39. static FILEID lastid = FILEID_STDERR;        /* last allocated file id */
  40.  
  41.  
  42.  
  43. /*
  44.  * Open the specified file name for reading or writing as determined by
  45.  * the specified mode ("r", "w", or "a").  Returns a file id which can be
  46.  * used to do I/O to the file, or else FILEID_NONE if the open failed.
  47.  * Aborts with an error if too many files are opened or the mode is illegal.
  48.  */
  49. FILEID
  50. openid(name, mode)
  51.     char *name;        /* file name */
  52.     char *mode;        /* open mode */
  53. {
  54.     FILEIO *fiop;        /* file structure */
  55.     FILEID id;        /* new file id */
  56.     int count;
  57.  
  58.     if (((*mode != 'r') && (*mode != 'w') && (*mode != 'a')) || mode[1])
  59.         error("Illegal mode for fopen");
  60.  
  61.     count = MAXFILES;
  62.     do {
  63.         if (--count < 0)
  64.             error("Too many open files");
  65.         id = ++lastid;
  66.         fiop = &files[id % MAXFILES];
  67.  
  68.     } while (fiop->reading || fiop->writing);
  69.  
  70.     fiop->name = (char *)malloc(strlen(name) + 1);
  71.     if (fiop->name == NULL) {
  72.         lastid--;
  73.         error("No memory for filename");
  74.     }
  75.     strcpy(fiop->name, name);
  76.  
  77.     fiop->fp = f_open(name, mode);
  78.     if (fiop->fp == NULL) {
  79.         free(fiop->name);
  80.         fiop->name = NULL;
  81.         lastid--;
  82.         return FILEID_NONE;
  83.     }
  84.  
  85.     switch (*mode) {
  86.         case 'r':
  87.             fiop->mode = "reading";
  88.             fiop->reading = TRUE;
  89.             break;
  90.         case 'w':
  91.             fiop->mode = "writing";
  92.             fiop->writing = TRUE;
  93.             break;
  94.         case 'a':
  95.             fiop->mode = "appending";
  96.             fiop->writing = TRUE;
  97.             break;
  98.     }
  99.  
  100.     fiop->id = id;
  101.  
  102.     return id;
  103. }
  104.  
  105.  
  106. /*
  107.  * Find the file I/O structure for the specified file id, and verify that
  108.  * it is opened in the required manner ('r' for reading or 'w' for writing).
  109.  * If mode is 0, then no open checks are made at all, and NULL is then
  110.  * returned if the id represents a closed file.
  111.  */
  112. static FILEIO *
  113. findid(id, mode)
  114.     FILEID id;
  115. {
  116.     FILEIO *fiop;        /* file structure */
  117.     char *msg;
  118.     BOOL flag;
  119.  
  120.     if ((id < 0) || (id > lastid))
  121.         error("Illegal file id");
  122.  
  123.     fiop = &files[id % MAXFILES];
  124.  
  125.     switch (mode) {
  126.         case 'r':
  127.             msg = "Reading from";
  128.             flag = fiop->reading;
  129.             break;
  130.         case 'w':
  131.             msg = "Writing to";
  132.             flag = fiop->writing;
  133.             break;
  134.         case 0:
  135.             msg = NULL;
  136.             break;
  137.         default:
  138.             error("Unknown findid mode");
  139.     }
  140.  
  141.     if (fiop->id != id) {
  142.         if (msg)
  143.             error("%s closed file", msg);
  144.         return NULL;
  145.     }
  146.  
  147.     if (msg && !flag)
  148.         error("%s file not opened that way", msg);
  149.     
  150.     return fiop;
  151. }
  152.  
  153.  
  154. /*
  155.  * Return whether or not a file id is valid.  This is used for if tests.
  156.  */
  157. BOOL
  158. validid(id)
  159.     FILEID id;
  160. {
  161.     return (findid(id, 0) != NULL);
  162. }
  163.  
  164.  
  165. /*
  166.  * Return the file id for the entry in the file table at the specified index.
  167.  * Returns FILEID_NONE if the index is illegal or the file is closed.
  168.  */
  169. FILEID
  170. indexid(index)
  171.     long index;
  172. {
  173.     FILEIO *fiop;        /* file structure */
  174.  
  175.     if ((index < 0) || (index >= MAXFILES))
  176.         return FILEID_NONE;
  177.  
  178.     fiop = &files[index];
  179.     if (fiop->reading || fiop->writing)
  180.         return fiop->id;
  181.  
  182.     return FILEID_NONE;
  183. }
  184.  
  185.  
  186. /*
  187.  * Close the specified file id.  Returns TRUE if there was an error.
  188.  * Closing of stdin, stdout, or stderr is illegal, but closing of already
  189.  * closed files is allowed.
  190.  */
  191. BOOL
  192. closeid(id)
  193.     FILEID id;
  194. {
  195.     FILEIO *fiop;        /* file structure */
  196.     int err;
  197.  
  198.     if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
  199.         (id == FILEID_STDERR))
  200.             error("Cannot close stdin, stdout, or stderr");
  201.  
  202.     fiop = findid(id, 0);
  203.     if (fiop == NULL)
  204.         return FALSE;
  205.  
  206.     fiop->id = FILEID_NONE;
  207.     if (!fiop->reading && !fiop->writing)
  208.         error("Closing non-opened file");
  209.     fiop->reading = FALSE;
  210.     fiop->writing = FALSE;
  211.  
  212.     if (fiop->name)
  213.         free(fiop->name);
  214.     fiop->name = NULL;
  215.  
  216.     err = ferror(fiop->fp);
  217.     err |= fclose(fiop->fp);
  218.     fiop->fp = NULL;
  219.  
  220.     return (err != 0);
  221. }
  222.  
  223.  
  224. /*
  225.  * Return whether or not an error occurred to a file.
  226.  */
  227. BOOL
  228. errorid(id)
  229.     FILEID id;
  230. {
  231.     FILEIO *fiop;        /* file structure */
  232.  
  233.     fiop = findid(id, 0);
  234.     if (fiop == NULL)
  235.         error("Closed file for ferror");
  236.     return (ferror(fiop->fp) != 0);
  237. }
  238.  
  239.  
  240. /*
  241.  * Return whether or not end of file occurred to a file.
  242.  */
  243. BOOL
  244. eofid(id)
  245.     FILEID id;
  246. {
  247.     FILEIO *fiop;        /* file structure */
  248.  
  249.     fiop = findid(id, 0);
  250.     if (fiop == NULL)
  251.         error("Closed file for feof");
  252.     return (feof(fiop->fp) != 0);
  253. }
  254.  
  255.  
  256. /*
  257.  * Flush output to an opened file.
  258.  */
  259. void
  260. flushid(id)
  261.     FILEID id;
  262. {
  263.     FILEIO *fiop;        /* file structure */
  264.  
  265.     fiop = findid(id, 'w');
  266.     fflush(fiop->fp);
  267. }
  268.  
  269.  
  270. /*
  271.  * Read the next line from an opened file.
  272.  * Returns a pointer to an allocated string holding the null-terminated
  273.  * line (without any terminating newline), or else a NULL pointer on an
  274.  * end of file or error.
  275.  */
  276. void
  277. readid(id, retptr)
  278.     FILEID    id;        /* file to read from */
  279.     char **retptr;        /* returned pointer to string */
  280. {
  281.     FILEIO *fiop;        /* file structure */
  282.     char *str;        /* current string */
  283.     int len;        /* current length of string */
  284.     int totlen;        /* total length of string */
  285.     char buf[READSIZE];    /* temporary buffer */
  286.  
  287.     totlen = 0;
  288.     str = NULL;
  289.  
  290.     fiop = findid(id, 'r');
  291.  
  292.     while (fgets(buf, READSIZE, fiop->fp) && buf[0]) {
  293.         len = strlen(buf);
  294.         if (totlen)
  295.             str = (char *)realloc(str, totlen + len + 1);
  296.         else
  297.             str = (char *)malloc(len + 1);
  298.         if (str == NULL)
  299.             error("No memory in freadline");
  300.         strcpy(&str[totlen], buf);
  301.         totlen += len;
  302.         if (buf[len - 1] == '\n') {
  303.             str[totlen - 1] = '\0';
  304.             *retptr = str;
  305.             return;
  306.         }
  307.     }
  308.     if (totlen && ferror(fiop->fp)) {
  309.         free(str);
  310.         str = NULL;
  311.     }
  312.     *retptr = str;
  313. }
  314.  
  315.  
  316. /*
  317.  * Return the next character from an opened file.
  318.  * Returns EOF if there was an error or end of file.
  319.  */
  320. int
  321. getcharid(id)
  322.     FILEID id;
  323. {
  324.     return fgetc(findid(id, 'r')->fp);
  325. }
  326.  
  327.  
  328. /*
  329.  * Print out the name of an opened file.
  330.  * If the file has been closed, a null name is printed.
  331.  * If flags contain PRINT_UNAMBIG then extra information is printed
  332.  * identifying the output as a file and some data about it.
  333.  */
  334. void
  335. printid(id, flags)
  336.     FILEID id;
  337. {
  338.     FILEIO *fiop;        /* file structure */
  339.     FILE *fp;
  340.  
  341.     fiop = findid(id, 0);
  342.     if (fiop == NULL) {
  343.         math_str((flags & PRINT_UNAMBIG) ? "FILE (closed)" : "\"\"");
  344.         return;
  345.     }
  346.     if ((flags & PRINT_UNAMBIG) == 0) {
  347.         math_chr('"');
  348.         math_str(fiop->name);
  349.         math_chr('"');
  350.         return;
  351.     }
  352.  
  353.     fp = fiop->fp;
  354.     math_fmt("FILE \"%s\" (%s, pos %ld", fiop->name,  fiop->mode,
  355.         ftell(fp));
  356.     if (ferror(fp))
  357.         math_str(", error");
  358.     if (feof(fp))
  359.         math_str(", eof");
  360.     math_chr(')');
  361. }
  362.  
  363.  
  364. /*
  365.  * Print a formatted string similar to printf.  Various formats of output
  366.  * are possible, depending on the format string AND the actual types of the
  367.  * values.  Mismatches do not cause errors, instead something reasonable is
  368.  * printed instead.  The output goes to the file with the specified id.
  369.  */
  370. void
  371. idprintf(id, fmt, count, vals)
  372.     FILEID id;            /* file id to print to */
  373.     char *fmt;            /* standard format string */
  374.     VALUE **vals;            /* table of values to print */
  375. {
  376.     FILEIO *fiop;
  377.     VALUE *vp;
  378.     char *str;
  379.     int ch, len;
  380.     int oldmode, newmode;
  381.     long olddigits, newdigits;
  382.     long width, precision;
  383.     BOOL didneg, didprecision;
  384.  
  385.     fiop = findid(id, 'w');
  386.  
  387.     setfp(fiop->fp);
  388.  
  389.     while ((ch = *fmt++) != '\0') {
  390.         if (ch == '\\') {
  391.             ch = *fmt++;
  392.             switch (ch) {
  393.                 case 'n': ch = '\n'; break;
  394.                 case 'r': ch = '\r'; break;
  395.                 case 't': ch = '\t'; break;
  396.                 case 'f': ch = '\f'; break;
  397.                 case 'v': ch = '\v'; break;
  398.                 case 'b': ch = '\b'; break;
  399.                 case 0:
  400.                     setfp(stdout);
  401.                     return;
  402.             }
  403.             math_chr(ch);
  404.             continue;
  405.         }
  406.  
  407.         if (ch != '%') {
  408.             math_chr(ch);
  409.             continue;
  410.         }
  411.  
  412.         /*
  413.          * Here to handle formats.
  414.          */
  415.         didneg = FALSE;
  416.         didprecision = FALSE;
  417.         width = 0;
  418.         precision = 0;
  419.  
  420.         ch = *fmt++;
  421.         if (ch == '-') {
  422.             didneg = TRUE;
  423.             ch = *fmt++;
  424.         }
  425.         while ((ch >= '0') && (ch <= '9')) {
  426.             width = width * 10 + (ch - '0');
  427.             ch = *fmt++;
  428.         }
  429.         if (ch == '.') {
  430.             didprecision = TRUE;
  431.             ch = *fmt++;
  432.             while ((ch >= '0') && (ch <= '9')) {
  433.                 precision = precision * 10 + (ch - '0');
  434.                 ch = *fmt++;
  435.             }
  436.         }
  437.         if (ch == 'l')
  438.             ch = *fmt++;
  439.  
  440.         oldmode = _outmode_;
  441.         newmode = oldmode;
  442.         olddigits = _outdigits_;
  443.         newdigits = olddigits;
  444.         if (didprecision)
  445.             newdigits = precision;
  446.  
  447.         switch (ch) {
  448.             case 'd':
  449.             case 's':
  450.             case 'c':
  451.                 break;
  452.             case 'f':
  453.                 newmode = MODE_REAL;
  454.                 break;
  455.             case 'e':
  456.                 newmode = MODE_EXP;
  457.                 break;
  458.             case 'r':
  459.                 newmode = MODE_FRAC;
  460.                 break;
  461.             case 'o':
  462.                 newmode = MODE_OCTAL;
  463.                 break;
  464.             case 'x':
  465.                 newmode = MODE_HEX;
  466.                 break;
  467.             case 'b':
  468.                 newmode = MODE_BINARY;
  469.                 break;
  470.             case 0:
  471.                 setfp(stdout);
  472.                 return;
  473.             default:
  474.                 math_chr(ch);
  475.                 continue;
  476.         }
  477.  
  478.         if (--count < 0)
  479.             error("Not enough arguments for fprintf");
  480.         vp = *vals++;
  481.  
  482.         setdigits(newdigits);
  483.         setmode(newmode);
  484.  
  485.         /*
  486.          * If there is no width specification, or if the type of
  487.          * value requires multiple lines, then just output the
  488.          * value directly.
  489.          */
  490.         if ((width == 0) ||
  491.             (vp->v_type == V_MAT) || (vp->v_type == V_LIST))
  492.         {
  493.             printvalue(vp, PRINT_NORMAL);
  494.             setmode(oldmode);
  495.             setdigits(olddigits);
  496.             continue;
  497.         }
  498.  
  499.         /*
  500.          * There is a field width.  Collect the output in a string,
  501.          * print it padded appropriately with spaces, and free it.
  502.          * However, if the output contains a newline, then ignore
  503.          * the field width.
  504.          */
  505.         divertio();
  506.         printvalue(vp, PRINT_NORMAL);
  507.         str = getdivertedio();
  508.         if (strchr(str, '\n'))
  509.             width = 0;
  510.         len = strlen(str);
  511.         while (!didneg && (width > len)) {
  512.             width--;
  513.             math_chr(' ');
  514.         }
  515.         math_str(str);
  516.         free(str);
  517.         while (didneg && (width > len)) {
  518.             width--;
  519.             math_chr(' ');
  520.         }
  521.         setmode(oldmode);
  522.         setdigits(olddigits);
  523.     }
  524.     setfp(stdout);
  525. }
  526.  
  527. /* END CODE */
  528.