home *** CD-ROM | disk | FTP | other *** search
Text File | 1987-10-25 | 28.6 KB | 1,511 lines |
-
- /*
- * linkleft()
- *
- * Complain about files with unseen links.
- */
- STATIC void
- linkleft()
- {
- reg Link *lp;
- reg Link **base;
-
- for (base = linkbase; base < linkbase + nel(linkbase); ++base)
- for (lp = *base; lp; lp = lp->l_forw)
- if (lp->l_nlink)
- VOID warn(lp->l_path->p_name, "Unseen link(s)");
- }
-
- /*
- * linkto()
- *
- * Remember a file with outstanding links. Returns a
- * pointer to the associated link structure, or NULL
- * when linking is not possible.
- */
- STATIC Link *
- linkto(name, asb)
- char *name;
- reg Stat *asb;
- {
- reg Link *linkp;
- reg Path *path;
- reg Link **abase;
-
- if ((asb->sb_mode & S_IFMT) == S_IFDIR
- || (linkp = (Link *) memget(sizeof(Link))) == NULL
- || (path = (Path *) memget(sizeof(Path))) == NULL
- || (path->p_name = memstr(name)) == NULL)
- return (NULL);
- linkp->l_dev = asb->sb_dev;
- linkp->l_ino = asb->sb_ino;
- linkp->l_nlink = asb->sb_nlink - 1;
- linkp->l_size = asb->sb_size;
- linkp->l_path = path;
- path->p_forw = NULL;
- path->p_back = path;
- if (linkp->l_forw = *(abase = linkhash(asb->sb_ino)))
- linkp->l_forw->l_back = linkp;
- linkp->l_back = NULL;
- return (*abase = linkp);
- }
-
- #ifndef MEMCPY
-
- /*
- * memcpy()
- *
- * A simple block move.
- */
- STATIC void
- memcpy(to, from, len)
- reg char *to;
- reg char *from;
- uint len;
- {
- reg char *toend;
-
- for (toend = to + len; to < toend; *to++ = *from++)
- ;
- }
-
- #endif /* MEMCPY */
-
- /*
- * memget()
- *
- * Allocate memory.
- */
- STATIC char *
- memget(len)
- uint len;
- {
- reg char *mem;
- static short outofmem;
-
- if ((mem = malloc(len)) == NULL && !outofmem)
- outofmem = warn("memget()", "Out of memory");
- return (mem);
- }
-
- /*
- * memstr()
- *
- * Duplicate a string into dynamic memory.
- */
- STATIC char *
- memstr(str)
- reg char *str;
- {
- reg char *mem;
-
- if (mem = memget((uint) strlen(str) + 1))
- VOID strcpy(mem, str);
- return (mem);
- }
-
- #ifndef MKDIR
-
- /*
- * mkdir()
- *
- * Make a directory via "/bin/mkdir". Sets errno to a
- * questionably sane value upon failure.
- */
- STATIC int
- mkdir(name, mode)
- reg char *name;
- reg ushort mode;
- {
- reg int pid;
-
- if ((pid = xfork("mkdir()")) == 0) {
- VOID close(fileno(stdin));
- VOID close(fileno(stdout));
- VOID close(fileno(stderr));
- VOID open("/dev/null", O_RDWR);
- VOID dup(fileno(stdin));
- VOID dup(fileno(stdin));
- VOID umask(~mode);
- VOID execl("/bin/mkdir", "mkdir", name, (char *) NULL);
- exit(1);
- }
- if (xwait(pid, "mkdir()") == 0)
- return (0);
- errno = EACCES;
- return (-1);
- }
-
- #endif /* MKDIR */
-
- /*
- * nameadd()
- *
- * Add a name to the pattern list.
- */
- STATIC void
- nameadd(name, not)
- reg char *name;
- int not;
- {
- reg Pattern *px;
-
- px = (Pattern *) memget(sizeof(Pattern));
- px->p_forw = pattern;
- px->p_str = name;
- px->p_len = strlen(name);
- px->p_not = not;
- pattern = px;
- }
-
- /*
- * namecmp()
- *
- * Compare a pathname with the pattern list. Returns 0 for
- * a match, -1 otherwise.
- */
- STATIC int
- namecmp(name)
- reg char *name;
- {
- reg Pattern *px;
- reg int positive;
- reg int match;
-
- positive = match = 0;
- for (px = pattern; px; px = px->p_forw) {
- if (!px->p_not)
- ++positive;
- if (strncmp(name, px->p_str, px->p_len) == 0
- && (name[px->p_len] == '/' || name[px->p_len] == '\0')) {
- if (px->p_not)
- return (-1);
- ++match;
- }
- }
- return (match || !positive ? 0 : -1);
- }
-
- /*
- * nameopt()
- *
- * Optimize a pathname. Confused by "<symlink>/.." twistiness.
- * Returns the number of final pathname elements (zero for "/"
- * or ".") or -1 if unsuccessful.
- */
- STATIC int
- nameopt(begin)
- char *begin;
- {
- reg char *name;
- reg char *item;
- reg int idx;
- int absolute;
- auto char *element[PATHELEM];
-
- absolute = (*(name = begin) == '/');
- idx = 0;
- for (;;) {
- if (idx == PATHELEM)
- return (warn(begin, "Too many elements"));
- while (*name == '/')
- ++name;
- if (*name == '\0')
- break;
- element[idx] = item = name;
- while (*name && *name != '/')
- ++name;
- if (*name)
- *name++ = '\0';
- if (strcmp(item, "..") == 0)
- if (idx == 0)
- if (absolute)
- ;
- else
- ++idx;
- else if (strcmp(element[idx - 1], "..") == 0)
- ++idx;
- else
- --idx;
- else if (strcmp(item, ".") != 0)
- ++idx;
- }
- if (idx == 0)
- element[idx++] = absolute ? "" : ".";
- element[idx] = NULL;
- name = begin;
- if (absolute)
- *name++ = '/';
- for (idx = 0; item = element[idx]; ++idx, *name++ = '/')
- while (*item)
- *name++ = *item++;
- *--name = '\0';
- return (idx);
- }
-
- /*
- * next()
- *
- * Advance to the next archive volume.
- */
- STATIC void
- next(mode, why)
- reg int mode;
- reg char *why;
- {
- reg time_t began;
- auto char msg[200];
- auto char answer[20];
-
- began = time((time_t *) NULL);
- nextclos();
- VOID warnarch(why, (off_t) 0);
- if (arfd == STDIN || arfd == STDOUT)
- exit(1);
- VOID sprintf(msg, "\
- %s: Ready for volume %u on \"%s\"\n\
- %s: Type \"go\" when ready to proceed (or \"quit\" to abort): \07",
- myname, arvolume + 1, arspec, myname);
- for (;;) {
- nextask(msg, answer, sizeof(answer));
- if (strcmp(answer, "quit") == 0)
- fatal(arspec, "Aborted");
- if (strcmp(answer, "go") == 0 && nextopen(mode) == 0)
- break;
- }
- VOID warnarch("Continuing", (off_t) 0);
- timewait += time((time_t *) NULL) - began;
- }
-
- /*
- * nextask()
- *
- * Ask a question and get a response. Ignores spaces and tabs.
- */
- STATIC void
- nextask(msg, answer, limit)
- reg char *msg;
- reg char *answer;
- reg int limit;
- {
- reg int idx;
- reg int got;
- auto char c;
-
- if (ttyf < 0)
- fatal(TTY, "Unavailable");
- VOID write(ttyf, msg, (uint) strlen(msg));
- idx = 0;
- while ((got = read(ttyf, &c, 1)) == 1)
- if (c == '\04' || c == '\n')
- break;
- else if (c == ' ' || c == '\t')
- continue;
- else if (idx < limit - 1)
- answer[idx++] = c;
- if (got < 0)
- fatal(TTY, syserr());
- answer[idx] = '\0';
- }
-
- /*
- * nextclos()
- *
- * Close an archive.
- */
- STATIC void
- nextclos()
- {
- if (arfd != STDIN && arfd != STDOUT)
- VOID close(arfd);
- areof = 0;
- if (arname && *arname == '!')
- pipewait();
- }
-
- /*
- * nextopen()
- *
- * Open an archive. Returns 0 if successful, -1 otherwise.
- */
- STATIC int
- nextopen(mode)
- int mode;
- {
- if (*arname == '!')
- arfd = pipeopen(mode);
- else if (strcmp(arname, "-") == 0)
- arfd = mode ? STDOUT : STDIN;
- else {
- #ifdef CTC3B2
- if (Cflag) {
- reg int oops;
- reg int fd;
-
- oops = ((fd = open(arname, O_RDWR | O_CTSPECIAL)) < 0
- || ioctl(fd, STREAMON) < 0);
- VOID close(fd);
- if (oops)
- return (warnarch(syserr(), (off_t) 0));
- }
- #endif /* CTC3B2 */
- arfd = mode ? creat(arname, 0666 & ~mask) : open(arname, mode);
- }
- if (arfd < 0)
- return (warnarch(syserr(), (off_t) 0));
- arleft = aruntil;
- ++arvolume;
- return (0);
- }
-
- /*
- * openi()
- *
- * Open the next input file. Returns a file descriptor, 0 if no data
- * exists, or -1 at EOF. This kludge works because standard input is
- * in use, preventing open() from returning zero.
- */
- STATIC int
- openi(name, asb)
- char *name;
- reg Stat *asb;
- {
- reg int fd;
- auto char local[PATHSIZE];
-
- for (;;) {
- if (lineget(stdin, name) < 0)
- return (-1);
- if (nameopt(name) < 0)
- continue;
- if (!gflag)
- VOID strcpy(local, name);
- else if (dirchg(name, local) < 0)
- continue;
- if ((hflag ? STAT(local, asb) : LSTAT(local, asb)) < 0) {
- VOID warn(name, syserr());
- continue;
- }
- switch (asb->sb_mode & S_IFMT) {
- case S_IFDIR:
- asb->sb_nlink = 1;
- asb->sb_size = 0;
- return (0);
- #ifdef S_IFLNK
- case S_IFLNK:
- if ((asb->sb_size = readlink(local,
- asb->sb_link, sizeof(asb->sb_link) - 1)) < 0) {
- VOID warn(name, syserr());
- continue;
- }
- asb->sb_link[asb->sb_size] = '\0';
- return (0);
- #endif /* S_IFLNK */
- case S_IFREG:
- if (asb->sb_size == 0)
- return (0);
- if ((fd = open(local, O_RDONLY)) >= 0)
- return (fd);
- VOID warn(name, syserr());
- break;
- default:
- asb->sb_size = 0;
- return (0);
- }
- }
- }
-
- /*
- * openo()
- *
- * Open an output file. Returns the output file descriptor,
- * 0 if no data is required or -1 if unsuccessful. Note that
- * UNIX open() will never return 0 because the standard input
- * is in use.
- */
- STATIC int
- openo(name, asb, linkp, ispass)
- char *name;
- reg Stat *asb;
- Link *linkp;
- reg int ispass;
- {
- reg int exists;
- reg int fd;
- reg ushort perm;
- ushort operm;
- Path *path;
- auto Stat osb;
- #ifdef S_IFLNK
- reg int ssize;
- auto char sname[PATHSIZE];
- #endif /* S_IFLNK */
-
- if (exists = (LSTAT(name, &osb) == 0))
- if (ispass
- && osb.sb_ino == asb->sb_ino
- && osb.sb_dev == asb->sb_dev)
- return (warn(name, "Same file"));
- else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT))
- operm = osb.sb_mode & (xflag ? S_IPERM : S_IPOPN);
- else if (remove(name, &osb) < 0)
- return (warn(name, syserr()));
- else
- exists = 0;
- if (linkp) {
- if (exists)
- if (asb->sb_ino == osb.sb_ino
- && asb->sb_dev == osb.sb_dev)
- return (0);
- else if (unlink(name) < 0)
- return (warn(name, syserr()));
- else
- exists = 0;
- for (path = linkp->l_path; path; path = path->p_forw)
- if (link(path->p_name, name) == 0
- || (errno == ENOENT
- && dirneed(name) == 0
- && link(path->p_name, name) == 0))
- return (0);
- else if (errno != EXDEV)
- return (warn(name, syserr()));
- VOID warn(name, "Link broken");
- linkalso(linkp, name);
- }
- perm = asb->sb_mode & (xflag ? S_IPERM : S_IPOPN);
- switch (asb->sb_mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
- fd = 0;
- if (exists)
- if (asb->sb_rdev == osb.sb_rdev)
- if (perm != operm && chmod(name, perm) < 0)
- return (warn(name, syserr()));
- else
- break;
- else if (remove(name, &osb) < 0)
- return (warn(name, syserr()));
- else
- exists = 0;
- if (mknod(name, asb->sb_mode, asb->sb_rdev) < 0
- && (errno != ENOENT
- || dirneed(name) < 0
- || mknod(name, asb->sb_mode, asb->sb_rdev) < 0))
- return (warn(name, syserr()));
- break;
- case S_IFDIR:
- if (exists)
- if (perm != operm && chmod(name, perm) < 0)
- return (warn(name, syserr()));
- else
- ;
- else if (dirneed(name) < 0 || dirmake(name, asb) < 0)
- return (warn(name, syserr()));
- return (0);
- #ifdef S_IFIFO
- case S_IFIFO:
- fd = 0;
- if (exists)
- if (perm != operm && chmod(name, perm) < 0)
- return (warn(name, syserr()));
- else
- ;
- else if (mknod(name, asb->sb_mode, (dev_t) 0) < 0
- && (errno != ENOENT
- || dirneed(name) < 0
- || mknod(name, asb->sb_mode, (dev_t) 0) < 0))
- return (warn(name, syserr()));
- break;
- #endif /* S_IFIFO */
- #ifdef S_IFLNK
- case S_IFLNK:
- if (exists)
- if ((ssize = readlink(name, sname, sizeof(sname))) < 0)
- return (warn(name, syserr()));
- else if (strncmp(sname, asb->sb_link, ssize) == 0)
- return (0);
- else if (remove(name, &osb) < 0)
- return (warn(name, syserr()));
- else
- exists = 0;
- if (symlink(asb->sb_link, name) < 0
- && (errno != ENOENT
- || dirneed(name) < 0
- || symlink(asb->sb_link, name) < 0))
- return (warn(name, syserr()));
- return (0); /* Can't chown()/chmod() a symbolic link */
- #endif /* S_IFLNK */
- case S_IFREG:
- if (exists)
- if (nflag && osb.sb_mtime > asb->sb_mtime)
- return (warn(name, "Newer file exists"));
- else if (unlink(name) < 0)
- return (warn(name, syserr()));
- else
- exists = 0;
- if ((fd = creat(name, perm)) < 0
- && (errno != ENOENT
- || dirneed(name) < 0
- || (fd = creat(name, perm)) < 0))
- return (warn(name, syserr()));
- break;
- default:
- return (warn(name, "Unknown filetype"));
- }
- if (xflag
- && (!exists
- || asb->sb_uid != osb.sb_uid
- || asb->sb_gid != osb.sb_gid))
- VOID chown(name,
- uid == 0 ? ush(asb->sb_uid) : uid,
- ush(asb->sb_gid));
- if (linkp == NULL && asb->sb_nlink > 1)
- VOID linkto(name, asb);
- return (fd);
- }
-
- /*
- * openq()
- *
- * Open the terminal for interactive queries (sigh). Assumes that
- * background processes ignore interrupts and that the open() or
- * the isatty() will fail for processes which are not attached to
- * terminals. Returns a file descriptor (-1 if unsuccessful).
- */
- STATIC int
- openq()
- {
- reg int fd;
- reg VOIDFN (*intr)();
-
- if ((intr = signal(SIGINT, SIG_IGN)) == SIG_IGN)
- return (-1);
- VOID signal(SIGINT, intr);
- if ((fd = open(TTY, O_RDWR)) < 0)
- return (-1);
- if (isatty(fd))
- return (fd);
- VOID close(fd);
- return (-1);
- }
-
- /*
- * options()
- *
- * Decode most reasonable forms of UNIX option syntax. Takes main()-
- * style argument indices (argc/argv) and a string of valid option
- * letters. Letters denoting options with arguments must be followed
- * by colons. With valid options, returns the option letter and points
- * "optarg" at the associated argument (if any). Returns '?' for bad
- * options and missing arguments. Returns zero when no options remain,
- * leaving "optind" indexing the first remaining argument.
- */
- STATIC int
- options(ac, av, proto)
- int ac;
- register char **av;
- char *proto;
- {
- register int c;
- register char *idx;
- static int optsub;
-
- if (optind == 0) {
- optind = 1;
- optsub = 0;
- }
- optarg = NULL;
- if (optind >= ac)
- return (0);
- if (optsub == 0 && (av[optind][0] != '-' || av[optind][1] == '\0'))
- return (0);
- switch (c = av[optind][++optsub]) {
- case '\0':
- ++optind;
- optsub = 0;
- return (options(ac, av, proto));
- case '-':
- ++optind;
- optsub = 0;
- return (0);
- case ':':
- return ('?');
- }
- if ((idx = strchr(proto, c)) == NULL)
- return ('?');
- if (idx[1] != ':')
- return (c);
- optarg = &av[optind][++optsub];
- ++optind;
- optsub = 0;
- if (*optarg)
- return (c);
- if (optind >= ac)
- return ('?');
- optarg = av[optind++];
- return (c);
- }
-
- /*
- * optsize()
- *
- * Interpret a "size" argument. Recognizes suffices for blocks
- * (512-byte), kilobytes and megabytes and blocksize. Returns
- * the size in bytes.
- */
- STATIC off_t
- optsize(str)
- char *str;
- {
- reg char *idx;
- reg off_t number;
- reg off_t result;
-
- result = 0;
- idx = str;
- for (;;) {
- number = 0;
- while (*idx >= '0' && *idx <= '9')
- number = number * 10 + *idx++ - '0';
- switch (*idx++) {
- case 'b':
- result += number * 512;
- continue;
- case 'k':
- result += number * 1024;
- continue;
- case 'm':
- result += number * 1024 * 1024;
- continue;
- case 'x':
- result += number * arbsize;
- continue;
- case '+':
- result += number;
- continue;
- case '\0':
- result += number;
- break;
- default:
- break;
- }
- break;
- }
- if (*--idx)
- fatal(str, "Unrecognizable value");
- return (result);
- }
-
- /*
- * out()
- *
- * Write an archive.
- */
- STATIC VOIDFN
- out(av)
- char **av;
- {
- reg int fd;
- auto Stat sb;
- auto char name[PATHSIZE];
-
- if (*av)
- fatal(*av, "Extraneous argument");
- while ((fd = openi(name, &sb)) >= 0) {
- if (!lflag && sb.sb_nlink > 1)
- if (linkfrom(&sb))
- sb.sb_size = 0;
- else
- VOID linkto(name, &sb);
- outhead(name, &sb);
- if (fd)
- VOID close(outdata(fd, name, sb.sb_size));
- if (vflag)
- VOID fprintf(stderr, "%s\n", name);
- }
- outeof(TRAILER, TRAILZ);
- }
-
- /*
- * outalloc()
- *
- * Allocate buffer space previously referenced by outavail().
- */
- STATIC void
- outalloc(len)
- reg uint len;
- {
- bufidx += len;
- total += len;
- }
-
- /*
- * outavail()
- *
- * Index buffer space for archive output. Stores a buffer pointer
- * at a given location. Returns the number of bytes available.
- */
- STATIC uint
- outavail(bufp)
- reg char **bufp;
- {
- reg uint have;
-
- while ((have = bufend - bufidx) == 0)
- outflush();
- *bufp = bufidx;
- return (have);
- }
-
- /*
- * outdata()
- *
- * Write archive data. Continues after file read errors, padding with
- * null characters if neccessary. Always returns the given input file
- * descriptor.
- */
- STATIC int
- outdata(fd, name, size)
- int fd;
- char *name;
- reg off_t size;
- {
- reg uint chunk;
- reg int got;
- reg int oops;
- reg uint avail;
- auto char *buf;
-
- oops = got = 0;
- while (size) {
- avail = outavail(&buf);
- size -= (chunk = size < avail ? (uint) size : avail);
- if (oops == 0 && (got = read(fd, buf, chunk)) < 0) {
- oops = warn(name, syserr());
- got = 0;
- }
- if (got < chunk) {
- if (oops == NULL)
- oops = warn(name, "Early EOF");
- while (got < chunk)
- buf[got++] = '\0';
- }
- outalloc(chunk);
- }
- return (fd);
- }
-
- /*
- * outeof()
- *
- * Write an archive trailer.
- */
- STATIC void
- outeof(name, namelen)
- char *name;
- reg uint namelen;
- {
- reg off_t pad;
- auto char header[M_STRLEN + H_STRLEN + 1];
-
- if (pad = (total + M_STRLEN + H_STRLEN + namelen) % arpad)
- pad = arpad - pad;
- VOID strcpy(header, M_ASCII);
- VOID sprintf(header + M_STRLEN, H_PRINT, 0, 0,
- 0, 0, 0, 1, 0, (time_t) 0, namelen, pad);
- outwrite(header, M_STRLEN + H_STRLEN);
- outwrite(name, namelen);
- outpad(pad);
- outflush();
- if (fflag)
- outwait();
- }
-
- /*
- * outflush()
- *
- * Flush the output buffer. Optionally fork()s to allow the
- * parent to refill the buffer while the child waits for the
- * write() to complete.
- */
- STATIC void
- outflush()
- {
- reg char *buf;
- reg int got;
- reg uint len;
-
- if (aruntil && arleft == 0)
- next(O_WRONLY, "Output limit reached");
- if (fflag) {
- outwait();
- if ((outpid = xfork("outflush()")) == 0)
- VOID nice(-40);
- }
- if (!fflag || outpid == 0) {
- for (buf = buffer; len = bufidx - buf; ) {
- if ((got = write(arfd, buf,
- *arname == '!' ? len : min(len, arbsize))) > 0) {
- buf += got;
- arleft -= got;
- } else if (fflag) {
- VOID warn(arspec, got < 0
- ? syserr()
- : "Apparently full");
- _exit(1);
- } else if (got < 0)
- fatal(arspec, syserr());
- else
- next(O_WRONLY, "Apparently full");
- }
- }
- if (fflag) {
- if (outpid == 0)
- _exit(0);
- else
- arleft -= bufidx - buffer;
- }
- bufend = (bufidx = buffer) + (aruntil ? min(buflen, arleft) : buflen);
- }
-
- /*
- * outhead()
- *
- * Write an archive header.
- */
- STATIC void
- outhead(name, asb)
- reg char *name;
- reg Stat *asb;
- {
- reg uint namelen;
- auto char header[M_STRLEN + H_STRLEN + 1];
-
- if (name[0] == '/')
- if (name[1])
- ++name;
- else
- name = ".";
- namelen = (uint) strlen(name) + 1;
- VOID strcpy(header, M_ASCII);
- VOID sprintf(header + M_STRLEN, H_PRINT, ush(asb->sb_dev),
- ush(asb->sb_ino), ush(asb->sb_mode), ush(asb->sb_uid),
- ush(asb->sb_gid), ush(asb->sb_nlink), ush(asb->sb_rdev),
- mflag ? timenow : asb->sb_mtime, namelen, asb->sb_size);
- outwrite(header, M_STRLEN + H_STRLEN);
- outwrite(name, namelen);
- #ifdef S_IFLNK
- if ((asb->sb_mode & S_IFMT) == S_IFLNK)
- outwrite(asb->sb_link, (uint) asb->sb_size);
- #endif /* S_IFLNK */
- }
-
- /*
- * outpad()
- *
- * Pad the archive.
- */
- STATIC void
- outpad(pad)
- reg off_t pad;
- {
- reg int idx;
- reg int len;
-
- while (pad) {
- if ((len = bufend - bufidx) > pad)
- len = pad;
- for (idx = 0; idx < len; ++idx)
- *bufidx++ = '\0';
- total += len;
- outflush();
- pad -= len;
- }
- }
-
- /*
- * outwait()
- *
- * Wait for the last background outflush() process (if any). The child
- * exit value is zero if successful, 255 if a write() returned zero or
- * the value of errno if a write() was unsuccessful.
- */
- STATIC void
- outwait()
- {
- auto int status;
-
- if (outpid == 0)
- return;
- status = xwait(outpid, "outwait()");
- outpid = 0;
- if (status)
- fatal(arspec, "Child error");
- }
-
- /*
- * outwrite()
- *
- * Write archive data.
- */
- STATIC void
- outwrite(idx, len)
- reg char *idx;
- uint len;
- {
- reg uint have;
- reg uint want;
- reg char *endx = idx + len;
-
- while (want = endx - idx) {
- while ((have = bufend - bufidx) == 0)
- outflush();
- if (have > want)
- have = want;
- memcpy(bufidx, idx, have);
- bufidx += have;
- idx += have;
- total += have;
- }
- }
-
- /*
- * pass()
- *
- * Copy within the filesystem.
- */
- STATIC VOIDFN
- pass(av)
- reg char **av;
- {
- reg int fd;
- reg char **avx;
- auto Stat sb;
- auto char name[PATHSIZE];
-
- for (avx = av; *avx; ++avx) {
- if (gflag && **avx != '/')
- fatal(*avx, "Relative pathname");
- if (STAT(*avx, &sb) < 0)
- fatal(*avx, syserr());
- if ((sb.sb_mode & S_IFMT) != S_IFDIR)
- fatal(*avx, "Not a directory");
- }
- while ((fd = openi(name, &sb)) >= 0) {
- if (passitem(name, &sb, fd, av))
- VOID close(fd);
- if (vflag)
- VOID fprintf(stderr, "%s\n", name);
- }
- }
-
- /*
- * passdata()
- *
- * Copy data to one file. Doesn't believe in input file
- * descriptor zero (see description of kludge in openi()
- * comments). Closes the provided output file descriptor.
- */
- STATIC void
- passdata(from, ifd, to, ofd)
- char *from;
- reg int ifd;
- char *to;
- reg int ofd;
- {
- reg int got;
- reg int sparse;
- auto char block[FSBUF];
-
- if (ifd) {
- VOID lseek(ifd, (off_t) 0, 0);
- sparse = 0;
- while ((got = read(ifd, block, sizeof(block))) > 0
- && (sparse = swrite(ofd, block, (uint) got)) >= 0)
- total += got;
- if (got)
- VOID warn(got < 0 ? from : to, syserr());
- else if (sparse > 0
- && (lseek(ofd, (off_t) -sparse, 1) < 0
- || write(ofd, block, (uint) sparse) != sparse))
- VOID warn(to, syserr());
- }
- VOID close(ofd);
- }
-
- /*
- * passitem()
- *
- * Copy one file. Returns given input file descriptor.
- */
- STATIC int
- passitem(from, asb, ifd, dir)
- char *from;
- Stat *asb;
- reg int ifd;
- reg char **dir;
- {
- reg int ofd;
- auto time_t tstamp[2];
- auto char to[PATHSIZE];
-
- while (*dir) {
- if (nameopt(strcat(strcat(strcpy(to, *dir++), "/"), from)) < 0)
- continue;
- if ((ofd = openo(to, asb,
- lflag ? linkto(from, asb) : linkfrom(asb), 1)) < 0)
- continue;
- if (ofd > 0)
- passdata(from, ifd, to, ofd);
- tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime;
- VOID utime(to, tstamp);
- }
- return (ifd);
- }
-
- /*
- * pipechld()
- *
- * Child portion of pipeline fork.
- */
- STATIC int
- pipechld(mode, pfd)
- int mode;
- reg int *pfd;
- {
- reg char **av;
- auto char *arg[32];
-
- av = arg;
- if ((*av = getenv("SHELL")) && **av)
- ++av;
- else
- *av++ = "/bin/sh";
- *av++ = "-c";
- *av++ = arname + 1;
- *av = NULL;
- if (mode) {
- VOID close(pfd[1]);
- VOID close(STDIN);
- VOID dup(pfd[0]);
- VOID close(pfd[0]);
- VOID close(STDOUT);
- VOID open("/dev/null", O_WRONLY);
- } else {
- VOID close(STDIN);
- VOID open("/dev/null", O_RDONLY);
- VOID close(pfd[0]);
- VOID close(STDOUT);
- VOID dup(pfd[1]);
- VOID close(pfd[1]);
- }
- if (ttyf >= 0)
- VOID close(ttyf);
- VOID execvp(arg[0], arg);
- VOID warn(arg[0], syserr());
- _exit(1);
- }
-
- /*
- * pipeopen()
- *
- * Open an archive via a pipeline. Returns a file
- * descriptor, or -1 if unsuccessful.
- */
- STATIC int
- pipeopen(mode)
- reg int mode;
- {
- auto int pfd[2];
-
- if (pipe(pfd) < 0)
- return (-1);
- if ((pipepid = xfork("pipeopen()")) == 0)
- pipechld(mode, pfd);
- if (mode) {
- VOID close(pfd[0]);
- return (pfd[1]);
- } else {
- VOID close(pfd[1]);
- return (pfd[0]);
- }
- }
-
- /*
- * pipewait()
- *
- * Await a pipeline.
- */
- STATIC void
- pipewait()
- {
- reg int status;
-
- if (pipepid == 0)
- return;
- status = xwait(pipepid, "pipewait()");
- pipepid = 0;
- if (status)
- fatal(arspec, "Pipeline error");
- }
-
- /*
- * prsize()
- *
- * Print a file offset.
- */
- STATIC void
- prsize(stream, size)
- FILE *stream;
- reg off_t size;
- {
- reg off_t n;
-
- if (n = (size / (1024 * 1024))) {
- VOID fprintf(stream, "%ldm+", n);
- size -= n * 1024 * 1024;
- }
- if (n = (size / 1024)) {
- VOID fprintf(stream, "%ldk+", n);
- size -= n * 1024;
- }
- VOID fprintf(stream, "%ld", size);
- }
-
- #ifndef MKDIR
-
- /*
- * rmdir()
- *
- * Remove a directory via "/bin/rmdir". Sets errno to a
- * questionably sane value upon failure.
- */
- STATIC int
- rmdir(name)
- reg char *name;
- {
- reg int pid;
-
- if ((pid = xfork("rmdir()")) == 0) {
- VOID close(fileno(stdin));
- VOID close(fileno(stdout));
- VOID close(fileno(stderr));
- VOID open("/dev/null", O_RDWR);
- VOID dup(fileno(stdin));
- VOID dup(fileno(stdin));
- VOID execl("/bin/rmdir", "rmdir", name, (char *) NULL);
- exit(1);
- }
- if (xwait(pid, "rmdir()") == 0)
- return (0);
- errno = EACCES;
- return (-1);
- }
-
- #endif /* MKDIR */
-
- /*
- * swrite()
- *
- * Write a filesystem block. Seeks past sparse blocks. Returns
- * 0 if the block was written, the given length for a sparse
- * block or -1 if unsuccessful.
- */
- STATIC int
- swrite(fd, buf, len)
- int fd;
- char *buf;
- uint len;
- {
- reg char *bidx;
- reg char *bend;
-
- if (jflag)
- return (write(fd, buf, len) == len ? 0 : -1);
- bend = (bidx = buf) + len;
- while (bidx < bend)
- if (*bidx++)
- return (write(fd, buf, len) == len ? 0 : -1);
- return (lseek(fd, (off_t) len, 1) < 0 ? -1 : len);
- }
-
- /*
- * syserr()
- *
- * Return pointer to appropriate system error message.
- */
- STATIC char *
- syserr()
- {
- static char msg[40];
-
- if (errno > 0 && errno < sys_nerr)
- return (sys_errlist[errno]);
- VOID sprintf(msg, "Unknown error (errno %d)", errno);
- return (msg);
- }
-
- /*
- * toc()
- *
- * Print archive table of contents.
- */
- STATIC VOIDFN
- toc(av)
- reg char **av;
- {
- auto Stat sb;
- auto char name[PATHSIZE];
-
- if (*av)
- fatal(*av, "Extraneous argument");
- name[0] = '\0';
- while (inhead(name, &sb) == 0) {
- if (namecmp(name) == 0)
- tocentry(name, &sb);
- if (inskip(sb.sb_size) < 0)
- VOID warn(name, "File data is corrupt");
- }
- }
-
- /*
- * tocentry()
- *
- * Print a single table-of-contents entry.
- */
- STATIC void
- tocentry(name, asb)
- char *name;
- reg Stat *asb;
- {
- reg Time *atm;
- reg Link *from;
- reg Passwd *pwp;
- reg Group *grp;
- static char *month[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
-
- if (vflag) {
- tocmode(asb->sb_mode);
- VOID printf(" %2d", asb->sb_nlink);
- atm = localtime(&asb->sb_mtime);
- if (pwp = getpwuid(ush(asb->sb_uid)))
- VOID printf(" %-8s", pwp->pw_name);
- else
- VOID printf(" %-8u", ush(asb->sb_uid));
- if (grp = getgrgid(ush(asb->sb_gid)))
- VOID printf(" %-8s", grp->gr_name);
- else
- VOID printf(" %-8u", ush(asb->sb_gid));
- switch (asb->sb_mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
- VOID printf(" %3d, %3d",
- major(asb->sb_rdev), minor(asb->sb_rdev));
- break;
- case S_IFREG:
- VOID printf(" %8ld", asb->sb_size);
- break;
- default:
- VOID printf(" ");
- }
- VOID printf(" %3s %2d %02d:%02d:%02d %4d ",
- month[atm->tm_mon], atm->tm_mday, atm->tm_hour,
- atm->tm_min, atm->tm_sec, atm->tm_year + 1900);
- }
- VOID printf("%s", name);
- if (vflag || lflag) {
- if (asb->sb_nlink > 1)
- if (from = linkfrom(asb))
- VOID printf(" -> %s",
- from->l_path->p_name);
- else
- VOID linkto(name, asb);
- #ifdef S_IFLNK
- if ((asb->sb_mode & S_IFMT) == S_IFLNK)
- VOID printf(" S-> %s", asb->sb_link);
- #endif /* S_IFLNK */
- }
- putchar('\n');
- }
-
- /*
- * tocmode()
- *
- * Fancy file mode display.
- */
- STATIC void
- tocmode(mode)
- reg ushort mode;
- {
- switch (mode & S_IFMT) {
- case S_IFREG: putchar('-'); break;
- case S_IFDIR: putchar('d'); break;
- #ifdef S_IFLNK
- case S_IFLNK: putchar('l'); break;
- #endif /* S_IFLNK */
- case S_IFBLK: putchar('b'); break;
- case S_IFCHR: putchar('c'); break;
- #ifdef S_IFIFO
- case S_IFIFO: putchar('p'); break;
- #endif /* S_IFIFO */
- default:
- VOID printf("[%o]", mode >> S_IFSHF);
- }
- putchar(mode & 0400 ? 'r' : '-');
- putchar(mode & 0200 ? 'w' : '-');
- putchar(mode & 0100
- ? mode & 04000 ? 's' : 'x'
- : mode & 04000 ? 'S' : '-');
- putchar(mode & 0040 ? 'r' : '-');
- putchar(mode & 0020 ? 'w' : '-');
- putchar(mode & 0010
- ? mode & 02000 ? 's' : 'x'
- : mode & 02000 ? 'S' : '-');
- putchar(mode & 0004 ? 'r' : '-');
- putchar(mode & 0002 ? 'w' : '-');
- putchar(mode & 0001
- ? mode & 01000 ? 't' : 'x'
- : mode & 01000 ? 'T' : '-');
- }
-
- /*
- * usage()
- *
- * Print a helpful message and exit.
- */
- STATIC void
- usage()
- {
- VOID fprintf(stderr, "\
- Usage: %s -o [ -fghlmuvz ] [ -(bces) n ] archive\n\
- %s -i [ -djkmnuvxz ] [ -(bcs) n ] [ -y prefix ] archive\n\
- %s -t [ -kuvz ] [ -(bcs) n ] [ -y prefix ] archive\n\
- %s -p [ -dghjlmnuvxz ] dir [ ... ]\n",
- myname, myname, myname, myname);
- exit(1);
- }
-
- /*
- * warn()
- *
- * Print a warning message. Always returns -1.
- */
- STATIC int
- warn(what, why)
- char *what;
- char *why;
- {
- VOID fprintf(stderr,
- "%s: \"%s\": %s\n",
- myname, what, why);
- return (-1);
- }
-
- /*
- * warnarch()
- *
- * Print an archive-related warning message, including
- * an adjusted file offset. Always returns -1.
- */
- STATIC int
- warnarch(msg, adjust)
- char *msg;
- off_t adjust;
- {
- VOID fprintf(stderr, "%s: \"%s\" [offset ", myname, arspec);
- prsize(stderr, total - adjust);
- VOID fprintf(stderr, "]: %s\n", msg);
- return (-1);
- }
-
- /*
- * xfork()
- *
- * Create a child.
- */
- STATIC int
- xfork(what)
- reg char *what;
- {
- reg int pid;
- reg Child *cp;
- reg int idx;
- static uint delay[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 };
-
- for (idx = 0; (pid = fork()) < 0; ++idx) {
- if (idx == sizeof(delay))
- fatal(arspec, syserr());
- VOID warn(what, "Trouble forking...");
- sleep(delay[idx]);
- }
- if (idx)
- VOID warn(what, "...successful fork");
- cp = (Child *) memget(sizeof(*cp));
- cp->c_pid = pid;
- cp->c_flags = 0;
- cp->c_status = 0;
- cp->c_forw = children;
- children = cp;
- return (pid);
- }
-
- /*
- * xpause()
- *
- * Await a child.
- */
- STATIC void
- xpause()
- {
- reg Child *cp;
- reg int pid;
- auto int status;
-
- do {
- while ((pid = wait(&status)) < 0)
- ;
- for (cp = children; cp && cp->c_pid != pid; cp = cp->c_forw)
- ;
- } while (cp == NULL);
- cp->c_flags |= CF_EXIT;
- cp->c_status = status;
- }
-
- /*
- * xwait()
- *
- * Find the status of a child.
- */
- STATIC int
- xwait(pid, what)
- reg int pid;
- char *what;
- {
- reg int status;
- reg Child *cp;
- reg Child **acp;
- auto char why[100];
-
- for (acp = &children; cp = *acp; acp = &cp->c_forw)
- if (cp->c_pid == pid)
- break;
- if (cp == NULL)
- fatal(what, "Lost child");
- while ((cp->c_flags & CF_EXIT) == 0)
- xpause();
- status = cp->c_status;
- *acp = cp->c_forw;
- free((char *) cp);
- if (status == 0)
- return (0);
- if (status & 0377)
- VOID sprintf(why, "Killed by signal %d%s",
- status & 0177, status & 0200 ? " -- core dumped" : "");
- else
- VOID sprintf(why, "Exit %d", (status >> 8) & 0377);
- return (warn(what, why));
- }
-