bug fixes and added `special' command.
authorRalph Campbell <ralph@ucbvax.Berkeley.EDU>
Wed, 30 Nov 1983 08:00:49 +0000 (00:00 -0800)
committerRalph Campbell <ralph@ucbvax.Berkeley.EDU>
Wed, 30 Nov 1983 08:00:49 +0000 (00:00 -0800)
SCCS-vsn: usr.bin/rdist/defs.h 4.8
SCCS-vsn: usr.bin/rdist/docmd.c 4.9
SCCS-vsn: usr.bin/rdist/expand.c 4.7
SCCS-vsn: usr.bin/rdist/gram.y 4.7
SCCS-vsn: usr.bin/rdist/main.c 4.9
SCCS-vsn: usr.bin/rdist/server.c 4.8

usr/src/usr.bin/rdist/defs.h
usr/src/usr.bin/rdist/docmd.c
usr/src/usr.bin/rdist/expand.c
usr/src/usr.bin/rdist/gram.y
usr/src/usr.bin/rdist/main.c
usr/src/usr.bin/rdist/server.c

index 1702017..928138b 100644 (file)
@@ -1,4 +1,4 @@
-/*     defs.h  4.7     83/11/01        */
+/*     defs.h  4.8     83/11/29        */
 
 #include <stdio.h>
 #include <ctype.h>
 
 #include <stdio.h>
 #include <ctype.h>
 #define ARROW  5
 #define DCOLON 6
 #define NAME   7
 #define ARROW  5
 #define DCOLON 6
 #define NAME   7
-#define INSTALL        8
-#define NOTIFY 9
-#define EXCEPT 10
-#define OPTION 11
-#define VAR    12
+#define STRING 8
+#define INSTALL        9
+#define NOTIFY 10
+#define EXCEPT 11
+#define SPECIAL        12
+#define OPTION 13
+#define VAR    14
 
        /* lexical definitions */
 #define        QUOTE   0200            /* used internally for quoted characters */
 
        /* lexical definitions */
 #define        QUOTE   0200            /* used internally for quoted characters */
 #define COMPARE        0x8
 #define REMOVE 0x10
 
 #define COMPARE        0x8
 #define REMOVE 0x10
 
+       /* expand type definitions */
+#define E_VARS 0x1
+#define E_SHELL        0x2
+#define E_TILDE        0x4
+#define E_ALL  0x7
+
 #define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
 
 #define ALLOC(x) (struct x *) malloc(sizeof(struct x))
 #define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
 
 #define ALLOC(x) (struct x *) malloc(sizeof(struct x))
@@ -81,6 +89,7 @@ extern char *sys_errlist[];
 struct block *lookup();
 struct block *makeblock();
 struct block *expand();
 struct block *lookup();
 struct block *makeblock();
 struct block *expand();
+char *exptilde();
 char *malloc();
 char *rindex();
 char *index();
 char *malloc();
 char *rindex();
 char *index();
index 1ef8233..a9ad132 100644 (file)
@@ -1,10 +1,11 @@
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)docmd.c     4.8 (Berkeley) 83/11/01";
+static char *sccsid = "@(#)docmd.c     4.9 (Berkeley) 83/11/29";
 #endif
 
 #include "defs.h"
 
 FILE   *lfp;           /* log file for recording files updated */
 #endif
 
 #include "defs.h"
 
 FILE   *lfp;           /* log file for recording files updated */
+struct block *special; /* list of special commands */
 
 /*
  * Process commands for sending files to other machines.
 
 /*
  * Process commands for sending files to other machines.
@@ -19,18 +20,19 @@ dohcmds(files, hosts, cmds)
        if (debug)
                printf("dohcmds(%x, %x, %x)\n", files, hosts, cmds);
 
        if (debug)
                printf("dohcmds(%x, %x, %x)\n", files, hosts, cmds);
 
-       files = expand(files, 0);
+       files = expand(files, E_VARS|E_SHELL);
        if (files == NULL) {
                error("no files to be updated\n");
                return;
        }
        if (files == NULL) {
                error("no files to be updated\n");
                return;
        }
-       hosts = expand(hosts, 1);
+       hosts = expand(hosts, E_VARS|E_SHELL);
        if (hosts == NULL) {
                error("empty list of hosts to be updated\n");
                return;
        }
        if (!mkexceptlist(cmds))
                return;
        if (hosts == NULL) {
                error("empty list of hosts to be updated\n");
                return;
        }
        if (!mkexceptlist(cmds))
                return;
+       special = cmds;
 
        ddir = files->b_next != NULL;
 
 
        ddir = files->b_next != NULL;
 
@@ -136,68 +138,57 @@ bad:
        return(0);
 }
 
        return(0);
 }
 
-struct tstamp {
-       time_t  lastmod;
-       FILE    *tfp;
-} ts[NSTAMPS];
-
-int    nstamps;
-
-extern char target[], *tp;
+time_t lastmod;
+FILE   *tfp;
+extern char target[], *tp;
 
 /*
  * Process commands for comparing files to time stamp files.
  */
 
 /*
  * Process commands for comparing files to time stamp files.
  */
-dofcmds(files, stamps, cmds)
-       struct block *files, *stamps, *cmds;
+dofcmds(files, stamp, cmds)
+       struct block *files, *stamp, *cmds;
 {
        register struct block *b;
 {
        register struct block *b;
-       register struct tstamp *t;
        register char **cpp;
        struct timeval tv[2];
        struct timezone tz;
        struct stat stb;
        register char **cpp;
        struct timeval tv[2];
        struct timezone tz;
        struct stat stb;
-       extern char *tmpinc;
 
        if (debug)
                printf("dofcmds()\n");
 
 
        if (debug)
                printf("dofcmds()\n");
 
-       files = expand(files, 0);
-       if (files == NULL){
+       files = expand(files, E_ALL);
+       if (files == NULL) {
                error("no files to be updated\n");
                return;
        }
                error("no files to be updated\n");
                return;
        }
-       stamps = expand(stamps, 0);
-       if (stamps == NULL) {
-               error("empty time stamp file list\n");
+       stamp = expand(stamp, E_ALL);
+       if (stamp == NULL || stamp->b_next != NULL) {
+               error("Only one time stamp file allowed\n");
                return;
        }
        if (!mkexceptlist(cmds))
                return;
 
                return;
        }
        if (!mkexceptlist(cmds))
                return;
 
-       t = ts;
-       nstamps = 0;
-       for (b = stamps; b != NULL; b = b->b_next) {
-               if (stat(b->b_name, &stb) < 0) {
-                       error("%s: %s\n", b->b_name, sys_errlist[errno]);
-                       continue;
-               }
-               if (++nstamps > NSTAMPS)
-                       fatal("too many time stamp files in one command\n");
-               if (debug)
-                       printf("%s: %d\n", b->b_name, stb.st_mtime);
-               t->lastmod = stb.st_mtime;
+       if (stat(stamp->b_name, &stb) < 0) {
+               error("%s: %s\n", stamp->b_name, sys_errlist[errno]);
+               return;
+       }
+       if (debug)
+               printf("%s: %d\n", stamp->b_name, stb.st_mtime);
+       if (!nflag) {
+               lastmod = stb.st_mtime;
                (void) gettimeofday(&tv[0], &tz);
                tv[1] = tv[0];
                (void) gettimeofday(&tv[0], &tz);
                tv[1] = tv[0];
-               (void) utimes(b->b_name, tv);
-               if (!nflag && !(options & VERIFY)) {
-                       if ((t->tfp = fopen(tmpfile, "w")) == NULL)
-                               error("%s: %s\n", b->b_name, sys_errlist[errno]);
-                       (*tmpinc)++;
-               } else
-                       t->tfp = NULL;
-               t++;
-       }
+               (void) utimes(stamp->b_name, tv);
+               if (options & VERIFY)
+                       tfp = NULL;
+               else if ((tfp = fopen(tmpfile, "w")) == NULL) {
+                       error("%s: %s\n", stamp->b_name, sys_errlist[errno]);
+                       return;
+               }
+       } else
+               tfp = NULL;
 
        for (b = files; b != NULL; b = b->b_next) {
                if (filec) {
 
        for (b = files; b != NULL; b = b->b_next) {
                if (filec) {
@@ -211,17 +202,13 @@ dofcmds(files, stamps, cmds)
                cmptime(b->b_name);
        }
 
                cmptime(b->b_name);
        }
 
-       *tmpinc = 'A';
-       for (t = ts; t < &ts[nstamps]; t++) {
-               if (t->tfp != NULL)
-                       (void) fclose(t->tfp);
-               for (b = cmds; b != NULL; b = b->b_next)
-                       if (b->b_type == NOTIFY)
-                               notify(tmpfile, NULL, b->b_args, t->lastmod);
-               if (!nflag && !(options & VERIFY))
-                       (void) unlink(tmpfile);
-               (*tmpinc)++;
-       }
+       if (tfp != NULL)
+               (void) fclose(tfp);
+       for (b = cmds; b != NULL; b = b->b_next)
+               if (b->b_type == NOTIFY)
+                       notify(tmpfile, NULL, b->b_args, lastmod);
+       if (!nflag && !(options & VERIFY))
+               (void) unlink(tmpfile);
 }
 
 /*
 }
 
 /*
@@ -230,15 +217,19 @@ dofcmds(files, stamps, cmds)
 cmptime(name)
        char *name;
 {
 cmptime(name)
        char *name;
 {
-       register struct tstamp *t;
        struct stat stb;
 
        if (debug)
                printf("cmptime(%s)\n", name);
 
        struct stat stb;
 
        if (debug)
                printf("cmptime(%s)\n", name);
 
-       if (exclude(name))
+       if (inlist(except, name))
                return;
 
                return;
 
+       if (nflag) {
+               printf("comparing dates: %s\n", name);
+               return;
+       }
+
        /*
         * first time cmptime() is called?
         */
        /*
         * first time cmptime() is called?
         */
@@ -267,10 +258,8 @@ cmptime(name)
                return;
        }
 
                return;
        }
 
-       for (t = ts; t < &ts[nstamps]; t++) {
-               if (stb.st_mtime > t->lastmod)
-                       log(t->tfp, "new: %s\n", name);
-       }
+       if (stb.st_mtime > lastmod)
+               log(tfp, "new: %s\n", name);
 }
 
 rcmptime(st)
 }
 
 rcmptime(st)
@@ -325,7 +314,7 @@ notify(file, rhost, to, lmod)
        FILE *pf, *popen();
        struct stat stb;
 
        FILE *pf, *popen();
        struct stat stb;
 
-       if (options & VERIFY)
+       if ((options & VERIFY) || to == NULL)
                return;
        if (!qflag) {
                printf("notify ");
                return;
        if (!qflag) {
                printf("notify ");
@@ -392,14 +381,15 @@ notify(file, rhost, to, lmod)
 struct block *except;          /* list of files to exclude */
 
 /*
 struct block *except;          /* list of files to exclude */
 
 /*
- * Return true if name is in list.
+ * Return true if name is in the list.
  */
  */
-exclude(file)
+inlist(list, file)
+       struct block *list;
        char *file;
 {
        register struct block *c;
 
        char *file;
 {
        register struct block *c;
 
-       for (c = except; c != NULL; c = c->b_next)
+       for (c = list; c != NULL; c = c->b_next)
                if (!strcmp(file, c->b_name))
                        return(1);
        return(0);
                if (!strcmp(file, c->b_name))
                        return(1);
        return(0);
@@ -422,16 +412,10 @@ mkexceptlist(cmds)
                if (c->b_type != EXCEPT)
                        continue;
                for (a = c->b_args; a != NULL; a = a->b_next) {
                if (c->b_type != EXCEPT)
                        continue;
                for (a = c->b_args; a != NULL; a = a->b_next) {
-                       cp = a->b_name;
-                       if (*cp == '~') {
-                               if (exptilde(buf, cp) == NULL)
-                                       return(0);
-                               cp = buf;
-                       }
                        if (f == NULL)
                        if (f == NULL)
-                               except = f = expand(makeblock(NAME, cp), 0);
+                               except = f = expand(makeblock(NAME, a->b_name), E_ALL);
                        else
                        else
-                               f->b_next = expand(makeblock(NAME, cp), 0);
+                               f->b_next = expand(makeblock(NAME, a->b_name), E_ALL);
                        while (f->b_next != NULL)
                                f = f->b_next;
                }
                        while (f->b_next != NULL)
                                f = f->b_next;
                }
index 3311eaa..6328bd3 100644 (file)
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)expand.c    4.6 (Berkeley) 83/11/01";
+static char *sccsid = "@(#)expand.c    4.7 (Berkeley) 83/11/29";
 #endif
 
 #include "defs.h"
 
 #endif
 
 #include "defs.h"
 
-char   shchars[] = "{[*?";
+#define LC '{'
+#define RC '}'
 
 
-int    argc;
-char   **argv;
-char   *path, *pathp, *lastpathp;
-int    nleft;
+static char    shchars[] = "${[*?";
 
 
-int    argcnt;
-int    expany;         /* any expansions done? */
-char   *entp;
-char   **sortbase;
+static int     which;          /* bit mask of types to expand */
+static int     argc;           /* expanded arg count */
+static char    **argv;         /* expanded arg vectors */
+static char    *path;
+static char    *pathp;
+static char    *lastpathp;
+static char    *tilde;         /* null if expanding tilde */
+static char    *tpathp;
+static int     nleft;
+
+static int     expany;         /* any expansions done? */
+static char    *entp;
+static char    **sortbase;
 
 char   *index();
 struct block *copy();
 
 /*
  * Take a list of names and expand any macros, etc.
 
 char   *index();
 struct block *copy();
 
 /*
  * Take a list of names and expand any macros, etc.
+ * wh = E_VARS if expanding variables.
+ * wh = E_SHELL if expanding shell characters.
+ * wh = E_TILDE if expanding `~'.
+ * or any of these or'ed together.
  */
 struct block *
  */
 struct block *
