dono@killer.DALLAS.TX.US (Don OConnell) (12/27/88)
---------------------------CUT HERE------------------------------------- #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: afio.2.c afio.doc # Wrapped by dono@killer on Mon Dec 26 14:49:57 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f afio.2.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"afio.2.c\" else echo shar: Extracting \"afio.2.c\" \(24932 characters\) sed "s/^X//" >afio.2.c <<'END_OF_afio.2.c' X/* X * next() X * X * Advance to the next archive volume. X */ XSTATIC void Xnext(mode, why) Xreg int mode; Xreg char *why; X{ X reg time_t began; X auto char msg[200]; X auto char answer[20]; X X began = time((time_t *) NULL); X nextclos(); X VOID warnarch(why, (off_t) 0); X if (arfd == STDIN || arfd == STDOUT) X exit(1); X VOID sprintf(msg, "\ X%s: Ready for volume %u on \"%s\"\n\ X%s: Type \"go\" when ready to proceed (or \"quit\" to abort): \07", X myname, arvolume + 1, arspec, myname); X for (;;) { X nextask(msg, answer, sizeof(answer)); X if (strcmp(answer, "quit") == 0) X fatal(arspec, "Aborted"); X if (strcmp(answer, "go") == 0 && nextopen(mode) == 0) X break; X } X VOID warnarch("Continuing", (off_t) 0); X timewait += time((time_t *) NULL) - began; X} X X/* X * nextask() X * X * Ask a question and get a response. Ignores spaces and tabs. X */ XSTATIC void Xnextask(msg, answer, limit) Xreg char *msg; Xreg char *answer; Xreg int limit; X{ X reg int idx; X reg int got; X auto char c; X X if (ttyf < 0) X fatal(TTY, "Unavailable"); X VOID write(ttyf, msg, (uint) strlen(msg)); X idx = 0; X while ((got = read(ttyf, &c, 1)) == 1) X if (c == '\04' || c == '\n') X break; X else if (c == ' ' || c == '\t') X continue; X else if (idx < limit - 1) X answer[idx++] = c; X if (got < 0) X fatal(TTY, syserr()); X answer[idx] = '\0'; X} X X/* X * nextclos() X * X * Close an archive. X */ XSTATIC void Xnextclos() X{ X if (arfd != STDIN && arfd != STDOUT) X VOID close(arfd); X areof = 0; X if (arname && *arname == '!') X pipewait(); X} X X/* X * nextopen() X * X * Open an archive. Returns 0 if successful, -1 otherwise. X */ XSTATIC int Xnextopen(mode) Xint mode; X{ X if (*arname == '!') X arfd = pipeopen(mode); X else if (strcmp(arname, "-") == 0) X arfd = mode ? STDOUT : STDIN; X else { X#ifdef CTC3B2 X if (Cflag) { X reg int oops; X reg int fd; X X oops = ((fd = open(arname, O_RDWR | O_CTSPECIAL)) < 0 X || ioctl(fd, STREAMON) < 0); X VOID close(fd); X if (oops) X return (warnarch(syserr(), (off_t) 0)); X } X#endif /* CTC3B2 */ X arfd = mode ? creat(arname, 0666 & ~mask) : open(arname, mode); X } X if (arfd < 0) X return (warnarch(syserr(), (off_t) 0)); X arleft = aruntil; X ++arvolume; X return (0); X} X X/* X * openi() X * X * Open the next input file. Returns a file descriptor, 0 if no data X * exists, or -1 at EOF. This kludge works because standard input is X * in use, preventing open() from returning zero. X */ XSTATIC int Xopeni(name, asb) Xchar *name; Xreg Stat *asb; X{ X reg int fd; X auto char local[PATHSIZE]; X X for (;;) { X if (lineget(stdin, name) < 0) X return (-1); X if (nameopt(name) < 0) X continue; X if (!gflag) X VOID strcpy(local, name); X else if (dirchg(name, local) < 0) X continue; X if ((hflag ? STAT(local, asb) : LSTAT(local, asb)) < 0) { X VOID warn(name, syserr()); X continue; X } X switch (asb->sb_mode & S_IFMT) { X case S_IFDIR: X asb->sb_nlink = 1; X asb->sb_size = 0; X return (0); X#ifdef S_IFLNK X case S_IFLNK: X if ((asb->sb_size = readlink(local, X asb->sb_link, sizeof(asb->sb_link) - 1)) < 0) { X VOID warn(name, syserr()); X continue; X } X asb->sb_link[asb->sb_size] = '\0'; X return (0); X#endif /* S_IFLNK */ X case S_IFREG: X if (asb->sb_size == 0) X return (0); X if ((fd = open(local, O_RDONLY)) >= 0) X return (fd); X VOID warn(name, syserr()); X break; X default: X asb->sb_size = 0; X return (0); X } X } X} X X/* X * openo() X * X * Open an output file. Returns the output file descriptor, X * 0 if no data is required or -1 if unsuccessful. Note that X * UNIX open() will never return 0 because the standard input X * is in use. X */ XSTATIC int Xopeno(name, asb, linkp, ispass) Xchar *name; Xreg Stat *asb; XLink *linkp; Xreg int ispass; X{ X reg int exists; X reg int fd; X reg ushort perm; X ushort operm; X Path *path; X auto Stat osb; X#ifdef S_IFLNK X reg int ssize; X auto char sname[PATHSIZE]; X#endif /* S_IFLNK */ X X if (exists = (LSTAT(name, &osb) == 0)) X if (ispass X && osb.sb_ino == asb->sb_ino X && osb.sb_dev == asb->sb_dev) X return (warn(name, "Same file")); X else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT)) X operm = osb.sb_mode & (xflag ? S_IPERM : S_IPOPN); X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (linkp) { X if (exists) X if (asb->sb_ino == osb.sb_ino X && asb->sb_dev == osb.sb_dev) X return (0); X else if (unlink(name) < 0) X return (warn(name, syserr())); X else X exists = 0; X for (path = linkp->l_path; path; path = path->p_forw) X if (link(path->p_name, name) == 0 X || (errno == ENOENT X && dirneed(name) == 0 X && link(path->p_name, name) == 0)) X return (0); X else if (errno != EXDEV) X return (warn(name, syserr())); X VOID warn(name, "Link broken"); X linkalso(linkp, name); X } X perm = asb->sb_mode & (xflag ? S_IPERM : S_IPOPN); X switch (asb->sb_mode & S_IFMT) { X case S_IFBLK: X case S_IFCHR: X fd = 0; X if (exists) X if (asb->sb_rdev == osb.sb_rdev) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else X break; X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (mknod(name, asb->sb_mode, asb->sb_rdev) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || mknod(name, asb->sb_mode, asb->sb_rdev) < 0)) X return (warn(name, syserr())); X break; X case S_IFDIR: X if (exists) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else X ; X else if (dirneed(name) < 0 || dirmake(name, asb) < 0) X return (warn(name, syserr())); X return (0); X#ifdef S_IFIFO X case S_IFIFO: X fd = 0; X if (exists) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else X ; X else if (mknod(name, asb->sb_mode, (dev_t) 0) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || mknod(name, asb->sb_mode, (dev_t) 0) < 0)) X return (warn(name, syserr())); X break; X#endif /* S_IFIFO */ X#ifdef S_IFLNK X case S_IFLNK: X if (exists) X if ((ssize = readlink(name, sname, sizeof(sname))) < 0) X return (warn(name, syserr())); X else if (strncmp(sname, asb->sb_link, ssize) == 0) X return (0); X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (symlink(asb->sb_link, name) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || symlink(asb->sb_link, name) < 0)) X return (warn(name, syserr())); X return (0); /* Can't chown()/chmod() a symbolic link */ X#endif /* S_IFLNK */ X case S_IFREG: X if (exists) X if (nflag && osb.sb_mtime > asb->sb_mtime) X return (warn(name, "Newer file exists")); X else if (unlink(name) < 0) X return (warn(name, syserr())); X else X exists = 0; X if ((fd = creat(name, perm)) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || (fd = creat(name, perm)) < 0)) X return (warn(name, syserr())); X break; X default: X return (warn(name, "Unknown filetype")); X } X if (xflag X && (!exists X || asb->sb_uid != osb.sb_uid X || asb->sb_gid != osb.sb_gid)) X VOID chown(name, X uid == 0 ? ush(asb->sb_uid) : uid, X ush(asb->sb_gid)); X if (linkp == NULL && asb->sb_nlink > 1) X VOID linkto(name, asb); X return (fd); X} X X/* X * openq() X * X * Open the terminal for interactive queries (sigh). Assumes that X * background processes ignore interrupts and that the open() or X * the isatty() will fail for processes which are not attached to X * terminals. Returns a file descriptor (-1 if unsuccessful). X */ XSTATIC int Xopenq() X{ X reg int fd; X reg VOIDFN (*intr)(); X X if ((intr = signal(SIGINT, SIG_IGN)) == SIG_IGN) X return (-1); X VOID signal(SIGINT, intr); X if ((fd = open(TTY, O_RDWR)) < 0) X return (-1); X if (isatty(fd)) X return (fd); X VOID close(fd); X return (-1); X} X X/* X * options() X * X * Decode most reasonable forms of UNIX option syntax. Takes main()- X * style argument indices (argc/argv) and a string of valid option X * letters. Letters denoting options with arguments must be followed X * by colons. With valid options, returns the option letter and points X * "optarg" at the associated argument (if any). Returns '?' for bad X * options and missing arguments. Returns zero when no options remain, X * leaving "optind" indexing the first remaining argument. X */ XSTATIC int Xoptions(ac, av, proto) Xint ac; Xregister char **av; Xchar *proto; X{ X register int c; X register char *idx; X static int optsub; X X if (optind == 0) { X optind = 1; X optsub = 0; X } X optarg = NULL; X if (optind >= ac) X return (0); X if (optsub == 0 && (av[optind][0] != '-' || av[optind][1] == '\0')) X return (0); X switch (c = av[optind][++optsub]) { X case '\0': X ++optind; X optsub = 0; X return (options(ac, av, proto)); X case '-': X ++optind; X optsub = 0; X return (0); X case ':': X return ('?'); X } X if ((idx = strchr(proto, c)) == NULL) X return ('?'); X if (idx[1] != ':') X return (c); X optarg = &av[optind][++optsub]; X ++optind; X optsub = 0; X if (*optarg) X return (c); X if (optind >= ac) X return ('?'); X optarg = av[optind++]; X return (c); X} X X/* X * optsize() X * X * Interpret a "size" argument. Recognizes suffices for blocks X * (512-byte), kilobytes and megabytes and blocksize. Returns X * the size in bytes. X */ XSTATIC off_t Xoptsize(str) Xchar *str; X{ X reg char *idx; X reg off_t number; X reg off_t result; X X result = 0; X idx = str; X for (;;) { X number = 0; X while (*idx >= '0' && *idx <= '9') X number = number * 10 + *idx++ - '0'; X switch (*idx++) { X case 'b': X result += number * 512; X continue; X case 'k': X result += number * 1024; X continue; X case 'm': X result += number * 1024 * 1024; X continue; X case 'x': X result += number * arbsize; X continue; X case '+': X result += number; X continue; X case '\0': X result += number; X break; X default: X break; X } X break; X } X if (*--idx) X fatal(str, "Unrecognizable value"); X return (result); X} X X/* X * out() X * X * Write an archive. X */ XSTATIC VOIDFN Xout(av) Xchar **av; X{ X reg int fd; X auto Stat sb; X auto char name[PATHSIZE]; X X if (*av) X fatal(*av, "Extraneous argument"); X while ((fd = openi(name, &sb)) >= 0) { X if (!lflag && sb.sb_nlink > 1) X if (linkfrom(&sb)) X sb.sb_size = 0; X else X VOID linkto(name, &sb); X outhead(name, &sb); X if (fd) X VOID close(outdata(fd, name, sb.sb_size)); X if (vflag) X VOID fprintf(stderr, "%s\n", name); X } X outeof(TRAILER, TRAILZ); X} X X/* X * outalloc() X * X * Allocate buffer space previously referenced by outavail(). X */ XSTATIC void Xoutalloc(len) Xreg uint len; X{ X bufidx += len; X total += len; X} X X/* X * outavail() X * X * Index buffer space for archive output. Stores a buffer pointer X * at a given location. Returns the number of bytes available. X */ XSTATIC uint Xoutavail(bufp) Xreg char **bufp; X{ X reg uint have; X X while ((have = bufend - bufidx) == 0) X outflush(); X *bufp = bufidx; X return (have); X} X X/* X * outdata() X * X * Write archive data. Continues after file read errors, padding with X * null characters if neccessary. Always returns the given input file X * descriptor. X */ XSTATIC int Xoutdata(fd, name, size) Xint fd; Xchar *name; Xreg off_t size; X{ X reg uint chunk; X reg int got; X reg int oops; X reg uint avail; X auto char *buf; X X oops = got = 0; X while (size) { X avail = outavail(&buf); X size -= (chunk = size < avail ? (uint) size : avail); X if (oops == 0 && (got = read(fd, buf, chunk)) < 0) { X oops = warn(name, syserr()); X got = 0; X } X if (got < chunk) { X if (oops == NULL) X oops = warn(name, "Early EOF"); X while (got < chunk) X buf[got++] = '\0'; X } X outalloc(chunk); X } X return (fd); X} X X/* X * outeof() X * X * Write an archive trailer. X */ XSTATIC void Xouteof(name, namelen) Xchar *name; Xreg uint namelen; X{ X reg off_t pad; X auto char header[M_STRLEN + H_STRLEN + 1]; X X if (pad = (total + M_STRLEN + H_STRLEN + namelen) % arpad) X pad = arpad - pad; X VOID strcpy(header, M_ASCII); X VOID sprintf(header + M_STRLEN, H_PRINT, 0, 0, X 0, 0, 0, 1, 0, (time_t) 0, namelen, pad); X outwrite(header, M_STRLEN + H_STRLEN); X outwrite(name, namelen); X outpad(pad); X outflush(); X if (fflag) X outwait(); X} X X/* X * outflush() X * X * Flush the output buffer. Optionally fork()s to allow the X * parent to refill the buffer while the child waits for the X * write() to complete. X */ XSTATIC void Xoutflush() X{ X reg char *buf; X reg int got; X reg uint len; X X if (aruntil && arleft == 0) X next(O_WRONLY, "Output limit reached"); X if (fflag) { X outwait(); X /* if ((outpid = xfork("outflush()")) == 0) X VOID nice(-40); */ X X } X if (!fflag || outpid == 0) { X for (buf = buffer; len = bufidx - buf; ) { X if ((got = write(arfd, buf, X *arname == '!' ? len : min(len, arbsize))) > 0) { X buf += got; X arleft -= got; X } else if (fflag) { X VOID warn(arspec, got < 0 X ? syserr() X : "Apparently full"); X _exit(1); X } else if (got < 0) X fatal(arspec, syserr()); X else X next(O_WRONLY, "Apparently full"); X } X } X if (fflag) { X if (outpid == 0) X _exit(0); X else X arleft -= bufidx - buffer; X } X bufend = (bufidx = buffer) + (aruntil ? min(buflen, arleft) : buflen); X} X X/* X * outhead() X * X * Write an archive header. X */ XSTATIC void Xouthead(name, asb) Xreg char *name; Xreg Stat *asb; X{ X reg uint namelen; X auto char header[M_STRLEN + H_STRLEN + 1]; X X if (name[0] == '/') X if (name[1]) X ++name; X else X name = "."; X namelen = (uint) strlen(name) + 1; X VOID strcpy(header, M_ASCII); X VOID sprintf(header + M_STRLEN, H_PRINT, ush(asb->sb_dev), X ush(asb->sb_ino), ush(asb->sb_mode), ush(asb->sb_uid), X ush(asb->sb_gid), ush(asb->sb_nlink), ush(asb->sb_rdev), X mflag ? timenow : asb->sb_mtime, namelen, asb->sb_size); X outwrite(header, M_STRLEN + H_STRLEN); X outwrite(name, namelen); X#ifdef S_IFLNK X if ((asb->sb_mode & S_IFMT) == S_IFLNK) X outwrite(asb->sb_link, (uint) asb->sb_size); X#endif /* S_IFLNK */ X} X X/* X * outpad() X * X * Pad the archive. X */ XSTATIC void Xoutpad(pad) Xreg off_t pad; X{ X reg int idx; X reg int len; X X while (pad) { X if ((len = bufend - bufidx) > pad) X len = pad; X for (idx = 0; idx < len; ++idx) X *bufidx++ = '\0'; X total += len; X outflush(); X pad -= len; X } X} X X/* X * outwait() X * X * Wait for the last background outflush() process (if any). The child X * exit value is zero if successful, 255 if a write() returned zero or X * the value of errno if a write() was unsuccessful. X */ XSTATIC void Xoutwait() X{ X auto int status; X X if (outpid == 0) X return; X status = xwait(outpid, "outwait()"); X outpid = 0; X if (status) X fatal(arspec, "Child error"); X} X X/* X * outwrite() X * X * Write archive data. X */ XSTATIC void Xoutwrite(idx, len) Xreg char *idx; Xuint len; X{ X reg uint have; X reg uint want; X reg char *endx = idx + len; X X while (want = endx - idx) { X while ((have = bufend - bufidx) == 0) X outflush(); X if (have > want) X have = want; X memcpy(bufidx, idx, have); X bufidx += have; X idx += have; X total += have; X } X} X X/* X * pass() X * X * Copy within the filesystem. X */ XSTATIC VOIDFN Xpass(av) Xreg char **av; X{ X reg int fd; X reg char **avx; X auto Stat sb; X auto char name[PATHSIZE]; X X for (avx = av; *avx; ++avx) { X if (gflag && **avx != '/') X fatal(*avx, "Relative pathname"); X if (STAT(*avx, &sb) < 0) X fatal(*avx, syserr()); X if ((sb.sb_mode & S_IFMT) != S_IFDIR) X fatal(*avx, "Not a directory"); X } X while ((fd = openi(name, &sb)) >= 0) { X if (passitem(name, &sb, fd, av)) X VOID close(fd); X if (vflag) X VOID fprintf(stderr, "%s\n", name); X } X} X X/* X * passdata() X * X * Copy data to one file. Doesn't believe in input file X * descriptor zero (see description of kludge in openi() X * comments). Closes the provided output file descriptor. X */ XSTATIC void Xpassdata(from, ifd, to, ofd) Xchar *from; Xreg int ifd; Xchar *to; Xreg int ofd; X{ X reg int got; X reg int sparse; X auto char block[FSBUF]; X X if (ifd) { X VOID lseek(ifd, (off_t) 0, 0); X sparse = 0; X while ((got = read(ifd, block, sizeof(block))) > 0 X && (sparse = swrite(ofd, block, (uint) got)) >= 0) X total += got; X if (got) X VOID warn(got < 0 ? from : to, syserr()); X else if (sparse > 0 X && (lseek(ofd, (off_t) -sparse, 1) < 0 X || write(ofd, block, (uint) sparse) != sparse)) X VOID warn(to, syserr()); X } X VOID close(ofd); X} X X/* X * passitem() X * X * Copy one file. Returns given input file descriptor. X */ XSTATIC int Xpassitem(from, asb, ifd, dir) Xchar *from; XStat *asb; Xreg int ifd; Xreg char **dir; X{ X reg int ofd; X auto time_t tstamp[2]; X auto char to[PATHSIZE]; X X while (*dir) { X if (nameopt(strcat(strcat(strcpy(to, *dir++), "/"), from)) < 0) X continue; X if ((ofd = openo(to, asb, X lflag ? linkto(from, asb) : linkfrom(asb), 1)) < 0) X continue; X if (ofd > 0) X passdata(from, ifd, to, ofd); X tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime; X VOID utime(to, tstamp); X } X return (ifd); X} X X/* X * pipechld() X * X * Child portion of pipeline fork. X */ XSTATIC int Xpipechld(mode, pfd) Xint mode; Xreg int *pfd; X{ X reg char **av; X auto char *arg[32]; X X av = arg; X if ((*av = getenv("SHELL")) && **av) X ++av; X else X *av++ = "/bin/sh"; X *av++ = "-c"; X *av++ = arname + 1; X *av = NULL; X if (mode) { X VOID close(pfd[1]); X VOID close(STDIN); X VOID dup(pfd[0]); X VOID close(pfd[0]); X VOID close(STDOUT); X VOID open("/dev/null", O_WRONLY); X } else { X VOID close(STDIN); X VOID open("/dev/null", O_RDONLY); X VOID close(pfd[0]); X VOID close(STDOUT); X VOID dup(pfd[1]); X VOID close(pfd[1]); X } X if (ttyf >= 0) X VOID close(ttyf); X VOID execvp(arg[0], arg); X VOID warn(arg[0], syserr()); X _exit(1); X} X X/* X * pipeopen() X * X * Open an archive via a pipeline. Returns a file X * descriptor, or -1 if unsuccessful. X */ XSTATIC int Xpipeopen(mode) Xreg int mode; X{ X auto int pfd[2]; X X if (pipe(pfd) < 0) X return (-1); X if ((pipepid = xfork("pipeopen()")) == 0) X pipechld(mode, pfd); X if (mode) { X VOID close(pfd[0]); X return (pfd[1]); X } else { X VOID close(pfd[1]); X return (pfd[0]); X } X} X X/* X * pipewait() X * X * Await a pipeline. X */ XSTATIC void Xpipewait() X{ X reg int status; X X if (pipepid == 0) X return; X status = xwait(pipepid, "pipewait()"); X pipepid = 0; X if (status) X fatal(arspec, "Pipeline error"); X} X X/* X * prsize() X * X * Print a file offset. X */ XSTATIC void Xprsize(stream, size) XFILE *stream; Xreg off_t size; X{ X reg off_t n; X X if (n = (size / (1024 * 1024))) { X VOID fprintf(stream, "%ldm+", n); X size -= n * 1024 * 1024; X } X if (n = (size / 1024)) { X VOID fprintf(stream, "%ldk+", n); X size -= n * 1024; X } X VOID fprintf(stream, "%ld", size); X} X X#ifndef MKDIR X X/* X * rmdir() X * X * Remove a directory via "/bin/rmdir". Sets errno to a X * questionably sane value upon failure. X */ XSTATIC int Xrmdir(name) Xreg char *name; X{ X reg int pid; X X if ((pid = xfork("rmdir()")) == 0) { X VOID close(fileno(stdin)); X VOID close(fileno(stdout)); X VOID close(fileno(stderr)); X VOID open("/dev/null", O_RDWR); X VOID dup(fileno(stdin)); X VOID dup(fileno(stdin)); X VOID execl("/bin/rmdir", "rmdir", name, (char *) NULL); X exit(1); X } X if (xwait(pid, "rmdir()") == 0) X return (0); X errno = EACCES; X return (-1); X} X X#endif /* MKDIR */ X X/* X * swrite() X * X * Write a filesystem block. Seeks past sparse blocks. Returns X * 0 if the block was written, the given length for a sparse X * block or -1 if unsuccessful. X */ XSTATIC int Xswrite(fd, buf, len) Xint fd; Xchar *buf; Xuint len; X{ X reg char *bidx; X reg char *bend; X X if (jflag) X return (write(fd, buf, len) == len ? 0 : -1); X bend = (bidx = buf) + len; X while (bidx < bend) X if (*bidx++) X return (write(fd, buf, len) == len ? 0 : -1); X return (lseek(fd, (off_t) len, 1) < 0 ? -1 : len); X} X X/* X * syserr() X * X * Return pointer to appropriate system error message. X */ XSTATIC char * Xsyserr() X{ X static char msg[40]; X X if (errno > 0 && errno < sys_nerr) X return (sys_errlist[errno]); X VOID sprintf(msg, "Unknown error (errno %d)", errno); X return (msg); X} X X/* X * toc() X * X * Print archive table of contents. X */ XSTATIC VOIDFN Xtoc(av) Xreg char **av; X{ X auto Stat sb; X auto char name[PATHSIZE]; X X if (*av) X fatal(*av, "Extraneous argument"); X name[0] = '\0'; X while (inhead(name, &sb) == 0) { X if (namecmp(name) == 0) X tocentry(name, &sb); X if (inskip(sb.sb_size) < 0) X VOID warn(name, "File data is corrupt"); X } X} X X/* X * tocentry() X * X * Print a single table-of-contents entry. X */ XSTATIC void Xtocentry(name, asb) Xchar *name; Xreg Stat *asb; X{ X reg Time *atm; X reg Link *from; X reg Passwd *pwp; X reg Group *grp; X static char *month[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X }; X X if (vflag) { X tocmode(asb->sb_mode); X VOID printf(" %2d", asb->sb_nlink); X atm = localtime(&asb->sb_mtime); X if (pwp = getpwuid(ush(asb->sb_uid))) X VOID printf(" %-8s", pwp->pw_name); X else X VOID printf(" %-8u", ush(asb->sb_uid)); X if (grp = getgrgid(ush(asb->sb_gid))) X VOID printf(" %-8s", grp->gr_name); X else X VOID printf(" %-8u", ush(asb->sb_gid)); X switch (asb->sb_mode & S_IFMT) { X case S_IFBLK: X case S_IFCHR: X VOID printf(" %3d, %3d", X major(asb->sb_rdev), minor(asb->sb_rdev)); X break; X case S_IFREG: X VOID printf(" %8ld", asb->sb_size); X break; X default: X VOID printf(" "); X } X VOID printf(" %3s %2d %02d:%02d:%02d %4d ", X month[atm->tm_mon], atm->tm_mday, atm->tm_hour, X atm->tm_min, atm->tm_sec, atm->tm_year + 1900); X } X VOID printf("%s", name); X if (vflag || lflag) { X if (asb->sb_nlink > 1) X if (from = linkfrom(asb)) X VOID printf(" -> %s", X from->l_path->p_name); X else X VOID linkto(name, asb); X#ifdef S_IFLNK X if ((asb->sb_mode & S_IFMT) == S_IFLNK) X VOID printf(" S-> %s", asb->sb_link); X#endif /* S_IFLNK */ X } X putchar('\n'); X} X X/* X * tocmode() X * X * Fancy file mode display. X */ XSTATIC void Xtocmode(mode) Xreg ushort mode; X{ X switch (mode & S_IFMT) { X case S_IFREG: putchar('-'); break; X case S_IFDIR: putchar('d'); break; X#ifdef S_IFLNK X case S_IFLNK: putchar('l'); break; X#endif /* S_IFLNK */ X case S_IFBLK: putchar('b'); break; X case S_IFCHR: putchar('c'); break; X#ifdef S_IFIFO X case S_IFIFO: putchar('p'); break; X#endif /* S_IFIFO */ X default: X VOID printf("[%o]", mode >> S_IFSHF); X } X putchar(mode & 0400 ? 'r' : '-'); X putchar(mode & 0200 ? 'w' : '-'); X putchar(mode & 0100 X ? mode & 04000 ? 's' : 'x' X : mode & 04000 ? 'S' : '-'); X putchar(mode & 0040 ? 'r' : '-'); X putchar(mode & 0020 ? 'w' : '-'); X putchar(mode & 0010 X ? mode & 02000 ? 's' : 'x' X : mode & 02000 ? 'S' : '-'); X putchar(mode & 0004 ? 'r' : '-'); X putchar(mode & 0002 ? 'w' : '-'); X putchar(mode & 0001 X ? mode & 01000 ? 't' : 'x' X : mode & 01000 ? 'T' : '-'); X} X X/* X * usage() X * X * Print a helpful message and exit. X */ XSTATIC void Xusage() X{ X VOID fprintf(stderr, "\ XUsage: %s -o [ -fghlmuvz ] [ -(bces) n ] archive\n\ X %s -i [ -djkmnuvxz ] [ -(bcs) n ] [ -y prefix ] archive\n\ X %s -t [ -kuvz ] [ -(bcs) n ] [ -y prefix ] archive\n\ X %s -p [ -dghjlmnuvxz ] dir [ ... ]\n", X myname, myname, myname, myname); X exit(1); X} X X/* X * warn() X * X * Print a warning message. Always returns -1. X */ XSTATIC int Xwarn(what, why) Xchar *what; Xchar *why; X{ X VOID fprintf(stderr, X "%s: \"%s\": %s\n", X myname, what, why); X return (-1); X} X X/* X * warnarch() X * X * Print an archive-related warning message, including X * an adjusted file offset. Always returns -1. X */ XSTATIC int Xwarnarch(msg, adjust) Xchar *msg; Xoff_t adjust; X{ X VOID fprintf(stderr, "%s: \"%s\" [offset ", myname, arspec); X prsize(stderr, total - adjust); X VOID fprintf(stderr, "]: %s\n", msg); X return (-1); X} X X/* X * xfork() X * X * Create a child. X */ XSTATIC int Xxfork(what) Xreg char *what; X{ X reg int pid; X reg Child *cp; X reg int idx; X static uint delay[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 }; X X for (idx = 0; (pid = fork()) < 0; ++idx) { X if (idx == sizeof(delay)) X fatal(arspec, syserr()); X VOID warn(what, "Trouble forking..."); X sleep(delay[idx]); X } X if (idx) X VOID warn(what, "...successful fork"); X cp = (Child *) memget(sizeof(*cp)); X cp->c_pid = pid; X cp->c_flags = 0; X cp->c_status = 0; X cp->c_forw = children; X children = cp; X return (pid); X} X X/* X * xpause() X * X * Await a child. X */ XSTATIC void Xxpause() X{ X reg Child *cp; X reg int pid; X auto int status; X X do { X while ((pid = wait(&status)) < 0) X ; X for (cp = children; cp && cp->c_pid != pid; cp = cp->c_forw) X ; X } while (cp == NULL); X cp->c_flags |= CF_EXIT; X cp->c_status = status; X} X X/* X * xwait() X * X * Find the status of a child. X */ XSTATIC int Xxwait(pid, what) Xreg int pid; Xchar *what; X{ X reg int status; X reg Child *cp; X reg Child **acp; X auto char why[100]; X X for (acp = &children; cp = *acp; acp = &cp->c_forw) X if (cp->c_pid == pid) X break; X if (cp == NULL) X fatal(what, "Lost child"); X while ((cp->c_flags & CF_EXIT) == 0) X xpause(); X status = cp->c_status; X *acp = cp->c_forw; X free((char *) cp); X if (status == 0) X return (0); X if (status & 0377) X VOID sprintf(why, "Killed by signal %d%s", X status & 0177, status & 0200 ? " -- core dumped" : ""); X else X VOID sprintf(why, "Exit %d", (status >> 8) & 0377); X return (warn(what, why)); X} X X END_OF_afio.2.c if test 24932 -ne `wc -c <afio.2.c`; then echo shar: \"afio.2.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f afio.doc -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"afio.doc\" else echo shar: Extracting \"afio.doc\" \(7880 characters\) sed "s/^X//" >afio.doc <<'END_OF_afio.doc' X X X X AFIO(1) UNIX SYSTEM V AFIO(1) X X X X X NAME X afio - manipulate archives and files X X SYNOPSIS X afio -o [ options ] archive X afio -t [ options ] archive X afio -i [ options ] archive X afio -p [ options ] directory [ ... ] X X DESCRIPTION X Afio manipulates groups of files, copying them within the X (collective) filesystem or between the filesystem and an X afio archive. Note that afio archives are portable, as they X contain only ASCII-formatted header information. They are X also compatible with ASCII cpio(1) archives (ala cpio -c). X X With -o, reads pathnames from the standard input and writes X an archive. X X With -t, reads an archive and writes a table-of-contents to X the standard output. X X With -i, installs the contents of an archive relative to the X working directory. X X With -p, reads pathnames from the standard input and copies X the files to each directory. X X Creates missing directories as necessary, with permissions X to match their parents. X X Generates sparse filesystem blocks (with lseek(2)) when X possible. X X Supports multi-volume archives during interactive operation X (i.e., when /dev/tty is accessible and SIGINT is not being X ignored). X X Options: X X -b size Read or write size-character archive blocks. X Suffices of b, k and m denote multiples of 512, X 1024 and 1048576, respectively. Defaults to X 5120 for compatibility with cpio(1). X X -c count Buffer count archive blocks between I/O X operations. A large count is recommended with X streaming magnetic tape drives. X X -d Don't create missing directories. X X X X X Page 1 (printed 7/19/88) X X X X X X X AFIO(1) UNIX SYSTEM V AFIO(1) X X X X X -e bound Pad the archive to a multiple of bound X characters. Recognizes the same suffices as X -s. Defaults to 1x (the -b block size) for X compatibility with cpio(1). X X -f Spawn a child process to actually write to the X archive; provides a clumsy form of double- X buffering. Requires -s for multi-volume X archive support. X X -g Change to input file directories. Avoids X quadratic filesystem behavior with long similar X pathnames. Requires all absolute pathnames, X including those for the -o archive and the -p X directories. X X -h Follow symbolic links, treating them as X ordinary files and directories. X X -j Don't generate sparse filesystem blocks. X X -k Skip corrupt data at the beginning of an X archive (rather than complaining about X unrecognizable input). X X -l With -o, write file contents with each hard X link. X X With -t, report hard links. X X With -p, attempt to link files rather than X copying them. X X -m Mark output files with a common current X timestamp (rather than with input file X modification times). X X -n Protect newer existing files (comparing file X modification times). X X -s limit Restrict each portion of a multi-volume archive X to limit characters. Recognizes the same X suffices as -b. Also, the suffix x denotes a X multiple of the -b block size (and must follow X any -b specification). Useful with finite- X length devices which do not return short counts X at end of media (sigh); output to magnetic tape X typically falls into this category. X X -u Report files with unseen links. X X X X X Page 2 (printed 7/19/88) X X X X X X X AFIO(1) UNIX SYSTEM V AFIO(1) X X X X X -v Verbose. Report pathnames as they are X processed. With -t, gives an ls -l style report X (including link information). X X -x Retain file ownership and setuid/setgid X permissions. This is the default for the X super-user; he may use -X to override it. X X -y prefix Restrict archive processing to names beginning X with prefix. Specify once for each prefix to X be recognized. Use -Y to supply prefixes which X are not to be processed. X X -z Print execution statistics. This is meant for X human consumption; use by other programs is X officially discouraged. X X Special-case archive names: X X o Specify - to read or write the standard input or X output, respectively. This disables multi-volume X archive handling. X X o Prefix a command string to be executed with an X exclamation mark (!). The command is executed once X for each archive volume, with its standard input or X output piped to afio. It is expected to produce a X zero exit code when all is well. X X o Use system:file to access an archive in file on X system. This is really just a special case of X pipelining. It requires a 4.2BSD-style remote shell X (rsh(1C)) and a remote copy of afio. X X o Anything else specifies a local file or device. An X output file will be created if it does not already X exist. X X Recognizes obsolete binary cpio(1) archives (including those X from machines with reversed byte order), but cannot write X them. X X Recovers from archive corruption by searching for a valid X magic number. This is rather simplistic, but, much like a X disassembler, almost always works. X X Optimizes pathnames with respect to the current and parent X directories. For example, ./src/sh/../misc/afio.c becomes X src/misc/afio.c. X X BUGS X X X X Page 3 (printed 7/19/88) X X X X X X X AFIO(1) UNIX SYSTEM V AFIO(1) X X X X X There are too many options. X X Restricts pathnames to 1023 characters and 255 meaningful X elements. X X There is no sequence information within multi-volume X archives. Input sequence errors generally masquerade as X data corruption. A solution would probably be mutually X exclusive with cpio(1) compatibility. X X Degenerate uses of symbolic links are mangled by pathname X optimization. For example, assuming that "usr.src" is a X symbolic link to "/usr/src", the pathname X "usr.src/../bin/cu" is mis-optimized into "bin/cu" (rather X than "/usr/bin/cu"). X X SEE ALSO X cpio(1), find(1), tar(1), tp(1). X X AUTHOR X Mark Brukhartz X ..!ihnp4!laidbak!mdb X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X Page 4 (printed 7/19/88) X X X X END_OF_afio.doc if test 7880 -ne `wc -c <afio.doc`; then echo shar: \"afio.doc\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of shell archive. exit 0