[comp.os.minix] afio part 2

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