-expand(list, noshexp)
+expand(list, wh)
        struct block *list;
        struct block *list;
-       int noshexp;
+       int wh;
 {
 {
-       register struct block *prev, *bp, *tp;
-       register char *cp;
+       register struct block *bp, *prev;
        register int n;
        register int n;
-       char *tail;
-       int c;
        char pathbuf[BUFSIZ];
        char *argvbuf[GAVSIZ];
 
        if (debug) {
        char pathbuf[BUFSIZ];
        char *argvbuf[GAVSIZ];
 
        if (debug) {
-               printf("expand(%x, %d)\nlist = ", list, noshexp);
+               printf("expand(%x, %d)\nlist = ", list, wh);
                prnames(list);
        }
 
                prnames(list);
        }
 
-       for (prev = NULL, bp = list; bp != NULL; prev = bp, bp = bp->b_next) {
-       again:
-               cp = index(bp->b_name, '$');
-               if (cp == NULL)
-                       continue;
-               *cp++ = '\0';
-               if (*cp == '\0')
-                       fatal("no variable name after '$'");
-               if (*cp == '{') {
-                       cp++;
-                       if ((tail = index(cp, '}')) == NULL)
-                               fatal("missing '}'");
-                       *tail++ = c = '\0';
-                       if (*cp == '\0')
-                               fatal("no variable name after '$'");
-               } else {
-                       tail = cp + 1;
-                       c = *tail;
-                       *tail = '\0';
-               }
-               tp = lookup(cp, NULL, 0);
-               if (c != '\0')
-                       *tail = c;
-               if ((tp = tp->b_args) != NULL) {
-                       struct block *first = bp;
-
-                       for (bp = prev; tp != NULL; tp = tp->b_next) {
-                               if (bp == NULL)
-                                       list = bp = copy(tp, first->b_name, tail);
-                               else {
-                                       bp->b_next = copy(tp, first->b_name, tail);
-                                       bp = bp->b_next;
-                               }
-                       }
-                       bp->b_next = first->b_next;
-                       free(first->b_name);
-                       free(first);
-                       if (prev == NULL)
-                               bp = list;
-                       else
-                               bp = prev->b_next;
-                       goto again;
-               } else {
-                       if (prev == NULL)
-                               list = tp = bp->b_next;
-                       else
-                               prev->b_next = tp = bp->b_next;
-                       free(bp->b_name);
-                       free(bp);
-                       if (tp != NULL) {
-                               bp = tp;
-                               goto again;
-                       }
-                       break;
-               }
-       }
-
-       if (noshexp)
+       if (wh == 0)
                return(list);
 
                return(list);
 
-       if (debug) {
-               printf("shexpand ");
-               prnames(list);
-       }
-
-       path = pathp = pathbuf;
+       which = wh;
+       path = tpathp = pathp = pathbuf;
        *pathp = '\0';
        lastpathp = &path[sizeof pathbuf - 2];
        *pathp = '\0';
        lastpathp = &path[sizeof pathbuf - 2];
+       tilde = NULL;
        argc = 0;
        argv = sortbase = argvbuf;
        *argv = 0;
        nleft = NCARGS - 4;
        argc = 0;
        argv = sortbase = argvbuf;
        *argv = 0;
        nleft = NCARGS - 4;
-       argcnt = 0;
-       for (bp = list; bp != NULL; bp = bp->b_next)
-               expsh(bp->b_name);
-       for (bp = list; bp != NULL; bp = tp) {
-               tp = bp->b_next;
+       /*
+        * Walk the block list and expand names into argv[];
+        */
+       for (bp = list; bp != NULL; bp = bp->b_next) {
+               expstr(bp->b_name);
                free(bp->b_name);
                free(bp);
        }
                free(bp->b_name);
                free(bp);
        }
-       prev = NULL;
+       /*
+        * Take expanded list of names from argv[] and build a block list.
+        */
+       list = prev = NULL;
        for (n = 0; n < argc; n++) {
                bp = ALLOC(block);
                if (bp == NULL)
        for (n = 0; n < argc; n++) {
                bp = ALLOC(block);
                if (bp == NULL)
@@ -135,53 +86,106 @@ expand(list, noshexp)
                        prev = bp;
                }
        }
                        prev = bp;
                }
        }
