home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / elvis_1.4.tar.Z / elvis_1.4.tar / tmp.c < prev    next >
C/C++ Source or Header  |  1990-12-06  |  13KB  |  592 lines

  1. /* tmpfile.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains functions which create & readback a TMPFILE */
  12.  
  13.  
  14. #include "config.h"
  15. #include <ctype.h>
  16. #include "vi.h"
  17. #if TOS
  18. # include <stat.h>
  19. #else
  20. # if OSK
  21. #  include "osk.h"
  22. # else
  23. #  include <sys/stat.h>
  24. # endif
  25. #endif
  26.  
  27.  
  28. #ifndef NO_MODELINE
  29. static void do_modeline(l, stop)
  30.     long    l;    /* line number to start at */
  31.     long    stop;    /* line number to stop at */
  32. {
  33.     char    *str;    /* used to scan through the line */
  34.     char    *start;    /* points to the start of the line */
  35.     char    buf[80];
  36.  
  37.     /* if modelines are disabled, then do nothing */
  38.     if (!*o_modeline)
  39.     {
  40.         return;
  41.     }
  42.  
  43.     /* for each line... */
  44.     for (l = 1; l <= stop; l++)
  45.     {
  46.         /* for each position in the line.. */
  47.         for (str = fetchline(l); *str; str++)
  48.         {
  49.             /* if it is the start of a modeline command... */
  50.             if ((str[0] == 'e' && str[1] == 'x'
  51.               || str[0] == 'v' && str[1] == 'i')
  52.               && str[2] == ':')
  53.             {
  54.                 start = str += 3;
  55.  
  56.                 /* find the end */
  57.                 while (*str && *str != ':')
  58.                 {
  59.                     str++;
  60.                 }
  61.  
  62.                 /* if it is a well-formed modeline, execute it */
  63.                 if (*str && str - start < sizeof buf)
  64.                 {
  65.                     strncpy(buf, start, (int)(str - start));
  66.                     buf[str - start] = '\0';
  67.                     doexcmd(buf);
  68.                     break;
  69.                 }
  70.             }
  71.         }
  72.     }
  73. }
  74. #endif
  75.  
  76.  
  77. /* The FAIL() macro prints an error message and then exits. */
  78. #define FAIL(why,arg)    mode = MODE_EX; msg(why, arg); endwin(); exit(9)
  79.  
  80. /* This is the name of the temp file */
  81. static char    tmpname[80];
  82.  
  83. /* This function creates the temp file and copies the original file into it.
  84.  * Returns if successful, or stops execution if it fails.
  85.  */
  86. int tmpstart(filename)
  87.     char        *filename; /* name of the original file */
  88. {
  89.     int        origfd;    /* fd used for reading the original file */
  90.     struct stat    statb;    /* stat buffer, used to examine inode */
  91.     REG BLK        *this;    /* pointer to the current block buffer */
  92.     REG BLK        *next;    /* pointer to the next block buffer */
  93.     int        inbuf;    /* number of characters in a buffer */
  94.     int        nread;    /* number of bytes read */
  95.     REG int        j, k;
  96.     int        i;
  97.     int        sum;    /* used for calculating a checksum for this */
  98.     char        *scan;
  99.     long        nbytes;
  100.  
  101.     /* switching to a different file certainly counts as a change */
  102.     changes++;
  103.     redraw(MARK_UNSET, FALSE);
  104.  
  105.     /* open the original file for reading */
  106.     *origname = '\0';
  107.     if (filename && *filename)
  108.     {
  109.         strcpy(origname, filename);
  110.         origfd = open(origname, O_RDONLY);
  111.         if (origfd < 0 && errno != ENOENT)
  112.         {
  113.             msg("Can't open \"%s\"", origname);
  114.             return tmpstart("");
  115.         }
  116.         if (origfd >= 0)
  117.         {
  118.             if (stat(origname, &statb) < 0)
  119.             {
  120.                 FAIL("Can't stat \"%s\"", origname);
  121.             }
  122. #if TOS
  123.             if (origfd >= 0 && (statb.st_mode & S_IJDIR))
  124. #else
  125. # if OSK
  126.             if (origfd >= 0 && (statb.st_mode & S_IFDIR))
  127. # else
  128.             if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
  129. # endif
  130. #endif
  131.             {
  132.                 msg("\"%s\" is not a regular file", origname);
  133.                 return tmpstart("");
  134.             }
  135.         }
  136.         else
  137.         {
  138.             stat(".", &statb);
  139.         }
  140.         if (origfd >= 0)
  141.         {
  142.             origtime = statb.st_mtime;
  143. #if MSDOS || OSK
  144.             if (*o_readonly || !(statb.st_mode & S_IWRITE))
  145. #endif
  146. #if TOS
  147.             if (*o_readonly || (statb.st_mode & S_IJRON))
  148. #endif
  149. #if ANY_UNIX
  150.             if (*o_readonly || !(statb.st_mode &
  151.                   (statb.st_uid != geteuid() ? 0022 : 0200)))
  152. #endif
  153.             {
  154.                 setflag(file, READONLY);
  155.             }
  156.         }
  157.         else
  158.         {
  159.             origtime = 0L;
  160.         }
  161.     }
  162.     else
  163.     {
  164.         setflag(file, NOFILE);
  165.         origfd = -1;
  166.         origtime = 0L;
  167.         stat(".", &statb);
  168.     }
  169.  
  170.     /* generate a checksum from the file's name */
  171.     for (sum = 0, scan = origname + strlen(origname);
  172.          --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
  173.          sum = sum + *scan)
  174.     {
  175.     }
  176.     sum &= 0xf;
  177.  
  178.     /* make a name for the tmp file */
  179. #if MSDOS || TOS
  180.     /* MS-Dos doesn't allow multiple slashes, but supports drives
  181.      * with current directories.
  182.      * This relies on TMPNAME beginning with "%s\\"!!!!
  183.      */
  184.     strcpy(tmpname, o_directory);
  185.     if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
  186.         tmpname[i++]=SLASH;
  187.     sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
  188. #else
  189.     sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
  190. #endif
  191.  
  192.     /* make sure nobody else is editing the same file */
  193.     if (access(tmpname, 0) == 0)
  194.     {
  195.         if (*origname)
  196.         {
  197.             msg("\"%s\" is busy", filename);
  198.             return tmpstart("");
  199.         }
  200.         FAIL("\"%s\" is busy", filename);
  201.     }
  202.  
  203.     /* create the temp file */
  204. #if ANY_UNIX
  205.     close(creat(tmpname, 0600));        /* only we can read it */
  206. #else
  207.     close(creat(tmpname, FILEPERMS));    /* anybody body can read it, alas */
  208. #endif
  209.     tmpfd = open(tmpname, O_RDWR | O_BINARY);
  210.     if (tmpfd < 0)
  211.     {
  212.         FAIL("Can't create temporary file, errno=%d", errno);
  213.         return 1;
  214.     }
  215.  
  216.     /* allocate space for the header in the file */
  217.     write(tmpfd, hdr.c, (unsigned)BLKSIZE);
  218.  
  219. #ifndef NO_RECYCLE
  220.     /* initialize the block allocator */
  221.     /* This must already be done here, before the first attempt
  222.      * to write to the new file! GB */
  223.     garbage();
  224. #endif
  225.  
  226.     /* initialize lnum[] */
  227.     for (i = 1; i < MAXBLKS; i++)
  228.     {
  229.         lnum[i] = INFINITY;
  230.     }
  231.     lnum[0] = 0;
  232.  
  233.     /* if there is no original file, then create a 1-line file */
  234.     if (origfd < 0)
  235.     {
  236.         hdr.n[0] = 0;    /* invalid inode# denotes new file */
  237.  
  238.         this = blkget(1);     /* get the new text block */
  239.         strcpy(this->c, "\n");    /* put a line in it */
  240.  
  241.         lnum[1] = 1L;    /* block 1 ends with line 1 */
  242.         nlines = 1L;    /* there is 1 line in the file */
  243.         nbytes = 1L;
  244.  
  245.         if (*origname)
  246.         {
  247.             msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
  248.         }
  249.         else
  250.         {
  251.             msg("\"[NO FILE]\"  1 line, 1 char");
  252.         }
  253.     }
  254.     else /* there is an original file -- read it in */
  255.     {
  256.         hdr.n[0] = statb.st_ino;
  257.         nbytes = nlines = 0;
  258.  
  259.         /* preallocate 1 "next" buffer */
  260.         i = 1;
  261.         next = blkget(i);
  262.         inbuf = 0;
  263.  
  264.         /* loop, moving blocks from orig to tmp */
  265.         for (;;)
  266.         {
  267.             /* "next" buffer becomes "this" buffer */
  268.             this = next;
  269.  
  270.             /* read [more] text into this block */
  271.             nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
  272.             if (nread < 0)
  273.             {
  274.                 close(origfd);
  275.                 close(tmpfd);
  276.                 tmpfd = -1;
  277.                 unlink(tmpname);
  278.                 FAIL("Error reading \"%s\"", origname);
  279.             }
  280.  
  281.             /* convert NUL characters to something else */
  282.             for (k = inbuf; k < inbuf + nread; k++)
  283.             {
  284.                 if (!this->c[k])
  285.                 {
  286.                     setflag(file, HADNUL);
  287.                     this->c[k] = 0x80;
  288.                 }
  289.             }
  290.             inbuf += nread;
  291.  
  292.             /* if the buffer is empty, quit */
  293.             if (inbuf == 0)
  294.             {
  295.                 goto FoundEOF;
  296.             }
  297.  
  298. #if MSDOS || TOS
  299. /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
  300.    but leaving garbage at end of buf. The same is true for TURBOC. GB. */
  301.  
  302.             memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
  303. #endif
  304.  
  305.             /* search backward for last newline */
  306.             for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
  307.             {
  308.             }
  309.             if (k++ < 0)
  310.             {
  311.                 if (inbuf >= BLKSIZE - 1)
  312.                 {
  313.                     k = 80;
  314.                 }
  315.                 else
  316.                 {
  317.                     k = inbuf;
  318.                 }
  319.             }
  320.  
  321.             /* allocate next buffer */
  322.             next = blkget(++i);
  323.  
  324.             /* move fragmentary last line to next buffer */
  325.             inbuf -= k;
  326.             for (j = 0; k < BLKSIZE; j++, k++)
  327.             {
  328.                 next->c[j] = this->c[k];
  329.                 this->c[k] = 0;
  330.             }
  331.  
  332.             /* if necessary, add a newline to this buf */
  333.             for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
  334.             {
  335.             }
  336.             if (this->c[k] != '\n')
  337.             {
  338.                 setflag(file, ADDEDNL);
  339.                 this->c[k + 1] = '\n';
  340.             }
  341.  
  342.             /* count the lines in this block */
  343.             for (k = 0; k < BLKSIZE && this->c[k]; k++)
  344.             {
  345.                 if (this->c[k] == '\n')
  346.                 {
  347.                     nlines++;
  348.                 }
  349.                 nbytes++;
  350.             }
  351.             lnum[i - 1] = nlines;
  352.         }
  353. FoundEOF:
  354.  
  355.         /* if this is a zero-length file, add 1 line */
  356.         if (nlines == 0)
  357.         {
  358.             this = blkget(1);     /* get the new text block */
  359.             strcpy(this->c, "\n");    /* put a line in it */
  360.  
  361.             lnum[1] = 1;    /* block 1 ends with line 1 */
  362.             nlines = 1;    /* there is 1 line in the file */
  363.             nbytes = 1;
  364.         }
  365.  
  366. #if MSDOS || TOS
  367.         /* each line has an extra CR that we didn't count yet */
  368.         nbytes += nlines;
  369. #endif
  370.  
  371.         /* report the number of lines in the file */
  372.         msg("\"%s\" %s %ld line%s, %ld char%s",
  373.             origname,
  374.             (tstflag(file, READONLY) ? "[READONLY]" : ""),
  375.             nlines,
  376.             nlines == 1 ? "" : "s",
  377.             nbytes,
  378.             nbytes == 1 ? "" : "s");
  379.     }
  380.  
  381.     /* initialize the cursor to start of line 1 */
  382.     cursor = MARK_FIRST;
  383.  
  384.     /* close the original file */
  385.     close(origfd);
  386.  
  387.     /* any other messages? */
  388.     if (tstflag(file, HADNUL))
  389.     {
  390.         msg("This file contained NULs.  They've been changed to \\x80 chars");
  391.     }
  392.     if (tstflag(file, ADDEDNL))
  393.     {
  394.         msg("Newline characters have been inserted to break up long lines");
  395.     }
  396.  
  397. #ifndef NO_MODELINE
  398.     if (nlines > 10)
  399.     {
  400.         do_modeline(1L, 5L);
  401.         do_modeline(nlines - 4L, nlines);
  402.     }
  403.     else
  404.     {
  405.         do_modeline(1L, nlines);
  406.     }
  407. #endif
  408.     return 0;
  409. }
  410.  
  411.  
  412.  
  413. /* This function copies the temp file back onto an original file.
  414.  * Returns TRUE if successful, or FALSE if the file could NOT be saved.
  415.  */
  416. int tmpsave(filename, bang)
  417.     char    *filename;    /* the name to save it to */
  418.     int    bang;        /* forced write? */
  419. {
  420.     int        fd;    /* fd of the file we're writing to */
  421.     REG int        len;    /* length of a text block */
  422.     REG BLK        *this;    /* a text block */
  423.     long        bytes;    /* byte counter */
  424.     REG int        i;
  425.  
  426.     /* if no filename is given, assume the original file name */
  427.     if (!filename || !*filename)
  428.     {
  429.         filename = origname;
  430.     }
  431.  
  432.     /* if still no file name, then fail */
  433.     if (!*filename)
  434.     {
  435.         msg("Don't know a name for this file -- NOT WRITTEN");
  436.         return FALSE;
  437.     }
  438.  
  439.     /* can't rewrite a READONLY file */
  440.     if (!strcmp(filename, origname) && *o_readonly && !bang)
  441.     {
  442.         msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
  443.         return FALSE;
  444.     }
  445.  
  446.     /* open the file */
  447.     if (*filename == '>' && filename[1] == '>')
  448.     {
  449.         filename += 2;
  450.         while (*filename == ' ' || *filename == '\t')
  451.         {
  452.             filename++;
  453.         }
  454. #ifdef O_APPEND
  455.         fd = open(filename, O_WRONLY|O_APPEND);
  456. #else
  457.         fd = open(filename, O_WRONLY);
  458.         lseek(fd, 0L, 2);
  459. #endif
  460.     }
  461.     else
  462.     {
  463.         /* either the file must not exist, or it must be the original
  464.          * file, or we must have a bang
  465.          */
  466.         if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
  467.         {
  468.             msg("File already exists - Use :w! to overwrite");
  469.             return FALSE;
  470.         }
  471.         fd = creat(filename, FILEPERMS);
  472.     }
  473.     if (fd < 0)
  474.     {
  475.         msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
  476.         return FALSE;
  477.     }
  478.  
  479.     /* write each text block to the file */
  480.     bytes = 0L;
  481.     for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
  482.     {
  483.         for (len = 0; len < BLKSIZE && this->c[len]; len++)
  484.         {
  485.         }
  486.         twrite(fd, this->c, len);
  487.         bytes += len;
  488.     }
  489.  
  490.     /* reset the "modified" flag */
  491.     clrflag(file, MODIFIED);
  492.     significant = FALSE;
  493.  
  494.     /* report lines & characters */
  495. #if MSDOS || TOS
  496.     bytes += nlines; /* for the inserted carriage returns */
  497. #endif
  498.     if (strncmp(filename, o_directory, strlen(o_directory)))
  499.     {
  500.         msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
  501.     }
  502.  
  503.     /* close the file */
  504.     close(fd);
  505.  
  506.     return TRUE;
  507. }
  508.  
  509.  
  510. /* This function deletes the temporary file.  If the file has been modified
  511.  * and "bang" is FALSE, then it returns FALSE without doing anything; else
  512.  * it returns TRUE.
  513.  *
  514.  * If the "autowrite" option is set, then instead of returning FALSE when
  515.  * the file has been modified and "bang" is false, it will call tmpend().
  516.  */
  517. int tmpabort(bang)
  518.     int    bang;
  519. {
  520.     /* if there is no file, return successfully */
  521.     if (tmpfd < 0)
  522.     {
  523.         return TRUE;
  524.     }
  525.  
  526.     /* see if we must return FALSE -- can't quit */
  527.     if (!bang && tstflag(file, MODIFIED))
  528.     {
  529.         /* if "autowrite" is set, then act like tmpend() */
  530.         if (*o_autowrite)
  531.             return tmpend(bang);
  532.         else
  533.             return FALSE;
  534.     }
  535.  
  536.     /* delete the tmp file */
  537.     cutswitch(tmpname);
  538.     close(tmpfd);
  539.     tmpfd = -1;
  540.     unlink(tmpname);
  541.     strcpy(prevorig, origname);
  542.     prevline = markline(cursor);
  543.     *origname = '\0';
  544.     origtime = 0L;
  545.     blkinit();
  546.     nlines = 0;
  547.     initflags();
  548.     return TRUE;
  549. }
  550.  
  551. /* This function saves the file if it has been modified, and then deletes
  552.  * the temporary file. Returns TRUE if successful, or FALSE if the file
  553.  * needs to be saved but can't be.  When it returns FALSE, it will not have
  554.  * deleted the tmp file, either.
  555.  */
  556. int tmpend(bang)
  557.     int    bang;
  558. {
  559.     /* save the file if it has been modified */
  560.     if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
  561.     {
  562.         return FALSE;
  563.     }
  564.  
  565.     /* delete the tmp file */
  566.     tmpabort(TRUE);
  567.  
  568.     return TRUE;
  569. }
  570.  
  571.  
  572. /* If the tmp file has been changed, then this function will force those
  573.  * changes to be written to the disk, so that the tmp file will survive a
  574.  * system crash or power failure.
  575.  */
  576. #if MSDOS || TOS || OSK
  577. sync()
  578. {
  579. # if OSK
  580.     /* OS9 doesn't need an explicit sync operation, but the linker
  581.      * demands something called sync(), so this is a dummy function.
  582.      */
  583. #else
  584.     /* MS-DOS and TOS don't flush their buffers until the file is closed,
  585.      * so here we close the tmp file and then immediately reopen it.
  586.      */
  587.     close(tmpfd);
  588.     tmpfd = open(tmpname, O_RDWR | O_BINARY);
  589. #endif
  590. }
  591. #endif
  592.