BSD 4_4 release
[unix-history] / usr / src / usr.bin / ex / ex_io.c
index d89f1ff..d38a673 100644 (file)
@@ -1,27 +1,58 @@
-/* Copyright (c) 1979 Regents of the University of California */
+/*-
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This module is believed to contain source code proprietary to AT&T.
+ * Use and redistribution is subject to the Berkeley Software License
+ * Agreement and your Software Agreement with AT&T (Western Electric).
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_io.c    8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
 #include "ex.h"
 #include "ex_argv.h"
 #include "ex_temp.h"
 #include "ex_tty.h"
 #include "ex_vis.h"
 #include "ex.h"
 #include "ex_argv.h"
 #include "ex_temp.h"
 #include "ex_tty.h"
 #include "ex_vis.h"
+#include <sys/file.h>
+#include <sys/exec.h>
+#include "pathnames.h"
 
 /*
 
 /*
- * File input/output, unix escapes, source, filtering preserve and recover
+ * File input/output, source, preserve and recover
  */
 
 /*
  * Following remember where . was in the previous file for return
  * on file switching.
  */
  */
 
 /*
  * Following remember where . was in the previous file for return
  * on file switching.
  */
-short  altdot;
-short  oldadot;
+int    altdot;
+int    oldadot;
 bool   wasalt;
 bool   wasalt;
+short  isalt;
 
 long   cntch;                  /* Count of characters on unit io */
 
 long   cntch;                  /* Count of characters on unit io */
+#ifndef VMUNIX
 short  cntln;                  /* Count of lines " */
 short  cntln;                  /* Count of lines " */
+#else
+int    cntln;
+#endif
 long   cntnull;                /* Count of nulls " */
 long   cntodd;                 /* Count of non-ascii characters " */
 
 long   cntnull;                /* Count of nulls " */
 long   cntodd;                 /* Count of non-ascii characters " */
 
+#ifdef FLOCKFILE
+/*
+ * The alternate, saved and current file are locked the extent of the
+ * time that they are active. If the saved file is exchanged
+ * with the alternate file, the file descriptors are exchanged
+ * and the lock is not released.
+ */
+int    io_savedfile, io_altfile, io_curr ;
+int    lock_savedfile, lock_altfile, lock_curr ;
+#endif FLOCKFILE
+
 /*
  * Parse file name for command encoded by comm.
  * If comm is E then command is doomed and we are
 /*
  * Parse file name for command encoded by comm.
  * If comm is E then command is doomed and we are
@@ -32,14 +63,27 @@ filename(comm)
 {
        register int c = comm, d;
        register int i;
 {
        register int c = comm, d;
        register int i;
+#ifdef FLOCKFILE
+       int lock ;
+
+       lock = 0 ;
+#endif FLOCKFILE
 
 
-       d = getchar();
+       d = ex_getchar();
        if (endcmd(d)) {
                if (savedfile[0] == 0 && comm != 'f')
                        error("No file|No current filename");
                CP(file, savedfile);
        if (endcmd(d)) {
                if (savedfile[0] == 0 && comm != 'f')
                        error("No file|No current filename");
                CP(file, savedfile);
-               wasalt = 0;
+#ifdef FLOCKFILE
+               if (io_curr && io_curr != io_savedfile) close(io_curr) ;
+               lock = lock_curr = lock_savedfile ;
+               io_curr = io_savedfile ;
+#endif FLOCKFILE
+               wasalt = (isalt > 0) ? isalt-1 : 0;
+               isalt = 0;
                oldadot = altdot;
                oldadot = altdot;
+               if (c == 'e' || c == 'E')
+                       altdot = lineDOT();
                if (d == EOF)
                        ungetchar(d);
        } else {
                if (d == EOF)
                        ungetchar(d);
        } else {
@@ -60,17 +104,41 @@ filename(comm)
 
                case 'e':
                        if (savedfile[0]) {
 
                case 'e':
                        if (savedfile[0]) {
+#ifdef FLOCKFILE
+                               if (strcmp(file,savedfile) == 0) break ;
+#endif FLOCKFILE
                                altdot = lineDOT();
                                CP(altfile, savedfile);
                                altdot = lineDOT();
                                CP(altfile, savedfile);
+#ifdef FLOCKFILE
+                               if (io_altfile) close (io_altfile) ;
+                               io_altfile = io_savedfile ;
+                               lock_altfile = lock_savedfile ;
+                               io_savedfile = 0 ;
+#endif FLOCKFILE
                        }
                        CP(savedfile, file);
                        }
                        CP(savedfile, file);
+#ifdef FLOCKFILE
+                       io_savedfile = io_curr ;
+                       lock_savedfile = lock_curr ;
+                       io_curr = 0 ;           lock = lock_curr = 0 ;
+#endif FLOCKFILE
                        break;
 
                default:
                        if (file[0]) {
                        break;
 
                default:
                        if (file[0]) {
+#ifdef FLOCKFILE
+                               if (wasalt) break ;
+#endif
                                if (c != 'E')
                                        altdot = lineDOT();
                                CP(altfile, file);
                                if (c != 'E')
                                        altdot = lineDOT();
                                CP(altfile, file);
+#ifdef FLOCKFILE
+                               if (io_altfile
+                               && io_altfile != io_curr) close (io_altfile) ;
+                               io_altfile = io_curr ;
+                               lock_altfile = lock_curr ;
+                               io_curr = 0 ;           lock = lock_curr = 0 ;
+#endif FLOCKFILE
                        }
                        break;
                }
                        }
                        break;
                }
@@ -80,18 +148,26 @@ filename(comm)
        if (file[0] != 0) {
                lprintf("\"%s\"", file);
                if (comm == 'f') {
        if (file[0] != 0) {
                lprintf("\"%s\"", file);
                if (comm == 'f') {
+                       if (value(READONLY))
+                               ex_printf(" [Read only]");
                        if (!edited)
                        if (!edited)
-                               printf(" [Not edited]");
+                               ex_printf(" [Not edited]");
                        if (tchng)
                        if (tchng)
-                               printf(" [Modified]");
+                               ex_printf(" [Modified]");
+#ifdef FLOCKFILE
+                       if (lock == LOCK_SH)
+                               ex_printf(" [Shared lock]") ;
+                       else if (lock == LOCK_EX)
+                               ex_printf(" [Exclusive lock]") ;
+#endif FLOCKFILE
                }
                flush();
        } else
                }
                flush();
        } else
-               printf("No file ");
+               ex_printf("No file ");
        if (comm == 'f') {
                if (!(i = lineDOL()))
                        i++;
        if (comm == 'f') {
                if (!(i = lineDOL()))
                        i++;
-               printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
+               ex_printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
                    (long) 100 * lineDOT() / i);
        }
 }
                    (long) 100 * lineDOT() / i);
        }
 }
@@ -109,11 +185,11 @@ getargs()
        pastwh();
        if (peekchar() == '+') {
                for (cp = fpatbuf;;) {
        pastwh();
        if (peekchar() == '+') {
                for (cp = fpatbuf;;) {
-                       c = *cp++ = getchar();
+                       c = *cp++ = ex_getchar();
                        if (cp >= &fpatbuf[sizeof(fpatbuf)])
                                error("Pattern too long");
                        if (c == '\\' && isspace(peekchar()))
                        if (cp >= &fpatbuf[sizeof(fpatbuf)])
                                error("Pattern too long");
                        if (c == '\\' && isspace(peekchar()))
-                               c = getchar();
+                               c = ex_getchar();
                        if (c == EOF || isspace(c)) {
                                ungetchar(c);
                                *--cp = 0;
                        if (c == EOF || isspace(c)) {
                                ungetchar(c);
                                *--cp = 0;
@@ -126,7 +202,7 @@ getargs()
                return (0);
        CP(genbuf, "echo "); cp = &genbuf[5];
        for (;;) {
                return (0);
        CP(genbuf, "echo "); cp = &genbuf[5];
        for (;;) {
-               c = getchar();
+               c = ex_getchar();
                if (endcmd(c)) {
                        ungetchar(c);
                        break;
                if (endcmd(c)) {
                        ungetchar(c);
                        break;
@@ -134,8 +210,8 @@ getargs()
                switch (c) {
 
                case '\\':
                switch (c) {
 
                case '\\':
-                       if (any(peekchar(), "#%"))
-                               c = getchar();
+                       if (any(peekchar(), "#%|"))
+                               c = ex_getchar();
                        /* fall into... */
 
                default:
                        /* fall into... */
 
                default:
@@ -202,7 +278,7 @@ glob(gp)
        }
        if (pipe(pvec) < 0)
                error("Can't make pipe to glob");
        }
        if (pipe(pvec) < 0)
                error("Can't make pipe to glob");
-       pid = fork();
+       pid = vfork();
        io = pvec[0];
        if (pid < 0) {
                close(pvec[1]);
        io = pvec[0];
        if (pid < 0) {
                close(pvec[1]);
@@ -211,11 +287,21 @@ glob(gp)
        if (pid == 0) {
                int oerrno;
 
        if (pid == 0) {
                int oerrno;
 
+               if (genbuf) {
+                       register char *ccp = genbuf;
+                       while (*ccp)
+                               *ccp++ &= TRIM;
+               }
                close(1);
                dup(pvec[1]);
                close(pvec[0]);
                close(1);
                dup(pvec[1]);
                close(pvec[0]);
+               close(2);       /* so errors don't mess up the screen */
+               ignore(open(_PATH_DEVNULL, 1));
                execl(svalue(SHELL), "sh", "-c", genbuf, 0);
                execl(svalue(SHELL), "sh", "-c", genbuf, 0);
-               oerrno = errno; close(1); dup(2); errno = oerrno;
+               oerrno = errno;
+               close(1);
+               dup(2);
+               errno = oerrno;
                filioerr(svalue(SHELL));
        }
        close(pvec[1]);
                filioerr(svalue(SHELL));
        }
        close(pvec[1]);
@@ -244,7 +330,7 @@ glob(gp)
        } while (c >= 0);
        waitfor();
        if (gp->argc0 == 0)
        } while (c >= 0);
        waitfor();
        if (gp->argc0 == 0)
-               error(NOSTR);
+               error("No match");
 }
 
 /*
 }
 
 /*
@@ -253,21 +339,23 @@ glob(gp)
  */
 gscan()
 {
  */
 gscan()
 {
+#ifndef        vms                     /* Never have meta-characters in vms */
        register char *cp;
 
        for (cp = genbuf; *cp; cp++)
                if (any(*cp, "~{[*?$`'\"\\"))
                        return (1);
        register char *cp;
 
        for (cp = genbuf; *cp; cp++)
                if (any(*cp, "~{[*?$`'\"\\"))
                        return (1);
+#endif
        return (0);
 }
 
 /*
  * Parse one filename into file.
  */
        return (0);
 }
 
 /*
  * Parse one filename into file.
  */