+       if (debug) {
+               printf("expanded list = ");
+               prnames(list);
+       }
        return(list);
 }
 
        return(list);
 }
 
-/*
- * Return a new NAME block named "head, bp->b_name, tail"
- */
-struct block *
-copy(bp, head, tail)
-       struct block *bp;
-       char *head, *tail;
+expstr(s)
+       char *s;
 {
 {
-       register int n;
-       register char *cp;
-       register struct block *np;
-
-       np = ALLOC(block);
-       if (np == NULL)
-               fatal("ran out of memory\n");
-       np->b_type = NAME;
-       np->b_next = bp->b_args = NULL;
-       n = strlen(bp->b_name) + strlen(head) + strlen(tail) + 1;
-       np->b_name = cp = malloc(n);
-       if (cp == NULL)
-               fatal("ran out of memory");
-       sprintf(cp, "%s%s%s", head, bp->b_name, tail);
-       return(np);
-}
+       register char *cp, *cp1;
+       register struct block *tp;
+       char *tail, *opathp;
+       char buf[BUFSIZ];
+       int savec, oargc;
+       extern char homedir[];
 
 
-/*
- * If there are any Shell meta characters in the name,
- * expand into a list, after searching directory
- */
-expsh(s)
-       register char *s;
-{
-       register int oargc = argc;
+       if (s == NULL || *s == '\0')
+               return;
 
 
-       if (!strcmp(s, "{") || !strcmp(s, "{}")) {
+       if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
+               *cp++ = '\0';
+               if (*cp == '\0') {
+                       error("no variable name after '$'\n");
+                       return;
+               }
+               if (*cp == LC) {
+                       cp++;
+                       if ((tail = index(cp, RC)) == NULL) {
+                               error("unmatched %c\n", *cp);
+                               return;
+                       }
+                       *tail++ = savec = '\0';
+                       if (*cp == '\0') {
+                               error("no variable name after '$'\n");
+                               return;
+                       }
+               } else {
+                       tail = cp + 1;
+                       savec = *tail;
+                       *tail = '\0';
+               }
+               tp = lookup(cp, NULL, 0);
+               if (savec != '\0')
+                       *tail = savec;
+               if ((tp = tp->b_args) != NULL) {
+                       for (; tp != NULL; tp = tp->b_next) {
+                               sprintf(buf, "%s%s%s", s, tp->b_name, tail);
+                               expstr(buf);
+                       }
+                       return;
+               }
+               sprintf(buf, "%s%s", s, tail);
+               expstr(buf);
+               return;
+       }
+       if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
                Cat(s, "");
                sort();
                return;
        }
                Cat(s, "");
                sort();
                return;
        }
-
-       pathp = path;
-       *pathp = 0;
+       if (*s == '~') {
+               cp = ++s;
+               if (*cp == '\0' || *cp == '/') {
+                       tilde = "~";
+                       cp1 = homedir;
+               } else {
+                       tilde = cp1 = buf;
+                       *cp1++ = '~';
+                       do
+                               *cp1++ = *cp++;
+                       while (*cp && *cp != '/');
+                       *cp1 = '\0';
+                       if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
+                               if ((pw = getpwnam(buf+1)) == NULL) {
+                                       error("unknown user %s\n", buf+1);
+                                       return;
+                               }
+                       }
+                       cp1 = pw->pw_dir;
+                       s = cp;
+               }
+               for (cp = path; *cp++ = *cp1++; )
+                       ;
+               tpathp = pathp = cp - 1;
+       } else
+               pathp = path;
+       *pathp = '\0';
+       if (!(which & E_SHELL)) {
+               if (which & E_TILDE)
+                       Cat(path, s);
+               else
+                       Cat(tilde, s);
+               sort();
+               return;
+       }
+       oargc = argc;
        expany = 0;
        expany = 0;
-       expstr(s);
+       expsh(s);
+       pathp = opathp;
        if (argc != oargc)
                sort();
 }
        if (argc != oargc)
                sort();
 }
@@ -205,7 +209,11 @@ sort()
        sortbase = ap;
 }
 
        sortbase = ap;
 }
 
-expstr(s)
+/*
+ * If there are any Shell meta characters in the name,
+ * expand into a list, after searching directory
+ */
+expsh(s)
        char *s;
 {
        register char *cp;
        char *s;
 {
        register char *cp;
@@ -216,11 +224,11 @@ expstr(s)
        cp = s;
        while (!any(*cp, shchars)) {
                if (*cp == '\0') {
        cp = s;
        while (!any(*cp, shchars)) {
                if (*cp == '\0') {
-                       if (!expany)
-                               Cat(path, "");
-                       else if (stat(path, &stb) >= 0) {
-                               Cat(path, "");
-                               argcnt++;
+                       if (!expany || stat(path, &stb) >= 0) {
+                               if (which & E_TILDE)
+                                       Cat(path, "");
+                               else
+                                       Cat(tilde, tpathp);
                        }
                        goto endit;
                }
                        }
                        goto endit;
                }
@@ -263,8 +271,13 @@ matchdir(pattern)
        }
        while ((dp = readdir(dirp)) != NULL)
                if (match(dp->d_name, pattern)) {
        }
        while ((dp = readdir(dirp)) != NULL)
                if (match(dp->d_name, pattern)) {
-                       Cat(path, dp->d_name);
-                       argcnt++;
+                       if (which & E_TILDE)
+                               Cat(path, dp->d_name);
+                       else {
+                               strcpy(pathp, dp->d_name);
+                               Cat(tilde, tpathp);
+                               *pathp = '\0';
+                       }
                }
        closedir(dirp);
        return;
                }
        closedir(dirp);
        return;
@@ -272,7 +285,7 @@ matchdir(pattern)
 patherr1:
        closedir(dirp);
 patherr2:
 patherr1:
        closedir(dirp);
 patherr2:
-       fatal("%s: %s\n", path, sys_errlist[errno]);
+       error("%s: %s\n", path, sys_errlist[errno]);
 }
 
 execbrc(p, s)
 }
 
 execbrc(p, s)
@@ -302,7 +315,7 @@ execbrc(p, s)
                        for (pe++; *pe && *pe != ']'; pe++)
                                continue;
                        if (!*pe)
                        for (pe++; *pe && *pe != ']'; pe++)
                                continue;
                        if (!*pe)
