From 82572cb6d0bf06d426eb607fb33ad54cb661c41b Mon Sep 17 00:00:00 2001 From: Ralph Campbell Date: Tue, 27 Sep 1983 22:39:44 -0800 Subject: [PATCH] First stable version. SCCS-vsn: usr.bin/rdist/defs.h 4.2 SCCS-vsn: usr.bin/rdist/Makefile 4.2 SCCS-vsn: usr.bin/rdist/docmd.c 4.2 SCCS-vsn: usr.bin/rdist/expand.c 4.2 SCCS-vsn: usr.bin/rdist/gram.y 4.2 SCCS-vsn: usr.bin/rdist/main.c 4.2 SCCS-vsn: usr.bin/rdist/server.c 4.2 SCCS-vsn: usr.bin/rdist/lookup.c 4.2 --- usr/src/usr.bin/rdist/Makefile | 8 +- usr/src/usr.bin/rdist/defs.h | 33 ++- usr/src/usr.bin/rdist/docmd.c | 251 ++++++++++++++--- usr/src/usr.bin/rdist/expand.c | 484 +++++++++++++++++++++++++++++---- usr/src/usr.bin/rdist/gram.y | 208 +++++++------- usr/src/usr.bin/rdist/lookup.c | 2 +- usr/src/usr.bin/rdist/main.c | 118 ++++++-- usr/src/usr.bin/rdist/server.c | 246 ++++++++++------- 8 files changed, 1042 insertions(+), 308 deletions(-) diff --git a/usr/src/usr.bin/rdist/Makefile b/usr/src/usr.bin/rdist/Makefile index a4850c5653..b90520133a 100644 --- a/usr/src/usr.bin/rdist/Makefile +++ b/usr/src/usr.bin/rdist/Makefile @@ -1,8 +1,8 @@ # Makefile 4.1 83/09/07 DESTDIR= -SRCS = main.c gram.y docmd.c expand.c server.c -OBJS = main.o gram.o docmd.o expand.o server.o +SRCS = docmd.c expand.c gram.y lookup.c main.c server.c +OBJS = docmd.o expand.o gram.o lookup.o main.o server.o LINT = lint -ps CFLAGS= @@ -17,8 +17,8 @@ clean: install: install -s rdist ${DESTDIR}/usr/ucb/rdist -lint: main.c gram.c docmd.c expand.c server.c - ${LINT} main.c gram.c docmd.c expand.c server.c +lint: docmd.c expand.c gram.c lookup.c main.c server.c + ${LINT} docmd.c expand.c gram.c lookup.c main.c server.c print: ${SRCS} lpr -p ${SRCS} defs.h diff --git a/usr/src/usr.bin/rdist/defs.h b/usr/src/usr.bin/rdist/defs.h index 0ab56e95ef..342835ca78 100644 --- a/usr/src/usr.bin/rdist/defs.h +++ b/usr/src/usr.bin/rdist/defs.h @@ -1,4 +1,4 @@ -/* defs.h 4.1 83/09/07 */ +/* defs.h 4.2 83/09/27 */ #include #include @@ -16,18 +16,29 @@ /* defines for yacc */ #define EQUAL 1 -#define ARROW 2 -#define LP 3 -#define RP 4 -#define NAME 5 -#define INSTALL 6 -#define VERIFY 7 -#define NOTIFY 8 -#define EXCEPT 9 +#define LP 2 +#define RP 3 +#define ARROW 4 +#define DCOLON 5 +#define NAME 6 +#define INSTALL 7 +#define VERIFY 8 +#define NOTIFY 9 +#define EXCEPT 10 + +#define VAR 11 + + /* lexical definitions */ +#define QUOTE 0200 /* used internally for quoted characters */ +#define TRIM 0177 /* Mask to strip quote bit */ /* table sizes */ #define HASHSIZE 1021 #define INMAX 3500 +#define NCARGS 10240 +#define GAVSIZ NCARGS / 6 +#define NSTAMPS 15 + #define ALLOC(x) (struct x *) malloc(sizeof(struct x)) @@ -49,7 +60,7 @@ extern int rem; /* remote file descriptor */ extern int iamremote; /* acting as remote server */ extern int filec; /* number of files to update */ extern char **filev; /* list of files/directories to update */ -extern char *tmpfile; /* file name for logging changes */ +extern char tmpfile[]; /* file name for logging changes */ extern char host[]; /* host name of master copy */ extern char *rhost; /* host name of remote being updated */ extern struct block *except; /* list of files to exclude */ @@ -58,5 +69,7 @@ extern int errno; /* system error number */ extern char *sys_errlist[]; struct block *lookup(); +struct block *makeblock(); struct block *expand(); char *rindex(); +char *index(); diff --git a/usr/src/usr.bin/rdist/docmd.c b/usr/src/usr.bin/rdist/docmd.c index 248ce8e416..d4c0615f88 100644 --- a/usr/src/usr.bin/rdist/docmd.c +++ b/usr/src/usr.bin/rdist/docmd.c @@ -1,5 +1,5 @@ #ifndef lint -static char *sccsid = "@(#)docmd.c 4.1 (Berkeley) 83/09/07"; +static char *sccsid = "@(#)docmd.c 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" @@ -7,62 +7,70 @@ static char *sccsid = "@(#)docmd.c 4.1 (Berkeley) 83/09/07"; FILE *lfp; /* log file for recording files updated */ /* - * Routines to process commands. + * Process commands for sending files to other machines. */ -docmd(files, hosts, cmds) +dohcmds(files, hosts, cmds) struct block *files, *hosts, *cmds; { register struct block *h, *f, *c; register char *cp, **cpp; - int n; + int n, ddir; if (debug) - printf("docmd()\n"); + printf("dohcmds(%x, %x, %x)\n", files, hosts, cmds); - files = expand(files); - hosts = expand(hosts); + files = expand(files, 0); + hosts = expand(hosts, 1); if (files == NULL) fatal("no files to be updated\n"); if (hosts == NULL) fatal("empty list of hosts to be updated\n"); except = cmds; + ddir = files->b_next != NULL; for (h = hosts; h != NULL; h = h->b_next) { + if (!qflag) + printf("updating host %s\n", h->b_name); if (!nflag) { + if (!makeconn(h->b_name)) + continue; if ((lfp = fopen(tmpfile, "w")) == NULL) { fatal("cannot open %s\n", tmpfile); exit(1); } - if (!makeconn(h->b_name)) - continue; } for (f = files; f != NULL; f = f->b_next) { if (filec) { for (cpp = filev; *cpp; cpp++) if (!strcmp(f->b_name, *cpp)) goto found; + if (!nflag) { + (void) fclose(lfp); + } continue; } found: n = 0; for (c = cmds; c != NULL; c = c->b_next) if (c->b_type == INSTALL) { - install(f->b_name, c->b_name, 0); + install(f->b_name, c->b_name, ddir, 0); n++; } else if (c->b_type == VERIFY) { - install(f->b_name, c->b_name, 1); + install(f->b_name, c->b_name, ddir, 1); n++; } if (n == 0) - install(f->b_name, f->b_name, 0); + install(f->b_name, f->b_name, 0, 0); } if (!nflag) { - (void) fclose(lfp); + /* signal end of connection */ + (void) write(rem, "\2\n", 2); (void) close(rem); + (void) fclose(lfp); } for (c = cmds; c != NULL; c = c->b_next) if (c->b_type == NOTIFY) - notify(h->b_name, c->b_args); + notify(tmpfile, h->b_name, c->b_args); } if (!nflag) (void) unlink(tmpfile); @@ -79,7 +87,7 @@ makeconn(rhost) (void) sprintf(buf, "/usr/local/rdist -Server%s%s%s%s%s", vflag ? " -v" : "", qflag ? " -q" : "", nflag ? " -n" : "", - yflag ? " -y" : "", debug ? " -d" : ""); + yflag ? " -y" : "", debug ? " -D" : ""); ruser = rindex(rhost, '.'); if (ruser != NULL) { @@ -95,6 +103,7 @@ makeconn(rhost) printf("buf = %s\n", buf); } + fflush(stdout); rem = rcmd(&rhost, IPPORT_CMDSERVER, user, ruser, buf, 0); if (rem < 0) return(0); @@ -103,38 +112,204 @@ makeconn(rhost) return(1); } +extern char target[], *tp; + /* * Update the file(s) if they are different. + * destdir = 1 if destination should be a directory + * (i.e., more than one source is being copied to the same destination). */ -install(src, dest, verify) +install(src, dest, destdir, verify) char *src, *dest; - int verify; + int destdir, verify; { - register char *cp; - extern char *tp; - char lbuf[BUFSIZ]; + if (exclude(src)) + return; - if (!qflag) + if (nflag) { printf("%s %s %s\n", verify ? "verify" : "install", src, dest); - if (nflag) return; + } /* * Pass the destination file/directory name to remote. */ - (void) sprintf(buf, "T%s\n", dest); + (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest); if (debug) printf("buf = %s", buf); (void) write(rem, buf, strlen(buf)); tp = NULL; - shexpand(lbuf, src); - sendf(lbuf, verify); + sendf(src, verify); +} + +struct tstamp { + time_t lastmod; + FILE *tfp; +} ts[NSTAMPS]; + +int nstamps; + +/* + * Process commands for comparing files to time stamp files. + */ +dofcmds(files, stamps, cmds) + struct block *files, *stamps, *cmds; +{ + register struct block *b; + register struct tstamp *t; + register char **cpp; + struct stat stb; + extern char *tmpinc; + int n; + + if (debug) + printf("dofcmds()\n"); + + files = expand(files, 0); + stamps = expand(stamps, 1); + if (files == NULL) + fatal("no files to be updated\n"); + if (stamps == NULL) + fatal("empty time stamp file list\n"); + except = cmds; + + 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 (!nflag && !vflag) { + if ((t->tfp = fopen(tmpfile, "w")) == NULL) + error("%s: %s\n", b->b_name, sys_errlist[errno]); + (*tmpinc)++; + } else + t->tfp = NULL; + t++; + } + for (b = files; b != NULL; b = b->b_next) { + if (filec) { + for (cpp = filev; *cpp; cpp++) + if (!strcmp(b->b_name, *cpp)) + goto found; + continue; + } + found: + tp = NULL; + cmptime(b->b_name); + } + if (!nflag && !vflag) + for (t = ts; t < &ts[n]; t++) + if (t->tfp != NULL) + (void) fclose(t->tfp); + *tmpinc = 'A'; + while (n--) { + for (b = cmds; b != NULL; b = b->b_next) + if (b->b_type == NOTIFY) + notify(tmpfile, NULL, b->b_args); + if (!nflag && !vflag) + (void) unlink(tmpfile); + (*tmpinc)++; + } +} + +/* + * Compare the mtime of file to the list of time stamps. + */ +cmptime(name) + char *name; +{ + register struct tstamp *t; + struct stat stb; + + if (debug) + printf("cmptime(%s)\n", name); + + if (exclude(name)) + return; + + /* + * first time cmptime() is called? + */ + if (tp == NULL) { + exptilde(target, name); + tp = name = target; + while (*tp) + tp++; + } + if (access(name, 4) < 0 || stat(name, &stb) < 0) { + error("%s: %s\n", name, sys_errlist[errno]); + return; + } + + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + + case S_IFDIR: + rcmptime(&stb); + return; + + default: + error("%s: not a plain file\n", name); + return; + } + + for (t = ts; t < &ts[nstamps]; t++) { + if (stb.st_mtime <= t->lastmod) + return; + log(t->tfp, "updating: %s\n", name); + } +} + +rcmptime(st) + struct stat *st; +{ + register DIR *d; + register struct direct *dp; + register char *cp; + char *otp; + int len; + + if (debug) + printf("rcmptime(%x)\n", st); + + if ((d = opendir(target)) == NULL) { + error("%s: %s\n", target, sys_errlist[errno]); + return; + } + otp = tp; + len = tp - target; + while (dp = readdir(d)) { + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { + error("%s/%s: Name too long\n", target, dp->d_name); + continue; + } + tp = otp; + *tp++ = '/'; + cp = dp->d_name; + while (*tp++ = *cp++) + ; + tp--; + cmptime(target); + } + closedir(d); + tp = otp; + *tp = '\0'; } /* * Notify the list of people the changes that were made. */ -notify(host, to) - char *host; +notify(file, host, to) + char *file, *host; register struct block *to; { register int fd, len; @@ -144,14 +319,25 @@ notify(host, to) if (vflag) return; if (!qflag) { - printf("notify @%s ", host); + printf("notify "); + if (host) + printf("@%s ", host); prnames(to); } if (nflag) return; - if ((fd = open(tmpfile, 0)) < 0) { - error("%s: %s\n", tmpfile, sys_errlist[errno]); + if ((fd = open(file, 0)) < 0) { + error("%s: %s\n", file, sys_errlist[errno]); + return; + } + if (fstat(fd, &stb) < 0) { + error("%s: %s\n", file, sys_errlist[errno]); + (void) close(fd); + return; + } + if (stb.st_size == 0) { + (void) close(fd); return; } /* @@ -166,7 +352,10 @@ notify(host, to) fprintf(pf, "From: rdist (Remote distribution program)\n"); fprintf(pf, "To:"); while (to != NULL) { - fprintf(pf, " %s@%s", to->b_name, host); + if (!any('@', to->b_name)) + fprintf(pf, " %s@%s", to->b_name, host); + else + fprintf(pf, " %s", to->b_name); to = to->b_next; } putc('\n', pf); diff --git a/usr/src/usr.bin/rdist/expand.c b/usr/src/usr.bin/rdist/expand.c index 37189e1c60..a5e42a3c66 100644 --- a/usr/src/usr.bin/rdist/expand.c +++ b/usr/src/usr.bin/rdist/expand.c @@ -1,69 +1,38 @@ #ifndef lint -static char *sccsid = "@(#)expand.c 4.1 (Berkeley) 83/09/07"; +static char *sccsid = "@(#)expand.c 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" -static struct block *hashtab[HASHSIZE]; -static int nhashed = 0; +char shchars[] = "{[*?"; -/* - * Lookup name in the table and return a pointer to it. - * if insert, insert or replace name with value. - */ - -struct block * -lookup(name, insert, value) - char *name; - int insert; - struct block *value; -{ - register unsigned n; - register char *cp; - register struct block *b; - - if (debug) - printf("lookup(%s, %d, %x)\n", name, insert, value); - - n = 0; - for (cp = name; *cp; ) - n += *cp++; - n %= HASHSIZE; +int argc; +char **argv; +char *path, *pathp, *lastpathp; +int nleft; - for (b = hashtab[n]; b != NULL; b = b->b_next) { - if (strcmp(name, b->b_name)) - continue; - if (insert) { - warn("%s redefined\n", name); - b->b_args = value->b_args; - free(value->b_name); - free(value); - } - return(b); - } - - if (!insert) - fatal("%s not defined", name); - - value->b_next = hashtab[n]; - hashtab[n] = value; - return(value); -} +int argcnt; +int expany; /* any expansions done? */ +char *entp; +char **sortbase; char *index(); /* - * Take a list of names and expand any macros found. + * Take a list of names and expand any macros, etc. */ struct block * -expand(list) +expand(list, noshexp) struct block *list; + int noshexp; { register struct block *prev, *bp, *tp; register char *cp, *s; register int n; char *var, *tail; int c; + char pathbuf[BUFSIZ]; + char *argvbuf[GAVSIZ]; for (prev = NULL, bp = list; bp != NULL; prev = bp, bp = bp->b_next) { again: @@ -85,7 +54,7 @@ expand(list) c = *tail; *tail = '\0'; } - tp = lookup(cp, 0, NULL); + tp = lookup(cp, NULL, 0); if ((tp = tp->b_args) != NULL) { struct block *first = tp; @@ -107,7 +76,7 @@ expand(list) goto again; } else { if (prev == NULL) - list = tp = list->b_next; + list = tp = bp->b_next; else prev->b_next = tp = bp->b_next; free(bp->b_name); @@ -119,6 +88,40 @@ expand(list) break; } } + + if (noshexp) + return(list); + + path = pathp = pathbuf; + *pathp = '\0'; + lastpathp = &path[sizeof pathbuf - 2]; + 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; + free(bp->b_name); + free(bp); + } + prev = NULL; + for (n = 0; n < argc; n++) { + bp = ALLOC(block); + if (bp == NULL) + fatal("ran out of memory\n"); + bp->b_type = NAME; + bp->b_next = bp->b_args = NULL; + bp->b_name = argv[n]; + if (prev == NULL) + list = prev = bp; + else { + prev->b_next = bp; + prev = bp; + } + } return(list); } @@ -146,9 +149,387 @@ makestr(bp, head, tail) /* * If there are any Shell meta characters in the name, * expand into a list, after searching directory - * For now, only do ~name. */ -shexpand(buf, file) +expsh(s) + register char *s; +{ + register int i; + register int oargc = argc; + + if (!strcmp(s, "{") || !strcmp(s, "{}")) { + Cat(s, ""); + sort(); + return; + } + + pathp = path; + *pathp = 0; + expany = 0; + expstr(s); + if (argc != oargc) + sort(); +} + +/* + * Bubble sort any new entries + */ +sort() +{ + register char **p1, **p2, *c; + char **ap = &argv[argc]; + + p1 = sortbase; + while (p1 < ap-1) { + p2 = p1; + while (++p2 < ap) + if (strcmp(*p1, *p2) > 0) + c = *p1, *p1 = *p2, *p2 = c; + p1++; + } + sortbase = ap; +} + +expstr(s) + char *s; +{ + register char *cp; + register char *spathp, *oldcp; + struct stat stb; + + spathp = pathp; + cp = s; + while (!any(*cp, shchars)) { + if (*cp == '\0') { + if (!expany) + Cat(path, ""); + else if (stat(path, &stb) >= 0) { + Cat(path, ""); + argcnt++; + } + goto endit; + } + addpath(*cp++); + } + oldcp = cp; + while (cp > s && *cp != '/') + cp--, pathp--; + if (*cp == '/') + cp++, pathp++; + *pathp = '\0'; + if (*oldcp == '{') { + execbrc(cp, NULL); + return; + } + matchdir(cp); +endit: + pathp = spathp; + *pathp = '\0'; +} + +matchdir(pattern) + char *pattern; +{ + struct stat stb; + register struct direct *dp; + DIR *dirp; + register int cnt; + + dirp = opendir(path); + if (dirp == NULL) { + if (expany) + return; + goto patherr2; + } + if (fstat(dirp->dd_fd, &stb) < 0) + goto patherr1; + if ((stb.st_mode & S_IFMT) != S_IFDIR) { + errno = ENOTDIR; + goto patherr1; + } + while ((dp = readdir(dirp)) != NULL) + if (match(dp->d_name, pattern)) { + Cat(path, dp->d_name); + argcnt++; + } + closedir(dirp); + return; + +patherr1: + closedir(dirp); +patherr2: + fatal("%s: %s\n", path, sys_errlist[errno]); +} + +execbrc(p, s) + char *p, *s; +{ + char restbuf[BUFSIZ + 2]; + register char *pe, *pm, *pl; + int brclev = 0; + char *lm, savec, *spathp; + + for (lm = restbuf; *p != '{'; *lm++ = *p++) + continue; + for (pe = ++p; *pe; pe++) + switch (*pe) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev == 0) + goto pend; + brclev--; + continue; + + case '[': + for (pe++; *pe && *pe != ']'; pe++) + continue; + if (!*pe) + fatal("Missing ]\n"); + continue; + } +pend: + if (brclev || !*pe) + fatal("Missing }\n"); + for (pl = pm = p; pm <= pe; pm++) + switch (*pm & (QUOTE|TRIM)) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev) { + brclev--; + continue; + } + goto doit; + + case ',': + if (brclev) + continue; +doit: + savec = *pm; + *pm = 0; + strcpy(lm, pl); + strcat(restbuf, pe + 1); + *pm = savec; + if (s == 0) { + spathp = pathp; + expstr(restbuf); + pathp = spathp; + *pathp = 0; + } else if (amatch(s, restbuf)) + return (1); + sort(); + pl = pm + 1; + continue; + + case '[': + for (pm++; *pm && *pm != ']'; pm++) + continue; + if (!*pm) + fatal("Missing ]\n"); + continue; + } + return (0); +} + +match(s, p) + char *s, *p; +{ + register int c; + register char *sentp; + char sexpany = expany; + + if (*s == '.' && *p != '.') + return (0); + sentp = entp; + entp = s; + c = amatch(s, p); + entp = sentp; + expany = sexpany; + return (c); +} + +amatch(s, p) + register char *s, *p; +{ + register int scc; + int ok, lc; + char *spathp; + struct stat stb; + int c, cc; + + expany = 1; + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '{': + return (execbrc(p - 1, s - 1)); + + case '[': + ok = 0; + lc = 077777; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + fatal("Missing ]\n"); + continue; + + case '*': + if (!*p) + return (1); + if (*p == '/') { + p++; + goto slash; + } + for (s--; *s; s++) + if (amatch(s, p)) + return (1); + return (0); + + case '\0': + return (scc == '\0'); + + default: + if (c != scc) + return (0); + continue; + + case '?': + if (scc == '\0') + return (0); + continue; + + case '/': + if (scc) + return (0); +slash: + s = entp; + spathp = pathp; + while (*s) + addpath(*s++); + addpath('/'); + if (stat(path, &stb) == 0 && + (stb.st_mode & S_IFMT) == S_IFDIR) + if (*p == '\0') { + Cat(path, ""); + argcnt++; + } else + expstr(p); + pathp = spathp; + *pathp = '\0'; + return (0); + } + } +} + +smatch(s, p) + register char *s, *p; +{ + register int scc; + int ok, lc; + int c, cc; + + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '[': + ok = 0; + lc = 077777; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + fatal("Missing ]\n"); + continue; + + case '*': + if (!*p) + return (1); + for (s--; *s; s++) + if (smatch(s, p)) + return (1); + return (0); + + case '\0': + return (scc == '\0'); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + } + } +} + +Cat(s1, s2) + register char *s1, *s2; +{ + int len = strlen(s1) + strlen(s2) + 1; + register char *s, *ep; + + nleft -= len; + if (nleft <= 0 || ++argc >= GAVSIZ) + fatal("Arguments too long\n"); + argv[argc] = 0; + argv[argc - 1] = s = (char *) malloc(len); + if (s == NULL) + fatal("ran out of memory\n"); + while (*s++ = *s1++ & TRIM) + ; + s--; + while (*s++ = *s2++ & TRIM) + ; +} + +addpath(c) + char c; +{ + + if (pathp >= lastpathp) + fatal("Pathname too long\n"); + *pathp++ = c; + *pathp = '\0'; +} + +/* + * Expand file names beginning with `~' into the + * user's home directory path name. + */ +exptilde(buf, file) char buf[]; register char *file; { @@ -171,10 +552,9 @@ shexpand(buf, file) *s3 = '\0'; else s3 = NULL; - setpwent(); pw = getpwnam(file); if (pw == NULL) { - error("unknown user %s\n", file); + fatal("unknown user %s\n", file); if (s3 != NULL) *s3 = '/'; return; diff --git a/usr/src/usr.bin/rdist/gram.y b/usr/src/usr.bin/rdist/gram.y index c9f12c3ffb..34a7c08d00 100644 --- a/usr/src/usr.bin/rdist/gram.y +++ b/usr/src/usr.bin/rdist/gram.y @@ -1,23 +1,25 @@ %{ #ifndef lint -static char *sccsid = "@(#)gram.y 4.1 (Berkeley) 83/09/07"; +static char *sccsid = "@(#)gram.y 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" -struct block *last; +struct block *lastn; +struct block *lastc; %} %term EQUAL 1 -%term ARROW 2 -%term LP 3 -%term RP 4 -%term NAME 5 -%term INSTALL 6 -%term VERIFY 7 -%term NOTIFY 8 -%term EXCEPT 9 +%term LP 2 +%term RP 3 +%term ARROW 4 +%term DCOLON 5 +%term NAME 6 +%term INSTALL 7 +%term VERIFY 8 +%term NOTIFY 9 +%term EXCEPT 10 %union { @@ -34,10 +36,13 @@ file: /* VOID */ command: NAME EQUAL namelist = { $1->b_args = $3; - (void) lookup($1->b_name, 1, $1); + (void) lookup($1->b_name, $1, 1); } | namelist ARROW namelist cmdlist = { - docmd($1, $3, $4); + dohcmds($1, $3, $4); + } + | namelist DCOLON namelist cmdlist = { + dofcmds($1, $3, $4); } | error ; @@ -51,49 +56,53 @@ namelist: NAME = { ; names: /* VOID */ { - $$ = last = NULL; + $$ = lastn = NULL; } | names NAME = { - if (last == NULL) - $$ = last = $2; + if (lastn == NULL) + $$ = lastn = $2; else { - last->b_next = $2; - last = $2; + lastn->b_next = $2; + lastn = $2; $$ = $1; } } ; cmdlist: /* VOID */ { - $$ = last = NULL; + $$ = lastc = NULL; } | cmdlist cmd = { - if (last == NULL) - $$ = last = $2; + if (lastc == NULL) + $$ = lastc = $2; else { - last->b_next = $2; - last = $2; + lastc->b_next = $2; + lastc = $2; $$ = $1; } } ; cmd: INSTALL NAME = { - $1->b_name = $2->b_name; + register struct block *b; + + inst_name: + b = expand($2, 0); + if (b == NULL || b->b_next != NULL) + fatal("exactly one name allowed\n"); + $1->b_name = b->b_name; free($2); $$ = $1; } | VERIFY NAME = { - $1->b_name = $2->b_name; - free($2); - $$ = $1; + goto inst_name; } | NOTIFY namelist = { - $1->b_args = $2; + $1->b_args = expand($2, 1); $$ = $1; } | EXCEPT namelist = { - $1->b_args = expand($2); + $1->b_args = expand($2, 0); $$ = $1; } ; @@ -109,82 +118,85 @@ yylex() register int c; register char *cp1, *cp2; register struct block *bp; + static char quotechars[] = "[]{}*?"; - for (;;) { - switch (c = getc(fin)) { - case EOF: /* end of file */ +again: + switch (c = getc(fin)) { + case EOF: /* end of file */ + return(0); + + case '#': /* start of comment */ + while ((c = getc(fin)) != EOF && c != '\n') + ; + if (c == EOF) return(0); - - case '#': /* start of comment */ - while ((c = getc(fin)) != EOF && c != '\n') - ; - if (c == EOF) - return(0); - case '\n': - yylineno++; - case ' ': - case '\t': /* skip blanks */ - continue; - - case '=': /* EQUAL */ - return(EQUAL); - - case '(': /* LP */ - return(LP); - - case ')': /* RP */ - return(RP); - - case '-': /* -> */ - if ((c = getc(fin)) == '>') - return(ARROW); - ungetc(c, fin); - c = '-'; - } - /* - * Start of a name. - */ - cp1 = yytext; - cp2 = &yytext[INMAX - 1]; - for (;;) { - if (cp1 >= cp2) { - fatal("input line too long\n"); - break; - } - *cp1++ = c; - c = getc(fin); - if (c == EOF || any(c, " \t()=\n")) { - ungetc(c, fin); + case '\n': + yylineno++; + case ' ': + case '\t': /* skip blanks */ + goto again; + + case '=': /* EQUAL */ + return(EQUAL); + + case '(': /* LP */ + return(LP); + + case ')': /* RP */ + return(RP); + + case '-': /* -> */ + if ((c = getc(fin)) == '>') + return(ARROW); + ungetc(c, fin); + c = '-'; + break; + + case ':': /* :: */ + if ((c = getc(fin)) == ':') + return(DCOLON); + ungetc(c, fin); + c = ':'; + } + /* + * Start of a name. + */ + cp1 = yytext; + cp2 = &yytext[INMAX - 1]; + for (;;) { + if (cp1 >= cp2) { + fatal("input line too long\n"); + break; + } + if (c == '\\') { + if ((c = getc(fin)) != EOF) { + if (any(c, quotechars)) + c |= QUOTE; + } else { + *cp1++ = '\\'; break; } } - *cp1 = '\0'; - yylval.blk = bp = ALLOC(block); - if (bp == NULL) - fatal("ran out of memory\n"); - if (!strcmp(yytext, "install")) - c = INSTALL; - else if (!strcmp(yytext, "verify")) - c = VERIFY; - else if (!strcmp(yytext, "notify")) - c = NOTIFY; - else if (!strcmp(yytext, "except")) - c = EXCEPT; - else - c = NAME; - bp->b_type = c; - bp->b_next = bp->b_args = NULL; - if (c == NAME) { - c = strlen(yytext) + 1; - bp->b_name = cp1 = (char *) malloc(c); - if (cp1 == NULL) - fatal("ran out of memory\n"); - for (cp2 = yytext; *cp1++ = *cp2++; ) - ; - } else - bp->b_name = NULL; - return(bp->b_type); + *cp1++ = c; + c = getc(fin); + if (c == EOF || any(c, " \t()=\n")) { + ungetc(c, fin); + break; + } } + *cp1 = '\0'; + if (!strcmp(yytext, "install")) + c = INSTALL; + else if (!strcmp(yytext, "verify")) + c = VERIFY; + else if (!strcmp(yytext, "notify")) + c = NOTIFY; + else if (!strcmp(yytext, "except")) + c = EXCEPT; + else + c = NAME; + yylval.blk = bp = makeblock(c, yytext); + return(c); } any(c, str) diff --git a/usr/src/usr.bin/rdist/lookup.c b/usr/src/usr.bin/rdist/lookup.c index 08840e526a..6a5836c19b 100644 --- a/usr/src/usr.bin/rdist/lookup.c +++ b/usr/src/usr.bin/rdist/lookup.c @@ -1,5 +1,5 @@ #ifndef lint -static char *sccsid = "@(#)lookup.c 4.1 (Berkeley) 83/09/27"; +static char *sccsid = "@(#)lookup.c 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" diff --git a/usr/src/usr.bin/rdist/main.c b/usr/src/usr.bin/rdist/main.c index 80c52ae219..6399478f29 100644 --- a/usr/src/usr.bin/rdist/main.c +++ b/usr/src/usr.bin/rdist/main.c @@ -1,5 +1,5 @@ #ifndef lint -static char *sccsid = "@(#)main.c 4.1 (Berkeley) 83/09/07"; +static char *sccsid = "@(#)main.c 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" @@ -9,7 +9,9 @@ static char *sccsid = "@(#)main.c 4.1 (Berkeley) 83/09/07"; */ char *distfile = "distfile"; -char *tmpfile = "/tmp/rdistXXXXXX"; +char tmpfile[] = "/tmp/rdistAXXXXXX"; +char *tmpname = &tmpfile[5]; +char *tmpinc = &tmpfile[10]; int debug; /* debugging flag */ int nflag; /* NOP flag, just print commands without executing */ @@ -27,7 +29,8 @@ int errs; /* number of errors while sending/receiving */ char user[10]; /* user's name */ char homedir[128]; /* user's home directory */ int userid; /* user's user ID */ -int usergid; /* user's group ID */ +int groupid; /* user's group ID */ +int iamupdate; int cleanup(); int lostconn(); @@ -39,16 +42,22 @@ main(argc, argv) register char *arg; register struct passwd *pw; - setpwent(); + arg = rindex(argv[0], '/'); + if (arg == NULL) + arg = argv[0]; + else + arg++; + if (!strcmp(arg, "update")) + iamupdate++; + pw = getpwuid(userid = getuid()); - endpwent(); if (pw == NULL) { - fprintf(stderr, "rdist: Who are you?\n"); + fprintf(stderr, "%s: Who are you?\n", argv[0]); exit(1); } strcpy(user, pw->pw_name); strcpy(homedir, pw->pw_dir); - usergid = pw->pw_gid; + groupid = pw->pw_gid; gethostname(host, sizeof(host)); while (--argc > 0) { @@ -67,6 +76,12 @@ main(argc, argv) break; case 'd': + if (--argc <= 0) + usage(); + define(*++argv); + break; + + case 'D': debug++; break; @@ -90,31 +105,102 @@ main(argc, argv) usage(); } } + + mktemp(tmpfile); signal(SIGPIPE, lostconn); if (iamremote) { server(); exit(errs); } - filec = argc; - filev = argv; - if (fin == NULL && (fin = fopen(distfile, "r")) == NULL) { - perror(distfile); - exit(1); - } - mktemp(tmpfile); signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); - yyparse(); + if (iamupdate) + doupdate(argc, argv); + else { + filec = argc; + filev = argv; + if (fin == NULL && (fin = fopen(distfile, "r")) == NULL) { + perror(distfile); + exit(1); + } + yyparse(); + } + exit(errs); } usage() { - printf("Usage: rdist [-f distfile] [-n] [-q] [-y] [-d] [file ...]\n"); + printf("Usage: rdist [-f distfile] [-d var=value] [-nqyD] [file ...]\n"); + exit(1); +} + +/* + * rcp like interface for distributing files. + */ +doupdate(nargs, args) + int nargs; + char *args[]; +{ + struct block *bp, *files, *hosts, *cmds, *prev; + int i, firsttime = 1; + char *pos, dest[BUFSIZ]; + + if (nargs < 2) + upusage(); + + prev = NULL; + bp = files = ALLOC(block); + for (i = 0; i < nargs - 1; bp = ALLOC(block), i++) { + bp->b_type = NAME; + bp->b_name = args[i]; + if (prev != NULL) + prev->b_next = bp; + bp->b_next = bp->b_args = NULL; + prev = bp; + } + + hosts = ALLOC(block); + hosts->b_type = NAME; + hosts->b_name = args[i]; + hosts->b_name = args[i]; + hosts->b_next = hosts->b_args = NULL; + if ((pos = index(hosts->b_name, ':')) != NULL) { + *pos++ = '\0'; + strcpy(dest, pos); + } else + dest[0] = '\0'; + + hosts = expand(hosts, 0); + + if (dest[0] == '\0') + cmds = NULL; + else { + cmds = ALLOC(block); + if (vflag) + cmds->b_type = VERIFY; + else + cmds->b_type = INSTALL; + cmds->b_name = dest; + cmds->b_next = cmds->b_args = NULL; + } + + if (debug) { + printf("doupdate()\nfiles = "); + prnames(files); + printf("hosts = "); + prnames(hosts); + } + dohcmds(files, hosts, cmds); +} + +upusage() +{ + printf("Usage: update [-nqyD] source [...] machine[:dest]\n"); exit(1); } diff --git a/usr/src/usr.bin/rdist/server.c b/usr/src/usr.bin/rdist/server.c index da9f0c75a5..efb827e5c3 100644 --- a/usr/src/usr.bin/rdist/server.c +++ b/usr/src/usr.bin/rdist/server.c @@ -1,16 +1,21 @@ #ifndef lint -static char *sccsid = "@(#)server.c 4.1 (Berkeley) 83/09/07"; +static char *sccsid = "@(#)server.c 4.2 (Berkeley) 83/09/27"; #endif #include "defs.h" #define ga() (void) write(rem, "", 1) -char buf[BUFSIZ]; /* gerneral purpose buffer */ +char buf[BUFSIZ]; /* general purpose buffer */ char target[BUFSIZ]; /* target/source directory name */ char *tp; /* pointer to end of target name */ int catname; /* cat name to target name */ +static struct passwd *p = NULL; +static struct group *g = NULL; + +extern FILE *lfp; /* log file for mailing changes */ + /* * Server routine to read requests and process them. * Commands are: @@ -40,7 +45,7 @@ server() do { if (read(rem, cp, 1) != 1) lostconn(); - } while (*cp++ != '\n'); + } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]); *--cp = '\0'; cp = cmdbuf; switch (*cp++) { @@ -66,8 +71,13 @@ server() continue; case 'T': /* init target file/directory name */ + catname = 1; /* target should be directory */ + goto dotarget; + + case 't': /* init target file/directory name */ catname = 0; - shexpand(target, cp); + dotarget: + exptilde(target, cp); tp = target; while (*tp) tp++; @@ -93,13 +103,17 @@ server() case 'E': /* End. (of directory) */ *tp = '\0'; - cp = rindex(target, '/'); - if (cp == NULL || --catname < 0) { + if (--catname < 0) { error("too many 'E's\n"); continue; } - *cp = '\0'; - tp = cp; + cp = rindex(target, '/'); + if (cp == NULL) + tp = NULL; + else { + *cp = '\0'; + tp = cp; + } ga(); continue; @@ -111,6 +125,13 @@ server() query(cp); continue; + case '\1': + errs++; + continue; + + case '\2': + return; + default: error("unknown command type %s\n", cp); case '\0': @@ -127,10 +148,8 @@ sendf(name, verify) int verify; { register char *last; - struct passwd *p; - struct group *g; struct stat stb; - int sizerr, f; + int sizerr, f, u; off_t i; if (debug) @@ -139,6 +158,15 @@ sendf(name, verify) if (exclude(name)) return; + /* + * first time sendf() is called? + */ + if (tp == NULL) { + exptilde(target, name); + tp = name = target; + while (*tp) + tp++; + } if (access(name, 4) < 0 || stat(name, &stb) < 0) { error("%s: %s\n", name, sys_errlist[errno]); return; @@ -148,21 +176,19 @@ sendf(name, verify) last = name; else last++; - if (!update(last, &stb)) + if ((u = update(last, &stb)) == 0) return; - setpwent(); - if ((p = getpwuid(stb.st_uid)) == NULL) { - error("no password entry for uid %d\n", stb.st_uid); - return; - } - endpwent(); - setgrent(); - if ((g = getgrgid(stb.st_gid)) == NULL) { - error("no name for group %d\n", stb.st_gid); - return; - } - endgrent(); + if (p == NULL || p->pw_uid != stb.st_uid) + if ((p = getpwuid(stb.st_uid)) == NULL) { + error("no password entry for uid %d\n", stb.st_uid); + return; + } + if (g == NULL || g->gr_gid != stb.st_gid) + if ((g = getgrgid(stb.st_gid)) == NULL) { + error("no name for group %d\n", stb.st_gid); + return; + } switch (stb.st_mode & S_IFMT) { case S_IFREG: @@ -177,15 +203,15 @@ sendf(name, verify) return; } - if ((f = open(name, 0)) < 0) { - error("%s: %s\n", name, sys_errlist[errno]); - return; - } - log("updating: %s\n", name); + log(lfp, "%s: %s\n", u == 2 ? "updating" : "installing", name); if (verify || vflag) return; + if ((f = open(name, 0)) < 0) { + error("%s: %s\n", name, sys_errlist[errno]); + return; + } (void) sprintf(buf, "R%04o %D %D %s %s %s\n", stb.st_mode & 07777, stb.st_size, stb.st_mtime, p->pw_name, g->gr_name, last); if (debug) @@ -222,6 +248,7 @@ rsendf(name, verify, st, owner, group) struct direct *dp; register char *last; char *otp; + int len; if (debug) printf("rsendf(%s, %d, %x, %s, %s)\n", name, verify, st, @@ -245,23 +272,12 @@ rsendf(name, verify, st, owner, group) closedir(d); return; } - /* - * first time rsendf() is called? - */ - if (tp == NULL) { - tp = target; - last = name; - while (*tp++ = *last++) - ; - tp--; - } otp = tp; + len = tp - target; while (dp = readdir(d)) { - if (dp->d_ino == 0) - continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; - if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { + if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { error("%s/%s: Name too long\n", name, dp->d_name); continue; } @@ -282,6 +298,7 @@ rsendf(name, verify, st, owner, group) /* * Check to see if file needs to be updated on the remote machine. + * Returns 0 if no update, 1 if remote doesn't exist, and 2 if out of date. */ update(name, st) char *name; @@ -305,7 +322,7 @@ update(name, st) do { if (read(rem, cp, 1) != 1) lostconn(); - } while (*cp++ != '\n'); + } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); *--cp = '\0'; cp = buf; if (debug) @@ -315,11 +332,19 @@ update(name, st) case 'Y': break; - case 'N': /* file doesn't exist so update it */ + case 'N': /* file doesn't exist so install it */ return(1); case '\1': - error("%s\n", cp); + errs++; + if (*cp != '\0') { + if (!iamremote) { + fflush(stdout); + (void) write(2, cp, strlen(cp)); + } + if (lfp != NULL) + (void) fwrite(cp, 1, strlen(cp), lfp); + } return(0); default: @@ -329,9 +354,8 @@ update(name, st) if (*cp == '\0') { if ((st->st_mode & S_IFMT) == S_IFDIR) - return(1); - error("file -> directory\n"); - return(0); + return(2); + return(1); } size = 0; @@ -352,10 +376,9 @@ update(name, st) * File needs to be updated? */ if (st->st_mtime == mtime && st->st_size == size || - yflag && st->st_mtime < mtime) { + yflag && st->st_mtime < mtime) return(0); - } - return(1); + return(2); } /* @@ -407,6 +430,7 @@ recvf(cmd, isdir) struct timeval tvp[2]; char *owner, *group, *dir; char new[BUFSIZ]; + extern char *tmpname; mode = 0; for (cp = cmd; cp < cmd+4; cp++) { @@ -451,6 +475,7 @@ recvf(cmd, isdir) } *cp++ = '\0'; + new[0] = '\0'; if (isdir) { if (catname++) { *tp++ = '/'; @@ -458,6 +483,10 @@ recvf(cmd, isdir) ; tp--; } + if (vflag) { + ga(); + return; + } if (stat(target, &stb) == 0) { if ((stb.st_mode & S_IFMT) != S_IFDIR) { errno = ENOTDIR; @@ -477,16 +506,15 @@ recvf(cmd, isdir) dir = target; *cp = '\0'; } - f = access(dir, 2); + if (access(dir, 2) < 0) + goto bad2; if (cp != NULL) *cp = '/'; - if (f < 0) - goto bad; if (mkdir(target, mode) < 0) goto bad; + if (chog(target, owner, group, mode) < 0) + return; } - if (chog(target, owner, group) < 0) - return; ga(); return; } @@ -522,17 +550,23 @@ recvf(cmd, isdir) dir = target; *cp = '\0'; } - (void) sprintf(new, "%s/rdistXXXXXX", dir); - mktemp(new); - f = access(dir, 2); + if (access(dir, 2) < 0) { +bad2: + error("%s: %s\n", dir, sys_errlist[errno]); + if (cp != NULL) + *cp = '/'; + return; + } + (void) sprintf(new, "%s/%s", dir, tmpname); if (cp != NULL) *cp = '/'; - if (f < 0) - goto bad; if ((f = creat(new, mode)) < 0) goto bad1; - if (chog(new, owner, group) < 0) + if (chog(new, owner, group, mode) < 0) { + (void) close(f); + (void) unlink(new); return; + } ga(); wrerr = 0; @@ -545,8 +579,11 @@ recvf(cmd, isdir) do { int j = read(rem, cp, amt); - if (j <= 0) + if (j <= 0) { + (void) close(f); + (void) unlink(new); cleanup(); + } amt -= j; cp += j; } while (amt > 0); @@ -561,6 +598,8 @@ recvf(cmd, isdir) (void) response(); if (wrerr) { error("%s: %s\n", cp, sys_errlist[olderrno]); + (void) close(f); + (void) unlink(new); return; } @@ -576,12 +615,16 @@ recvf(cmd, isdir) if (utimes(new, tvp) < 0) { bad1: error("%s: %s\n", new, sys_errlist[errno]); + if (new[0]) + (void) unlink(new); return; } if (rename(new, target) < 0) { bad: error("%s: %s\n", target, sys_errlist[errno]); + if (new[0]) + (void) unlink(new); return; } ga(); @@ -590,38 +633,44 @@ bad: /* * Change owner and group of file. */ -chog(file, owner, group) +chog(file, owner, group, mode) char *file, *owner, *group; + int mode; { - extern int userid, usergid; - extern char *user; - struct passwd *p; - struct group *g; + extern int userid, groupid; + extern char user[]; register int i; int uid, gid; uid = userid; if (userid == 0) { - p = getpwnam(owner); - if (p == NULL) { - error("%s: unknown login name\n", owner); - return(-1); - } - uid = p->pw_uid; - } - setgrent(); - g = getgrnam(group); - if (g == NULL) { - error("%s: unknown group\n", group); - return(-1); - } - gid = g->gr_gid; - if (userid && usergid != gid) { - for (i = 0; g->gr_mem[i]; i++) + if (p == NULL || strcmp(owner, p->pw_name) != 0) { + if ((p = getpwnam(owner)) == NULL) { + if (mode & 04000) { + error("%s: unknown login name\n", owner); + return(-1); + } + } else + uid = p->pw_uid; + } else + uid = p->pw_uid; + } + gid = groupid; + if (g == NULL || strcmp(group, g->gr_name) != 0) { + if ((g = getgrnam(group)) == NULL) { + if (mode & 02000) { + error("%s: unknown group\n", group); + return(-1); + } + } else + gid = g->gr_gid; + } else + gid = g->gr_gid; + if (userid && groupid != gid) { + for (i = 0; g->gr_mem[i] != NULL; i++) if (!(strcmp(user, g->gr_mem[i]))) goto ok; - error("You are not a member of the %s group\n", group); - return(-1); + gid = groupid; } ok: if (chown(file, uid, gid) < 0) { @@ -631,10 +680,9 @@ ok: return(0); } -extern FILE *lfp; /* log file for mailing changes */ - /*VARARGS*/ -log(fmt, a1, a2, a3) +log(fp, fmt, a1, a2, a3) + FILE *fp; char *fmt; int a1, a2, a3; { @@ -643,8 +691,8 @@ log(fmt, a1, a2, a3) printf(fmt, a1, a2, a3); /* Save changes (for mailing) if really updating files */ - if (!vflag) - fprintf(lfp, fmt, a1, a2, a3); + if (!vflag && fp != NULL) + fprintf(fp, fmt, a1, a2, a3); } /*VARARGS*/ @@ -657,8 +705,10 @@ error(fmt, a1, a2, a3) (void) sprintf(buf+8, fmt, a1, a2, a3); (void) write(rem, buf, strlen(buf)); if (buf[1] != '\0') { - if (!iamremote) + if (!iamremote) { + fflush(stdout); (void) write(2, buf+1, strlen(buf+1)); + } if (lfp != NULL) (void) fwrite(buf+1, 1, strlen(buf+1), lfp); } @@ -674,8 +724,10 @@ fatal(fmt, a1, a2,a3) (void) sprintf(buf+8, fmt, a1, a2, a3); (void) write(rem, buf, strlen(buf)); if (buf[1] != '\0') { - if (!iamremote) + if (!iamremote) { + fflush(stdout); (void) write(2, buf+1, strlen(buf+1)); + } if (lfp != NULL) (void) fwrite(buf+1, 1, strlen(buf+1), lfp); } @@ -705,10 +757,12 @@ response() do { if (read(rem, cp, 1) != 1) lostconn(); - } while (cp < &buf[BUFSIZ] && *cp++ != '\n'); - if (buf[1] != '\0') { - if (!iamremote) + } while (*cp++ != '\n' && cp < &buf[BUFSIZ]); + if (buf[0] != '\n') { + if (!iamremote) { + fflush(stdout); (void) write(2, buf, cp - buf); + } if (lfp != NULL) (void) fwrite(buf, 1, cp - buf, lfp); } -- 2.20.1