home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / v / vim_src.zip / FILEIO.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  16KB  |  686 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * fileio.c: read from and write to a file
  13.  */
  14.  
  15. /*
  16.  * special feature of this version: NUL characters in the file are
  17.  * replaced by newline characters in memory. This allows us to edit
  18.  * binary files!
  19.  */
  20.  
  21. #include "vim.h"
  22. #include "globals.h"
  23. #include "proto.h"
  24. #include "param.h"
  25. #include "fcntl.h"
  26.  
  27. #define BUFSIZE 4096
  28. static void do_mlines __ARGS((void));
  29.  
  30.     void
  31. filemess(name, s)
  32.     char        *name;
  33.     char        *s;
  34. {
  35.     smsg("\"%s\" %s", ((name == NULL) ? "" : name), s);
  36. }
  37.  
  38. /*
  39.  * Read lines from file 'fname' into the buffer after line 'from'.
  40.  *
  41.  * 1. We allocate blocks with m_blockalloc, as big as possible.
  42.  * 2. Each block is filled with characters from the file with a single read().
  43.  * 3. The lines are inserted in the buffer with appendline().
  44.  *
  45.  * (caller must check that fname != NULL)
  46.  */
  47.     int
  48. readfile(fname, from, newfile)
  49.     char           *fname;
  50.     linenr_t        from;
  51.     int                newfile;
  52. {
  53. #ifdef UNIX
  54.     int                 fd = -1;
  55. #else
  56.     int                 fd;
  57. #endif
  58.     register u_char     c;
  59.     register linenr_t    lnum = from;
  60.     register u_char     *ptr = NULL;            /* pointer into read buffer */
  61.     register u_char        *buffer = NULL;            /* read buffer */
  62.     register long        size;
  63.     long                filesize;
  64. #define UNKNOWN        0x0fffffff                    /* file size is unknown */
  65.     linenr_t            linecnt = line_count;
  66.     int                    incomplete = FALSE;     /* was the last line incomplete? */
  67.     int                 error = 0;                /* read errors encountered */
  68.     long                linerest = 0;            /* remaining characters in line */
  69.     long                filerest;                /* remaining characters in file */
  70.  
  71.     if (bufempty())        /* special case: buffer has no lines */
  72.         linecnt = 0;
  73.  
  74.     if (
  75. #ifdef UNIX
  76.         !(getperm(fname) & 0200) ||                /* root's way to check RO */
  77. #endif
  78.         (fd = open(fname, O_RDWR)) == -1)        /* cannot open r/w */
  79.     {
  80.         if ((fd = open(fname, O_RDONLY)) == -1) /* cannot open at all */
  81.         {
  82.             if (newfile)
  83.                 filemess(fname, "[New File]");
  84.  
  85. #ifdef MSDOS        /* the screen may be messed up by the "insert disk
  86.                             in drive b: and hit return" message */
  87.             updateScreen(CLEAR);
  88. #endif
  89.             return TRUE;
  90.         }
  91.         if (newfile)                            /* set file readonly */
  92.             p_ro = TRUE;
  93.     }
  94.     else if (newfile && !readonlymode)        /* set file not readonly */
  95.         p_ro = FALSE;
  96.  
  97.     if (
  98. #ifdef MSDOS
  99.     /* the CR characters disappear in read(), so the file lenght is wrong */
  100.         p_tx == TRUE ||
  101. #endif
  102.             (filesize = lseek(fd, 0L, 2)) < 0)    /* get length of file */
  103.         filesize = UNKNOWN;
  104.     lseek(fd, 0L, 0);
  105.  
  106.     filemess(fname, "");
  107.  
  108.     for (filerest = filesize; !error && !got_int && filerest != 0; breakcheck())
  109.     {
  110.         /*
  111.          * We allocate as much space for the file as we can get, plus
  112.          * space for the old line, one NUL in front and one NUL at the tail.
  113.          * The amount is limited by the fact that read() only can read
  114.          * upto max_unsigned characters.
  115.          * If we don't know the file size, just get one Kbyte.
  116.          */
  117.         if (filesize >= UNKNOWN)
  118.             size = 1024;
  119.         else if (filerest > 0xff00L)
  120.             size = 0xff00L;
  121.         else if (filerest < 10)
  122.             size = 10;
  123.         else
  124.             size = filerest;
  125.  
  126.         for ( ; size >= 10; size /= 2)
  127.         {
  128.             if ((buffer = (u_char *)m_blockalloc((u_long)(size + linerest + 4), FALSE))
  129.                         != NULL)
  130.                 break;
  131.         }
  132.         if (buffer == NULL)
  133.         {
  134.             emsg(e_outofmem);
  135.             error = 1;
  136.             break;
  137.         }
  138.         buffer[0] = NUL;    /* make sure there is a NUL in front of the first line */
  139.         ++buffer;
  140.         if (linerest)        /* copy characters from the previous buffer */
  141.         {
  142.             ptr -= linerest;
  143.             memmove((char *)buffer, (char *)ptr, linerest);
  144.             memset((char *)ptr, 1, linerest);    /* fill with non-NULs */
  145.             ptr[linerest - 1] = NUL;            /* add a NUL on the end */
  146.             free_line((char *)ptr);                /* free the space we don't use */
  147.         }
  148.         ptr = buffer + linerest;
  149.         
  150.         if ((size = (unsigned)read(fd, (char *)ptr, (size_t)size)) <= 0)
  151.         {
  152.             error = 2;
  153.             break;
  154.         }
  155.         if (filesize >= UNKNOWN)            /* if we don't know the file size */
  156.             filesize += size;                /* .. count the number of characters */
  157.         else                                /* .. otherwise */
  158.             filerest -= size;                /* .. compute the remaining length */
  159.  
  160.         /*
  161.          * This loop is executed once for every character read.
  162.          * Keep it fast!
  163.          */
  164.         --ptr;
  165.         while (++ptr, --size >= 0)
  166.         {
  167.             if ((c = *ptr) != NUL && c != NL)    /* catch most common case */
  168.                 continue;
  169.             if (c == NUL)
  170.                 *ptr = NL;        /* NULs are replaced by newlines! */
  171.             else
  172.             {
  173.                 *ptr = NUL;        /* end of line */
  174.                 if (!appendline(lnum, (char *)buffer))
  175.                 {
  176.                     error = 1;
  177.                     break;
  178.                 }
  179.                 ++lnum;
  180.                 buffer = ptr + 1;
  181.             }
  182.         }
  183.         linerest = ptr - buffer;
  184.     }
  185.     if (error != 1 && linerest != 0)
  186.     {
  187.         /*
  188.          * If we get EOF in the middle of a line, note the fact and
  189.          * complete the line ourselves.
  190.          */
  191.         incomplete = TRUE;
  192.         *ptr = NUL;
  193.         if (!appendline(lnum, (char *)buffer))
  194.             error = 1;
  195.     }
  196.     if (error == 2 && filesize >= UNKNOWN)    /* no error, just EOF encountered */
  197.     {
  198.         filesize -= UNKNOWN;
  199.         error = 0;
  200.     }
  201.  
  202.     close(fd);
  203.  
  204. #ifdef MSDOS        /* the screen may be messed up by the "insert disk
  205.                             in drive b: and hit return" message */
  206.     updateScreen(CLEAR);
  207. #endif
  208.  
  209.     if (got_int)
  210.     {
  211.         filemess(fname, e_interr);
  212.         return FALSE;            /* an interrupt isn't really an error */
  213.     }
  214.  
  215.     linecnt = line_count - linecnt;
  216.     smsg("\"%s\" %s%s%s%ld line%s, %ld character%s",
  217.             fname,
  218.             p_ro ? "[readonly] " : "",
  219.             incomplete ? "[Incomplete last line] " : "",
  220.             error ? "[READ ERRORS] " : "",
  221.             (long)linecnt, plural((long)linecnt),
  222.             filesize, plural(filesize));
  223.  
  224.     u_clearline();        /* cannot use "U" command after adding lines */
  225.  
  226.     if (newfile)        /* edit a new file: read mode from lines */
  227.         do_mlines();
  228.     if (from < line_count)
  229.     {
  230.         Curpos.lnum = from + 1;    /* put cursor at first new line */
  231.         Curpos.col = 0;
  232.     }
  233.  
  234.     return FALSE;
  235. }
  236.  
  237. /*
  238.  * writeit - write to file 'fname' lines 'start' through 'end'
  239.  *
  240.  * If either 'start' or 'end' contain null line pointers, the default is to use
  241.  * the start or end of the file respectively.
  242.  *
  243.  * We do our own buffering here because fwrite() is so slow.
  244.  *
  245.  * If forceit is true, we don't care for errors when attempting backups (jw).
  246.  * In case of an error everything possible is done to restore the original file.
  247.  * But when forceit is TRUE, we risk loosing it.
  248.  */
  249.     int
  250. writeit(fname, start, end, append, forceit)
  251.     char            *fname;
  252.     linenr_t        start, end;
  253.     int                append;
  254.     int                forceit;
  255. {
  256.     int                 fd;
  257.     char               *backup = NULL;
  258.     register char       *s;
  259.     register u_char       *ptr;
  260.     register u_char        c;
  261.     register int        len;
  262.     register linenr_t    lnum;
  263.     long                nchars;
  264.     char                *errmsg = NULL;
  265.     char                *buffer;
  266.     long                 perm = -1;            /* file permissions */
  267.     int                    retval = TRUE;
  268. #ifdef UNIX
  269.     struct stat            old;
  270. #endif
  271.  
  272. #ifdef DEBUG
  273. # ifdef AMIGA
  274.     if (writelock(fname) == FALSE)    /* file is locked, so what? */
  275.     {
  276.         emsg("File is locked");
  277.         return FALSE;
  278.     }
  279. # endif
  280. #endif
  281.  
  282.     filemess(fname, "");
  283.  
  284.     buffer = alloc(BUFSIZE);
  285.     if (buffer == NULL)
  286.         return FALSE;
  287.  
  288. #ifdef UNIX
  289.         /* get information about original file */
  290.     old.st_dev = old.st_ino = 0;
  291.     stat(fname, &old);
  292.     perm = getperm(fname);
  293. /*
  294.  * If we are not appending, the file exists, and the 'writebackup' or
  295.  * 'backup' option is set, try to make a backup copy of the file.
  296.  */
  297.     if (!append && perm >= 0 && (p_wb || p_bk) &&
  298.                     (fd = open(fname, O_RDONLY)) >= 0)
  299.     {
  300.         int                bfd, buflen, wlen;
  301.         char            buf[BUFSIZE + 1], *wp;
  302.         int                some_error = FALSE;
  303.         struct stat        new;
  304.  
  305.         new.st_dev = new.st_ino = 0;
  306.  
  307.         /*
  308.          * Unix semantics has it, that we may have a writable file, 
  309.          * that cannot be recreated with a simple open(..., O_CREATE, ) e.g:
  310.          *  - the directory is not writable, 
  311.          *  - the file may be a symbolic link, 
  312.          *  - the file may belong to another user/group, etc.
  313.          *
  314.          * For these reasons, the existing writable file must be truncated and
  315.          * reused. Creation of a backup COPY will be attempted.
  316.          */
  317.         if ((backup = modname(fname, ".bak")) == NULL)
  318.         {
  319.             some_error = TRUE;
  320.             goto nobackup;
  321.         }            
  322.         stat(backup, &new);
  323.         if (new.st_dev == old.st_dev && new.st_ino == old.st_ino)
  324.         {
  325.             /*
  326.              * may happen when modname gave the same file back.
  327.              * E.g. silly link, or filename-length reached.
  328.              * If we don't check here, we either ruin the file when
  329.              * copying or erase it after writing. jw.
  330.              */
  331.             errmsg = "Invalid backup file";
  332.             free(backup);
  333.             backup = NULL;    /* there is no backup file to delete */
  334.             goto nobackup;
  335.         }
  336.         remove(backup);        /* remove old backup, if present */
  337.         if ((bfd = open(backup, O_WRONLY | O_CREAT, 0666)) < 0)
  338.         {
  339.             char *home;
  340.  
  341.             /* 
  342.              * oops, no write/create permission here?
  343.              * try again in p_bdir directory. 
  344.              */
  345.             for (wp = fname + strlen(fname); wp >= fname; wp--)
  346.                 if (*wp == '/')
  347.                     break;
  348.             ++wp;
  349.             if (p_bdir[0] == '~' && p_bdir[1] == '/' && (home = getenv("HOME")) != NULL)
  350.                 sprintf(buf, "%s/%s/%s", home, p_bdir + 2, wp);
  351.             else
  352.                 sprintf(buf, "%s/%s", p_bdir, wp);
  353.             free(backup);
  354.             if ((backup = modname(buf, ".bak")) == NULL)
  355.             {
  356.                 some_error = TRUE;
  357.                 goto nobackup;
  358.             }
  359.             stat(backup, &new);
  360.             if (new.st_dev == old.st_dev && new.st_ino == old.st_ino)
  361.             {
  362.                 errmsg = "Invalid backup file";
  363.                 free(backup);
  364.                 backup = NULL;    /* there is no backup file to delete */
  365.                 goto nobackup;
  366.             }
  367.             remove(backup);
  368.             if ((bfd = open(backup, O_WRONLY | O_CREAT, 0666)) < 0)
  369.             {
  370.                 free(backup);
  371.                 backup = NULL;    /* there is no backup file to delete */
  372.                 errmsg = "Can't make backup file";
  373.                 goto nobackup;
  374.             }
  375.         }
  376.         /* set file protection same as original file, but strip s-bit */
  377.         setperm(backup, perm & 0777);
  378.  
  379.         /* copy the file. */
  380.         while ((buflen = read(fd, buf, BUFSIZE)) > 0)
  381.         {
  382.             wp = buf;
  383.             do
  384.             {
  385.                 if ((wlen = write(bfd, wp, buflen)) <= 0)
  386.                 {
  387.                     errmsg = "Can't write to backup file";
  388.                     goto writeerr;
  389.                 }
  390.                 wp += wlen;
  391.                 buflen -= wlen;
  392.             }
  393.             while (buflen > 0);
  394.         }
  395. writeerr:
  396.         close(bfd);
  397.         if (buflen < 0)
  398.             errmsg = "Can't read file for backup";
  399. nobackup:
  400.         close(fd);
  401.     /* ignore errors when forceit is TRUE */
  402.         if ((some_error || errmsg) && !forceit)
  403.         {
  404.             retval = FALSE;
  405.             goto fail;
  406.         }
  407.         errmsg = NULL;
  408.     }
  409.         /* if forceit and the file was read-only: make it writable */
  410.     if (forceit && (old.st_uid == getuid()) && perm >= 0 && !(perm & 0200))
  411.      {
  412.         perm |= 0200;    
  413.         setperm(fname, perm);
  414.      }
  415. #else /* UNIX */
  416.  
  417. /*
  418.  * If we are not appending, the file exists, and the 'writebackup' or
  419.  * 'backup' option is set, make a backup.
  420.  * Do not make any backup, if "writebackup" and "backup" are 
  421.  * both switched off. This helps when editing large files on
  422.  * almost-full disks. (jw)
  423.  */
  424.     if (!append && (perm = getperm(fname)) >= 0 && (p_wb || p_bk))
  425.     {
  426.         /*
  427.          * Form the backup file name - change path/fo.o.h to path/fo.o.h.bak
  428.          */
  429.         backup = modname(fname, ".bak");
  430.         if (backup == NULL)
  431.         {
  432.             if (!forceit)
  433.                 goto fail;
  434.         }
  435.         else
  436.         {
  437.             /*
  438.              * Delete any existing backup and move the current version to the backup.
  439.              * For safety, we don't remove the backup until the write has finished
  440.              * successfully. And if the 'backup' option is set, leave it around.
  441.              */
  442.             remove(backup);
  443.             if (rename(fname, backup) != 0)
  444.             {
  445.                 if (forceit)
  446.                 {
  447.                     free(backup);    /* don't do the rename below */
  448.                     backup = NULL;
  449.                 }
  450.                 else
  451.                 {
  452.                     errmsg = "Can't make backup file";
  453.                     goto fail;
  454.                 }
  455.             }
  456.         }
  457.     }
  458. #endif /* UNIX */
  459.  
  460.         /* 
  461.          * We may try to open the file twice: If we can't write to the
  462.          * file and forceit is TRUE we delete the existing file and try to create
  463.          * a new one. If this still fails we lost the original file!
  464.          */
  465.     while ((fd = open(fname, O_WRONLY | (append ? O_APPEND : (O_CREAT | O_TRUNC)), 0666)) < 0)
  466.      {
  467.         /*
  468.          * A forced write will try to create a new file if the old one is
  469.          * still readonly. This may also happen when the directory is
  470.          * read-only. In that case the remove() will fail.
  471.          */
  472.         if (!errmsg)
  473.         {
  474.             errmsg = "Can't open file for writing";
  475.             if (forceit)
  476.             {
  477. #ifdef UNIX
  478.                 /* we write to the file, thus it should be marked
  479.                                                     writable after all */
  480.                 perm |= 0200;        
  481.                 if (perm >= 0 && ((old.st_uid != getuid()) || (old.st_gid != getgid())))
  482.                     perm &= 0777;
  483. #endif /* UNIX */
  484.                 remove(fname);
  485.                 continue;
  486.             }
  487.         }
  488. /*
  489.  * If we failed to open the file, we don't need a backup.
  490.  * If we moved or removed the original file try to put the backup in its place.
  491.  */
  492. #ifdef UNIX
  493.          if (backup)
  494.         {
  495.             if (forceit)
  496.                 rename(backup, fname);    /* we removed the original, move
  497.                                                 the copy in its place */
  498.             else
  499.                 remove(backup);            /* it was a copy, throw away */
  500.         }
  501. #else
  502.          if (backup)
  503.              rename(backup, fname);    /* try to put the original file back */
  504. #endif
  505.          goto fail;
  506.      }
  507.     errmsg = NULL;
  508.  
  509. #ifdef MSDOS
  510.     setperm(fname, 0); 
  511.     /* 
  512.      * Just to be sure we can write it next time. 
  513.      */
  514. #endif
  515.  
  516.     len = 0;
  517.     s = buffer;
  518.     nchars = 0;
  519.     for (lnum = start; lnum <= end; ++lnum)
  520.     {
  521.         /*
  522.          * The next loop is done once for each character written.
  523.          * Keep it fast!
  524.          */
  525.         ptr = (u_char *)nr2ptr(lnum) - 1;
  526.         while ((c = *++ptr) != NUL)
  527.         {
  528.             if (c == NL)
  529.                 *s = NUL;        /* replace newlines with NULs */
  530.             else
  531.                 *s = c;
  532.             ++s;
  533.             if (++len != BUFSIZE)
  534.                 continue;
  535.             if (write(fd, buffer, (size_t)BUFSIZE) == -1)
  536.             {
  537.                 end = 0;                /* write error: break loop */
  538.                 break;
  539.             }
  540.             nchars += BUFSIZE;
  541.             s = buffer;
  542.             len = 0;
  543.         }
  544.         *s = NL;
  545.         ++s;
  546.         if (++len == BUFSIZE)
  547.         {
  548.             if (write(fd, buffer, (size_t)BUFSIZE) == -1)
  549.                 end = 0;                /* write error: break loop */
  550.             nchars += BUFSIZE;
  551.             s = buffer;
  552.             len = 0;
  553.         }
  554.     }
  555.     if (len)
  556.     {
  557.         if (write(fd, buffer, (size_t)len) == -1)
  558.             end = 0;                /* write error */
  559.         nchars += len;
  560.     }
  561.  
  562.     if (close(fd) != 0)
  563.     {
  564.         errmsg = "Close failed";
  565.         goto fail;
  566.     }
  567.     if (perm >= 0)
  568.         setperm(fname, perm);    /* set permissions of new file same as old file */
  569.  
  570.     if (end == 0)
  571.     {
  572.         errmsg = "write error (file system full?)";
  573.         goto fail;
  574.     }
  575.  
  576. #ifdef MSDOS        /* the screen may be messed up by the "insert disk
  577.                             in drive b: and hit return" message */
  578.     updateScreen(CLEAR);
  579. #endif
  580.  
  581.     lnum -= start;        /* compute number of written lines */
  582.     smsg("\"%s\"%s %ld line%s, %ld character%s",
  583.             fname,
  584.             (backup == NULL && !append) ? " [New File]" : " ",
  585.             (long)lnum, plural((long)lnum),
  586.             nchars, plural(nchars));
  587.     if (start == 1 && end == line_count)        /* when written everything */
  588.     {
  589.         UNCHANGED;
  590.         startscript();        /* re-start auto script file */
  591.     }
  592.  
  593.     /*
  594.      * Remove the backup unless 'backup' option is set
  595.      */
  596.     if (!p_bk && backup != NULL && remove(backup) != 0)
  597.         emsg("Can't delete backup file");
  598.     
  599.     goto nofail;
  600.  
  601. fail:
  602. #ifdef MSDOS        /* the screen may be messed up by the "insert disk
  603.                             in drive b: and hit return" message */
  604.     updateScreen(CLEAR);
  605. #endif
  606. nofail:
  607.  
  608.     free((char *) backup);
  609.     free(buffer);
  610.  
  611.     if (errmsg != NULL)
  612.     {
  613.         filemess(fname, errmsg);
  614.         retval = FALSE;
  615.     }
  616.     return retval;
  617. }
  618.  
  619. /*
  620.  * do_mlines() - process mode lines for the current file
  621.  *
  622.  * Returns immediately if the "ml" parameter isn't set.
  623.  */
  624. static void     chk_mline __ARGS((linenr_t));
  625.  
  626.     static void
  627. do_mlines()
  628. {
  629.         linenr_t        lnum;
  630.         int             nmlines;
  631.  
  632.         if ((nmlines = p_ml) == 0)
  633.                 return;
  634.  
  635.         for (lnum = 1; lnum <= line_count && lnum <= nmlines; ++lnum)
  636.                 chk_mline(lnum);
  637.  
  638.         for (lnum = line_count; lnum > 0 && lnum > nmlines &&
  639.                                 lnum > line_count - nmlines; --lnum)
  640.                 chk_mline(lnum);
  641. }
  642.  
  643. /*
  644.  * chk_mline() - check a single line for a mode string
  645.  */
  646.     static void
  647. chk_mline(lnum)
  648.     linenr_t lnum;
  649. {
  650.     register char    *s;
  651.     register char    *e;
  652.     char            *cs;            /* local copy of any modeline found */
  653.     char            prev;
  654.     int                end;
  655.  
  656.     prev = ' ';
  657.     for (s = nr2ptr(lnum); *s != NUL; ++s)
  658.     {
  659.         if (isspace(prev) && (strncmp(s, "vi:", (size_t)3) == 0 || strncmp(s, "ex:", (size_t)3) == 0))
  660.         {
  661.             s += 3;
  662.             end = FALSE;
  663.             s = cs = strsave(s);
  664.             if (cs == NULL)
  665.                 break;
  666.             while (end == FALSE)
  667.             {
  668.                 while (*s == ' ' || *s == TAB)
  669.                     ++s;
  670.                 if (*s == NUL)
  671.                     break;
  672.                 for (e = s; *e != ':' && *e != NUL; ++e)
  673.                     ;
  674.                 if (*e == NUL)
  675.                     end = TRUE;
  676.                 *e = NUL;
  677.                 doset(s);
  678.                 s = e + 1;
  679.             }
  680.             free(cs);
  681.             break;
  682.         }
  683.         prev = *s;
  684.     }
  685. }
  686.