-                               fatal("Missing ]\n");
+                               error("Missing ]\n");
                        continue;
                }
 pend:
                        continue;
                }
 pend:
@@ -333,7 +346,7 @@ doit:
                        *pm = savec;
                        if (s == 0) {
                                spathp = pathp;
                        *pm = savec;
                        if (s == 0) {
                                spathp = pathp;
-                               expstr(restbuf);
+                               expsh(restbuf);
                                pathp = spathp;
                                *pathp = 0;
                        } else if (amatch(s, restbuf))
                                pathp = spathp;
                                *pathp = 0;
                        } else if (amatch(s, restbuf))
@@ -346,7 +359,7 @@ doit:
                        for (pm++; *pm && *pm != ']'; pm++)
                                continue;
                        if (!*pm)
                        for (pm++; *pm && *pm != ']'; pm++)
                                continue;
                        if (!*pm)
-                               fatal("Missing ]\n");
+                               error("Missing ]\n");
                        continue;
                }
        return (0);
                        continue;
                }
        return (0);
@@ -442,10 +455,12 @@ slash:
                        addpath('/');
                        if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
                                if (*p == '\0') {
                        addpath('/');
                        if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
                                if (*p == '\0') {
-                                       Cat(path, "");
-                                       argcnt++;
+                                       if (which & E_TILDE)
+                                               Cat(path, "");
+                                       else
+                                               Cat(tilde, tpathp);
                                } else
                                } else
-                                       expstr(p);
+                                       expsh(p);
                        pathp = spathp;
                        *pathp = '\0';
                        return (0);
                        pathp = spathp;
                        *pathp = '\0';
                        return (0);
@@ -517,7 +532,7 @@ Cat(s1, s2)
 
        nleft -= len;
        if (nleft <= 0 || ++argc >= GAVSIZ)
 
        nleft -= len;
        if (nleft <= 0 || ++argc >= GAVSIZ)
-               fatal("Arguments too long\n");
+               error("Arguments too long\n");
        argv[argc] = 0;
        argv[argc - 1] = s = malloc(len);
        if (s == NULL)
        argv[argc] = 0;
        argv[argc - 1] = s = malloc(len);
        if (s == NULL)
index 172cbbb..323a3ec 100644 (file)
@@ -1,6 +1,6 @@
 %{
 #ifndef lint
 %{
 #ifndef lint
-static char *sccsid = "@(#)gram.y      4.6 (Berkeley) 83/10/26";
+static char *sccsid = "@(#)gram.y      4.7 (Berkeley) 83/11/29";
 #endif
 
 #include "defs.h"
 #endif
 
 #include "defs.h"
@@ -10,26 +10,30 @@ struct      block *lastc;
 
 %}
 
 
 %}
 
-%term EQUAL 1
-%term LP 2
-%term RP 3
-%term SM 4
-%term ARROW 5
-%term DCOLON 6
-%term NAME 7
-%term INSTALL 8
-%term NOTIFY 9
-%term EXCEPT 10
-%term OPTION 11
+%term EQUAL    1
+%term LP       2
+%term RP       3
+%term SM       4
+%term ARROW    5
+%term DCOLON   6
+%term NAME     7
+%term STRING   8
+%term INSTALL  9
+%term NOTIFY   10
+%term EXCEPT   11
+%term SPECIAL  12
+%term OPTION   13
 
 %union {
        struct block *blk;
        int intval;
 
 %union {
        struct block *blk;
        int intval;
+       char *string;
 }
 
 }
 
-%type <blk> NAME, INSTALL, NOTIFY, EXCEPT
-%type <blk> namelist, names, opt_name, cmdlist, cmd
+%type <blk> NAME, INSTALL, NOTIFY, EXCEPT, SPECIAL
+%type <blk> namelist, names, opt_name, opt_namelist, cmdlist, cmd
 %type <intval> OPTION, options
 %type <intval> OPTION, options
+%type <string> STRING
 
 %%
 
 
 %%
 
