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