document distributed with 4.1BSD
[unix-history] / usr / src / usr.bin / ex / ex_io.c
index 3f0a227..fdfdba5 100644 (file)
@@ -1,4 +1,13 @@
-/* Copyright (c) 1979 Regents of the University of California */
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char *sccsid = "@(#)ex_io.c     7.11 (Berkeley) %G%";
+#endif not lint
+
 #include "ex.h"
 #include "ex_argv.h"
 #include "ex_temp.h"
 #include "ex.h"
 #include "ex_argv.h"
 #include "ex_temp.h"
@@ -6,7 +15,7 @@
 #include "ex_vis.h"
 
 /*
 #include "ex_vis.h"
 
 /*
- * File input/output, unix escapes, source, filtering preserve and recover
+ * File input/output, source, preserve and recover
  */
 
 /*
  */
 
 /*
@@ -16,6 +25,7 @@
 int    altdot;
 int    oldadot;
 bool   wasalt;
 int    altdot;
 int    oldadot;
 bool   wasalt;
+short  isalt;
 
 long   cntch;                  /* Count of characters on unit io */
 #ifndef VMUNIX
 
 long   cntch;                  /* Count of characters on unit io */
 #ifndef VMUNIX
@@ -42,8 +52,11 @@ filename(comm)
                if (savedfile[0] == 0 && comm != 'f')
                        error("No file|No current filename");
                CP(file, savedfile);
                if (savedfile[0] == 0 && comm != 'f')
                        error("No file|No current filename");
                CP(file, savedfile);
-               wasalt = 0;
+               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 {
@@ -84,6 +97,8 @@ 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))
+                               printf(" [Read only]");
                        if (!edited)
                                printf(" [Not edited]");
                        if (tchng)
                        if (!edited)
                                printf(" [Not edited]");
                        if (tchng)
@@ -138,7 +153,7 @@ getargs()
                switch (c) {
 
                case '\\':
                switch (c) {
 
                case '\\':
-                       if (any(peekchar(), "#%"))
+                       if (any(peekchar(), "#%|"))
                                c = getchar();
                        /* fall into... */
 
                                c = getchar();
                        /* fall into... */
 
@@ -270,10 +285,10 @@ gscan()
 /*
  * Parse one filename into file.
  */
 /*
  * 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");
@@ -297,11 +312,24 @@ rop(c)
        register int i;
        struct stat stbuf;
        short magic;
        register int i;
        struct stat stbuf;
        short magic;
+       static int ovro;        /* old value(READONLY) */
+       static int denied;      /* 1 if READONLY was set due to file permissions */
 
        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) {
+                               printf(" [New file]");
+                               noonl();
+                               return;
+                       }
+               }
                syserror();
        }
        if (fstat(io, &stbuf))
                syserror();
        }
        if (fstat(io, &stbuf))
@@ -322,28 +350,64 @@ rop(c)
                error(" Directory");
 
        case S_IFREG:
                error(" Directory");
 
        case S_IFREG:
+#ifdef CRYPT
+               if (xflag)
+                       break;
+#endif
                i = read(io, (char *) &magic, sizeof(magic));
                lseek(io, 0l, 0);
                if (i != sizeof(magic))
                        break;
                switch (magic) {
 
                i = read(io, (char *) &magic, sizeof(magic));
                lseek(io, 0l, 0);
                if (i != sizeof(magic))
                        break;
                switch (magic) {
 
-               case 0405:
-               case 0407:
-               case 0410:
-               case 0411:
+               case 0405:      /* data overlay on exec */
+               case 0407:      /* unshared */
+               case 0410:      /* shared text */
+               case 0411:      /* separate I/D */
+               case 0413:      /* 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");
 
                default:
                case 0177545:
                case 0177555:
                        error(" Archive");
 
                default:
+#ifdef mbb
+                       /* C/70 has a 10 bit byte */
+                       if (magic & 03401600)
+#else
+                       /* Everybody else has an 8 bit byte */
                        if (magic & 0100200)
                        if (magic & 0100200)
+#endif
                                error(" Non-ascii file");
                        break;
                }
        }
                                error(" Non-ascii file");
                        break;
                }
        }
+       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)) {
+               printf(" [Read only]");
+               flush();
+       }
        if (c == 'r')
                setdot();
        else
        if (c == 'r')
                setdot();
        else
@@ -356,10 +420,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)
@@ -398,10 +486,6 @@ other:
                        vreplace(0, LINES, lineDOL());
                }
        }
                        vreplace(0, LINES, lineDOL());
                }
        }
-       if (laste) {
-               laste = 0;
-               sync();
-       }
 }
 
 /*
 }
 
 /*
@@ -462,7 +546,8 @@ bool dofname;       /* if 1 call filename, else use savedfile */
        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)
                
                case NOTEDF:
                        if (nonexist)
@@ -481,7 +566,14 @@ 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:
@@ -495,6 +587,7 @@ cre:
 #endif
                if (io < 0)
                        syserror();
 #endif
                if (io < 0)
                        syserror();
+               writing = 1;
                if (hush == 0)
                        if (nonexist)
                                printf(" [New file]");
                if (hush == 0)
                        if (nonexist)
                                printf(" [New file]");
@@ -512,7 +605,7 @@ cre:
                lseek(io, 0l, 2);
                break;
        }
                lseek(io, 0l, 2);
                break;
        }
-       putfile();
+       putfile(0);
        ignore(iostats());
        if (c != 2 && addr1 == one && addr2 == dol) {
                if (eq(file, savedfile))
        ignore(iostats());
        if (c != 2 && addr1 == one && addr2 == dol) {
                if (eq(file, savedfile))
@@ -523,6 +616,7 @@ cre:
                addr1 = saddr1;
                addr2 = saddr2;
        }
                addr1 = saddr1;
                addr2 = saddr2;
        }
+       writing = 0;
 }
 
 /*
 }
 
 /*
@@ -539,322 +633,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 && Outchar != termchar) {
-                       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);
-#ifndef VMUNIX
-               close(erfile);
-#endif
-               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) {
-               if(FIXUNDO)
-                       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()
 {
@@ -865,15 +647,29 @@ getfile()
        fp = nextip;
        do {
                if (--ninbuf < 0) {
        fp = nextip;
        do {
                if (--ninbuf < 0) {
-                       ninbuf = read(io, genbuf, LBSIZE) - 1;
+                       ninbuf = read(io, genbuf, bsize) - 1;
                        if (ninbuf < 0) {
                                if (lp != linebuf) {
                        if (ninbuf < 0) {
                                if (lp != linebuf) {
+                                       lp++;
                                        printf(" [Incomplete last line]");
                                        break;
                                }
                                return (EOF);
                        }
                                        printf(" [Incomplete last line]");
                                        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");
@@ -891,7 +687,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++;
@@ -901,18 +696,27 @@ getfile()
 /*
  * Write a range onto the io stream.
  */
 /*
  * Write a range onto the io stream.
  */
-putfile()
+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++);
@@ -920,11 +724,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) {
@@ -934,6 +742,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();
        }
@@ -957,7 +769,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;
@@ -965,12 +778,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;
@@ -1004,6 +823,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);
 }
@@ -1027,6 +848,7 @@ clrstats()
 iostats()
 {
 
 iostats()
 {
 
+       (void) fsync(io);
        close(io);
        io = -1;
        if (hush == 0) {
        close(io);
        io = -1;
        if (hush == 0) {
@@ -1051,3 +873,36 @@ iostats()
        }
        return (cntnull != 0 || cntodd != 0);
 }
        }
        return (cntnull != 0 || cntodd != 0);
 }
+
+#if USG | USG3TTY
+/* It's so wonderful how we all speak the same language... */
+# define index strchr
+# define rindex strrchr
+#endif
+
+checkmodeline(line)
+char *line;
+{
+       char *beg, *end;
+       char cmdbuf[1024];
+       char *index(), *rindex();
+
+       beg = index(line, ':');
+       if (beg == NULL)
+               return;
+       if (&beg[-3] < line)
+               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);
+}