@@ -44,7 +48,7 @@ command:        NAME EQUAL namelist = {
                | namelist ARROW namelist cmdlist = {
                        dohcmds($1, $3, $4);
                }
                | namelist ARROW namelist cmdlist = {
                        dohcmds($1, $3, $4);
                }
-               | namelist DCOLON namelist cmdlist = {
+               | namelist DCOLON NAME cmdlist = {
                        dofcmds($1, $3, $4);
                }
                | error
                        dofcmds($1, $3, $4);
                }
                | error
@@ -91,7 +95,7 @@ cmd:            INSTALL options opt_name SM = {
 
                        $1->b_options = $2 | options;
                        if ($3 != NULL) {
 
                        $1->b_options = $2 | options;
                        if ($3 != NULL) {
-                               b = expand($3, 0);
+                               b = expand($3, E_VARS|E_SHELL);
                                if (b->b_next != NULL)
                                        yyerror("only one name allowed\n");
                                $1->b_name = b->b_name;
                                if (b->b_next != NULL)
                                        yyerror("only one name allowed\n");
                                $1->b_name = b->b_name;
@@ -99,13 +103,19 @@ cmd:                 INSTALL options opt_name SM = {
                        $$ = $1;
                }
                | NOTIFY namelist SM = {
                        $$ = $1;
                }
                | NOTIFY namelist SM = {
-                       $1->b_args = expand($2, 1);
+                       $1->b_args = expand($2, E_VARS);
                        $$ = $1;
                }
                | EXCEPT namelist SM = {
                        $1->b_args = $2;
                        $$ = $1;
                }
                        $$ = $1;
                }
                | EXCEPT namelist SM = {
                        $1->b_args = $2;
                        $$ = $1;
                }
+               | SPECIAL opt_namelist STRING SM = {
+                       if ($2 != NULL)
+                               $1->b_args = expand($2, E_ALL);
+                       $1->b_name = $3;
+                       $$ = $1;
+               }
                ;
 
 options:         /* VOID */ = {
                ;
 
 options:         /* VOID */ = {
@@ -124,6 +134,14 @@ opt_name:    /* VOID */ = {
                }
                ;
 
                }
                ;
 
+opt_namelist:    /* VOID */ = {
+                       $$ = NULL;
+               }
+               | namelist = {
+                       $$ = $1;
+               }
+               ;
+
 %%
 
 int    yylineno = 1;
 %%
 
 int    yylineno = 1;
@@ -171,6 +189,38 @@ again:
                c = '-';
                break;
 
                c = '-';
                break;
 
+       case '"':  /* STRING */
+               cp1 = yytext;
+               cp2 = &yytext[INMAX - 1];
+               for (;;) {
+                       if (cp1 >= cp2) {
+                               yyerror("command string too long\n");
+                               break;
+                       }
+                       c = getc(fin);
+                       if (c == EOF || c == '"')
+                               break;
+                       if (c == '\\') {
+                               if ((c = getc(fin)) == EOF) {
+                                       *cp1++ = '\\';
+                                       break;
+                               }
+                       }
+                       if (c == '\n')
+                               c = ' '; /* can't send '\n' */
+                       *cp1++ = c;
+               }
+               if (c != '"')
+                       yyerror("missing closing '\"'\n");
+               *cp1++ = '\0';
+               yylval.string = cp2 = malloc(cp1 - yytext);
+               if (cp2 == NULL)
+                       fatal("ran out of memory\n");
+               cp1 = yytext;
+               while (*cp2++ = *cp1++)
+                       ;
+               return(STRING);
+
        case ':':  /* :: */
                if ((c = getc(fin)) == ':')
                        return(DCOLON);
        case ':':  /* :: */
                if ((c = getc(fin)) == ':')
                        return(DCOLON);
@@ -181,7 +231,7 @@ again:
        cp2 = &yytext[INMAX - 1];
        for (;;) {
                if (cp1 >= cp2) {
        cp2 = &yytext[INMAX - 1];
        for (;;) {
                if (cp1 >= cp2) {
-                       fatal("input line too long\n");
+                       yyerror("input line too long\n");
                        break;
                }
                if (c == '\\') {
                        break;
                }
                if (c == '\\') {
@@ -207,7 +257,7 @@ again:
                        yylval.intval = COMPARE;
                        return(OPTION);
 
                        yylval.intval = COMPARE;
                        return(OPTION);
 
-               case 'r':
+               case 'R':
                        yylval.intval = REMOVE;
                        return(OPTION);
 
                        yylval.intval = REMOVE;
                        return(OPTION);
 
@@ -230,6 +280,8 @@ again:
                c = NOTIFY;
        else if (!strcmp(yytext, "except"))
                c = EXCEPT;
                c = NOTIFY;
        else if (!strcmp(yytext, "except"))
                c = EXCEPT;
+       else if (!strcmp(yytext, "special"))
+               c = SPECIAL;
        else
                c = NAME;
        yylval.blk = makeblock(c, yytext);
        else
                c = NAME;
        yylval.blk = makeblock(c, yytext);
index 31e73d9..6d1af62 100644 (file)
@@ -1,5 +1,5 @@
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)main.c      4.8 (Berkeley) 83/11/01";
+static char *sccsid = "@(#)main.c      4.9 (Berkeley) 83/11/29";
 #endif
 
 #include "defs.h"
 #endif
 
 #include "defs.h"
@@ -9,9 +9,8 @@ static  char *sccsid = "@(#)main.c      4.8 (Berkeley) 83/11/01";
  */
 
 char   *distfile = "distfile";
  */
 
 char   *distfile = "distfile";
-char   tmpfile[] = "/tmp/rdistAXXXXXX";
+char   tmpfile[] = "/tmp/rdistXXXXXX";
 char   *tmpname = &tmpfile[5];
 char   *tmpname = &tmpfile[5];
-char   *tmpinc = &tmpfile[10];
 
 int    debug;          /* debugging flag */
 int    nflag;          /* NOP flag, just print commands without executing */
 
 int    debug;          /* debugging flag */
 int    nflag;          /* NOP flag, just print commands without executing */
@@ -83,6 +82,10 @@ main(argc, argv)
                                break;
 
                        case 'n':
                                break;
 
                        case 'n':
+                               if (options & VERIFY) {
+                                       printf("rdist: -n overrides -v\n");
+                                       options &= ~VERIFY;
+                               }
                                nflag++;
                                break;
 
                                nflag++;
                                break;
 
@@ -94,11 +97,15 @@ main(argc, argv)
                                options |= COMPARE;
                                break;
 
                                options |= COMPARE;
                                break;
 
-                       case 'r':
+                       case 'R':
                                options |= REMOVE;
                                break;
 
                        case 'v':
                                options |= REMOVE;
                                break;
 
                        case 'v':
+                               if (nflag) {
+                                       printf("rdist: -n overrides -v\n");
+                                       break;
+                               }
                                options |= VERIFY;
                                break;
 
                                options |= VERIFY;
                                break;
 
@@ -185,7 +192,7 @@ docmdargs(nargs, args)
        } else
                dest[0] = '\0';
 
        } else
                dest[0] = '\0';
 
-       hosts = expand(hosts, 0);
+       hosts = expand(hosts, 1);
 
        if (dest[0] == '\0')
                cmds = NULL;
 
        if (dest[0] == '\0')
                cmds = NULL;
@@ -211,10 +218,7 @@ docmdargs(nargs, args)
  */
 cleanup()
 {
  */
 cleanup()
 {
-       do {
-               (void) unlink(tmpfile);
-               (*tmpinc)--;
-       } while (*tmpinc >= 'A');
+       (void) unlink(tmpfile);
        exit(1);
 }
 
        exit(1);
 }
 
index a8d28aa..1a8a5cc 100644 (file)
@@ -1,10 +1,11 @@
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)server.c    4.7 (Berkeley) 83/11/01";
+static char *sccsid = "@(#)server.c    4.8 (Berkeley) 83/11/29";
 #endif
 
 #include "defs.h"
 
 #endif
 
 #include "defs.h"
 
-#define        ga()    (void) write(rem, "\0\n", 2)
+#define        ack()   (void) write(rem, "\0\n", 2)
+#define        err()   (void) write(rem, "\1\n", 2)
 
 char   buf[BUFSIZ];            /* general purpose buffer */
 char   target[BUFSIZ];         /* target/source directory name */
 
 char   buf[BUFSIZ];            /* general purpose buffer */
 char   target[BUFSIZ];         /* target/source directory name */
@@ -15,8 +16,6 @@ int   oumask;                 /* old umask for creating files */
 
 extern FILE *lfp;              /* log file for mailing changes */
 
 
 extern FILE *lfp;              /* log file for mailing changes */
 
-extern char *exptilde();
-
 /*
  * Server routine to read requests and process them.
  * Commands are:
 /*
  * Server routine to read requests and process them.
  * Commands are:
@@ -32,7 +31,7 @@ server()
        int opts;
 
        oumask = umask(0);
        int opts;
 
        oumask = umask(0);
-       ga();
+       ack();
 
        for (;;) {
                cp = cmdbuf;
 
        for (;;) {
                cp = cmdbuf;
@@ -61,7 +60,7 @@ server()
                        tp = target;
                        while (*tp)
                                tp++;
                        tp = target;
                        while (*tp)
                                tp++;
-                       ga();
+                       ack();
                        continue;
 
                case 'R':  /* Receive. Transfer file. */
                        continue;
 
                case 'R':  /* Receive. Transfer file. */
@@ -80,23 +79,21 @@ server()
                        }
                        tp = stp[--catname];
                        *tp = '\0';
                        }
                        tp = stp[--catname];
                        *tp = '\0';
-                       ga();
+                       ack();
                        continue;
 
                case 'C':  /* Clean. Cleanup a directory */
                        continue;
 
                case 'C':  /* Clean. Cleanup a directory */
-                       opts = 0;
-                       while (*cp >= '0' && *cp <= '7')
-                               opts = (opts << 3) | (*cp++ - '0');
-                       if (*cp++ != ' ')
-                               error("server: options not delimited\n");
-                       else
-                               clean(cp, opts, 1);
+                       clean(cp);
                        continue;
 
                case 'Q':  /* Query. Does the file/directory exist? */
                        query(cp);
                        continue;
 
                        continue;
 
                case 'Q':  /* Query. Does the file/directory exist? */
                        query(cp);
                        continue;
 
+               case 'S':  /* Special. Execute commands */
+                       dospecial(cp);
+                       continue;
+
 #ifdef notdef
                /*
                 * These entries are reserved but not currently used.
 #ifdef notdef
                /*
                 * These entries are reserved but not currently used.
@@ -107,7 +104,7 @@ server()
                        except = bp = NULL;
                case 'x':  /* add name to list of files to exclude */
                        if (*cp == '\0') {
                        except = bp = NULL;
                case 'x':  /* add name to list of files to exclude */
                        if (*cp == '\0') {
-                               ga();
+                               ack();
                                continue;
                        }
                        if (*cp == '~') {
                                continue;
                        }
                        if (*cp == '~') {
@@ -116,15 +113,15 @@ server()
                                cp = buf;
                        }
                        if (bp == NULL)
                                cp = buf;
                        }
                        if (bp == NULL)
-                               except = bp = expand(makeblock(NAME, cp), 0);
+                               except = bp = expand(makeblock(NAME, cp), E_VARS);
                        else
                        else
-                               bp->b_next = expand(makeblock(NAME, cp), 0);
+                               bp->b_next = expand(makeblock(NAME, cp), E_VARS);
                        while (bp->b_next != NULL)
                                bp = bp->b_next;
                        while (bp->b_next != NULL)
                                bp = bp->b_next;
-                       ga();
+                       ack();
                        continue;
 
                        continue;
 
-               case 'S':  /* Send. Transfer file if out of date. */
+               case 'I':  /* Install. Transfer file if out of date. */
                        opts = 0;
                        while (*cp >= '0' && *cp <= '7')
                                opts = (opts << 3) | (*cp++ - '0');
                        opts = 0;
                        while (*cp >= '0' && *cp <= '7')
                                opts = (opts << 3) | (*cp++ - '0');
@@ -132,7 +129,7 @@ server()
                                error("server: options not delimited\n");
                                return;
                        }
                                error("server: options not delimited\n");
                                return;
                        }
