Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v12i046: Manipulate CPIO-format archive and files, Part01/02

18 views
Skip to first unread message

Rich Salz

unread,
Oct 26, 1987, 6:10:23 PM10/26/87
to
Submitted-by: Mark Brukhartz <ihnp4!laidbak!mdb>
Posting-number: Volume 12, Issue 46
Archive-name: afio/part01

[ This is a replacement for cpio, a (tape-) archive program. I wrote the
Makefile. I had to split the source file into two halves, which will
need to be joined together. --r$ ]

#! /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 archive 1 (of 2)."
# Contents: Makefile README afio.1 afio.c.P2
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(903 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X##
X## I wrote this Makefile, based on comments in the source. -rich $alz.
X## Define INDEX to use index() in place of strchr() (v7, BSD).
X1 = -DINDEX
X#1 = -UINDEX
X## Define MEMCPY when an efficient memcpy() exists (SysV).
X2 = -DMEMCPY
X#2 = -UMEMCPY
X## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3).
X3 = -DMKDIR
X#3 = -UMKDIR
X## Define NOVOID if your compiler doesn't like void casts.
X4 = -DNOVOID
X#4 = -UNOVOID
X## Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD).
X5 = -DSYSTEM
X#5 = -USYSTEM
X## Define VOIDFIX to allow pointers to functions returning void (non-PCC).
X6 = -DVOIDFIX
X#6 = -UVOIDFIX
X## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape.
X7 = -DCTC3B2
X#7 = -UCTC3B2
XCFLAGS = $1 $2 $3 $4 $5 $6 $7
X
X
Xall: afio afio.1
X
Xafio: afio.c
X $(CC) $(CFLAGS) -o afio afio.c
X
Xinstall: all
X @echo copy afio and afio.1 into the appropriate directories.
END_OF_FILE
if test 903 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(628 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
XAfio is a better way of dealing with cpio-format archives. It is
Xgenerally faster than cpio, provides more diverse magnetic tape
Xoptions and deals somewhat gracefully with input data corruption.
X
XAfio is quite portable. It is now running under 4.2BSD, System V
Xand even one or two UNIX-like operating systems. Please see the
Xbeginning of afio.c for a brief description of the compile-time
Xconfiguration options.
X
XDiscussion of features (including those unintended ones commonly
Xreferred to as "bugs") may be sent to ..!{ihnp4, sun}!laidbak!mdb.
X
X Mark Brukhartz
X Lachman Associates, Inc.
X ..!{ihnp4, sun}!laidbak!mdb
X
END_OF_FILE
if test 628 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'afio.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'afio.1'\"
else
echo shar: Extracting \"'afio.1'\" \(5834 characters\)
sed "s/^X//" >'afio.1' <<'END_OF_FILE'
X'br $Header: afio.1,v 1.20 87/03/18 06:10:34 mdb Exp $
X.TH AFIO 1
X.SH NAME
Xafio \- manipulate archives and files
X.SH SYNOPSIS
X.B "afio \-o"
X[
X.I options
X] archive
X.br
X.B "afio \-t"
X[
X.I options
X] archive
X.br
X.B "afio \-i"
X[
X.I options
X] archive
X.br
X.B "afio \-p"
X[
X.I options
X] directory [ ... ]
X.SH DESCRIPTION
X.I Afio
Xmanipulates groups of files, copying them within the (collective)
Xfilesystem or between the filesystem and an
X.I afio
Xarchive. Note that
X.I afio
Xarchives are portable, as they contain only ASCII-formatted
Xheader information. They are also compatible with ASCII
X.IR cpio (1)
Xarchives (ala
X.IR "cpio \-c" ).
X.PP
XWith
X.BR \-o ,
Xreads pathnames from the standard input
Xand writes an
X.IR archive .
X.PP
XWith
X.BR \-t ,
Xreads an
X.I archive
Xand writes a table-of-contents to the standard output.
X.PP
XWith
X.BR \-i ,
Xinstalls the contents of an
X.I archive
Xrelative to the working directory.
X.PP
XWith
X.BR \-p ,
Xreads pathnames from the standard input
Xand copies the files to each
X.IR directory .
X.PP
XCreates missing directories as necessary, with permissions
Xto match their parents.
X.PP
XGenerates sparse filesystem blocks (with
X.IR lseek (2))
Xwhen possible.
X.PP
XSupports multi-volume archives during interactive operation
X(i.e., when
X.I /dev/tty
Xis accessible and
X.I SIGINT
Xis not being ignored).
X.PP
XOptions:
X.TP 13
X.BI \-b "\ size"
XRead or write
X.IR size -character
Xarchive blocks.
XSuffices of
X.BR b ,
X.B k
Xand
X.B m
Xdenote multiples of
X.IR 512 ,
X.I 1024
Xand
X.IR 1048576 ,
Xrespectively.
XDefaults to
X.I 5120
Xfor compatibility with
X.IR cpio (1).
X.TP
X.BI \-c "\ count"
XBuffer
X.I count
Xarchive blocks between I/O operations. A large
X.I count
Xis recommended with streaming magnetic tape drives.
X.TP
X.B \-d
XDon't create missing directories.
X.TP
X.BI \-e "\ bound"
XPad the archive to a multiple of
X.I bound
Xcharacters.
XRecognizes the same suffices as
X.BR \-s .
XDefaults to
X.I 1x\^
X(the
X.B \-b
Xblock size)
Xfor compatibility with
X.IR cpio (1).
X.TP
X.B \-f
XSpawn a child process to actually write to the archive; provides
Xa clumsy form of double-buffering.
XRequires
X.B \-s
Xfor multi-volume archive support.
X.TP
X.B \-g
XChange to input file directories. Avoids quadratic filesystem
Xbehavior with long similar pathnames. Requires all absolute
Xpathnames, including those for the
X.B \-o
X.I archive
Xand the
X.B \-p
X.IR directories .
X.TP
X.B \-h
XFollow symbolic links, treating them as ordinary files and directories.
X.TP
X.B \-j
XDon't generate sparse filesystem blocks.
X.TP
X.B \-k
XSkip corrupt data at the
X.I beginning
Xof an archive (rather
Xthan complaining about unrecognizable input).
X.TP
X.B \-l
XWith
X.BR \-o ,
Xwrite file contents with each hard link.
X.sp
XWith
X.BR \-t ,
Xreport hard links.
X.sp
XWith
X.BR \-p ,
Xattempt to link files rather than copying them.
X.TP
X.B \-m
XMark output files with a common current timestamp
X(rather than with input file modification times).
X.TP
X.B \-n
XProtect newer existing files (comparing file modification times).
X.TP
X.BI \-s "\ limit"
XRestrict each portion of a multi-volume archive to
X.I limit
Xcharacters.
XRecognizes the same suffices as
X.BR \-b .
XAlso, the suffix
X.B x
Xdenotes a multiple of the
X.B \-b
Xblock size (and must follow any
X.B \-b
Xspecification).
XUseful with finite-length devices which do not return short
Xcounts at end of media (sigh); output to magnetic tape typically
Xfalls into this category.
X.TP
X.B \-u
XReport files with unseen links.
X.TP
X.B \-v
XVerbose. Report pathnames as they are processed. With
X.BR \-t ,
Xgives an
X.I "ls \-l"
Xstyle report (including link information).
X.TP
X.B \-x
XRetain file ownership and setuid/setgid permissions.
XThis is the default for the super-user; he may use
X.B \-X
Xto override it.
X.TP
X.BI \-y "\ prefix"
XRestrict archive processing to names beginning with
X.IR prefix .
XSpecify once for each prefix to be recognized.
XUse
X.B \-Y
Xto supply prefixes which are
X.I not
Xto be processed.
X.TP
X.B \-z
XPrint execution statistics. This is meant for human consumption;
Xuse by other programs is officially discouraged.
X.PP
XSpecial-case archive names:
X.RS 3
X.TP 3
X.B o
XSpecify
X.I \-
Xto read or write the standard input or output, respectively.
XThis disables multi-volume archive handling.
X.TP
X.B o
XPrefix a command string to be executed with an exclamation mark
X.RI ( ! ).
XThe command is executed once for each archive volume,
Xwith its standard input or output piped to
X.IR afio .
XIt is expected to produce a zero exit code when all is well.
X.TP
X.B o
XUse
X.I system:file
Xto access an archive in
X.I file
Xon
X.IR system .
XThis is really just a special case of pipelining.
XIt requires a 4.2BSD-style remote shell
X.RI ( rsh (1C))
Xand a remote copy of
X.IR afio .
X.TP
X.B o
XAnything else specifies a local file or device.
XAn output file will be created if it does not already exist.
X.RE
X.PP
XRecognizes obsolete binary
X.IR cpio (1)
Xarchives (including those from machines with reversed byte order),
Xbut cannot write them.
X.PP
XRecovers from archive corruption by searching for a valid magic
Xnumber. This is rather simplistic, but, much like a disassembler,
Xalmost always works.
X.PP
XOptimizes pathnames with respect to the current and parent
Xdirectories. For example,
X.I ./src/sh/../misc/afio.c
Xbecomes
X.IR src/misc/afio.c .
X.SH BUGS
XThere are too many options.
X.PP
XRestricts pathnames to 1023 characters and 255 meaningful elements.
X.PP
XThere is no sequence information within multi-volume archives.
XInput sequence errors generally masquerade as data corruption.
XA solution would probably be mutually exclusive with
X.IR cpio (1)
Xcompatibility.
X.PP
XDegenerate uses of symbolic links are mangled by pathname optimization.
XFor example, assuming that "usr.src" is a symbolic link to "/usr/src",
Xthe pathname "usr.src/../bin/cu" is mis-optimized into "bin/cu" (rather
Xthan "/usr/bin/cu").
X.SH "SEE ALSO"
Xcpio(1), find(1), tar(1), tp(1).
X.SH AUTHOR
XMark Brukhartz
X.br
X.I "..!ihnp4!laidbak!mdb"
END_OF_FILE
if test 5834 -ne `wc -c <'afio.1'`; then
echo shar: \"'afio.1'\" unpacked with wrong size!
fi
# end of 'afio.1'
fi
if test -f 'afio.c.P2' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'afio.c.P2'\"
else
echo shar: Extracting \"'afio.c.P2'\" \(29238 characters\)
sed "s/^X//" >'afio.c.P2' <<'END_OF_FILE'
X
X/*
X * linkleft()
X *
X * Complain about files with unseen links.
X */
XSTATIC void
Xlinkleft()
X{
X reg Link *lp;
X reg Link **base;
X
X for (base = linkbase; base < linkbase + nel(linkbase); ++base)
X for (lp = *base; lp; lp = lp->l_forw)
X if (lp->l_nlink)
X VOID warn(lp->l_path->p_name, "Unseen link(s)");
X}
X
X/*
X * linkto()
X *
X * Remember a file with outstanding links. Returns a
X * pointer to the associated link structure, or NULL
X * when linking is not possible.
X */
XSTATIC Link *
Xlinkto(name, asb)
Xchar *name;
Xreg Stat *asb;
X{
X reg Link *linkp;
X reg Path *path;
X reg Link **abase;
X
X if ((asb->sb_mode & S_IFMT) == S_IFDIR
X || (linkp = (Link *) memget(sizeof(Link))) == NULL
X || (path = (Path *) memget(sizeof(Path))) == NULL
X || (path->p_name = memstr(name)) == NULL)
X return (NULL);
X linkp->l_dev = asb->sb_dev;
X linkp->l_ino = asb->sb_ino;
X linkp->l_nlink = asb->sb_nlink - 1;
X linkp->l_size = asb->sb_size;
X linkp->l_path = path;
X path->p_forw = NULL;
X path->p_back = path;
X if (linkp->l_forw = *(abase = linkhash(asb->sb_ino)))
X linkp->l_forw->l_back = linkp;
X linkp->l_back = NULL;
X return (*abase = linkp);
X}
X
X#ifndef MEMCPY
X
X/*
X * memcpy()
X *
X * A simple block move.
X */
XSTATIC void
Xmemcpy(to, from, len)
Xreg char *to;
Xreg char *from;
Xuint len;
X{
X reg char *toend;
X
X for (toend = to + len; to < toend; *to++ = *from++)
X ;
X}
X
X#endif /* MEMCPY */
X
X/*
X * memget()
X *
X * Allocate memory.
X */
XSTATIC char *
Xmemget(len)
Xuint len;
X{
X reg char *mem;
X static short outofmem;
X
X if ((mem = malloc(len)) == NULL && !outofmem)
X outofmem = warn("memget()", "Out of memory");
X return (mem);
X}
X
X/*
X * memstr()
X *
X * Duplicate a string into dynamic memory.
X */
XSTATIC char *
Xmemstr(str)
Xreg char *str;
X{
X reg char *mem;
X
X if (mem = memget((uint) strlen(str) + 1))
X VOID strcpy(mem, str);
X return (mem);
X}
X
X#ifndef MKDIR
X
X/*
X * mkdir()
X *
X * Make a directory via "/bin/mkdir". Sets errno to a
X * questionably sane value upon failure.
X */
XSTATIC int
Xmkdir(name, mode)
Xreg char *name;
Xreg ushort mode;
X{
X reg int pid;
X
X if ((pid = xfork("mkdir()")) == 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 umask(~mode);
X VOID execl("/bin/mkdir", "mkdir", name, (char *) NULL);
X exit(1);
X }
X if (xwait(pid, "mkdir()") == 0)
X return (0);
X errno = EACCES;
X return (-1);
X}
X
X#endif /* MKDIR */
X
X/*
X * nameadd()
X *
X * Add a name to the pattern list.
X */
XSTATIC void
Xnameadd(name, not)
Xreg char *name;
Xint not;
X{
X reg Pattern *px;
X
X px = (Pattern *) memget(sizeof(Pattern));
X px->p_forw = pattern;
X px->p_str = name;
X px->p_len = strlen(name);
X px->p_not = not;
X pattern = px;
X}
X
X/*
X * namecmp()
X *
X * Compare a pathname with the pattern list. Returns 0 for
X * a match, -1 otherwise.
X */
XSTATIC int
Xnamecmp(name)
Xreg char *name;
X{
X reg Pattern *px;
X reg int positive;
X reg int match;
X
X positive = match = 0;
X for (px = pattern; px; px = px->p_forw) {
X if (!px->p_not)
X ++positive;
X if (strncmp(name, px->p_str, px->p_len) == 0
X && (name[px->p_len] == '/' || name[px->p_len] == '\0')) {
X if (px->p_not)
X return (-1);
X ++match;
X }
X }
X return (match || !positive ? 0 : -1);
X}
X
X/*
X * nameopt()
X *
X * Optimize a pathname. Confused by "<symlink>/.." twistiness.
X * Returns the number of final pathname elements (zero for "/"
X * or ".") or -1 if unsuccessful.
X */
XSTATIC int
Xnameopt(begin)
Xchar *begin;
X{
X reg char *name;
X reg char *item;
X reg int idx;
X int absolute;
X auto char *element[PATHELEM];
X
X absolute = (*(name = begin) == '/');
X idx = 0;
X for (;;) {
X if (idx == PATHELEM)
X return (warn(begin, "Too many elements"));
X while (*name == '/')
X ++name;
X if (*name == '\0')
X break;
X element[idx] = item = name;
X while (*name && *name != '/')
X ++name;
X if (*name)
X *name++ = '\0';
X if (strcmp(item, "..") == 0)
X if (idx == 0)
X if (absolute)
X ;
X else
X ++idx;
X else if (strcmp(element[idx - 1], "..") == 0)
X ++idx;
X else
X --idx;
X else if (strcmp(item, ".") != 0)
X ++idx;
X }
X if (idx == 0)
X element[idx++] = absolute ? "" : ".";
X element[idx] = NULL;
X name = begin;
X if (absolute)
X *name++ = '/';
X for (idx = 0; item = element[idx]; ++idx, *name++ = '/')
X while (*item)
X *name++ = *item++;
X *--name = '\0';
X return (idx);
X}
X
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 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}
END_OF_FILE
if test 29238 -ne `wc -c <'afio.c.P2'`; then
echo shar: \"'afio.c.P2'\" unpacked with wrong size!
fi
# end of 'afio.c.P2'
fi
echo shar: End of archive 1 \(of 2\).
## End of shell archive.
exit 0

0 new messages