home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1709 / tmp.c < prev   
Encoding:
C/C++ Source or Header  |  1990-12-28  |  10.7 KB  |  505 lines

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