-                       sendf(cp, opts);
+                       install(cp, opts);
                        continue;
 
                case 'L':  /* Log. save message in log file */
                        continue;
 
                case 'L':  /* Log. save message in log file */
@@ -176,7 +173,7 @@ install(src, dest, destdir, opts)
                        opts & WHOLE ? " -w" : "",
                        opts & YOUNGER ? " -y" : "",
                        opts & COMPARE ? " -b" : "",
                        opts & WHOLE ? " -w" : "",
                        opts & YOUNGER ? " -y" : "",
                        opts & COMPARE ? " -b" : "",
-                       opts & REMOVE ? " -r" : "", src, dest);
+                       opts & REMOVE ? " -R" : "", src, dest);
                if (nflag)
                        return;
        }
                if (nflag)
                        return;
        }
@@ -189,19 +186,13 @@ install(src, dest, destdir, opts)
                tp++;
        /*
         * If we are renaming a directory and we want to preserve
                tp++;
        /*
         * If we are renaming a directory and we want to preserve
-        * the directory heirarchy (-w), we must strip off the first
+        * the directory heirarchy (-w), we must strip off the leading
         * directory name and preserve the rest.
         */
        if (opts & WHOLE) {
         * directory name and preserve the rest.
         */
        if (opts & WHOLE) {
-               if (!destdir) {
-                       rname = index(rname, '/');
-                       if (rname == NULL)
-                               rname = tp; /* doesn't matter what rname is */
-                       else {
-                               destdir++; /* cat rname to dest */
-                               rname++;
-                       }
-               }
+               while (*rname == '/')
+                       rname++;
+               destdir = 1;
        } else {
                rname = rindex(target, '/');
                if (rname == NULL)
        } else {
                rname = rindex(target, '/');
                if (rname == NULL)
@@ -233,24 +224,21 @@ sendf(rname, opts)
        int opts;
 {
        register char *cp;
        int opts;
 {
        register char *cp;
+       register struct block *c;
        struct stat stb;
        int sizerr, f, u;
        off_t i;
        struct stat stb;
        int sizerr, f, u;
        off_t i;
+       extern struct block *special;
 
        if (debug)
                printf("sendf(%s, %x)\n", rname, opts);
 
 
        if (debug)
                printf("sendf(%s, %x)\n", rname, opts);
 
-       if (exclude(target))
+       if (inlist(except, target))
                return;
                return;
-       if (access(target, 4) < 0 || stat(target, &stb) < 0) {
+       if (access(target, 4) < 0 || lstat(target, &stb) < 0) {
                error("%s: %s\n", target, sys_errlist[errno]);
                return;
        }
                error("%s: %s\n", target, sys_errlist[errno]);
                return;
        }
-       if (opts & REMOVE) {
-               opts &= ~REMOVE;
-               if (ISDIR(stb.st_mode))
-                       rmchk(rname, opts);
-       }
        if ((u = update(rname, opts, &stb)) == 0)
                return;
 
        if ((u = update(rname, opts, &stb)) == 0)
                return;
 
@@ -266,16 +254,22 @@ sendf(rname, opts)
                        return;
                }
        if (u == 1) {
                        return;
                }
        if (u == 1) {
+               if (opts & VERIFY) {
+                       log(lfp, "need to install: %s\n", target);
+                       goto dospecial;
+               }
                log(lfp, "installing: %s\n", target);
                log(lfp, "installing: %s\n", target);
-               if (opts & VERIFY)
-                       return;
-               opts &= ~COMPARE;
+               opts &= ~(COMPARE|REMOVE);
        }
 
        switch (stb.st_mode & S_IFMT) {
        case S_IFREG:
                break;
 
        }
 
        switch (stb.st_mode & S_IFMT) {
        case S_IFREG:
                break;
 
+       case S_IFLNK:
+               error("%s: cannot install soft links - use 'special'\n", target);
+               return;
+
        case S_IFDIR:
                rsendf(rname, opts, &stb, pw->pw_name, gr->gr_name);
                return;
        case S_IFDIR:
                rsendf(rname, opts, &stb, pw->pw_name, gr->gr_name);
                return;
@@ -286,10 +280,14 @@ sendf(rname, opts)
        }
 
        if (u == 2) {
        }
 
        if (u == 2) {
+               if (opts & VERIFY) {
+                       log(lfp, "need to update: %s\n", target);
+                       goto dospecial;
+               }
                log(lfp, "updating: %s\n", target);
                log(lfp, "updating: %s\n", target);
-               if (opts & VERIFY)
-                       return;
        }
        }
+       if (stb.st_nlink != 1)
+               log(lfp, "%s: Warning: more than one hard link\n", target);
 
        if ((f = open(target, 0)) < 0) {
                error("%s: %s\n", target, sys_errlist[errno]);
 
        if ((f = open(target, 0)) < 0) {
                error("%s: %s\n", target, sys_errlist[errno]);
@@ -315,11 +313,29 @@ sendf(rname, opts)
                (void) write(rem, buf, amt);
        }
        (void) close(f);
                (void) write(rem, buf, amt);
        }
        (void) close(f);
-       if (sizerr)
+       if (sizerr) {
                error("%s: file changed size\n", target);
                error("%s: file changed size\n", target);
-       else
-               ga();
-       (void) response();
+               err();
+       } else
+               ack();
+       if (response() == 0 && (opts & COMPARE))
+               return;
+dospecial:
+       for (c = special; c != NULL; c = c->b_next) {
+               if (c->b_type != SPECIAL)
+                       continue;
+               if (!inlist(c->b_args, target))
+                       continue;
+               log(lfp, "special \"%s\"\n", c->b_name);
+               if (opts & VERIFY)
+                       continue;
+               (void) sprintf(buf, "S%s\n", c->b_name);
+               if (debug)
+                       printf("buf = %s", buf);
+               (void) write(rem, buf, strlen(buf));
+               while (response() > 0)
+                       ;
+       }
 }
 
 rsendf(rname, opts, st, owner, group)
 }
 
 rsendf(rname, opts, st, owner, group)
@@ -342,7 +358,7 @@ rsendf(rname, opts, st, owner, group)
                return;
        }
        (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
                return;
        }
        (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
-               st->st_mode & 07777, owner, group, rname);
+               st->st_mode & 0777, owner, group, rname);
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
@@ -350,6 +366,10 @@ rsendf(rname, opts, st, owner, group)
                closedir(d);
                return;
        }
                closedir(d);
                return;
        }
+
+       if (opts & REMOVE)
+               rmchk(opts);
+
        otp = tp;
        len = tp - target;
        while (dp = readdir(d)) {
        otp = tp;
        len = tp - target;
        while (dp = readdir(d)) {
@@ -577,23 +597,23 @@ recvf(cmd, isdir)
                        tp--;
                }
                if (opts & VERIFY) {
                        tp--;
                }
                if (opts & VERIFY) {
-                       ga();
+                       ack();
                        return;
                }
                if (stat(target, &stb) == 0) {
                        return;
                }
                if (stat(target, &stb) == 0) {
-                       if (!ISDIR(stb.st_mode)) {
-                               errno = ENOTDIR;
-                               goto bad;
-                       }
-               } else {
-                       if (chkparent(target) < 0)
-                               goto bad;
-                       if (mkdir(target, mode) < 0)
-                               goto bad;
-                       if (chog(target, owner, group, mode) < 0)
+                       if (ISDIR(stb.st_mode)) {
+                               ack();
                                return;
                                return;
+                       }
+                       error("%s: %s\n", target, sys_errlist[ENOTDIR]);
+               } else if (chkparent(target) < 0 || mkdir(target, mode) < 0)
+                       error("%s: %s\n", target, sys_errlist[errno]);
+               else if (chog(target, owner, group, mode) == 0) {
+                       ack();
+                       return;
                }
                }
-               ga();
+               tp = stp[--catname];
+               *tp = '\0';
                return;
        }
 
                return;
        }
 
@@ -624,12 +644,7 @@ recvf(cmd, isdir)
                *cp = '/';
        if ((f = creat(new, mode)) < 0)
                goto bad1;
                *cp = '/';
        if ((f = creat(new, mode)) < 0)
                goto bad1;
