home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / x / xvisrc.zoo / fileio.c < prev    next >
C/C++ Source or Header  |  1992-07-28  |  15KB  |  648 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)fileio.c    2.1 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     fileio.c
  14. * module function:
  15.     File i/o routines.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25.  
  26. #ifdef    MEGAMAX
  27. overlay "fileio"
  28. #endif
  29.  
  30. /*
  31.  * Definition of a text file format.
  32.  *
  33.  * This structure may need additional entries to cope with very strange file
  34.  * formats (such as VMS).
  35.  */
  36. struct tfformat
  37. {
  38.     int            tf_eolnchars[2];    /* end of line markers */
  39.     int            tf_eofchar;        /* end of file marker */
  40.     unsigned char   tf_dynamic;        /* autodetect format? */
  41. };
  42.  
  43. /*
  44.  * Names of values for the P_format enumerated parameter.
  45.  *
  46.  * It is essential that these are in the same order as the fmt_...
  47.  * symbolic constants defined in xvi.h.
  48.  */
  49. char    *fmt_strings[] = {
  50.     "cstring",
  51.     "macintosh",
  52.     "msdos",
  53.     "os2",
  54.     "qnx",
  55.     "tos",
  56.     "unix",
  57.     NULL,
  58. };
  59.  
  60. /*
  61.  * Format structures.
  62.  *
  63.  * It is essential that these are in the same order as the fmt_...
  64.  * symbolic constants defined in xvi.h.
  65.  *
  66.  * We don't use '\r' or '\n' to define the end-of-line characters
  67.  * because some compilers interpret them differently & this code has
  68.  * to work the same on all systems.
  69.  */
  70. #define    NOCHAR    EOF
  71.  
  72. static const struct tfformat tftable [] = {
  73.     { { '\0',       NOCHAR    }, EOF,          FALSE },    /* fmt_CSTRING */
  74.     { { CTRL('M'), NOCHAR    }, EOF,          FALSE },    /* fmt_MACINTOSH */
  75.     { { CTRL('M'), CTRL('J')    }, CTRL('Z'), TRUE  },    /* fmt_MSDOS */
  76.     { { CTRL('M'), CTRL('J')    }, CTRL('Z'), TRUE  },    /* fmt_OS2 */
  77.     { { '\036',       NOCHAR    }, EOF,          FALSE },    /* fmt_QNX */
  78.     { { CTRL('M'), CTRL('J')    }, EOF,          TRUE  },    /* fmt_TOS */
  79.     { { CTRL('J'), NOCHAR    }, EOF,          FALSE }    /* fmt_UNIX */
  80. };
  81.  
  82. /*
  83.  * Index of last entry in tftable.
  84.  */
  85. #define    TFMAX    (sizeof tftable / sizeof (struct tfformat) - 1)
  86.  
  87. /*
  88.  * Current text file format.
  89.  */
  90. static struct tfformat curfmt = { { 0, 0 }, 0, FALSE };
  91.  
  92. #define eolnchars    curfmt.tf_eolnchars
  93. #define eofchar        curfmt.tf_eofchar
  94.  
  95. /*
  96.  * Name of current text file format.
  97.  */
  98. static char *fmtname = "INTERNAL ERROR";
  99.  
  100. /*
  101.  * Copy the tftable entry indexed by tfindex into curfmt & update
  102.  * fmtname. Return FALSE if the parameter is invalid, otherwise TRUE.
  103.  *
  104.  * This is called from set_format() (below).
  105.  *
  106.  * Note that we copy a whole tfformat structure here, instead of just copying
  107.  * a pointer. This is so that curfmt.eolnchars & curfmt.eofchar will compile
  108.  * to absolute address references instead of indirections, which should be
  109.  * significantly more efficient because they are referenced for every
  110.  * character we read or write.
  111.  */
  112. static bool_t
  113. txtformset(tfindex)
  114. int    tfindex;
  115. {
  116.     if (tfindex < 0 || tfindex > TFMAX)
  117.     return FALSE;
  118.     (void) memcpy((char *) &curfmt, (const char *) &tftable[tfindex],
  119.                           sizeof curfmt);
  120.     fmtname = fmt_strings[tfindex];
  121.     return TRUE;
  122. }
  123.  
  124. /*
  125.  * Check value of P_format parameter.
  126.  */
  127. bool_t
  128. set_format(window, new_value, interactive)
  129. Xviwin    *window;
  130. Paramval new_value;
  131. bool_t    interactive;
  132. {
  133.     if (!txtformset(new_value.pv_i)) {
  134.     if (interactive) {
  135.         show_error(window, "Invalid text file format (%d)",
  136.                             new_value.pv_i);
  137.     }
  138.     return(FALSE);
  139.     }
  140.     return(TRUE);
  141. }
  142.  
  143. /*
  144.  * Find out if there's a format we know about with the single specified
  145.  * end-of-line character. If so, change to it.
  146.  */
  147. static bool_t
  148. eolnhack(c)
  149.     register int    c;
  150. {
  151.     register int    tfindex;
  152.  
  153.     for (tfindex = 0; tfindex <= TFMAX; tfindex++) {
  154.     register const int    *eolp;
  155.  
  156.     eolp = tftable[tfindex].tf_eolnchars;
  157.     if (eolp[0] == c && eolp[1] == NOCHAR) {
  158.         (void) txtformset(tfindex);
  159.         set_param(P_format, tfindex, (char **) NULL);
  160.         P_setchanged(P_format);
  161.         return TRUE;
  162.     }
  163.     }
  164.     return FALSE;
  165. }
  166.  
  167. /*
  168.  * Read in the given file, filling in the given "head" and "tail"
  169.  * arguments with pointers to the first and last elements of the
  170.  * linked list of Lines; if nothing was read, both pointers are set to
  171.  * NULL. The return value is the number of lines read, if successful
  172.  * (this can be 0 for an empty file), or an error return code, which
  173.  * can be gf_NEWFILE, gf_CANTOPEN, gf_IOERR or gf_NOMEM.
  174.  *
  175.  * If there is an error, such as not being able to read the file or
  176.  * running out of memory, an error message is printed; otherwise, a
  177.  * statistics line is printed using show_message().
  178.  *
  179.  * The "extra_str" string is printed just after the filename in the
  180.  * displayed line, and is typically used for "Read Only" messages. If
  181.  * the file doesn't appear to exist, the filename is printed again,
  182.  * immediately followed by the "no_file_str" string, & we return
  183.  * gf_NEWFILE.
  184.  */
  185. long
  186. get_file(window, filename, headp, tailp, extra_str, no_file_str)
  187. Xviwin        *window;
  188. char        *filename;
  189. Line        **headp;
  190. Line        **tailp;
  191. char        *extra_str;
  192. char        *no_file_str;
  193. {
  194.     register FILE    *fp;        /* ptr to open file */
  195. #ifndef i386
  196.     register
  197. #endif
  198.     unsigned long    nchars;        /* number of chars read */
  199.     unsigned long    nlines;        /* number of lines read */
  200.     unsigned long    nulls;        /* number of null chars */
  201.     unsigned long    toolong;    /*
  202.                      * number of lines
  203.                      * which were too long
  204.                      */
  205.     bool_t        incomplete;    /* incomplete last line */
  206.     Line        *lptr = NULL;    /* pointer to list of lines */
  207.     Line        *last = NULL;    /*
  208.                      * last complete line
  209.                      * read in
  210.                      */
  211.     Line        *lp;        /*
  212.                      * line currently
  213.                      * being read in
  214.                      */
  215.     register enum {
  216.     at_soln,
  217.     in_line,
  218.     got_eolnc0,
  219.     at_eoln,
  220.     at_eof
  221.     }            state;
  222.     register char    *buff;        /*
  223.                      * text of line
  224.                      * being read in
  225.                      */
  226.     register int    col;        /* current column in line */
  227.  
  228.     if (P_ischanged(P_format)) {
  229.     show_message(window, "\"%s\" [%s]%s", filename, fmtname, extra_str);
  230.     } else {
  231.     show_message(window, "\"%s\"%s", filename, extra_str);
  232.     }
  233.  
  234.     fp = fopenrb(filename);
  235.     if (fp == NULL) {
  236.     *headp = *tailp = NULL;
  237.     if (exists(filename)) {
  238.         show_error(window, "Can't read \"%s\"", filename);
  239.         return(gf_CANTOPEN);
  240.     } else {
  241.         show_message(window, "\"%s\"%s", filename, no_file_str);
  242.         return(gf_NEWFILE);
  243.     }
  244.     }
  245.  
  246. #ifdef    SETVBUF_AVAIL
  247.     {
  248.     unsigned int    bufsize;
  249.  
  250.     bufsize = READBUFSIZ;
  251.  
  252.     /*
  253.      * Keep trying to set the buffer size to something
  254.      * large, reducing the size by 1/2 each time.
  255.      * This will eventually work, and will not usually
  256.      * take very many calls. (jmd)
  257.      */
  258.     while (setvbuf(fp, (char *) NULL, _IOFBF, bufsize) != 0 &&
  259.                         bufsize > 1) {
  260.         bufsize /= 2;
  261.     }
  262.     }
  263. #endif /* SETVBUF_AVAIL */
  264.  
  265.     nchars = nlines = nulls = toolong = 0;
  266.     col = 0;
  267.     incomplete = FALSE;
  268.     state = at_soln;
  269.     while (state != at_eof) {
  270.  
  271.     register int    c;
  272.  
  273.     c = getc(fp);
  274.  
  275.     if (c == EOF || c == eofchar) {
  276.         if (state != at_soln) {
  277.         /*
  278.          * Reached EOF in the middle of a line; what
  279.          * we do here is to pretend we got a properly
  280.          * terminated line, and assume that a
  281.          * subsequent getc will still return EOF.
  282.          */
  283.         incomplete = TRUE;
  284.         state = at_eoln;
  285.         } else {
  286.         state = at_eof;
  287.         break;
  288.         }
  289.     } else {
  290.         nchars++;
  291.  
  292.         switch (state) {
  293.         case at_soln:
  294.         /*
  295.          * We're at the start of a line, &
  296.          * we've got at least one character,
  297.          * so we have to allocate a new Line
  298.          * structure.
  299.          *
  300.          * If we can't do it, we throw away
  301.          * the lines we've read in so far, &
  302.          * return gf_NOMEM.
  303.          */
  304.         if ((lp = newline(MAX_LINE_LENGTH)) == NULL) {
  305.             if (lptr != NULL) {
  306.             throw(lptr);
  307.             }
  308.             (void) fclose(fp);
  309.             *headp = *tailp = NULL;
  310.             return(gf_NOMEM);
  311.         } else {
  312.             buff = lp->l_text;
  313.         }
  314.         case in_line:
  315.         if (c == eolnchars[0]) {
  316.             if (eolnchars[1] == NOCHAR) {
  317.             state = at_eoln;
  318.             } else {
  319.             state = got_eolnc0;
  320.             continue;
  321.             }
  322.         } else if (c == eolnchars [1] && curfmt.tf_dynamic &&
  323.                          eolnhack(c)) {
  324.             /*
  325.              * If we get the second end-of-line
  326.              * marker, but not the first, see if
  327.              * we can accept the second one by
  328.              * itself as an end-of-line.
  329.              */
  330.             state = at_eoln;
  331.         }
  332.         break;
  333.         case got_eolnc0:
  334.         if (c == eolnchars[1]) {
  335.             state = at_eoln;
  336.         } else if (curfmt.tf_dynamic && eolnhack(eolnchars[0])) {
  337.             /*
  338.              * If we get the first end-of-line
  339.              * marker, but not the second, see
  340.              * if we can accept the first one
  341.              * by itself as an end-of-line.
  342.              */
  343.             (void) ungetc(c, fp);
  344.             state = at_eoln;
  345.         } else {
  346.             /*
  347.              * We can't. Just take the first one
  348.              * literally.
  349.              */
  350.             state = in_line;
  351.             (void) ungetc(c, fp);
  352.             c = eolnchars [0];
  353.         }
  354.         }
  355.     }
  356.  
  357.     if (state == at_eoln || col >= MAX_LINE_LENGTH - 1) {
  358.         /*
  359.          * First null-terminate the old line.
  360.          */
  361.         buff[col] = '\0';
  362.  
  363.         /*
  364.          * If this fails, we squeak at the user and
  365.          * then throw away the lines read in so far.
  366.          */
  367.         buff = realloc(buff, (unsigned) col + 1);
  368.         if (buff == NULL) {
  369.         if (lptr != NULL)
  370.             throw(lptr);
  371.         (void) fclose(fp);
  372.         *headp = *tailp = NULL;
  373.         return gf_NOMEM;
  374.         }
  375.         lp->l_text = buff;
  376.         lp->l_size = col + 1;
  377.  
  378.         /*
  379.          * Tack the line onto the end of the list,
  380.          * and then point "last" at it.
  381.          */
  382.         if (lptr == NULL) {
  383.         lptr = lp;
  384.         last = lptr;
  385.         } else {
  386.         last->l_next = lp;
  387.         lp->l_prev = last;
  388.         last = lp;
  389.         }
  390.  
  391.         nlines++;
  392.         col = 0;
  393.         if (state != at_eoln) {
  394.         toolong++;
  395.         /*
  396.          * We didn't get a properly terminated line,
  397.          * but we still have to do something with the
  398.          * character we've read.
  399.          */
  400.         (void) ungetc(c, fp);
  401.         }
  402.         state = at_soln;
  403.     } else {
  404.         /*
  405.          * Nulls are special; they can't show up in the file.
  406.          */
  407.         if (c == '\0') {
  408.         nulls++;
  409.         continue;
  410.         }
  411.         state = in_line;
  412.         buff[col++] = c;
  413.     }
  414.     }
  415.     (void) fclose(fp);
  416.  
  417.     {
  418.     /*
  419.      * Assemble error messages for status line.
  420.      */
  421.     Flexbuf        errbuf;
  422.     char        *errs;
  423.  
  424.     flexnew(&errbuf);
  425.     if (nulls > 0) {
  426.         (void) lformat(&errbuf, " (%ld null character%s)",
  427.                nulls, (nulls == 1 ? "" : "s"));
  428.     }
  429.     if (toolong > 0) {
  430.         (void) lformat(&errbuf, " (%ld line%s too long)",
  431.                toolong, (toolong == 1 ? "" : "s"));
  432.     }
  433.     if (incomplete) {
  434.         (void) lformat(&errbuf, " (incomplete last line)");
  435.     }
  436.  
  437.     /*
  438.      * Show status line.
  439.      */
  440.     errs = flexgetstr(&errbuf);
  441.     if (P_ischanged(P_format)) {
  442.         show_message(window, "\"%s\" [%s]%s %ld/%ld%s",
  443.                 filename, fmtname, extra_str,
  444.                 nlines, nchars, errs);
  445.     } else {
  446.         show_message(window, "\"%s\"%s %ld/%ld%s",
  447.                 filename, extra_str, nlines, nchars, errs);
  448.     }
  449.     flexdelete(&errbuf);
  450.     }
  451.  
  452.     *headp = lptr;
  453.     *tailp = last;
  454.  
  455.     return(nlines);
  456. }
  457.  
  458. /*
  459.  * writeit - write to file 'fname' lines 'start' through 'end'
  460.  *
  461.  * If either 'start' or 'end' are NULL, the default
  462.  * is to use the start or end of the file respectively.
  463.  *
  464.  * Unless the "force" argument is TRUE, we do not write
  465.  * out buffers which have the "readonly" flag set.
  466.  */
  467. bool_t
  468. writeit(window, fname, start, end, force)
  469. Xviwin    *window;
  470. char    *fname;
  471. Line    *start, *end;
  472. bool_t    force;
  473. {
  474.     FILE        *fp;
  475.     unsigned long    nc;
  476.     unsigned long    nl;
  477.     Buffer        *buffer;
  478.  
  479.     buffer = window->w_buffer;
  480.  
  481.     if (is_readonly(buffer) && !force) {
  482.     show_error(window, "\"%s\" File is read only", fname);
  483.     return(FALSE);
  484.     }
  485.  
  486.     show_message(window,
  487.         (P_ischanged(P_format) ? "\"%s\" [%s]" :  "\"%s\""),
  488.                         fname, fmtname);
  489.  
  490.     /*
  491.      * Preserve the buffer here so if the write fails it will at
  492.      * least have been saved.
  493.      */
  494.     if (!preservebuf(window)) {
  495.     return(FALSE);
  496.     }
  497.  
  498.     if (!can_write(fname)) {
  499.     show_error(window, "\"%s\" Permission denied", fname);
  500.     return(FALSE);
  501.     }
  502.  
  503.     fp = fopenwb(fname);
  504.     if (fp == NULL) {
  505.     show_error(window, "Can't write \"%s\"", fname);
  506.     return(FALSE);
  507.     }
  508.  
  509.     if (put_file(window, fp, start, end, &nc, &nl) == FALSE) {
  510.     return(FALSE);
  511.     }
  512.  
  513.     if (P_ischanged(P_format)) {
  514.     show_message(window, "\"%s\" [%s] %ld/%ld", fname, fmtname, nl, nc);
  515.     } else {
  516.     show_message(window, "\"%s\" %ld/%ld", fname, nl, nc);
  517.     }
  518.  
  519.     /*
  520.      * Make sure any preserve file is removed if it isn't wanted.
  521.      * It's not worth checking for the file's existence before
  522.      * trying to remove it; the remove() will do the check anyway.
  523.      */
  524.     if (Pn(P_preserve) < psv_PARANOID) {
  525.     if (buffer->b_tempfname != NULL) {
  526.         (void) remove(buffer->b_tempfname);
  527.     }
  528.     }
  529.  
  530.     /*
  531.      * If no start and end lines were specified, or they
  532.      * were specified as the start and end of the buffer,
  533.      * and we wrote out the whole file, then we can clear
  534.      * the modified status. This must be safe.
  535.      */
  536.     if ((start == NULL || start == buffer->b_file) &&
  537.             (end == NULL || end == buffer->b_lastline->l_prev)) {
  538.     buffer->b_flags &= ~FL_MODIFIED;
  539.     }
  540.  
  541.     return(TRUE);
  542. }
  543.  
  544. /*
  545.  * Write out the buffer between the given two line pointers
  546.  * (which default to start and end of buffer) to the given file
  547.  * pointer. The reference parameters ncp and nlp are filled in
  548.  * with the number of characters and lines written to the file.
  549.  * The return value is TRUE for success, FALSE for all kinds of
  550.  * failure.
  551.  */
  552. bool_t
  553. put_file(window, f, start, end, ncp, nlp)
  554. Xviwin        *window;
  555. register FILE    *f;
  556. Line        *start, *end;
  557. unsigned long    *ncp, *nlp;
  558. {
  559.     register Line        *lp;
  560.     register unsigned long    nchars;
  561.     unsigned long        nlines;
  562.     Buffer            *buffer;
  563.  
  564.     buffer = window->w_buffer;
  565.  
  566. #ifdef    SETVBUF_AVAIL
  567.     {
  568.     unsigned int    bufsize = WRTBUFSIZ;
  569.  
  570.     /*
  571.      * Keep trying to set the buffer size to something
  572.      * large, reducing the size by 1/2 each time.
  573.      * This will eventually work, and will not usually
  574.      * take very many calls. (jmd)
  575.      */
  576.     while (setvbuf(f, (char *) NULL, _IOFBF, bufsize) != 0 &&
  577.                             bufsize > 1) {
  578.         bufsize /= 2;
  579.     }
  580.     }
  581. #endif /* SETVBUF_AVAIL */
  582.  
  583.     /*
  584.      * If we were given a bound, start there. Otherwise just
  585.      * start at the beginning of the file.
  586.      */
  587.     if (start == NULL) {
  588.     lp = buffer->b_file;
  589.     } else {
  590.     lp = start;
  591.     }
  592.  
  593.     nlines = 0;
  594.     nchars = 0;
  595.     for ( ; lp != buffer->b_lastline; lp = lp->l_next) {
  596.  
  597.     register char    *cp;
  598.  
  599.     /*
  600.      * Write out the characters which comprise the line.
  601.      * Register declarations are used for all variables
  602.      * which form a part of this loop, in order to make
  603.      * it as fast as possible.
  604.      */
  605.     for (cp = lp->l_text; *cp != '\0'; cp++) {
  606.         putc(*cp, f);
  607.         nchars++;
  608.     }
  609.  
  610.     putc(eolnchars[0], f);
  611.     nchars++;
  612.  
  613.     if (eolnchars[1] != NOCHAR) {
  614.         putc(eolnchars[1], f);
  615.         nchars++;
  616.     }
  617.  
  618.     if (ferror(f)) {
  619.         (void) fclose(f);
  620.         return(FALSE);
  621.     }
  622.  
  623.     nlines++;
  624.  
  625.     /*
  626.      * If we were given an upper bound, and we
  627.      * just did that line, then bag it now.
  628.      */
  629.     if (end != NULL) {
  630.         if (end == lp)
  631.         break;
  632.     }
  633.     }
  634.  
  635.     if (fclose(f) != 0) {
  636.     return(FALSE);
  637.     }
  638.  
  639.     /*
  640.      * Success!
  641.      */
  642.     if (ncp != NULL)
  643.     *ncp = nchars;
  644.     if (nlp != NULL)
  645.     *nlp = nlines;
  646.     return(TRUE);
  647. }
  648.