+struct glob G;
 getone()
 {
        register char *str;
 getone()
 {
        register char *str;
-       struct glob G;
 
        if (getargs() == 0)
                error("Missing filename");
 
        if (getargs() == 0)
                error("Missing filename");
@@ -277,7 +365,6 @@ getone()
        str = G.argv[G.argc0 - 1];
        if (strlen(str) > FNSIZE - 4)
                error("Filename too long");
        str = G.argv[G.argc0 - 1];
        if (strlen(str) > FNSIZE - 4)
                error("Filename too long");
-samef:
        CP(file, str);
 }
 
        CP(file, str);
 }
 
@@ -290,12 +377,28 @@ rop(c)
 {
        register int i;
        struct stat stbuf;
 {
        register int i;
        struct stat stbuf;
-       short magic;
+       struct exec head;
+       static int ovro;        /* old value(READONLY) */
+       static int denied;      /* 1 if READONLY was set due to file permissions */
+#ifdef FLOCKFILE
+       int *lp, *iop;
+#endif FLOCKFILE
 
        io = open(file, 0);
        if (io < 0) {
 
        io = open(file, 0);
        if (io < 0) {
-               if (c == 'e' && errno == ENOENT)
+               if (c == 'e' && errno == ENOENT) {
                        edited++;
                        edited++;
+                       /*
+                        * If the user just did "ex foo" he is probably
+                        * creating a new file.  Don't be an error, since
+                        * this is ugly, and it screws up the + option.
+                        */
+                       if (!seenprompt) {
+                               ex_printf(" [New file]");
+                               noonl();
+                               return;
+                       }
+               }
                syserror();
        }
        if (fstat(io, &stbuf))
                syserror();
        }
        if (fstat(io, &stbuf))
@@ -308,7 +411,7 @@ rop(c)
        case S_IFCHR:
                if (isatty(io))
                        error(" Teletype");
        case S_IFCHR:
                if (isatty(io))
                        error(" Teletype");
-               if (samei(&stbuf, "/dev/null"))
+               if (samei(&stbuf, _PATH_DEVNULL))
                        break;
                error(" Character special file");
 
                        break;
                error(" Character special file");
 
@@ -316,33 +419,123 @@ rop(c)
                error(" Directory");
 
        case S_IFREG:
                error(" Directory");
 
        case S_IFREG:
-               i = read(io, (char *) &magic, sizeof(magic));
-               lseek(io, 0l, 0);
-               if (i != sizeof(magic))
+#ifdef CRYPT
+               if (xflag)
                        break;
                        break;
-               switch (magic) {
-
-               case 0405:
-               case 0407:
-               case 0410:
-               case 0411:
+#endif
+               i = read(io, (char *)&head, sizeof(head));
+               (void)lseek(io, 0L, L_SET);
+               if (i != sizeof(head))
+                       break;
+#ifndef vms
+               switch ((int)head.a_magic) {
+
+               case 0405:      /* data overlay on exec */
+               case OMAGIC:    /* unshared */
+               case NMAGIC:    /* shared text */
+               case 0411:      /* separate I/D */
+               case ZMAGIC:    /* VM/Unix demand paged */
+               case 0430:      /* PDP-11 Overlay shared */
+               case 0431:      /* PDP-11 Overlay sep I/D */
                        error(" Executable");
 
                        error(" Executable");
 
+               /*
+                * We do not forbid the editing of portable archives
+                * because it is reasonable to edit them, especially
+                * if they are archives of text files.  This is
+                * especially useful if you archive source files together
+                * and copy them to another system with ~%take, since
+                * the files sometimes show up munged and must be fixed.
+                */
                case 0177545:
                case 0177555:
                        error(" Archive");
                case 0177545:
                case 0177555:
                        error(" Archive");
+               case 070707:
+                       error(" Cpio file");
 
                default:
 
                default:
-                       if (magic & 0100200)
-                               error(" Non-ascii file");
+                       {
+                               char *bp = (char *)&head;
+                               if ((u_char)bp[0] == (u_char)'\037' &&
+                                   (u_char)bp[1] == (u_char)'\235')
+                                       error(" Compressed file");
+#ifdef ARCHIVES_ARE_OK
+                               if (!strncmp(bp, "!<arch>\n__.SYMDEF", 17)
+                                   || !strncmp(bp, "!<arch>\n", 8))
+                                       error(" Archive");
+#endif
+                       }
                        break;
                }
                        break;
                }
+#endif
+       }
+       if (c != 'r') {
+               if (value(READONLY) && denied) {
+                       value(READONLY) = ovro;
+                       denied = 0;
+               }
+               if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
+                       ovro = value(READONLY);
+                       denied = 1;
+                       value(READONLY) = 1;
+               }
+       }
+       if (value(READONLY)) {
+               ex_printf(" [Read only]");
+               flush();
        }
        }