-       if (chog(new, owner, group, mode) < 0) {
-               (void) close(f);
-               (void) unlink(new);
-               return;
-       }
-       ga();
+       ack();
 
        wrerr = 0;
        for (i = 0; i < size; i += BUFSIZ) {
 
        wrerr = 0;
        for (i = 0; i < size; i += BUFSIZ) {
@@ -677,7 +692,7 @@ recvf(cmd, isdir)
                                (void) fclose(f1);
                                (void) fclose(f2);
                                (void) unlink(new);
                                (void) fclose(f1);
                                (void) fclose(f2);
                                (void) unlink(new);
-                               ga();
+                               ack();
                                return;
                        }
                (void) fclose(f1);
                                return;
                        }
                (void) fclose(f1);
@@ -685,7 +700,7 @@ recvf(cmd, isdir)
                if (opts & VERIFY) {
                        (void) unlink(new);
                        buf[0] = '\0';
                if (opts & VERIFY) {
                        (void) unlink(new);
                        buf[0] = '\0';
-                       sprintf(buf + 1, "updating %s:%s\n", host, target);
+                       sprintf(buf + 1, "need to update %s:%s\n", host, target);
                        (void) write(rem, buf, strlen(buf + 1) + 1);
                        return;
                }
                        (void) write(rem, buf, strlen(buf + 1) + 1);
                        return;
                }
@@ -698,7 +713,7 @@ recvf(cmd, isdir)
        tvp[0].tv_usec = 0;
        tvp[1].tv_sec = mtime;
        tvp[1].tv_usec = 0;
        tvp[0].tv_usec = 0;
        tvp[1].tv_sec = mtime;
        tvp[1].tv_usec = 0;
-       if (utimes(new, tvp) < 0) {
+       if (utimes(new, tvp) < 0 || chog(new, owner, group, mode) < 0) {
 bad1:
                error("%s: %s\n", new, sys_errlist[errno]);
                if (new[0])
 bad1:
                error("%s: %s\n", new, sys_errlist[errno]);
                if (new[0])
@@ -718,7 +733,7 @@ bad:
                sprintf(buf + 1, "updated %s:%s\n", host, target);
                (void) write(rem, buf, strlen(buf + 1) + 1);
        } else
                sprintf(buf + 1, "updated %s:%s\n", host, target);
                (void) write(rem, buf, strlen(buf + 1) + 1);
        } else
-               ga();
+               ack();
 }
 
 /*
 }
 
 /*
@@ -769,7 +784,7 @@ bad:
 }
 
 /*
 }
 
 /*
- * Change owner and group of file.
+ * Change owner, group and mode of file.
  */
 chog(file, owner, group, mode)
        char *file, *owner, *group;
  */
 chog(file, owner, group, mode)
        char *file, *owner, *group;
@@ -792,7 +807,8 @@ chog(file, owner, group, mode)
                                uid = pw->pw_uid;
                } else
                        uid = pw->pw_uid;
                                uid = pw->pw_uid;
                } else
                        uid = pw->pw_uid;
-       }
+       } else if ((mode & 04000) && strcmp(user, owner) != 0)
+               mode &= ~04000;
        gid = groupid;
        if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
                if ((gr = getgrnam(group)) == NULL) {
        gid = groupid;
        if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
                if ((gr = getgrnam(group)) == NULL) {
@@ -808,6 +824,7 @@ chog(file, owner, group, mode)
                for (i = 0; gr->gr_mem[i] != NULL; i++)
                        if (!(strcmp(user, gr->gr_mem[i])))
                                goto ok;
                for (i = 0; gr->gr_mem[i] != NULL; i++)
                        if (!(strcmp(user, gr->gr_mem[i])))
                                goto ok;
+               mode &= ~02000;
                gid = groupid;
        }
 ok:
                gid = groupid;
        }
 ok:
@@ -815,6 +832,13 @@ ok:
                error("%s: %s\n", file, sys_errlist[errno]);
                return(-1);
        }
                error("%s: %s\n", file, sys_errlist[errno]);
                return(-1);
        }
+       /*
+        * Restore set-user-id or set-group-id bit if appropriate.
+        */
+       if ((mode & 06000) && chmod(file, mode) < 0) {
+               error("%s: %s\n", file, sys_errlist[errno]);
+               return(-1);
+       }
        return(0);
 }
 
        return(0);
 }
 
@@ -822,27 +846,24 @@ ok:
  * Check for files on the machine being updated that are not on the master
  * machine and remove them.
  */
  * Check for files on the machine being updated that are not on the master
  * machine and remove them.
  */
