386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Fri, 12 Jun 1992 03:53:03 +0000 (19:53 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Fri, 12 Jun 1992 03:53:03 +0000 (19:53 -0800)
Work on file usr/src/usr.bin/elvis/tmp.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/elvis/tmp.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/elvis/tmp.c b/usr/src/usr.bin/elvis/tmp.c
new file mode 100644 (file)
index 0000000..b991820
--- /dev/null
@@ -0,0 +1,762 @@
+/* tmp.c */
+
+/* Author:
+ *     Steve Kirkendall
+ *     14407 SW Teal Blvd. #C
+ *     Beaverton, OR 97005
+ *     kirkenda@cs.pdx.edu
+ */
+
+
+/* This file contains functions which create & readback a TMPFILE */
+
+
+#include "config.h"
+#include "vi.h"
+#if TOS
+# include <stat.h>
+#else
+# if OSK
+#  include "osk.h"
+# else
+#  if AMIGA
+#   include "amistat.h"
+#  else
+#   include <sys/stat.h>
+#  endif
+# endif
+#endif
+#if TURBOC
+# include <process.h>
+#endif
+
+#ifndef NO_MODELINES
+static void do_modelines(l, stop)
+       long    l;      /* line number to start at */
+       long    stop;   /* line number to stop at */
+{
+       char    *str;   /* used to scan through the line */
+       char    *start; /* points to the start of the line */
+       char    buf[80];
+
+       /* if modelines are disabled, then do nothing */
+       if (!*o_modelines)
+       {
+               return;
+       }
+
+       /* for each line... */
+       for (; l <= stop; l++)
+       {
+               /* for each position in the line.. */
+               for (str = fetchline(l); *str; str++)
+               {
+                       /* if it is the start of a modeline command... */
+                       if ((str[0] == 'e' && str[1] == 'x'
+                         || str[0] == 'v' && str[1] == 'i')
+                         && str[2] == ':')
+                       {
+                               start = str += 3;
+
+                               /* find the end */
+                               for (str = start + strlen(start); *--str != ':'; )
+                               {
+                               }
+
+                               /* if it is a well-formed modeline, execute it */
+                               if (str > start && str - start < sizeof buf)
+                               {
+                                       strncpy(buf, start, (int)(str - start));
+                                       exstring(buf, str - start, '\\');
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+#endif
+
+
+/* The FAIL() macro prints an error message and then exits. */
+#define FAIL(why,arg)  mode = MODE_EX; msg(why, arg); endwin(); exit(9)
+
+/* This is the name of the temp file */
+static char    tmpname[80];
+
+/* This function creates the temp file and copies the original file into it.
+ * Returns if successful, or stops execution if it fails.
+ */
+int tmpstart(filename)
+       char            *filename; /* name of the original file */
+{
+       int             origfd; /* fd used for reading the original file */
+       struct stat     statb;  /* stat buffer, used to examine inode */
+       REG BLK         *this;  /* pointer to the current block buffer */
+       REG BLK         *next;  /* pointer to the next block buffer */
+       int             inbuf;  /* number of characters in a buffer */
+       int             nread;  /* number of bytes read */
+       REG int         j, k;
+       int             i;
+       long            nbytes;
+
+       /* switching to a different file certainly counts as a change */
+       changes++;
+       redraw(MARK_UNSET, FALSE);
+
+       /* open the original file for reading */
+       *origname = '\0';
+       if (filename && *filename)
+       {
+               strcpy(origname, filename);
+               origfd = open(origname, O_RDONLY);
+               if (origfd < 0 && errno != ENOENT)
+               {
+                       msg("Can't open \"%s\"", origname);
+                       return tmpstart("");
+               }
+               if (origfd >= 0)
+               {
+                       if (stat(origname, &statb) < 0)
+                       {
+                               FAIL("Can't stat \"%s\"", origname);
+                       }
+#if TOS
+                       if (origfd >= 0 && (statb.st_mode & S_IJDIR))
+#else
+# if OSK
+                       if (origfd >= 0 && (statb.st_mode & S_IFDIR))
+# else
+                       if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
+# endif
+#endif
+                       {
+                               msg("\"%s\" is not a regular file", origname);
+                               return tmpstart("");
+                       }
+               }
+               else
+               {
+                       stat(".", &statb);
+               }
+               if (origfd >= 0)
+               {
+                       origtime = statb.st_mtime;
+#if OSK
+                       if (*o_readonly || !(statb.st_mode &
+                                 ((getuid() >> 16) == 0 ? S_IOWRITE | S_IWRITE :
+                                 ((statb.st_gid != (getuid() >> 16) ? S_IOWRITE : S_IWRITE)))))
+#endif
+#if AMIGA || MSDOS || (TOS && defined(__GNUC__))
+                       if (*o_readonly || !(statb.st_mode & S_IWRITE))
+#endif
+#if TOS && !defined(__GNUC__)
+                       if (*o_readonly || (statb.st_mode & S_IJRON))
+#endif
+#if ANY_UNIX
+                       if (*o_readonly || !(statb.st_mode &
+                                 ((geteuid() == 0) ? 0222 :
+                                 ((statb.st_uid != geteuid() ? 0022 : 0200)))))
+#endif
+#if VMS
+                       if (*o_readonly)
+#endif
+                       {
+                               setflag(file, READONLY);
+                       }
+               }
+               else
+               {
+                       origtime = 0L;
+               }
+       }
+       else
+       {
+               setflag(file, NOFILE);
+               origfd = -1;
+               origtime = 0L;
+               stat(".", &statb);
+       }
+
+       /* make a name for the tmp file */
+       tmpnum++;
+#if MSDOS || TOS
+       /* MS-Dos doesn't allow multiple slashes, but supports drives
+        * with current directories.
+        * This relies on TMPNAME beginning with "%s\\"!!!!
+        */
+       strcpy(tmpname, o_directory);
+       if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
+               tmpname[i++]=SLASH;
+       sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum);
+#else
+       sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum);
+#endif
+
+       /* make sure nobody else is editing the same file */
+       if (access(tmpname, 0) == 0)
+       {
+               FAIL("Temp file \"%s\" already exists?", tmpname);
+       }
+
+       /* create the temp file */
+#if ANY_UNIX
+       close(creat(tmpname, 0600));            /* only we can read it */
+#else
+       close(creat(tmpname, FILEPERMS));       /* anybody body can read it, alas */
+#endif
+       tmpfd = open(tmpname, O_RDWR | O_BINARY);
+       if (tmpfd < 0)
+       {
+               FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
+               return 1;
+       }
+
+       /* allocate space for the header in the file */
+       write(tmpfd, hdr.c, (unsigned)BLKSIZE);
+       write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
+
+#ifndef NO_RECYCLE
+       /* initialize the block allocator */
+       /* This must already be done here, before the first attempt
+        * to write to the new file! GB */
+       garbage();
+#endif
+
+       /* initialize lnum[] */
+       for (i = 1; i < MAXBLKS; i++)
+       {
+               lnum[i] = INFINITY;
+       }
+       lnum[0] = 0;
+
+       /* if there is no original file, then create a 1-line file */
+       if (origfd < 0)
+       {
+               hdr.n[0] = 0;   /* invalid inode# denotes new file */
+
+               this = blkget(1);       /* get the new text block */
+               strcpy(this->c, "\n");  /* put a line in it */
+
+               lnum[1] = 1L;   /* block 1 ends with line 1 */
+               nlines = 1L;    /* there is 1 line in the file */
+               nbytes = 1L;
+
+               if (*origname)
+               {
+                       msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
+               }
+               else
+               {
+                       msg("\"[NO FILE]\"  1 line, 1 char");
+               }
+       }
+       else /* there is an original file -- read it in */
+       {
+               nbytes = nlines = 0;
+
+               /* preallocate 1 "next" buffer */
+               i = 1;
+               next = blkget(i);
+               inbuf = 0;
+
+               /* loop, moving blocks from orig to tmp */
+               for (;;)
+               {
+                       /* "next" buffer becomes "this" buffer */
+                       this = next;
+
+                       /* read [more] text into this block */
+                       nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
+                       if (nread < 0)
+                       {
+                               close(origfd);
+                               close(tmpfd);
+                               tmpfd = -1;
+                               unlink(tmpname);
+                               FAIL("Error reading \"%s\"", origname);
+                       }
+
+                       /* convert NUL characters to something else */
+                       for (j = k = inbuf; k < inbuf + nread; k++)
+                       {
+                               if (!this->c[k])
+                               {
+                                       setflag(file, HADNUL);
+                                       this->c[j++] = 0x80;
+                               }
+#ifndef CRUNCH
+                               else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0)
+                               {
+                                       if (this->c[k] == '\t'
+                                        || this->c[k] == '\n'
+                                        || this->c[k] == '\f')
+                                       {
+                                               this->c[j++] = this->c[k];
+                                       }
+                                       else if (this->c[k] == '\b')
+                                       {
+                                               /* delete '\b', but complain */
+                                               setflag(file, HADBS);
+                                       }
+                                       /* else silently delete control char */
+                               }
+#endif
+                               else
+                               {
+                                       this->c[j++] = this->c[k];
+                               }
+                       }
+                       inbuf = j;
+
+                       /* if the buffer is empty, quit */
+                       if (inbuf == 0)
+                       {
+                               goto FoundEOF;
+                       }
+
+#if MSDOS || TOS
+/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
+   but leaving garbage at end of buf. The same is true for TURBOC. GB. */
+
+                       memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
+#endif
+
+                       /* search backward for last newline */
+                       for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
+                       {
+                       }
+                       if (k++ < 0)
+                       {
+                               if (inbuf >= BLKSIZE - 1)
+                               {
+                                       k = 80;
+                               }
+                               else
+                               {
+                                       k = inbuf;
+                               }
+                       }
+
+                       /* allocate next buffer */
+                       next = blkget(++i);
+
+                       /* move fragmentary last line to next buffer */
+                       inbuf -= k;
+                       for (j = 0; k < BLKSIZE; j++, k++)
+                       {
+                               next->c[j] = this->c[k];
+                               this->c[k] = 0;
+                       }
+
+                       /* if necessary, add a newline to this buf */
+                       for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
+                       {
+                       }
+                       if (this->c[k] != '\n')
+                       {
+                               setflag(file, ADDEDNL);
+                               this->c[k + 1] = '\n';
+                       }
+
+                       /* count the lines in this block */
+                       for (k = 0; k < BLKSIZE && this->c[k]; k++)
+                       {
+                               if (this->c[k] == '\n')
+                               {
+                                       nlines++;
+                               }
+                               nbytes++;
+                       }
+                       lnum[i - 1] = nlines;
+               }
+FoundEOF:
+
+               /* if this is a zero-length file, add 1 line */
+               if (nlines == 0)
+               {
+                       this = blkget(1);       /* get the new text block */
+                       strcpy(this->c, "\n");  /* put a line in it */
+
+                       lnum[1] = 1;    /* block 1 ends with line 1 */
+                       nlines = 1;     /* there is 1 line in the file */
+                       nbytes = 1;
+               }
+
+#if MSDOS || TOS
+               /* each line has an extra CR that we didn't count yet */
+               nbytes += nlines;
+#endif
+
+               /* report the number of lines in the file */
+               msg("\"%s\" %s %ld line%s, %ld char%s",
+                       origname,
+                       (tstflag(file, READONLY) ? "[READONLY]" : ""),
+                       nlines,
+                       nlines == 1 ? "" : "s",
+                       nbytes,
+                       nbytes == 1 ? "" : "s");
+       }
+
+       /* initialize the cursor to start of line 1 */
+       cursor = MARK_FIRST;
+
+       /* close the original file */
+       close(origfd);
+
+       /* any other messages? */
+       if (tstflag(file, HADNUL))
+       {
+               msg("This file contained NULs.  They've been changed to \\x80 chars");
+       }
+       if (tstflag(file, ADDEDNL))
+       {
+               msg("Newline characters have been inserted to break up long lines");
+       }
+#ifndef CRUNCH
+       if (tstflag(file, HADBS))
+       {
+               msg("Backspace characters deleted due to ':set beautify'");
+       }
+#endif
+
+       storename(origname);
+
+#ifndef NO_MODELINES
+       if (nlines > 10)
+       {
+               do_modelines(1L, 5L);
+               do_modelines(nlines - 4L, nlines);
+       }
+       else
+       {
+               do_modelines(1L, nlines);
+       }
+#endif
+
+       /* force all blocks out onto the disk, to support file recovery */
+       blksync();
+
+       return 0;
+}
+
+
+
+/* This function copies the temp file back onto an original file.
+ * Returns TRUE if successful, or FALSE if the file could NOT be saved.
+ */
+int tmpsave(filename, bang)
+       char    *filename;      /* the name to save it to */
+       int     bang;           /* forced write? */
+{
+       int             fd;     /* fd of the file we're writing to */
+       REG int         len;    /* length of a text block */
+       REG BLK         *this;  /* a text block */
+       long            bytes;  /* byte counter */
+       REG int         i;
+
+       /* if no filename is given, assume the original file name */
+       if (!filename || !*filename)
+       {
+               filename = origname;
+       }
+
+       /* if still no file name, then fail */
+       if (!*filename)
+       {
+               msg("Don't know a name for this file -- NOT WRITTEN");
+               return FALSE;
+       }
+
+       /* can't rewrite a READONLY file */
+#if AMIGA
+       if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
+#else
+       if (!strcmp(filename, origname) && *o_readonly && !bang)
+#endif
+       {
+               msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
+               return FALSE;
+       }
+
+       /* open the file */
+       if (*filename == '>' && filename[1] == '>')
+       {
+               filename += 2;
+               while (*filename == ' ' || *filename == '\t')
+               {
+                       filename++;
+               }
+#ifdef O_APPEND
+               fd = open(filename, O_WRONLY|O_APPEND);
+#else
+               fd = open(filename, O_WRONLY);
+               lseek(fd, 0L, 2);
+#endif
+       }
+       else
+       {
+               /* either the file must not exist, or it must be the original
+                * file, or we must have a bang, or "writeany" must be set.
+                */
+               if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang
+#ifndef CRUNCH
+                   && !*o_writeany
+#endif
+                                  )
+               {
+                       msg("File already exists - Use :w! to overwrite");
+                       return FALSE;
+               }
+#if VMS
+               /* Create a new VMS version of this file. */
+               { 
+               char *strrchr(), *ptr = strrchr(filename,';');
+               if (ptr) *ptr = '\0';  /* Snip off any ;number in the name */
+               }
+#endif
+               fd = creat(filename, FILEPERMS);
+       }
+       if (fd < 0)
+       {
+               msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
+               return FALSE;
+       }
+
+       /* write each text block to the file */
+       bytes = 0L;
+       for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
+       {
+               for (len = 0; len < BLKSIZE && this->c[len]; len++)
+               {
+               }
+               if (twrite(fd, this->c, len) < len)
+               {
+                       msg("Trouble writing to \"%s\"", filename);
+                       if (!strcmp(filename, origname))
+                       {
+                               setflag(file, MODIFIED);
+                       }
+                       close(fd);
+                       return FALSE;
+               }
+               bytes += len;
+       }
+
+       /* reset the "modified" flag, but not the "undoable" flag */
+       clrflag(file, MODIFIED);
+       significant = FALSE;
+
+       /* report lines & characters */
+#if MSDOS || TOS
+       bytes += nlines; /* for the inserted carriage returns */
+#endif
+       msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
+
+       /* close the file */
+       close(fd);
+
+       return TRUE;
+}
+
+
+/* This function deletes the temporary file.  If the file has been modified
+ * and "bang" is FALSE, then it returns FALSE without doing anything; else
+ * it returns TRUE.
+ *
+ * If the "autowrite" option is set, then instead of returning FALSE when
+ * the file has been modified and "bang" is false, it will call tmpend().
+ */
+int tmpabort(bang)
+       int     bang;
+{
+       /* if there is no file, return successfully */
+       if (tmpfd < 0)
+       {
+               return TRUE;
+       }
+
+       /* see if we must return FALSE -- can't quit */
+       if (!bang && tstflag(file, MODIFIED))
+       {
+               /* if "autowrite" is set, then act like tmpend() */
+               if (*o_autowrite)
+                       return tmpend(bang);
+               else
+                       return FALSE;
+       }
+
+       /* delete the tmp file */
+       cutswitch();
+       strcpy(prevorig, origname);
+       prevline = markline(cursor);
+       *origname = '\0';
+       origtime = 0L;
+       blkinit();
+       nlines = 0;
+       initflags();
+       close(tmpfd);
+       tmpfd = -1;
+       unlink(tmpname);
+       return TRUE;
+}
+
+/* This function saves the file if it has been modified, and then deletes
+ * the temporary file. Returns TRUE if successful, or FALSE if the file
+ * needs to be saved but can't be.  When it returns FALSE, it will not have
+ * deleted the tmp file, either.
+ */
+int tmpend(bang)
+       int     bang;
+{
+       /* save the file if it has been modified */
+       if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
+       {
+               return FALSE;
+       }
+
+       /* delete the tmp file */
+       tmpabort(TRUE);
+
+       return TRUE;
+}
+
+
+/* If the tmp file has been changed, then this function will force those
+ * changes to be written to the disk, so that the tmp file will survive a
+ * system crash or power failure.
+ */
+#if AMIGA || MSDOS || TOS
+sync()
+{
+       /* MS-DOS and TOS don't flush their buffers until the file is closed,
+        * so here we close the tmp file and then immediately reopen it.
+        */
+       close(tmpfd);
+       tmpfd = open(tmpname, O_RDWR | O_BINARY);
+       return 0;
+}
+#endif
+
+
+/* This function stores the file's name in the second block of the temp file.
+ * SLEAZE ALERT!  SLEAZE ALERT!  The "tmpblk" buffer is probably being used
+ * to store the arguments to a command, so we can't use it here.  Instead,
+ * we'll borrow the buffer that is used for "shift-U".
+ */
+storename(name)
+       char    *name;  /* the name of the file - normally origname */
+{
+#ifndef CRUNCH
+       int     len;
+       char    *ptr;
+#endif
+
+       /* we're going to clobber the U_text buffer, so reset U_line */
+       U_line = 0L;
+
+       if (!name)
+       {
+               strncpy(U_text, "", BLKSIZE);
+               U_text[1] = 127;
+       }
+#ifndef CRUNCH
+       else if (*name != SLASH)
+       {
+               /* get the directory name */
+               ptr = getcwd(U_text, BLKSIZE);
+               if (ptr != U_text)
+               {
+                       strcpy(U_text, ptr);
+               }
+
+               /* append a slash to the directory name */
+               len = strlen(U_text);
+               U_text[len++] = SLASH;
+
+               /* append the filename, padded with heaps o' NULs */
+               strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len);
+       }
+#endif
+       else
+       {
+               /* copy the filename into U_text */
+               strncpy(U_text, *name ? name : "foo", BLKSIZE);
+       }
+
+       if (tmpfd >= 0)
+       {
+               /* write the name out to second block of the temp file */
+               lseek(tmpfd, (long)BLKSIZE, 0);
+               write(tmpfd, U_text, (unsigned)BLKSIZE);
+       }
+       return 0;
+}
+
+
+
+/* This function handles deadly signals.  It restores sanity to the terminal
+ * preserves the current temp file, and deletes any old temp files.
+ */
+int deathtrap(sig)
+       int     sig;    /* the deadly signal that we caught */
+{
+       char    *why;
+
+       /* restore the terminal's sanity */
+       endwin();
+
+#ifdef CRUNCH
+       why = "-Elvis died";
+#else
+       /* give a more specific description of how Elvis died */
+       switch (sig)
+       {
+# ifdef SIGHUP
+         case SIGHUP:  why = "-the modem lost its carrier";            break;
+# endif
+# ifndef DEBUG
+#  ifdef SIGILL
+         case SIGILL:  why = "-Elvis hit an illegal instruction";      break;
+#  endif
+#  ifdef SIGBUS
+         case SIGBUS:  why = "-Elvis had a bus error";                 break;
+#  endif
+#  if defined(SIGSEGV) && !defined(TOS)
+         case SIGSEGV: why = "-Elvis had a segmentation violation";    break;
+#  endif
+#  ifdef SIGSYS
+         case SIGSYS:  why = "-Elvis munged a system call";            break;
+#  endif
+# endif /* !DEBUG */
+# ifdef SIGPIPE
+         case SIGPIPE: why = "-the pipe reader died";                  break;
+# endif
+# ifdef SIGTERM
+         case SIGTERM: why = "-Elvis was terminated";                  break;
+# endif
+# if !MINIX
+#  ifdef SIGUSR1
+         case SIGUSR1: why = "-Elvis was killed via SIGUSR1";          break;
+#  endif
+#  ifdef SIGUSR2
+         case SIGUSR2: why = "-Elvis was killed via SIGUSR2";          break;
+#  endif
+# endif
+         default:      why = "-Elvis died";                            break;
+       }
+#endif
+
+       /* if we had a temp file going, then preserve it */
+       if (tmpnum > 0 && tmpfd >= 0)
+       {
+               close(tmpfd);
+               sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
+               system(tmpblk.c);
+       }
+
+       /* delete any old temp files */
+       cutend();
+
+       /* exit with the proper exit status */
+       exit(sig);
+}