+#ifdef FLOCKFILE
+       /*
+        * Attempt to lock the file. We use an sharable lock if reading
+        * the file, and an exclusive lock if editting a file.
+        * The lock will be released when the file is no longer being
+        * referenced. At any time, the editor can have as many as
+        * three files locked, and with different lock statuses.
+        */
+       /*
+        * if this is either the saved or alternate file or current file,
+        * point to the appropriate descriptor and file lock status.
+        */
+       if (strcmp (file,savedfile) == 0) {
+               if (!io_savedfile) io_savedfile = dup(io) ;
+               lp = &lock_savedfile ;  iop = &io_savedfile ;
+       } else if (strcmp (file,altfile) == 0) {
+               if (!io_altfile) io_altfile = dup(io) ;
+               lp = &lock_altfile ;    iop = &io_altfile ;
+       } else {
+               /* throw away current lock, accquire new current lock */
+               if (io_curr) close (io_curr) ;
+               io_curr = dup(io) ;
+               lp = &lock_curr ;       iop = &io_curr ;
+               lock_curr = 0 ;
+       }
+       if (c == 'r' || value(READONLY) || *lp == 0) {
+               /* if we have a lock already, don't bother */
+               if (!*lp) {
+                       /* try for a shared lock */
+                       if (flock(*iop, LOCK_SH|LOCK_NB) < 0
+                       && errno == EWOULDBLOCK) {
+                               ex_printf (
+                       " [FILE BEING MODIFIED BY ANOTHER PROCESS]") ;
+                               flush();
+                               goto fail_lock ;
+                       } else *lp = LOCK_SH ;
+               }
+       }
+       if ( c != 'r'  && !value(READONLY) && *lp != LOCK_EX) {
+               /* if we are editting the file, upgrade to an exclusive lock. */
+               if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK) {
+                       ex_printf (" [File open by another process]") ;
+                       flush();
+               } else *lp = LOCK_EX ;
+       }
+fail_lock:
+#endif FLOCKFILE
        if (c == 'r')
                setdot();
        else
                setall();
        if (c == 'r')
                setdot();
        else
                setall();
-       if (inopen && c == 'r')
+       if (FIXUNDO && inopen && c == 'r')
                undap1 = undap2 = dot + 1;
        rop2();
        rop3(c);
                undap1 = undap2 = dot + 1;
        rop2();
        rop3(c);
@@ -350,10 +543,34 @@ rop(c)
 
 rop2()
 {
 
 rop2()
 {
+       line *first, *last, *a;
+       struct stat statb;
 
        deletenone();
        clrstats();
 
        deletenone();
        clrstats();
+       first = addr2 + 1;
+       if (fstat(io, &statb) < 0)
+               bsize = LBSIZE;
+       else {
+               bsize = statb.st_blksize;
+               if (bsize <= 0)
+                       bsize = LBSIZE;
+       }
        ignore(append(getfile, addr2));
        ignore(append(getfile, addr2));
+       last = dot;
+       /*
+        *      if the modeline variable is set,
+        *      check the first and last five lines of the file
+        *      for a mode line.
+        */
+       if (value(MODELINE)) {
+               for (a=first; a<=last; a++) {
+                       if (a==first+5 && last-first > 10)
+                               a = last - 4;
+                       getline(*a);
+                       checkmodeline(linebuf);
+               }
+       }
 }
 
 rop3(c)
 }
 
 rop3(c)
@@ -385,16 +602,13 @@ other:
                                        dot = one;
                                markpr(one);
                        }
                                        dot = one;
                                markpr(one);
                        }
-               undkind = UNDNONE;
+               if(FIXUNDO)
+                       undkind = UNDNONE;
                if (inopen) {
                        vcline = 0;
                        vreplace(0, LINES, lineDOL());
                }
        }
                if (inopen) {
                        vcline = 0;
                        vreplace(0, LINES, lineDOL());
                }
        }
-       if (laste) {
-               laste = 0;
-               sync();
-       }
 }
 
 /*
 }
 
 /*
@@ -425,6 +639,9 @@ bool dofname;       /* if 1 call filename, else use savedfile */
        register int c, exclam, nonexist;
        line *saddr1, *saddr2;
        struct stat stbuf;
        register int c, exclam, nonexist;
        line *saddr1, *saddr2;
        struct stat stbuf;
+#ifdef FLOCKFILE
+       int *lp, *iop ;
+#endif FLOCKFILE
 
        c = 0;
        exclam = 0;
 
        c = 0;
        exclam = 0;
@@ -438,6 +655,8 @@ bool dofname;       /* if 1 call filename, else use savedfile */
                        error("Write forms are 'w' and 'w>>'");
                filename('w');
        } else {
                        error("Write forms are 'w' and 'w>>'");
                filename('w');
        } else {
+               if (savedfile[0] == 0)
+                       error("No file|No current filename");
                saddr1=addr1;
                saddr2=addr2;
                addr1=one;
                saddr1=addr1;
                saddr2=addr2;
                addr1=one;
@@ -450,18 +669,36 @@ bool dofname;     /* if 1 call filename, else use savedfile */
                lprintf("\"%s\"", file);
        }
        nonexist = stat(file, &stbuf);
                lprintf("\"%s\"", file);
        }
        nonexist = stat(file, &stbuf);
+#ifdef FLOCKFILE
+       /*
+        * if this is either the saved or alternate file or current file,
+        * point to the appropriate descriptor and file lock status.
+        */
+       if (strcmp (file,savedfile) == 0) {
+               lp = &lock_savedfile ;  iop = &io_savedfile ;
+       } else if (strcmp (file,altfile) == 0) {
+               lp = &lock_altfile ;    iop = &io_altfile ;
+       } else {
+               lp = &lock_curr ;       iop = &io_curr ;
+       }
+       if (!*iop && !nonexist){
+               *lp = 0 ;
+               if ((*iop = open(file, 1)) < 0) *iop = 0 ;
+       }
+#endif FLOCKFILE
        switch (c) {
 
        case 0:
        switch (c) {
 
        case 0:
-               if (!exclam && !value(WRITEANY)) switch (edfile()) {
+               if (!exclam && (!value(WRITEANY) || value(READONLY)))
+               switch (edfile()) {
                
                case NOTEDF:
                        if (nonexist)
                                break;
                        if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
                
                case NOTEDF:
                        if (nonexist)
                                break;
                        if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
-                               if (samei(&stbuf, "/dev/null"))
+                               if (samei(&stbuf, _PATH_DEVNULL))
                                        break;
                                        break;
-                               if (samei(&stbuf, "/dev/tty"))
+                               if (samei(&stbuf, _PATH_TTY))
                                        break;
                        }
                        io = open(file, 1);
                                        break;
                        }
                        io = open(file, 1);
@@ -472,23 +709,52 @@ bool dofname;     /* if 1 call filename, else use savedfile */
                        close(io);
                        break;
 
                        close(io);
                        break;
 
+               case EDF:
+                       if (value(READONLY))
+                               error(" File is read only");
+                       break;
+
                case PARTBUF:
                case PARTBUF:
+                       if (value(READONLY))
+                               error(" File is read only");
                        error(" Use \"w!\" to write partial buffer");
                }
 cre:
                        error(" Use \"w!\" to write partial buffer");
                }
 cre:
+/*
                synctmp();
                synctmp();
+*/
+#ifdef FLOCKFILE
+       if (*iop && !*lp != LOCK_EX && !exclam) {
+               /*
+                * upgrade to a exclusive lock. if can't get, someone else 
+                * has the exclusive lock. bitch to the user.
+                */
+               if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK)
+       error (" File being modified by another process - use \"w!\" to write");
+                else *lp = LOCK_EX ;
+       }
+#endif FLOCKFILE
 #ifdef V6
                io = creat(file, 0644);
 #else
                io = creat(file, 0666);
 #ifdef V6
                io = creat(file, 0644);
 #else
                io = creat(file, 0666);
+#ifdef vms     /* to retain file protection modes on newer version of file */
+               if (!nonexist)
+                       chmod(file, stbuf.st_mode & 0777);
+#endif
 #endif
                if (io < 0)
                        syserror();
 #endif
                if (io < 0)
                        syserror();
+               writing = 1;
                if (hush == 0)
                        if (nonexist)
                if (hush == 0)
                        if (nonexist)
-                               printf(" [New file]");
+                               ex_printf(" [New file]");
                        else if (value(WRITEANY) && edfile() != EDF)
                        else if (value(WRITEANY) && edfile() != EDF)
-                               printf(" [Existing file]");
+                               ex_printf(" [Existing file]");
+#ifdef FLOCKFILE
+               if (!*iop)
+                       *iop = dup(io) ;
+#endif FLOCKFILE
                break;
 
        case 2:
                break;
 
        case 2:
@@ -499,19 +765,42 @@ cre:
                        syserror();
                }
                lseek(io, 0l, 2);
                        syserror();
                }
                lseek(io, 0l, 2);
+#ifdef FLOCKFILE
+               if (!*iop) *iop = dup(io) ;
+               if (*lp != LOCK_EX && !exclam) {
+                       /*
+                        * upgrade to a exclusive lock. if can't get,
+                        * someone else has the exclusive lock.
+                        * bitch to the user.
+                        */
+                       if (flock(*iop, LOCK_SH|LOCK_NB) < 0
+                       && errno == EWOULDBLOCK)
+                               error (
+" File being modified by another process - use \"w!>>\" to write");
+                       else *lp = LOCK_EX ;
+               }
+#endif FLOCKFILE
                break;
        }
                break;
        }
-       putfile();
+#ifdef FLOCKFILE
+       if (flock(*iop, LOCK_EX|LOCK_NB) >= 0)
+               *lp = LOCK_EX ;
+#endif FLOCKFILE
+       putfile(0);
+#ifndef        vms
+       (void) fsync(io);
+#endif
        ignore(iostats());
        if (c != 2 && addr1 == one && addr2 == dol) {
                if (eq(file, savedfile))
                        edited = 1;
        ignore(iostats());
        if (c != 2 && addr1 == one && addr2 == dol) {
                if (eq(file, savedfile))
                        edited = 1;
-               sync();
+               ex_sync();
        }
        if (!dofname) {
                addr1 = saddr1;
                addr2 = saddr2;
        }
        }
        if (!dofname) {
                addr1 = saddr1;
                addr2 = saddr2;
        }
+       writing = 0;
 }
 
 /*
 }
 
 /*
@@ -528,319 +817,10 @@ edfile()
        return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
 }
 
        return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
 }
 
-/*
- * First part of a shell escape,
- * parse the line, expanding # and % and ! and printing if implied.
- */
-unix0(warn)
-       bool warn;
-{
-       register char *up, *fp;
-       register short c;
-       char printub, puxb[UXBSIZE + sizeof (int)];
-
-       printub = 0;
-       CP(puxb, uxb);
-       c = getchar();
-       if (c == '\n' || c == EOF)
-               error("Incomplete shell escape command@- use 'shell' to get a shell");
-       up = uxb;
-       do {
-               switch (c) {
-
-               case '\\':
-                       if (any(peekchar(), "%#!"))
-                               c = getchar();
-               default:
-                       if (up >= &uxb[UXBSIZE]) {
-tunix:
-                               uxb[0] = 0;
-                               error("Command too long");
-                       }
-                       *up++ = c;
-                       break;
-
-               case '!':
-                       fp = puxb;
-                       if (*fp == 0) {
-                               uxb[0] = 0;
-                               error("No previous command@to substitute for !");
-                       }
-                       printub++;
-                       while (*fp) {
-                               if (up >= &uxb[UXBSIZE])
-                                       goto tunix;
-                               *up++ = *fp++;
-                       }
-                       break;
-
-               case '#':
-                       fp = altfile;
-                       if (*fp == 0) {
-                               uxb[0] = 0;
-                               error("No alternate filename@to substitute for #");
-                       }
-                       goto uexp;
-
-               case '%':
-                       fp = savedfile;
-                       if (*fp == 0) {
-                               uxb[0] = 0;
-                               error("No filename@to substitute for %%");
-                       }
-uexp:
-                       printub++;
-                       while (*fp) {
-                               if (up >= &uxb[UXBSIZE])
-                                       goto tunix;
-                               *up++ = *fp++ | QUOTE;
-                       }
-                       break;
-               }
-               c = getchar();
-       } while (c == '|' || !endcmd(c));
-       if (c == EOF)
-               ungetchar(c);
-       *up = 0;
-       if (!inopen)
-               resetflav();
-       if (warn)
-               ckaw();
-       if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) {
-               xchng = chng;
-               vnfl();
-               printf(mesg("[No write]|[No write since last change]"));
-               noonl();
-               flush();
-       } else
-               warn = 0;
-       if (printub) {
-               if (uxb[0] == 0)
-                       error("No previous command@to repeat");
-               if (inopen) {
-                       splitw++;
-                       vclean();
-                       vgoto(WECHO, 0);
-               }
-               if (warn)
-                       vnfl();
-               if (hush == 0)
-                       lprintf("!%s", uxb);
-               if (inopen) {
-                       vclreol();
-                       vgoto(WECHO, 0);
-               } else
-                       putnl();
-               flush();
-       }
-}
-
-/*
- * Do the real work for execution of a shell escape.
- * Mode is like the number passed to open system calls
- * and indicates filtering.  If input is implied, newstdin
- * must have been setup already.
- */
-unixex(opt, up, newstdin, mode)
-       char *opt, *up;
-       int newstdin, mode;
-{
-       int pvec[2], f;
-
-       signal(SIGINT, SIG_IGN);
-       if (inopen)
-               f = setty(normf);
-       if ((mode & 1) && pipe(pvec) < 0) {
-               /* Newstdin should be io so it will be closed */
-               if (inopen)
-                       setty(f);
-               error("Can't make pipe for filter");
-       }
-#ifndef VFORK
-       pid = fork();
-#else
-       pid = vfork();
-#endif
-       if (pid < 0) {
-               if (mode & 1) {
-                       close(pvec[0]);
-                       close(pvec[1]);
-               }
-               setrupt();
-               error("No more processes");
-       }
-       if (pid == 0) {
-               if (mode & 2) {
-                       close(0);
-                       dup(newstdin);
-                       close(newstdin);
-               }
-               if (mode & 1) {
-                       close(pvec[0]);
-                       close(1);
-                       dup(pvec[1]);
-                       if (inopen) {
-                               close(2);
-                               dup(1);
-                       }
-                       close(pvec[1]);
-               }
-               if (io)
-                       close(io);
-               if (tfile)
-                       close(tfile);
-               close(erfile);
-               signal(SIGHUP, oldhup);
-               signal(SIGQUIT, oldquit);
-               if (ruptible)
-                       signal(SIGINT, SIG_DFL);
-               execl(svalue(SHELL), "sh", opt, up, (char *) 0);
-               printf("No %s!\n", svalue(SHELL));
-               error(NOSTR);
-       }
-       if (mode & 1) {
-               io = pvec[0];
-               close(pvec[1]);
-       }
-       if (newstdin)
-               close(newstdin);
-       return (f);
-}
-
-/*
- * Wait for the command to complete.
- * F is for restoration of tty mode if from open/visual.
- * C flags suppression of printing.
- */
-unixwt(c, f)
-       bool c;
-       int f;
-{
-
-       waitfor();
-       if (inopen)
-               setty(f);
-       setrupt();
-       if (!inopen && c && hush == 0) {
-               printf("!\n");
-               flush();
-               termreset();
-               gettmode();
-       }
-}
-
-/*
- * Setup a pipeline for the filtration implied by mode
- * which is like a open number.  If input is required to
- * the filter, then a child editor is created to write it.
- * If output is catch it from io which is created by unixex.
- */
-filter(mode)
-       register int mode;
-{
-       static int pvec[2];
-       register int f;
-       register int lines = lineDOL();
-
-       mode++;
-       if (mode & 2) {
-               signal(SIGINT, SIG_IGN);
-               if (pipe(pvec) < 0)
-                       error("Can't make pipe");
-               pid = fork();
-               io = pvec[0];
-               if (pid < 0) {
-                       setrupt();
-                       close(pvec[1]);
-                       error("No more processes");
-               }
-               if (pid == 0) {
-                       setrupt();
-                       io = pvec[1];
-                       close(pvec[0]);
-                       putfile();
-                       exit(0);
-               }
-               close(pvec[1]);
-               io = pvec[0];
-               setrupt();
-       }
-       f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
-       if (mode == 3) {
-               delete(0);
-               addr2 = addr1 - 1;
-       }
-       if (mode & 1) {
-               undap1 = undap2 = addr2+1;
-               ignore(append(getfile, addr2));
-       }
-       close(io);
-       io = -1;
-       unixwt(!inopen, f);
-       netchHAD(lines);
-}
-
-/*
- * Set up to do a recover, getting io to be a pipe from
- * the recover process.
- */
-recover()
-{
-       static int pvec[2];
-
-       if (pipe(pvec) < 0)
-               error(" Can't make pipe for recovery");
-       pid = fork();
-       io = pvec[0];
-       if (pid < 0) {
-               close(pvec[1]);
-               error(" Can't fork to execute recovery");
-       }
-       if (pid == 0) {
-               close(2);
-               dup(1);
-               close(1);
-               dup(pvec[1]);
-               close(pvec[1]);
-               execl(EXRECOVER, "exrecover", svalue(DIRECTORY), file, (char *) 0);
-               close(1);
-               dup(2);
-               error(" No recovery routine");
-       }
-       close(pvec[1]);
-}
-
-/*
- * Wait for the process (pid an external) to complete.
- */
-waitfor()
-{
-
-       do
-               rpid = wait(&status);
-       while (rpid != pid && rpid != -1);
-       status = (status >> 8) & 0377;
-}
-
-/*
- * The end of a recover operation.  If the process
- * exits non-zero, force not edited; otherwise force
- * a write.
- */
-revocer()
-{
-
-       waitfor();
-       if (pid == rpid && status != 0)
-               edited = 0;
-       else
-               change();
-}
-
 /*
  * Extract the next line from the io stream.
  */
 /*
  * Extract the next line from the io stream.
  */
-static char *nextip;
+char *nextip;
 
 getfile()
 {
 
 getfile()
 {
@@ -851,15 +831,29 @@ getfile()
        fp = nextip;
        do {
                if (--ninbuf < 0) {
        fp = nextip;
        do {
                if (--ninbuf < 0) {
-                       ninbuf = read(io, genbuf, LBSIZE) - 1;
+                       ninbuf = read(io, genbuf, (int) bsize) - 1;
                        if (ninbuf < 0) {
                                if (lp != linebuf) {
                        if (ninbuf < 0) {
                                if (lp != linebuf) {
-                                       printf(" [Incomplete last line]");
+                                       lp++;
+                                       ex_printf(" [Incomplete last line]");
                                        break;
                                }
                                return (EOF);
                        }
                                        break;
                                }
                                return (EOF);
                        }
+#ifdef CRYPT
+                       if (kflag) {
+                               fp = genbuf;
+                               while(fp < &genbuf[ninbuf]) {
+                                       if (*fp++ & 0200) {
+                                               crblock(perm, genbuf, ninbuf+1,
+       cntch);
+                                               break;
+                                       }
+                               }
+                       }
+#endif
                        fp = genbuf;
                        fp = genbuf;
+                       cntch += ninbuf+1;
                }
                if (lp >= &linebuf[LBSIZE]) {
                        error(" Line too long");
                }
                if (lp >= &linebuf[LBSIZE]) {
                        error(" Line too long");
@@ -877,7 +871,6 @@ getfile()
                }
                *lp++ = c;
        } while (c != '\n');
                }
                *lp++ = c;
        } while (c != '\n');
-       cntch += lp - linebuf;
        *--lp = 0;
        nextip = fp;
        cntln++;
        *--lp = 0;
        nextip = fp;
        cntln++;
@@ -887,18 +880,28 @@ getfile()
 /*
  * Write a range onto the io stream.
  */
 /*
  * Write a range onto the io stream.
  */
-putfile()
+/* ARGSUSED */
+putfile(isfilter)
+int isfilter;
 {
        line *a1;
        register char *fp, *lp;
        register int nib;
 {
        line *a1;
        register char *fp, *lp;
        register int nib;
+       struct stat statb;
 
        a1 = addr1;
        clrstats();
        cntln = addr2 - a1 + 1;
        if (cntln == 0)
                return;
 
        a1 = addr1;
        clrstats();
        cntln = addr2 - a1 + 1;
        if (cntln == 0)
                return;
-       nib = BUFSIZ;
+       if (fstat(io, &statb) < 0)
+               bsize = LBSIZE;
+       else {
+               bsize = statb.st_blksize;
+               if (bsize <= 0)
+                       bsize = LBSIZE;
+       }
+       nib = bsize;
        fp = genbuf;
        do {
                getline(*a1++);
        fp = genbuf;
        do {
                getline(*a1++);
@@ -906,11 +909,15 @@ putfile()
                for (;;) {
                        if (--nib < 0) {
                                nib = fp - genbuf;
                for (;;) {
                        if (--nib < 0) {
                                nib = fp - genbuf;
+#ifdef CRYPT
+                               if(kflag && !isfilter)
+                                        crblock(perm, genbuf, nib, cntch);
+#endif
                                if (write(io, genbuf, nib) != nib) {
                                        wrerror();
                                }
                                cntch += nib;
                                if (write(io, genbuf, nib) != nib) {
                                        wrerror();
                                }
                                cntch += nib;
-                               nib = BUFSIZ - 1;
+                               nib = bsize - 1;
                                fp = genbuf;
                        }
                        if ((*fp++ = *lp++) == 0) {
                                fp = genbuf;
                        }
                        if ((*fp++ = *lp++) == 0) {
@@ -920,6 +927,10 @@ putfile()
                }
        } while (a1 <= addr2);
        nib = fp - genbuf;
                }
        } while (a1 <= addr2);
        nib = fp - genbuf;
+#ifdef CRYPT
+       if(kflag && !isfilter)
+               crblock(perm, genbuf, nib, cntch);
+#endif
        if (write(io, genbuf, nib) != nib) {
                wrerror();
        }
        if (write(io, genbuf, nib) != nib) {
                wrerror();
        }
@@ -943,7 +954,8 @@ wrerror()
  * Source command, handles nested sources.
  * Traps errors since it mungs unit 0 during the source.
  */
  * Source command, handles nested sources.
  * Traps errors since it mungs unit 0 during the source.
  */
-static short slevel;
+short slevel;
+short ttyindes;
 
 source(fil, okfail)
        char *fil;
 
 source(fil, okfail)
        char *fil;
@@ -951,12 +963,18 @@ source(fil, okfail)
 {
        jmp_buf osetexit;
        register int saveinp, ointty, oerrno;
 {
        jmp_buf osetexit;
        register int saveinp, ointty, oerrno;
-       int oprompt;
+       char *saveglobp;
+       short savepeekc;
 
        signal(SIGINT, SIG_IGN);
        saveinp = dup(0);
 
        signal(SIGINT, SIG_IGN);
        saveinp = dup(0);
+       savepeekc = peekc;
+       saveglobp = globp;
+       peekc = 0; globp = 0;
        if (saveinp < 0)
                error("Too many nested sources");
        if (saveinp < 0)
                error("Too many nested sources");
+       if (slevel <= 0)
+               ttyindes = saveinp;
        close(0);
        if (open(fil, 0) < 0) {
                oerrno = errno;
        close(0);
        if (open(fil, 0) < 0) {
                oerrno = errno;
@@ -990,6 +1008,8 @@ source(fil, okfail)
        close(0);
        dup(saveinp);
        close(saveinp);
        close(0);
        dup(saveinp);
        close(saveinp);
+       globp = saveglobp;
+       peekc = savepeekc;
        slevel--;
        resexit(osetexit);
 }
        slevel--;
        resexit(osetexit);
 }
@@ -1017,23 +1037,63 @@ iostats()
        io = -1;
        if (hush == 0) {
                if (value(TERSE))
        io = -1;
        if (hush == 0) {
                if (value(TERSE))
-                       printf(" %d/%D", cntln, cntch);
+                       ex_printf(" %d/%D", cntln, cntch);
                else
                else
-                       printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
+                       ex_printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
                            cntch, plural(cntch));
                if (cntnull || cntodd) {
                            cntch, plural(cntch));
                if (cntnull || cntodd) {
-                       printf(" (");
+                       ex_printf(" (");
                        if (cntnull) {
                        if (cntnull) {
-                               printf("%D null", cntnull);
+                               ex_printf("%D null", cntnull);
                                if (cntodd)
                                if (cntodd)
-                                       printf(", ");
+                                       ex_printf(", ");
                        }
                        if (cntodd)
                        }
                        if (cntodd)
-                               printf("%D non-ASCII", cntodd);
-                       putchar(')');
+                               ex_printf("%D non-ASCII", cntodd);
+                       ex_putchar(')');
                }
                noonl();
                flush();
        }
        return (cntnull != 0 || cntodd != 0);
 }
                }
                noonl();
                flush();
        }
        return (cntnull != 0 || cntodd != 0);
 }
+
+#ifdef USG
+# define index strchr
+# define rindex strrchr
+#endif
+#ifdef USG3TTY
+# define index strchr
+# define rindex strrchr
+#endif
+#ifdef vms
+# define index strchr
+# define rindex strrchr
+#endif
+
+checkmodeline(l)
+char *l;
+{
+       char *beg, *end;
+       char cmdbuf[1024];
+       char *index(), *rindex(), *strncpy();
+
+       beg = index(l, ':');
+       if (beg == NULL)
+               return;
+       if (&beg[-3] < l)
+               return;
+       if (!(  ( (beg[-3] == ' ' || beg[-3] == '\t')
+               && beg[-2] == 'e'
+               && beg[-1] == 'x')
+            || ( (beg[-3] == ' ' || beg[-3] == '\t')
+               && beg[-2] == 'v'
+               && beg[-1] == 'i'))) return;
+       strncpy(cmdbuf, beg+1, sizeof cmdbuf);
+       end = rindex(cmdbuf, ':');
+       if (end == NULL)
+               return;
+       *end = 0;
+       globp = cmdbuf;
+       commands(1, 1);
+}