-rmchk(rname, opts)
-       char *rname;
+rmchk(opts)
        int opts;
 {
        register char *cp, *s;
        struct stat stb;
 
        if (debug)
        int opts;
 {
        register char *cp, *s;
        struct stat stb;
 
        if (debug)
-               printf("rmchk(%s, %x)\n", rname, opts);
+               printf("rmchk()\n");
 
        /*
         * Tell the remote to clean the files from the last directory sent.
         */
 
        /*
         * Tell the remote to clean the files from the last directory sent.
         */
-       (void) sprintf(buf, "C%o %s\n", opts & VERIFY, rname);
+       (void) sprintf(buf, "C%o\n", opts & VERIFY);
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
        if (response() < 0)
                return;
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
        if (response() < 0)
                return;
-       stp[0] = tp;
-       catname = 1;
        for (;;) {
                cp = s = buf;
                do {
        for (;;) {
                cp = s = buf;
                do {
@@ -851,45 +872,22 @@ rmchk(rname, opts)
                } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
 
                switch (*s++) {
                } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
 
                switch (*s++) {
-               case 'Q': /* its a directory on the remote end */
-               case 'q': /* its a regular file on the remote end */
+               case 'Q': /* Query if file should be removed */
                        /*
                         * Return the following codes to remove query.
                        /*
                         * Return the following codes to remove query.
-                        * N\n -- file does not exisit, remove.
-                        * Y\n -- file exists and is a directory.
-                        * y\n -- file exists and is a regular file.
+                        * N\n -- file exists - DON'T remove.
+                        * Y\n -- file doesn't exist - REMOVE.
                         */
                        *--cp = '\0';
                        (void) sprintf(tp, "/%s", s);
                        if (debug)
                                printf("check %s\n", target);
                         */
                        *--cp = '\0';
                        (void) sprintf(tp, "/%s", s);
                        if (debug)
                                printf("check %s\n", target);
-                       if (exclude(target))
-                               (void) write(rem, "y\n", 2);
-                       else if (stat(target, &stb) < 0)
+                       if (inlist(except, target))
                                (void) write(rem, "N\n", 2);
                                (void) write(rem, "N\n", 2);
-                       else if (buf[0] == 'Q' && ISDIR(stb.st_mode)) {
-                               if (catname >= sizeof(stp)) {
-                                       error("%s: too many directory levels\n", target);
-                                       break;
-                               }
+                       else if (stat(target, &stb) < 0)
                                (void) write(rem, "Y\n", 2);
                                (void) write(rem, "Y\n", 2);
-                               if (response() < 0)
-                                       break;
-                               stp[catname++] = tp;
-                               while (*tp)
-                                       tp++;
-                       } else
-                               (void) write(rem, "y\n", 2);
-                       break;
-
-               case 'E':
-                       if (catname < 0)
-                               fatal("too many 'E's\n");
-                       ga();
-                       tp = stp[--catname];
-                       *tp = '\0';
-                       if (catname == 0)
-                               return;
+                       else
+                               (void) write(rem, "N\n", 2);
                        break;
 
                case '\0':
                        break;
 
                case '\0':
@@ -898,6 +896,11 @@ rmchk(rname, opts)
                                log(lfp, "%s\n", s);
                        break;
 
                                log(lfp, "%s\n", s);
                        break;
 
+               case 'E':
+                       *tp = '\0';
+                       ack();
+                       return;
+
                case '\1':
                case '\2':
                        errs++;
                case '\1':
                case '\2':
                        errs++;
@@ -914,64 +917,37 @@ rmchk(rname, opts)
                        break;
 
                default:
                        break;
 
                default:
-                       error("rmchk: unexpected response '%c'\n", buf[0]);
+                       error("rmchk: unexpected response '%s'\n", buf);
+                       err();
                }
        }
 }
 
 /*
                }
        }
 }
 
 /*
- * Check the directory initialized by the 'T' command to server()
+ * Check the current directory (initialized by the 'T' command to server())
  * for extraneous files and remove them.
  */
  * for extraneous files and remove them.
  */
-clean(name, opts, first)
-       char *name;
-       int opts, first;
+clean(opts)
 {
        DIR *d;
 {
        DIR *d;
-       struct direct *dp;
+       register struct direct *dp;
        register char *cp;
        struct stat stb;
        register char *cp;
        struct stat stb;
-       char *ootp, *otp;
-       int len;
+       char *otp;
+       int len, opts;
 
 
-       if (first) {
-               ootp = tp;
-               if (catname) {
-                       *tp++ = '/';
-                       cp = name;
-                       while (*tp++ = *cp++)
-                               ;
-                       tp--;
-               }
-               if (stat(target, &stb) < 0) {
-                       if (errno != ENOENT) {
-                               ga();
-                               goto done;
-                       }
-                       error("%s: %s\n", target, sys_errlist[errno]);
-                       tp = ootp;
-                       *tp = '\0';
-                       return;
-               }
-               /*
-                * This should be a directory because its a directory on the
-                * master machine. If not, let install complain about it.
-                */
-               if (!ISDIR(stb.st_mode)) {
-                       ga();
-                       goto done;
-               }
+       opts = 0;
+       while (*cp >= '0' && *cp <= '7')
+               opts = (opts << 3) | (*cp++ - '0');
+       if (*cp != '\0') {
+               error("clean: options not delimited\n");
+               return;
        }
        }
-
        if (access(target, 6) < 0 || (d = opendir(target)) == NULL) {
                error("%s: %s\n", target, sys_errlist[errno]);
        if (access(target, 6) < 0 || (d = opendir(target)) == NULL) {
                error("%s: %s\n", target, sys_errlist[errno]);
-               if (first) {
-                       tp = ootp;
-                       *tp = '\0';
-               }
                return;
        }
                return;
        }
-       ga();
+       ack();
 
        otp = tp;
        len = tp - target;
 
        otp = tp;
        len = tp - target;
@@ -992,8 +968,7 @@ clean(name, opts, first)
                        error("%s: %s\n", target, sys_errlist[errno]);
                        continue;
                }
                        error("%s: %s\n", target, sys_errlist[errno]);
                        continue;
                }
-               (void) sprintf(buf, "%c%s\n", ISDIR(stb.st_mode) ? 'Q' : 'q',
-                       dp->d_name);
+               (void) sprintf(buf, "Q%s\n", dp->d_name);
                (void) write(rem, buf, strlen(buf));
                cp = buf;
                do {
                (void) write(rem, buf, strlen(buf));
                cp = buf;
                do {
@@ -1002,24 +977,20 @@ clean(name, opts, first)
                } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
                *--cp = '\0';
                cp = buf;
                } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
                *--cp = '\0';
                cp = buf;
-               if (*cp != 'N') {
-                       if (*cp == 'Y' && ISDIR(stb.st_mode))
-                               clean(dp->d_name, opts, 0);
+               if (*cp != 'Y')
                        continue;
                        continue;
-               }
                if (opts & VERIFY) {
                        cp = buf;
                        *cp++ = '\0';
                if (opts & VERIFY) {
                        cp = buf;
                        *cp++ = '\0';
-                       (void) sprintf(cp, "removing %s\n", target);
+                       (void) sprintf(cp, "need to remove %s\n", target);
                        (void) write(rem, buf, strlen(cp) + 1);
                } else
                        remove(&stb);
        }
        closedir(d);
                        (void) write(rem, buf, strlen(cp) + 1);
                } else
                        remove(&stb);
        }
        closedir(d);
-done:
        (void) write(rem, "E\n", 2);
        (void) response();
        (void) write(rem, "E\n", 2);
        (void) response();
-       tp = (first) ? ootp : otp;
+       tp = otp;
        *tp = '\0';
 }
 
        *tp = '\0';
 }
 
@@ -1090,6 +1061,72 @@ removed:
        (void) write(rem, buf, strlen(cp) + 1);
 }
 
        (void) write(rem, buf, strlen(cp) + 1);
 }
 
+/*
+ * Execute a shell command to handle special cases.
+ */
+dospecial(cmd)
+       char *cmd;
+{
+       int fd[2], status, pid, i;
+       register char *cp, *s;
+       char sbuf[BUFSIZ];
+
+       if (pipe(fd) < 0) {
+               error("%s\n", sys_errlist[errno]);
+               return;
+       }
+       if ((pid = fork()) == 0) {
+               /*
+                * Return everything the shell commands print.
+                */
+               (void) close(0);
+               (void) close(1);
+               (void) close(2);
+               (void) open("/dev/null", 0);
+               (void) dup(fd[1]);
+               (void) dup(fd[1]);
+               (void) close(fd[0]);
+               (void) close(fd[1]);
+               execl("/bin/sh", "sh", "-c", cmd, 0);
+               _exit(127);
+       }
+       (void) close(fd[1]);
+       s = sbuf;
+       *s++ = '\0';
+       while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
+               cp = buf;
+               do {
+                       *s++ = *cp++;
+                       if (cp[-1] != '\n') {
+                               if (s < &sbuf[sizeof(sbuf)-1])
+                                       continue;
+                               *s++ = '\n';
+                       }
+                       /*
+                        * Throw away blank lines.
+                        */
+                       if (s == &sbuf[2]) {
+                               s--;
+                               continue;
+                       }
+                       (void) write(rem, sbuf, s - sbuf);
+                       s = &sbuf[1];
+               } while (--i);
+       }
+       if (s > &sbuf[1]) {
+               *s++ = '\n';
+               (void) write(rem, sbuf, s - sbuf);
+       }
+       while ((i = wait(&status)) != pid && i != -1)
+               ;
+       if (i == -1)
+               status = -1;
+       if (status)
+               error("shell returned %d\n", status);
+       else
+               ack();
+}
+
 /*VARARGS2*/
 log(fp, fmt, a1, a2, a3)
        FILE *fp;
 /*VARARGS2*/
 log(fp, fmt, a1, a2, a3)
        FILE *fp;
@@ -1113,11 +1150,11 @@ error(fmt, a1, a2, a3)
        errs++;
        strcpy(buf, "\1rdist: ");
        (void) sprintf(buf+8, fmt, a1, a2, a3);
        errs++;
        strcpy(buf, "\1rdist: ");
        (void) sprintf(buf+8, fmt, a1, a2, a3);
-       (void) write(rem, buf, strlen(buf));
        if (!iamremote) {
                fflush(stdout);
                (void) write(2, buf+1, strlen(buf+1));
        if (!iamremote) {
                fflush(stdout);
                (void) write(2, buf+1, strlen(buf+1));
-       }
+       } else
+               (void) write(rem, buf, strlen(buf));
        if (lfp != NULL)
                (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
 }
        if (lfp != NULL)
                (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
 }
@@ -1130,11 +1167,11 @@ fatal(fmt, a1, a2,a3)
        errs++;
        strcpy(buf, "\2rdist: ");
        (void) sprintf(buf+8, fmt, a1, a2, a3);
        errs++;
        strcpy(buf, "\2rdist: ");
        (void) sprintf(buf+8, fmt, a1, a2, a3);
-       (void) write(rem, buf, strlen(buf));
        if (!iamremote) {
                fflush(stdout);
                (void) write(2, buf+1, strlen(buf+1));
        if (!iamremote) {
                fflush(stdout);
                (void) write(2, buf+1, strlen(buf+1));
-       }
+       } else
+               (void) write(rem, buf, strlen(buf));
        if (lfp != NULL)
                (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
        cleanup();
        if (lfp != NULL)
                (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
        cleanup();
@@ -1156,8 +1193,10 @@ response()
        switch (*s++) {
        case '\0':
                *--cp = '\0';
        switch (*s++) {
        case '\0':
                *--cp = '\0';
-               if (*s != '\0')
+               if (*s != '\0') {
                        log(lfp, "%s\n", s);
                        log(lfp, "%s\n", s);
+                       return(1);
+               }
                return(0);
 
        default:
                return(0);
 
        default: