/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: cpio.c 1.31 90/08/05 $@(#)cpio:cpio.c	1.30.3.23"

/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*
 * Modified for POSIX compliance such that umask WILL modify the files
 * created unless the program is run by the root user.
 */

/*	/sccs/src/cmd/s.cpio.c
	cpio.c	1.30.3.23	6/9/87 17:33:41
	Reworked cpio which uses getopt(3) to interpret flag arguments and
	changes reels to the save file name.
	Performance and size improvements.
*/

/*	cpio	COMPILE:	cc -O cpio.c -s -i -o cpio -lgen
	cpio -- copy file collections

*/
#include "errmsg.h"
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <varargs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>		/* for MAXPATHLEN */

#define	WARN_EXIT	80	/* exit this value for warning -- delta only */
int	exit_val;

#define TRUE 1
#define EQ(x,y)	(strcmp(x,y)==0)

				/* MKSHORT:  for VAX, Interdata, ...	*/
				/* Take a 4-byte long, lv, and turn it	*/
				/* into an array of two 2-byte shorts, v*/
#define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}

#define BINARY	0		/* Cflag setting */
#define ASCII	1		/* Cflag setting */
#define NONE	2		/* Cflag setting not verified */
#define M_ASCII "070707"	/* ASCII  magic number */
#define M_BINARY 070707		/* Binary magic number */
#define M_STRLEN 6		/* number bytes in ASCII magic number */
#define PATHSIZE 256		/* maximum PATH length */
#define IN	'i'		/* copy in */
#define OUT	'o'		/* copy out */
#define PASS	'p'		/* direct copy */
#define HDRSIZE	(Hdr.h_name - (char *)&Hdr)	/* header size minus filename field */
#define LINKS	500		/* no. of links allocated per bunch */
#define CHARS	76		/* ASCII header size minus filename field */
#define BUFSIZE 512		/* In u370, can't use BUFSIZ or BSIZE */
#define CPIOBSZ 4096		/* file read/write */
#define MK_USHORT(a)	(a & 00000177777)	/* Make unsigned shorts for portable  */
						/* header.  Hardware may only know    */
						/* integer operations and sign extend */
						/* the large unsigned short resulting */
						/* in 8 rather than 6 octal char in   */
						/* the header.			      */

#define	FORMAT	"%b %e %H:%M:%S %Y"     /* Date time formats */
					/* b - abbreviated month name */
					/* e - day of month ( 1 - 31) */
					/* H - hour (00 -23) */
					/* M - minute (00 - 59) */
					/* S - seconds (00 -59) */
					/* Y - year as ccyy */

/* for new dev_t */
#define	devtoshort(a)	((short)(((a)>>8)&0xff00)|((a)&0xff))
#define	shorttodev(a)	((dev_t)(((a)<<8)&0xff0000)|((a)&0xff))

/* break dev to low and high bits */

#define dev_low_bits(a)		((short)(((a)>> 8)&0xff00)| ((a)    &0x00ff))
#define dev_high_bits(a)	((short)(((a)>>16)&0xff00)|(((a)>>8)&0x00ff))

#define low_bits_dev(a)		((dev_t)(((a)<< 8)&0x00ff0000)| ((a)    &0x00ff))
#define high_bits_dev(a)	((dev_t)(((a)<<16)&0xff000000)|(((a)<<8)&0xff00))

static struct	stat	Statb, Xstatb, Astatb;

	/* Cpio header format */
static struct header {
	short	h_magic;
	short	h_dev;
	ushort	h_ino;
	ushort	h_mode,
		h_uid,
		h_gid;
	short	h_nlink;
	short	h_rdev;
	short	h_mtime[2],
		h_namesize,
		h_filesize[2];
	char	h_name[256];
} Hdr;

static dev_t	real_dev;			/* full sized dev */
static unsigned	Bufsize = BUFSIZE;		/* default record size */
static char	*Buf, *Cbuf;
static char	*Cp;


static
short	Option,
	Dir,
	Uncond,
	PassLink,
	Lflag,
	Rename,
	Toc,
	Verbose,
	Mod_time,
	Acc_time,
	Cflag,
	fflag,
	kflag,
	xflag,
#ifdef DELTA
	lstflag,
#endif /* DELTA */
	Swap,
	byteswap,
	bothswap,
	halfswap;

static
mode_t
	orig_umask,	/* umask inherited from cpio's parent process */
	df_mode = 0777,	/* default file/directory protection modes */
	adj_mode;	/* adjust mode based on original umask */

static
int	Ifile,
	Ofile,
	Input = 0,
	Output = 1;
			/* sBlocks: short Blocks.  Cumulative character   */
			/* count for short reads in bread().  Encountered */
			/* with communication lines and pipes as in:      */
			/* split -100 cpio_archive; cat xa* | cpio -icd   */
static
long	sBlocks,
	Blocks,
	Longfile,
	Longtime;

static
char	Fullname[256],
	Name[256];
static
int	Pathend;
static
char	*swfile;
static
char	*eommsg = "Change to part %d and press RETURN key. [q] ";

static
FILE	*Rtty,
	*Wtty;
static
char	ttyname[] = "/dev/tty";

static
char	**Pattern = 0;
static
char	Chdr[500];
static
dev_t	Dev;
ushort	Uid,
	A_directory,
	A_special,
	A_symlink,
	Filetype = S_IFMT;

extern	errno;
extern	void exit();
extern	ushort getuid();
extern	void perror();
extern	char *sys_errlist[];
extern	long lseek();
extern	long ulimit();
char	*malloc();
FILE 	*popen();

static
union {
	long l;
	short s[2];
	char c[4];
} U;

static	char	time_buf[50]; 		/* array to hold date and time */

/* for VAX, Interdata, ... */
static
long mklong(v)
short v[];
{
	U.l = 1;
	if(U.c[0])
		U.s[0] = v[1], U.s[1] = v[0];
	else
		U.s[0] = v[0], U.s[1] = v[1];
	return U.l;
}

main(argc, argv)
char **argv;
{
	void pwd();
	register ct;
	register char *fullp;
	register i;
	long	filesz, maxsz;
	int ans,  rc, first;
	short select;			/* set when files are selected */
	extern char	*optarg;
	extern int	optind;
	struct {
		time_t actime;
		time_t modtime;
	} times;
	char symlinkbuf[MAXPATHLEN+1];		/* for sym link support */
	int index, ignore;			/* for sym link support */

	extern char *gettxt();
	char msgid[16];
	errsource( *argv );
	errverb("notag,notofix");
	errexit( 2 );
	signal(SIGSYS, 1);
	if(*argv[1] != '-')
		usage();
	Uid = getuid();
	orig_umask = umask(0);
	/*
	 * POSIX compliance requires we set umask 0 ONLY if root user.
	 * What we do is create a mask based on our original umask which
	 * adjust the umask values found in the tape headers.  If we are
	 * running as the root user we will set this mask to 777 as we
	 * don't want to alter modes found in headers.
	 */
	if (Uid == 0)
		adj_mode = 0777;
	else {
		(void) umask(orig_umask);
		adj_mode = ~orig_umask & 0777;
	}

	eommsg = gettxt("cpio:1", eommsg);

#ifndef DELTA
	while( (ans = getopt( argc, argv, "aBC:ifopcdklLmrSsbtuxvVM:6eI:O:")) != EOF ) {
#else
	while( (ans = getopt( argc, argv, "aBC:ifopcdklLmrSsbtuxvVM:6eI:O:D")) != EOF ) {
#endif /* DELTA */


		switch( ans ) {
		case 'a':		/* reset access time */
			Acc_time++;
			break;
		case 'B':		/* change record size to 5120 bytes */
			Bufsize = 5120;
			break;
		case 'C':		/* reset buffer size to arbitrary value */
			Bufsize = atoi( optarg );
			if( Bufsize == 0 )
				errmsg( EERROR,
					gettxt("cpio:2",
					"Illegal argument to -%c, '%s'."),
					ans, optarg );
			break;
		case 'i':
			Option = IN;
			break;
		case 'f':	/* copy files not matched by patterns */
			fflag++;
			break;
		case 'o':
			Option = OUT;
			break;
		case 'p':
			Option = PASS;
			break;
		case 'c':		/* ASCII header */
			Cflag = ASCII;
			break;
		case 'd':		/* create directories when needed */
			Dir++;
			break;
		case 'l':		/* link files, when necessary */
			PassLink++;
			break;
		case 'L':
			Lflag++;
			break;;
		case 'm':		/* retain mod time */
			Mod_time++;
			break;
		case 'r':		/* rename files interactively */
			Rename++;
			Rtty = fopen(ttyname, "r");
			Wtty = fopen(ttyname, "w");
			if(Rtty==NULL || Wtty==NULL) {
				errmsg( EERROR, gettxt("cpio:3","Cannot rename (%s missing)"),
					ttyname );
			}
			break;
		case 'S':		/* swap halfwords */
			halfswap++;
			Swap++;
			break;
		case 's':		/* swap bytes */
			byteswap++;
			Swap++;
			break;
		case 'b':		/* swap both bytes and halfwords */
			bothswap++;
			Swap++;
			break;
		case 't':		/* table of contents */
			Toc++;
			break;
		case 'u':		/* copy unconditionally */
			Uncond++;
			break;
		case 'v':		/* verbose - print out file names */
			Verbose = 1;
			break;
		case 'V':		/* print a dot '.' for each file */
			Verbose = 2;
			break;
		case 'M':		/* alternate message for end-of-media */
			eommsg = optarg;
			break;
		case '6':		/* for old, sixth-edition files */
			Filetype = 060000;
			break;
		case 'I':
			chkswfile( swfile, ans, Option );
#ifndef DELTA
			close( Input );
			if( open( optarg, O_RDONLY ) != Input )
				cannotopen( optarg, gettxt("cpio:4","input" ));
#else
			if( (Input = open( optarg, O_RDONLY )) == -1 )
				cannotopen( optarg, gettxt("cpio:4","input" ));
#endif /* DELTA */
			swfile = optarg;
			break;
		case 'O':
			chkswfile( swfile, ans, Option );
			close( Output );
			if( open( optarg, O_WRONLY | O_CREAT | O_TRUNC, 0666 )
				!= Output)
				cannotopen( optarg, gettxt("cpio:5","output" ));
			swfile = optarg;
			break;
#ifdef DELTA
		case 'D':
			lstflag++;
			break;
	
#endif /* DELTA */
		case 'k':
			kflag++;
			break;
		case 'x':
			xflag++;
			break;
		default:
			usage();
		}
	}
	switch(Option) {
		case IN:
			/* Cp = Cbuf = (char *)zmalloc(EERROR, Bufsize + CHARS + PATHSIZE) + CHARS + PATHSIZE; */
			/* 16-byte aligned */
			Cp = (char *)zmalloc(EERROR, Bufsize + CHARS + PATHSIZE + 511) + CHARS + PATHSIZE;
			Cbuf = (char *)(((int)Cp + 511) &~ 511);
			Cp = Cbuf;
			break;
		case OUT:
			/* Cp = Cbuf = (char *)zmalloc( EERROR, Bufsize); */
			/* 16-byte aligned */
			Cp = (char *)zmalloc( EERROR, Bufsize + 511);
			Cbuf = (char *)(((int)Cp + 511) &~ 511);
			Cp = Cbuf;
			(void)fstat(Output, &Astatb);
			if (kflag)
				fperr(gettxt("cpio:6","`k' option is irrelevant with the `-o' option\n"));
			break;
		case PASS:
			if(Rename)
				errmsg( EERROR, gettxt("cpio:7","Pass and Rename cannot be used together."));
			if(kflag)
				fperr(gettxt("cpio:8","`k' option is irrelevant with the `-p' option\n"));
			if( Bufsize != BUFSIZE ) {
				fperr(gettxt("cpio:9","`B' or `C' option is irrelevant with the '-p' option\n"));
				Bufsize = BUFSIZE;
			}
			break;
		default:
			errmsg( EERROR, gettxt("cpio:10","Options must include one: -o, -i, -p."));
	}

	/* 16-byte aligned */
	Buf = (char *)zmalloc(EERROR, CPIOBSZ + 511);
	Buf = (char *) (((int)Buf + 511) &~ 511);

	argc -= optind;
	argv += optind;

	switch(Option) {
	case OUT:
		if(argc != 0)
			usage();
		/* get filename, copy header and file out */
		while(getname()) {
			if(!A_special && ident(&Statb, &Astatb))
				continue;
			if ( (mklong(Hdr.h_filesize) == 0L) ||
			     ((Hdr.h_mode & S_IFMT) == S_IFBLK) ||
			     ((Hdr.h_mode & S_IFMT) == S_IFCHR)    ) {
				if( Cflag )
					bwrite(Chdr,CHARS+Hdr.h_namesize);
				else
					bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
				if(Verbose)
					verbdot( stderr, Hdr.h_name);
				continue;
			}
			if (A_symlink) {
				if (mklong(Hdr.h_filesize) > MAXPATHLEN) {
				    fperr(gettxt("cpio:72","%s skipped: symlink path exceeds internal buffer\n"),
				        Hdr.h_name);
				    continue;
				}
				ct = readlink(Hdr.h_name, symlinkbuf, MAXPATHLEN);
				if (ct != mklong(Hdr.h_filesize)) {
				    fperr(gettxt("cpio:73","%s skipped: readlink failed\n"), Hdr.h_name);
				    continue;
				}
				if( Cflag )
					bwrite(Chdr,CHARS+Hdr.h_namesize);
				else
					bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
				filesz = ct;
				index = 0;
				for (; filesz > 0 ; filesz -= CPIOBSZ) {
					ct = filesz > CPIOBSZ ? CPIOBSZ : filesz;
					memcpy(Buf, &symlinkbuf[index], ct);
					index += filesz;
					bwrite(Buf, ct);
				}
				if(Verbose)
					verbdot( stderr, Hdr.h_name);
				continue;
			}
			if((Ifile = open(Hdr.h_name, O_RDONLY)) < 0) {
				fperr("<%s> ?\n", Hdr.h_name);
				continue;
			}
			if ( Cflag )
				bwrite(Chdr,CHARS+Hdr.h_namesize);
			else
				bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
			for(filesz=mklong(Hdr.h_filesize); filesz>0; filesz-= CPIOBSZ){
				ct = filesz>CPIOBSZ? CPIOBSZ: filesz;
				errno = 0;
				if(read(Ifile, Buf, (unsigned)ct) < 0) {
					fperr(gettxt("cpio:11","Cannot read %s\n"), Hdr.h_name);
					continue;
				}
				bwrite(Buf,ct);
			}
			close(Ifile);
			if(Acc_time) {
				times.actime = Statb.st_atime;
				times.modtime = Statb.st_mtime;
				utime(Hdr.h_name, &times);
			}
			if(Verbose)
				verbdot( stderr, Hdr.h_name);
		}

	/* copy trailer, after all files have been copied */
		strcpy(Hdr.h_name, "TRAILER!!!");
		Hdr.h_magic = M_BINARY;
		MKSHORT(Hdr.h_filesize, 0L);
		Hdr.h_namesize = strlen("TRAILER!!!") + 1;
		if ( Cflag )  {
			bintochar(0L);
			bwrite(Chdr, CHARS+Hdr.h_namesize);
		}
		else
			bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
		bwrite(Cbuf, Bufsize);
		break;

	case IN:
		if(argc > 0 ) {	/* save patterns, if any */
			Pattern = argv;
#ifdef DELTA
			if (lstflag) {
				fperr(gettxt("cpio:74","`D' option is irrelevant when patterns are specified"));
				lstflag = 0;
			}
		} else {
			if (!swfile && lstflag)
				errmsg( EERROR, gettxt("cpio:75","-D option requires -I option"));
#endif /* DELTA */
		}
		Cflag = NONE;
		pwd();
		maxsz = 512 * ulimit(1);
#ifdef DELTA
		if (lstflag) {
			if (getiname() == 0)
				break;
		}
#endif /* DELTA */
		while(gethdr()) {
			if( (select = ckname(Hdr.h_name))  &&  !Toc )
				Ofile = openout(Hdr.h_name);
			else
				Ofile = 0;
			filesz=mklong(Hdr.h_filesize);
			if(select && maxsz < filesz) {
				select = Ofile = 0;
				fperr(gettxt("cpio:12","%s skipped: exceeds ulimit by %d bytes\n"),
					Hdr.h_name, filesz - maxsz);
				exit_val = WARN_EXIT;
			}
			first = 1;
			if (Cflag == BINARY && (filesz % 2) == 1)
				i = 1;
			else
				i = 0;
			index = 0;		/* for sym link support */
			ignore = 0;		/* for sym link support */
			for(; filesz>0; filesz-= CPIOBSZ){
				ct = filesz>CPIOBSZ? CPIOBSZ: filesz;
				errno = 0;
				if(bread(Buf, ct, filesz + i) == -1) {
					exit_val = WARN_EXIT;
					fperr(gettxt("cpio:13","cpio: i/o error, %s is corrupt\n"),
						Hdr.h_name);
					break;
				}
						/* skip to next file */
				if (A_symlink) {
					if (ignore || (Pattern && !select))
						continue;
					if ( (index == 0) &&
					     (mklong(Hdr.h_filesize) > MAXPATHLEN) ) {
						ignore = 1;
						select = 0;
						fperr(gettxt("cpio:76","%s skipped: path too long\n"),
						  Hdr.h_name);
						continue;
					}
					if (Swap)
						swap(Buf, ct);
					(void) memcpy(&symlinkbuf[index], Buf, ct);
					index += ct;
					symlinkbuf[index] = 0;
					if ((filesz - CPIOBSZ) > 0)
						continue;
					(void) umask(~(Hdr.h_mode & adj_mode) & 0777);
					if (symlink(symlinkbuf, Hdr.h_name) < 0) {
					    fperr(gettxt("cpio:77","%s skipped: symlink to %s failed\n"),
						Hdr.h_name, symlinkbuf);
					    exit_val = WARN_EXIT;
					    ignore = 1;
					    select = 0;
					    continue;
					}
					if (Uid == 0)
						(void) umask(0);
				}
				if(Ofile) {
					if(Swap)
						swap(Buf,ct);
					errno = 0;
					if((rc = write(Ofile, Buf, ct)) < ct) {
						if(rc < 0) {
							if(first++ == 1) {
					 			fperrno(
								gettxt("cpio:14","Cannot write %s\n"),
								Hdr.h_name);
							}
#ifdef DELTA
							if (errno == EFBIG)
								exit_val = WARN_EXIT;
							else
								 exit(2);
#else
							if (errno == EFBIG || errno == ENOSPC)
								continue;
							else
								exit(2);
#endif /* DELTA */
						}
						else {
					 		fperr(gettxt("cpio:15","%s truncated\n"),
								Hdr.h_name);
							continue;
						}
					}
				}
			}
			if( Ofile ) {
				zclose( EERROR, Ofile );
				if (zchmod( EWARN, Hdr.h_name, (Hdr.h_mode & (adj_mode | 07000))) != 0)
					exit_val = WARN_EXIT;
				set_time(Hdr.h_name, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
			}
			if(select) {
				if(Verbose)
					if(Toc)
						pentry(Hdr.h_name);
					else
						verbdot( stdout, Hdr.h_name);
				else if(Toc)
					puts(Hdr.h_name);
#ifdef DELTA
				if (lstflag) {
					if (getiname() == 0)
						break;
				}
#endif /* DELTA */
			}
		}
		break;

	case PASS:		/* move files around */
		if(argc != 1)
			usage();
		if(access(argv[0], 2) == -1) {
			errmsg( EERROR, gettxt("cpio:16","cannot write in <%s>"), argv[0]);
		}
		strcpy(Fullname, argv[0]);	/* destination directory */
		zstat( EERROR, Fullname, &Xstatb );
		if((Xstatb.st_mode&S_IFMT) != S_IFDIR)
			errmsg( EERROR, gettxt("cpio:17","<%s> not a directory."), Fullname );
		Dev = Xstatb.st_dev;
		if( Fullname[ strlen(Fullname) - 1 ] != '/' )
			strcat(Fullname, "/");
		fullp = Fullname + strlen(Fullname);

		while(getname()) {
			if (A_directory && !Dir)
				fperr(gettxt("cpio:18","Use `-d' option to copy <%s>\n"),
					Hdr.h_name);
			if(!ckname(Hdr.h_name))
				continue;
			i = 0;
			while(Hdr.h_name[i] == '/')
				i++;
			strcpy(fullp, &(Hdr.h_name[i]));

			if (A_symlink) {
				if (mklong(Hdr.h_filesize) > MAXPATHLEN) {
				    fperr(gettxt("cpio:78","%s skipped: symlink path exceeds internal buffer\n"),
				        Hdr.h_name);
				    continue;
				}
				ct = readlink(Hdr.h_name, symlinkbuf, MAXPATHLEN);
				if (ct != mklong(Hdr.h_filesize)) {
				    fperr(gettxt("cpio:73","%s skipped: readlink failed\n"), Hdr.h_name);
				    continue;
				}
				symlinkbuf[ct] = 0;
				(void) umask(~(Hdr.h_mode & adj_mode) & 0777);
				if(symlink(symlinkbuf, Fullname) < 0) {
					switch(errno) {
						case ENOENT:
							if(missdir(Fullname) != 0) {
								sprintf(msgid,"syserr:%d",errno+1);
								fperr(
									gettxt("cpio:19","cpio: cannot create directory for <%s>: %s\n"),
									Fullname, gettxt(msgid,sys_errlist[errno]));
								if (Uid == 0)
									(void) umask(0);
								continue;
							}
							break;
						case EEXIST:
							zstat( EERROR, Fullname, &Xstatb );
							if ((Xstatb.st_mode&S_IFMT) != S_IFDIR) {
								if(unlink(Fullname) < 0) {
									sprintf(msgid,"syserr:%d",errno+1);
									fperr(
										gettxt("cpio:20","cpio: cannot unlink <%s>: %s\n"),
										Fullname, gettxt(msgid,sys_errlist[errno]));
									if (Uid == 0)
										(void) umask(0);
									continue;
								}
							}
							break;
						default:
							sprintf(msgid,"syserr:%d",errno+1);
							fperr(
								gettxt("cpio:79","Cpio: cannot symlink <%s> to <%s>: %s\n"),
								symlinkbuf, Fullname, gettxt(msgid,sys_errlist[errno]));
							if (Uid == 0)
								(void) umask(0);
							continue;
						}
					(void) umask(~(Hdr.h_mode & adj_mode) & 0777);
					if(symlink(symlinkbuf, Fullname) < 0) {
						sprintf(msgid,"syserr:%d",errno+1);
						fperr(
							gettxt("cpio:79","Cpio: cannot symlink <%s> to <%s>: %s\n"),
							symlinkbuf, Fullname, gettxt(msgid,sys_errlist[errno]));
						if (Uid == 0)
							(void) umask(0);
						continue;
					}
				}
				if (Uid == 0)
					(void) umask(0);
				goto ckverbose;
			}

			if( PassLink  &&  !A_directory  &&  Dev == Statb.st_dev ) {
				if(link(Hdr.h_name, Fullname) < 0) {
					switch(errno) {
						case ENOENT:
							if(missdir(Fullname) != 0) {
								sprintf(msgid,"syserr:%d",errno+1);
								fperr(
									gettxt("cpio:19","cpio: cannot create directory for <%s>: %s\n"),
									Fullname, gettxt(msgid,sys_errlist[errno]));
								continue;
							}
							break;
						case EEXIST:
							zstat( EERROR, Fullname, &Xstatb );
							if ((Xstatb.st_mode&S_IFMT) != S_IFDIR) {
								if(unlink(Fullname) < 0) {
									sprintf(msgid,"syserr:%d",errno+1);
									fperr(
										gettxt("cpio:20","cpio: cannot unlink <%s>: %s\n"),
										Fullname, gettxt(msgid,sys_errlist[errno]));
									continue;
								}
							}
							break;
						default:
							sprintf(msgid,"syserr:%d",errno+1);
							fperr(
								gettxt("cpio:21","Cpio: cannot link <%s> to <%s>: %s\n"),
								Hdr.h_name, Fullname, gettxt(msgid,sys_errlist[errno]));
							continue;
						}
					if(link(Hdr.h_name, Fullname) < 0) {
							sprintf(msgid,"syserr:%d",errno+1);
							fperr(
								gettxt("cpio:21","Cpio: cannot link <%s> to <%s>: %s\n"),
								Hdr.h_name, Fullname, gettxt(msgid,sys_errlist[errno]));
						continue;
					}
				}

				goto ckverbose;
			}
			if(!(Ofile = openout(Fullname)))
				continue;
			if((Ifile = zopen( EWARN, Hdr.h_name, 0)) < 0) {
				close(Ofile);
				continue;
			}
			filesz = Statb.st_size;
			for(; filesz > 0; filesz -= CPIOBSZ) {
				ct = filesz>CPIOBSZ? CPIOBSZ: filesz;
				errno = 0;
				if(read(Ifile, Buf, (unsigned)ct) < 0) {
					fperr(gettxt("cpio:11","Cannot read %s\n"), Hdr.h_name);
					break;
				}
				if (Ofile) {
					errno = 0;
					if (write(Ofile, Buf, ct) < 0) {
						fperr(gettxt("cpio:14","Cannot write %s\n"), Hdr.h_name);
						break;
					}
				}
					/* Removed u370 ifdef which caused cpio */
					/* to report blocks in terms of 4096 bytes. */

				Blocks += ((ct + (BUFSIZE - 1)) / BUFSIZE);
			}
			close(Ifile);
			if(Acc_time)
				utime(Hdr.h_name, &Statb.st_atime);
			if(Ofile) {
				close(Ofile);
				zchmod( EWARN, Fullname, (Hdr.h_mode & (adj_mode | 07000)));
				set_time(Fullname, Statb.st_atime, mklong(Hdr.h_mtime));
ckverbose:
				if(Verbose)
					verbdot( stdout, Fullname );
			}
		}
	}
	/* print number of blocks actually copied */
	Blocks =  Blocks * (Bufsize / BUFSIZE) +
		  ((sBlocks + (BUFSIZE - 1)) / BUFSIZE);
#ifdef DELTA
	if (lstflag == 0)
#endif /* DELTA */
		fperr(gettxt("cpio:23","%ld blocks\n"), Blocks);

#ifdef DELTA
	exit(exit_val);
#endif /* DELTA */

	exit(0);
}

static
usage()
{
	errusage("%s\n\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n",
		    gettxt("cpio:83","-o[acvVBx] [-Cbufsize] [-Mmessage] <name-list >collection"),
	Err.source, gettxt("cpio:84","-o[acvVBx] -Ocollection [-Cbufsize] [-Mmessage] <name-list"),
	Err.source, gettxt("cpio:85","-i[bcdkmrsStuvVfB6x] [-Cbufsize] [-Mmessage] [pattern ...] <collection"),
	Err.source, gettxt("cpio:86","-i[bcdkmrsStuvVfB6x] -Icollection [-Cbufsize] [-Mmessage] [pattern ...]"),
	Err.source, gettxt("cpio:87","-p[adlmruvVx] directory <name-list"));
}

static
chkswfile( sp, c, option )
char	*sp;
char	c;
short	option;
{
	if( !option )
		errmsg( EERROR, gettxt("cpio:24","-%c must be specified before -%c option."),
			c == 'I' ? 'i' : 'o', c );
	if( (c == 'I'  &&  option != IN)  ||  (c == 'O'  &&  option != OUT) )
		errmsg( EERROR, gettxt("cpio:25","-%c option not permitted with -%c option."), c,
			option );
	if( !sp )
		return;
	errmsg( EERROR, gettxt("cpio:26","No more than one -I or -O flag permitted."));
}

static
cannotopen( sp, mode )
char	*sp, *mode;
{
	errmsg( EERROR, gettxt("cpio:27","Cannot open <%s> for %s."), sp, mode );
}

#ifdef DELTA
static
getiname()		/* get file name, and setup Pattern */
{
	static char *namep;

	namep = Name;
	if(gets(namep) == NULL)
		return 0;
	while(*namep == '.' && namep[1] == '/') {
		namep++;
		while(*namep == '/') namep++;
	}
	Pattern = &namep;
	return 1;
}
#endif /* DELTA */

static
long
GetLinkId()
{
	register long i;
	static struct ml {
		dev_t	m_dev;
		ino_t	m_ino;
	} **ml = 0;
	register struct ml	*mlp;
	static long mlsize = 0;
	static long mlinks = 0;


	if( !ml ) {
		mlsize = LINKS;
		ml = (struct ml **) malloc(mlsize * sizeof(struct ml *));
	}
	else if( mlinks == mlsize ) {
		mlsize += LINKS;
		ml = (struct ml **) realloc((char *) ml,
		    mlsize * sizeof(struct ml *));
	}
	if (ml == NULL) {
		fperr(gettxt("cpio:50","No memory for links (%d)\n"), mlsize);
		exit(2);
	}
	for(i = 0; i < mlinks; i++) {
		mlp = ml[i];
		if(mlp->m_ino == Statb.st_ino && mlp->m_dev == Statb.st_dev)
			return i;
	}
	if( !(ml[mlinks] = (struct ml *)malloc(sizeof(struct ml)))) {
		fperr(gettxt("cpio:50","No memory for links (%d)\n"), mlinks);
		exit(2);
	}
	ml[mlinks]->m_dev = Statb.st_dev;
	ml[mlinks]->m_ino = Statb.st_ino;
	return (mlinks++);
}

static
getname()		/* get file name, get info for header */
{
	register char *namep = Name;
	register ushort ftype;
	long tlong;

	for(;;) {
		if(gets(namep) == NULL)
			return 0;
		while(*namep == '.' && namep[1] == '/') {
			namep++;
			while(*namep == '/') namep++;
		}
		strcpy(Hdr.h_name, namep);
		if (Lflag) {
		    if(zstat( EWARN, namep, &Statb) < 0) {
			continue;
		    }
		} else {
		    if(zlstat( EWARN, namep, &Statb) < 0) {
			continue;
		    }
		}
		ftype = Statb.st_mode & Filetype;
		A_directory = (ftype == S_IFDIR);
		A_special = (ftype == S_IFBLK)
			|| (ftype == S_IFCHR)
			|| (ftype == S_IFIFO);
		A_symlink = (ftype == S_IFLNK);
		Hdr.h_magic = M_BINARY;
		Hdr.h_namesize = strlen(Hdr.h_name) + 1;
		Hdr.h_uid = Statb.st_uid;
		Hdr.h_gid = Statb.st_gid;
		if (!A_directory && Statb.st_nlink > 1) {
			tlong = GetLinkId();
			Hdr.h_dev = (tlong & 0xffff);
			Hdr.h_ino = ((tlong >> 16) & 0xffff);
		} else {
			Hdr.h_dev = devtoshort(Statb.st_dev);
			Hdr.h_ino = Statb.st_ino;
		}
		Hdr.h_mode = Statb.st_mode;
		MKSHORT(Hdr.h_mtime, Statb.st_mtime);
		Hdr.h_nlink = Statb.st_nlink;
		if (A_symlink)
		    tlong = Statb.st_size;
		else
		    tlong = (Hdr.h_mode&S_IFMT) == S_IFREG? Statb.st_size: 0L;
		MKSHORT(Hdr.h_filesize, tlong);
		if (xflag) {
		    if ( (ftype == S_IFBLK) || (ftype == S_IFCHR) ) {
			if (ftype == S_IFBLK)
			    fprintf(stderr, gettxt("cpio:80","< %s > : block special file, ignoring.\n"),
				Hdr.h_name);
			else
			    fprintf(stderr, gettxt("cpio:81","< %s > : character special file, ignoring.\n"),
				Hdr.h_name);
			continue;
		    }
		}
		if ( (ftype == S_IFBLK) || (ftype == S_IFCHR) ) {
			real_dev = Statb.st_rdev;
			Hdr.h_rdev = dev_low_bits(Statb.st_rdev);
			tlong = dev_high_bits(Statb.st_rdev);
			MKSHORT(Hdr.h_filesize, tlong);
		} else {
			Hdr.h_rdev = devtoshort(Statb.st_rdev);
		}
		if( Cflag )
			bintochar(tlong);
		return 1;
	}
}

static
bintochar(t)		/* ASCII header write */
long t;
{
	sprintf(Chdr,"%.6o%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.11lo%.6ho%.11lo%s",
		M_BINARY, MK_USHORT(Hdr.h_dev), MK_USHORT(Hdr.h_ino), Statb.st_mode, Statb.st_uid,
		Statb.st_gid, Statb.st_nlink, MK_USHORT(Hdr.h_rdev),
		Statb.st_mtime, (short)strlen(Hdr.h_name)+1, t, Hdr.h_name);
}

static
chartobin()		/* ASCII header read */
{
	dev_t d1, d2;

	sscanf(Chdr, "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6ho%11lo",
		&Hdr.h_magic, &Hdr.h_dev, &Hdr.h_ino, &Hdr.h_mode, &Hdr.h_uid,
		&Hdr.h_gid, &Hdr.h_nlink, &Hdr.h_rdev, &Longtime,
		&Hdr.h_namesize, &Longfile);
	if (! xflag && ( ((Hdr.h_mode & S_IFMT) == S_IFCHR) ||
	                 ((Hdr.h_mode & S_IFMT) == S_IFBLK)    ) ) {
		d1 = low_bits_dev(Hdr.h_rdev);
		d2 = high_bits_dev(Longfile);
		real_dev = d2 | d1;
		Longfile = 0L;
	}
	MKSHORT(Hdr.h_filesize, Longfile);
	MKSHORT(Hdr.h_mtime, Longtime);
}


static
gethdr()		/* get file headers */
{
	void synch();
	register ushort ftype;

	synch();
	if(EQ(Hdr.h_name, "TRAILER!!!"))
		return 0;
	ftype = Hdr.h_mode & Filetype;
	A_directory = (ftype == S_IFDIR);
	A_special = (ftype == S_IFBLK)
		||  (ftype == S_IFCHR)
		||  (ftype == S_IFIFO);
	A_symlink = (ftype == S_IFLNK);
	return 1;
}

static
ckname(namep)	/* check filenames with patterns given on cmd line */
register char *namep;
{
	char	buf[sizeof Hdr.h_name];

	if(fflag ^ !nmatch(namep, Pattern)) {
		return 0;
	}
	if(Rename && !A_directory) {	/* rename interactively */
		fprintf(Wtty, gettxt("cpio:28","Rename <%s>\n"), namep);
		fflush(Wtty);
		fgets(buf, sizeof buf, Rtty);
		if(feof(Rtty))
			exit(2);
		buf[strlen(buf) - 1] = '\0';
		if(EQ(buf, "")) {
			strcpy(namep,buf);
			printf(gettxt("cpio:29","Skipped\n"));
			return 0;
		}
		else if(EQ(buf, "."))
			printf(gettxt("cpio:30","Same name\n"));
		else
			strcpy(namep,buf);
	}
	return  1;
}

static
openout(namep)	/* open files for writing, set all necessary info */
register char *namep;
{
	register f;
	register char *np;
	int ans;

	if(!strncmp(namep, "./", 2))
		namep += 2;
	np = namep;
	if(A_directory) {
		if( !Dir  ||  Rename  ||  EQ(namep, "..") )
			/* do not consider .. files */
			return 0;
		else if( EQ(namep, ".") )
			/* set owner and modes on . files */
			goto ret;
		else if( EQ(namep, "") ) {
			/*
			 * if it is "" change it to "." and set owner
			 * and mode on it
			 */
			namep = ".";
			goto ret;
		}
		if(stat(namep, &Xstatb) == -1) {

/* try creating (only twice) */
			(void) umask(orig_umask);
			ans = 0;
			do {
				if (mkdir(namep, df_mode) < 0) {
					ans += 1;
				}else {
					ans = 0;
					break;
				}
			}while(ans < 2 && missdir(namep) == 0);
			if (Uid == 0)
				(void) umask(0);
			if(ans == 1) {
				fperrno(gettxt("cpio:31","Cannot create directory for <%s>"),
					namep);
				exit_val = WARN_EXIT;
				return(0);
			}else if(ans == 2) {
				fperrno(gettxt("cpio:32","Cannot create directory <%s>"), namep);
				exit_val = WARN_EXIT;
				return(0);
			}
		}

ret:
		if (zchmod( EWARN, namep, (Hdr.h_mode & (adj_mode | 07000))) != 0)
			exit_val = WARN_EXIT;
		if(Uid == 0) {
			if (zchown( EWARN, namep, Hdr.h_uid, Hdr.h_gid) != 0)
				exit_val = WARN_EXIT;
		}
		set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
		return 0;
	}

	if ( ((Hdr.h_mode & S_IFMT) == S_IFCHR) ||
	     ((Hdr.h_mode & S_IFMT) == S_IFBLK)    ) {
		MKSHORT(Hdr.h_filesize, 0L);
		if (xflag) {
		    if ((Hdr.h_mode & S_IFMT) == S_IFBLK)
			fprintf(stderr, gettxt("cpio:80","< %s > : block special file, ignoring.\n"),
			    namep);
		    else
			fprintf(stderr, gettxt("cpio:81","< %s > : character special file, ignoring.\n"),
			    namep);
		    return(0);
		}
	}

	if (A_symlink)
		return(0);
	if(Hdr.h_nlink > 1)
		if(!postml(namep, np))
			return 0;
	if(stat(namep, &Xstatb) == 0) {
		if(Uncond && ((Xstatb.st_mode & S_IFMT) != S_IFDIR) && !((!(Xstatb.st_mode & S_IWRITE) || A_special) && (Uid != 0))) {
			if(unlink(namep) < 0) {
				fperrno(gettxt("cpio:33","cannot unlink current <%s>"), namep);
			}
		}
		if(!Uncond && (mklong(Hdr.h_mtime) <= Xstatb.st_mtime)) {
		/* There's a newer or same aged version of file on destination */
			fperr(gettxt("cpio:34","current <%s> newer or same age\n"), np);
			return 0;
		}
	}
	if( Option == PASS
		&& Statb.st_ino == Xstatb.st_ino
		&& Statb.st_dev == Xstatb.st_dev) {
		errmsg( EERROR, gettxt("cpio:35","Attempt to pass file to self!"));
	}
	if(A_special) {
		if((Hdr.h_mode & Filetype) == S_IFIFO)
			real_dev = 0;

/* try creating (only twice) */
		ans = 0;
		do {
			if(mknod(namep, Hdr.h_mode, real_dev) < 0) {
				ans += 1;
			}else {
				ans = 0;
				break;
			}
		}while(ans < 2 && missdir(np) == 0);
		if(ans == 1) {
			fperrno(gettxt("cpio:31","Cannot create directory for <%s>"), namep);
			exit_val = WARN_EXIT;
			return(0);
		}else if(ans == 2) {
			fperrno(gettxt("cpio:36","Cannot mknod <%s>"), namep);
			exit_val = WARN_EXIT;
			return(0);
		}

		goto ret;
	}

/* try creating (only twice) */
	ans = 0;
	do {
		if((f = creat(namep, Hdr.h_mode)) < 0) {
			ans += 1;
		}else {
			ans = 0;
			break;
		}
	}while(ans < 2 && missdir(np) == 0);
	if(ans == 1) {
		fperrno(gettxt("cpio:31","Cannot create directory for <%s>"), namep);
		exit_val = WARN_EXIT;
		return(0);
	}else if(ans == 2) {
		fperrno(gettxt("cpio:37","Cannot create <%s>"), namep);
		exit_val = WARN_EXIT;
		return(0);
	}

	if(Uid == 0) {
		if (zchown( EWARN, namep, Hdr.h_uid, Hdr.h_gid) != 0)
			exit_val = WARN_EXIT;
	}
	return f;
}


/*	Shared by bread(), synch() and rstbuf()
*/
static int	nleft = 0;	/* unread chars left in Cbuf and expansion buffer*/
static char	*ip;		/* pointer to next char to be read from Cbuf and */
				/* expansion buffer*/
static int	filbuf = 0;	/* flag to bread() to fill buffer but transfer	*/
				/* no characters				*/

	/* bread() is called by rstbuf() in order to read the next block	*/
	/* from the input archive into Cbuf.  bread() is called by main(),	*/
	/* case: IN, in order to read in file content to then be written out.	*/
	/* In case of I/O error bread() exits with return code 2 unless kflag	*/
	/* is set.  If kflag is set then bread attempts a max of 10 times to	*/
	/* successfully lseek then read good data.  If 10 consecutive reads	*/
	/* fail bread() exits with return code 2.  Upon a successful read()	*/
	/* bread() leaves nleft and the pointers ip and p set correctly but no	*/
	/* characters are copied and bread() returns -1.  In the case of no I/O	*/
	/* errors bread() reads from the input archive to Cbuf and then copies	*/
	/* chars to a target buffer unless filbuf is set by rstbuf().		*/

	/* d, distance to lseek if I/O error encountered with -k option.*/
	/* Converted to a multiple of Bufsize				*/
static
bread(b, c, d)
register char	*b;
register int	c;
long d;
{
	register char	*p = ip;
	register int	dcr;
	register int	rv;
	int rc = 0, delta = 0, i = 0;
	char msgid[16];

	if (filbuf == 1) {	/* fill buffer, memcpy no chars */
		nleft = 0;
		c = 0;
	}
	if( !Cflag ) {
		/* round c up to an even number */
		c = (c+1)/2;
		c *= 2;
	}
	while ( c || filbuf )  {
		while (nleft == 0 ) {
			while (TRUE) {
				errno = 0;
				rv = read(Input, Cbuf, Bufsize);
				if ((rv == 0) || ((rv == -1) && (errno == ENOSPC || errno == ENXIO)))
					Input = chgreel(0, Input, rv);
				else
					break;
			}
			if( rv == -1 ) {
				int s, rvl;
				if( kflag ) {
					if (i++ > 10) {
						sprintf(msgid,"syserr:%d",errno+1);
						fperr(gettxt("cpio:38","cpio: cannot recover from I/O error, %s\n"),
							gettxt(msgid,sys_errlist[errno]));
						exit(2);
					}
					rvl = lseek(Input,
						(s = d / Bufsize) == 0 ? Bufsize : s * Bufsize, 1); 
					if(i == 1) {
						if (rvl != -1 && (d % Bufsize)
						   && d > Bufsize)
							delta = (d % Bufsize);
						d = 0;
						rc = -1;
					}
					else
						delta = 0;
				}
				else
					errmsg( EERROR,
						gettxt("cpio:39","Read() in bread() failed\n"));
			}
			else {
				if (rv == Bufsize)
					++Blocks;
				else 				/* short read */
					sBlocks += rv;
				if (rv > delta) {
					nleft = rv - delta;
					p = Cbuf + delta;
				}
				else {
					nleft = 0;
					delta -= rv;
				}
			}
		}
		if (filbuf || i > 0) {
			filbuf = 0;
			break;
		}
		if (nleft <= c)
			dcr = nleft;
		else
			dcr = c;
		memcpy( b, p, dcr );
		c -= dcr;
		d -= dcr;
		b += dcr;
		p += dcr;
		nleft -= dcr;
	}
	ip = p;
	return(rc);
}
		/* synch() searches for headers.  Any Cflag specification by the */
		/* user is ignored.  Cflag is set appropriately after a good    */
		/* header is found.  It searches for and verifies all headers.  */
		/* Unless kflag is set only one failure of the header causes an */
		/* exit(2).  I/O errors during examination of any part of the   */
		/* header causes synch() to throw away current data and begin	*/
		/* again.  Other errors during examination of any part of the   */
		/* header causes synch() to advance a single byte and continue   */
		/* the examination.						*/
static
void
synch()
{
	register char	*magic;
	register int	hit = NONE, cnt = 0;
	int hsize, offset, align = 0;
	static int min = CHARS;
	union {
		char bite[2];
		ushort temp;
	} mag;
	dev_t d1, d2;

	if (Cflag == NONE) {
		filbuf = 1;
		if (bread(Chdr, 0, 0) == -1) {
			fperr(gettxt("cpio:40","cpio: I/O error, searching to next header\n"));
			exit_val = WARN_EXIT;
		}
	}
	magic = ip;
	do {
		while (nleft < min) {
			if (rstbuf(magic) == -1) ;
			magic = ip;
		}
		if (Cflag == ASCII || Cflag == NONE) {
			if (strncmp(magic, M_ASCII, M_STRLEN) == 0) {
				memcpy(Chdr, magic, CHARS);
				chartobin();
				hit = ASCII;
				hsize = CHARS + Hdr.h_namesize;
			}
		}
		if (Cflag == BINARY || Cflag == NONE) {
		    mag.bite[0] = magic[0];
		    mag.bite[1] = magic[1];
		    if (mag.temp == M_BINARY) {
		    	memcpy(&Hdr, magic, HDRSIZE);
		    	if (!xflag && ( ((Hdr.h_mode & S_IFMT) == S_IFCHR) ||
	    		                ((Hdr.h_mode & S_IFMT) == S_IFBLK)   ) ) {
				    Longfile = mklong(Hdr.h_filesize);
				    d1 = low_bits_dev(Hdr.h_rdev);
				    d2 = high_bits_dev(Longfile);
				    real_dev = d2 | d1;
				    Longfile = 0L;
				    MKSHORT(Hdr.h_filesize, Longfile);
		    	}
		    	hit = BINARY;
		    	hsize = HDRSIZE + Hdr.h_namesize;
		    	align =	((Hdr.h_namesize % 2) == 0 ? 0 : 1);
		    }
		}
		if (hit == NONE) {
			magic++;
			nleft--;
		}
		else {
			if (hdck() == -1) {
				magic++;
				nleft--;
				hit = NONE;
				if (kflag) {
				    fperr(
				    gettxt("cpio:41","cpio: header corrupted.  File(s) may be lost\n"));
				    exit_val = WARN_EXIT;
				}
			}
			else {			/* consider possible alignment byte */
				while (nleft < hsize + align) {
					if (rstbuf(magic) == -1) {
						magic = ip;
						hit = NONE;
						break;
					}
					else 
						magic = ip;
				}
				if (hit == NONE)
					continue;
				if (*(magic + hsize - 1) != '\0') {
					magic++;
					nleft--;
					hit = NONE;
					continue;
				}
			}
		}
		if (cnt++ == 2) {
			fperr(
			gettxt("cpio:42","cpio: out of sync.  searching for magic number/header\n"));
			exit_val = WARN_EXIT;
		}
	}
	while (hit == NONE && kflag);

	if (cnt > 2)
		fperr(gettxt("cpio:43","cpio: re-synchronized on magic number/header\n"));
	if (hit == NONE) {
		if (Cflag == NONE)
			errmsg( EERROR,
				gettxt("cpio:44",
				"This is not a cpio file.  Bad header."));
		else
			errmsg( EERROR,
				gettxt("cpio:45",
				"Out of sync.  bad magic number/header."));
	}	
	if (hit == ASCII) {
		memcpy(Hdr.h_name, magic + CHARS, Hdr.h_namesize);
		Cflag = ASCII;
	}
	else {
		memcpy(Hdr.h_name, magic + HDRSIZE, Hdr.h_namesize + align);
		Cflag = BINARY;
		min = HDRSIZE;
	}
	offset = min + Hdr.h_namesize + align;
	ip = magic + offset;
	nleft -= offset;
}

		/* rstbuf(), reset bread() buffer,  moves incomplete potential	*/
		/* headers from Cbuf to an expansion buffer to the left of Cbuf.*/
		/* It then forces bread() to replenish Cbuf.  Rstbuf() returns	*/
		/* the value returned by bread() to warn synch() of I/O errors.	*/
		/* nleft and ip are updated to reflect the new data available.	*/
static
int
rstbuf(ptr)
char *ptr;
{
	int rc = 0, eleft;			/* eleft: amt in expansion buffer */

	smemcpy(Cbuf - nleft, ptr, nleft);	/* mv leftover bytes to expansion */
	eleft = nleft;				/*			   buffer */
	filbuf = 1;				/* force fill of Cbuf		  */

	if ((rc = bread(Chdr, 0, 0)) == -1)
		;				/* ip & nleft are ok */
	else {
		nleft += eleft;
		ip -= eleft;
	}
	return(rc);
}

			
	/* hdck() sanity checks the fixed length portion of the cpio header	*/
	/* -1 indicates a bad header and 0 indicates a good header		*/
static
int
hdck()
{
	if (Hdr.h_nlink < 1 ||
		mklong(Hdr.h_filesize) < 0 ||
		Hdr.h_namesize <= 0 ||
		Hdr.h_namesize >= PATHSIZE)
		return(-1);
	else
		return(0);
}

static
bwrite(rp, c)
register char *rp;
register c;
{
	register char	*cp = Cp;
	static unsigned	Ccnt = 0;
	register unsigned Cleft;
	register int	rv;

	if( !Cflag ) {
		/* round c up to an even number */
		c = (c+1)/2;
		c *= 2;
	}
	while( c )  {
		if( (Cleft = Bufsize - Ccnt) <= c ) {
			memcpy( cp, rp, Cleft );
			errno = 0;
			rv = write(Output, Cbuf, Bufsize);
			if( rv <= 0 )
				rv = eomchgreel(rv);
			if( rv == Bufsize )
				{
				Ccnt = 0;
				cp = Cbuf;
				}
			else if( rv < Bufsize )
				{
				Output = chgreel(1, Output, 0);
				smemcpy( Cbuf, &Cbuf[ Bufsize - rv ], rv );
				Ccnt = Bufsize - rv;
				cp = &Cbuf[ rv ];
				}
			else
				errmsg( EHALT,
					gettxt("cpio:46",
					"Impossible return from write(), %d\n"),
					rv );
			++Blocks;
			rp += Cleft;
			c -= Cleft;
		}
		else {
			memcpy( cp, rp, c );
			Ccnt += c;
			cp += c;
			rp += c;
			c = 0;
		}
	}
	Cp = cp;
}


static int	reelcount = 1;	/* used below and in chgreel() */

/*	Change reel due to reaching end-of-media.
	Keep trying to get a successful write before considering the
	change-of-reel as successful.
*/
static
int
eomchgreel(rv)
int rv;
{
	while( 1 ) {
		Output = chgreel(1, Output, rv);
		errno = 0;
		rv = write(Output, Cbuf, Bufsize);
		if( rv == Bufsize )
			return  rv;
		fperr( gettxt("cpio:47","Unable to write this medium.  Try again.\n" ));
		reelcount--;
	}
	/*NOTREACHED*/
}


static
postml(namep, np)		/* linking function:  Postml() is called after*/
register char *namep, *np;	/* namep is created.  Postml() checks to see  */
{				/* if namep should be linked to np.  If so,   */
				/* postml() removes the independent instance  */
	register i;		/* of namep and links namep to np.	      */
	static struct ml {
		short	m_dev;
		ushort	m_ino;
		char	m_name[2];
	} **ml = 0;
	register struct ml	*mlp;
	static unsigned	mlsize = 0;
	static unsigned	mlinks = 0;
	char		*lnamep;
	int		ans;

	if( !ml ) {
		mlsize = LINKS;
		ml = (struct ml **) zmalloc( EERROR, mlsize * sizeof(struct ml *));
	}
	else if( mlinks == mlsize ) {
		mlsize += LINKS;
		ml = (struct ml **) zrealloc( EERROR, ml, mlsize * sizeof(struct ml *));
	}
	for(i = 0; i < mlinks; ++i) {
		mlp = ml[i];
		if(mlp->m_ino==Hdr.h_ino  &&  mlp->m_dev==Hdr.h_dev) {
			if(Verbose == 1)
#ifdef DELTA
			    if (lstflag == 0)
#endif /* DELTA */
				fperr(gettxt("cpio:48","%s linked to %s\n"), mlp->m_name, np);
			if(Verbose && Option == PASS)
				verbdot(stdout, np);
			unlink(namep);
			if(Option == IN && *(mlp->m_name) != '/') {
				Fullname[Pathend] = '\0';
				strcat(Fullname, mlp->m_name);
				lnamep = Fullname;
			}
			lnamep = mlp->m_name;

/* try linking (only twice) */
			ans = 0;
			do {
				if(link(lnamep, namep) < 0) {
					ans += 1;
				}else {
					ans = 0;
					break;
				}
			}while(ans < 2 && missdir(np) == 0);
			if(ans == 1) {
				fperrno(gettxt("cpio:31","Cannot create directory for <%s>"), np);
				return(0);
			}else if(ans == 2) {
				fperrno(gettxt("cpio:49","Cannot link <%s> & <%s>"), lnamep, np);
				return(0);
			}

			set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
			return 0;
		}
	}
	if( !(ml[mlinks] = (struct ml *)zmalloc( EWARN, strlen(np) + 2 + sizeof(struct ml)))) {
		static int first=1;

		if(first)
			fperr(gettxt("cpio:50","No memory for links (%d)\n"), mlinks);
		first = 0;
		return 1;
	}
	ml[mlinks]->m_dev = Hdr.h_dev;
	ml[mlinks]->m_ino = Hdr.h_ino;
	strcpy(ml[mlinks]->m_name, np);
	++mlinks;
	return 1;
}

static
pentry(namep)		/* print verbose table of contents */
register char *namep;
{

	static short lastid = -1;
#include <pwd.h>
	static struct passwd *pw;
	struct passwd *getpwuid();

	printf("%-7o", MK_USHORT(Hdr.h_mode));
	if(lastid == Hdr.h_uid)
		printf("%-6s", pw->pw_name);
	else {
		setpwent();
		if(pw = getpwuid((int)Hdr.h_uid)) {
			printf("%-6s", pw->pw_name);
			lastid = Hdr.h_uid;
		} else {
			printf("%-6d", Hdr.h_uid);
			lastid = -1;
		}
	}
	printf("%7ld ", mklong(Hdr.h_filesize));
	U.l = mklong(Hdr.h_mtime);
	cftime(time_buf, FORMAT, (long *)&U.l);
	printf(" %s  %s\n", time_buf, namep);
}

		/* pattern matching functions */
static
nmatch(s, pat)
char *s, **pat;
{
	if( !pat )
		return 1;
#ifdef DELTA
	if (lstflag)  {
		if (strcmp(s, *pat) == 0)
			return 1;
		return 0;
	}
#endif
	while(*pat) {
		if((**pat == '!' && !gmatch(s, *pat+1))
		|| gmatch(s, *pat))
			return 1;
		++pat;
	}
	return 0;
}

static
swap(buf, ct)		/* swap halfwords, bytes or both */
register ct;
register char *buf;
{
	register char c;
	register union swp { long	longw; short	shortv[2]; char charv[4]; } *pbuf;
	int savect, n, i;
	char *savebuf;
	short cc;

	savect = ct;	savebuf = buf;
	if(byteswap || bothswap) {
		if (ct % 2) buf[ct] = 0;
		ct = (ct + 1) / 2;
		while (ct--) {
			c = *buf;
			*buf = *(buf + 1);
			*(buf + 1) = c;
			buf += 2;
		}
		if (bothswap) {
			ct = savect;
			pbuf = (union swp *)savebuf;
			if (n = ct % sizeof(union swp)) {
				if(n % 2)
					for(i = ct + 1; i <= ct + (sizeof(union swp) - n); i++) pbuf->charv[i] = 0;
				else
					for (i = ct; i < ct + (sizeof(union swp) - n); i++) pbuf->charv[i] = 0;
			}
			ct = (ct + (sizeof(union swp) -1)) / sizeof(union swp);
			while(ct--) {
				cc = pbuf->shortv[0];
				pbuf->shortv[0] = pbuf->shortv[1];
				pbuf->shortv[1] = cc;
				++pbuf;
			}
		}
	}
	else if (halfswap) {
		pbuf = (union swp *)buf;
		if (n = ct % sizeof(union swp))
			for (i = ct; i < ct + (sizeof(union swp) - n); i++) pbuf->charv[i] = 0;
		ct = (ct + (sizeof(union swp) -1)) / sizeof(union swp);
		while (ct--) {
			cc = pbuf->shortv[0];
			pbuf->shortv[0] = pbuf->shortv[1];
			pbuf->shortv[1] = cc;
			++pbuf;
		}
	}
}


static
set_time(namep, atime, mtime)	/* set access and modification times */
register char *namep;
time_t atime, mtime;
{
	static time_t timevec[2];

	if(!Mod_time)
		return;
	timevec[0] = atime;
	timevec[1] = mtime;
	utime(namep, timevec);
}



static
chgreel(x, fl, rv)
{
	register f;
	char str[BUFSIZ];
	struct stat statb;
	int tmperrno;

	tmperrno = errno;
	fstat(fl, &statb);
	if ((statb.st_mode&S_IFMT) != S_IFCHR)
		{
		if (x && rv == -1)
			switch (tmperrno)
				{
				case EFBIG:	fperr(gettxt("cpio:52","cpio: ulimit reached for output file\n"));
						break;
				case ENOSPC:	fperr(gettxt("cpio:53","cpio: no space left for output file\n"));
						break;
				default:	errmsg( EERROR,
							gettxt("cpio:54","write() in bwrite() failed\n"));
				}
		else
			fperr( gettxt("cpio:55","Can't read input:  end of file encountered \
prior to expected end of archive.\n"));
		exit(2);
		}
	if ((rv == 0) || ((rv == -1) && (tmperrno == ENOSPC  ||  tmperrno == ENXIO)))
		if(x)
		fperr( gettxt("cpio:92","\007Reached end of medium on output\n"));
		else
		fperr( gettxt("cpio:93","\007Reached end of medium on input\n"));
	else
		{
		if(x)
		fperr( gettxt("cpio:94","\007Encountered an error on output\n"));
		else
		fperr( gettxt("cpio:95","\007Encountered an error on input\n"));
		exit(2);
		}
	if( Rtty == NULL )
		Rtty = zfopen( EERROR, ttyname, "r");
	close(fl);
	reelcount++;
again:
	if( swfile ) {
	    askagain:
		fperr( eommsg, reelcount );
		fgets(str, sizeof str, Rtty);
		switch( *str ) {
		case '\n':
			strcpy( str, swfile );
			break;
		case 'q':
			exit(2);
		default:
			goto askagain;
		}
	}
	else {
		fperr(gettxt("cpio:56","If you want to go on, type device/file name when ready.\n"));
		fgets(str, sizeof str, Rtty);
		str[strlen(str) - 1] = '\0';
		if(!*str)
			exit(2);
	}
	if((f = open(str, x? 1: 0)) < 0) {
		fperr(gettxt("cpio:57","That didn't work, cannot open \"%s\"\n"), str);
		if( errno )
			perror("");
		goto again;
	}
	return f;
}



static
missdir(namep)
register char *namep;
{
	register char *np;
	register ct = 2;

	for(np = namep; *np; ++np)
		if(*np == '/') {
			if(np == namep) continue;	/* skip over 'root slash' */
			*np = '\0';
			if(stat(namep, &Xstatb) == -1) {
				if(Dir) {
					(void) umask(orig_umask);
					ct = mkdir(namep, df_mode);
					if (Uid == 0)
						(void) umask(0);
					if (ct != 0) {
						*np = '/';
						return(ct);
					}
				}else {
					fperr(gettxt("cpio:58","missing 'd' option\n"));
					return(-1);
				}
			}
			*np = '/';
		}
	if (ct == 2) ct = 0;		/* the file already exists */
	return ct;
}



static
void
pwd()		/* get working directory */
{
	FILE *dir;

	if((dir = popen("pwd", "r")) == NULL) {
		fperr(gettxt("cpio:59","cpio: popen() failed, cannot determine working directory\n"));
		exit(2);
	}
	fgets(Fullname, sizeof Fullname, dir);
	if(pclose(dir))
		exit(2);
	Pathend = strlen(Fullname);
	Fullname[Pathend - 1] = '/';
}


static int	verbcount = 0;
static FILE	*verbout = 0;
/*
	In -V verbose mode, print out a dot for each file processed.
*/
static
verbdot( fp, cp )
FILE	*fp;
char	*cp;
{

	if( !verbout )
		verbout = fp;
	if( Verbose == 2 ) {
		fputc( '.', fp );
		if( ++verbcount >= 50 ) {
			/* start a new line of dots */
			verbcount = 0;
			fputc( '\n', fp );
		}
	}
	else {
		fputs( cp, fp );
		fputc( '\n', fp );
#ifdef DELTA
		fflush( fp );
#endif /* DELTA */
	}
}


/*
	print message on the stderr
*/
static
fperr( va_alist )
va_dcl
{
	va_list	args;
	char	*fmt;

	resetverbcount();
	va_start( args );
	fmt = va_arg( args, char * );
	vfprintf( stderr, fmt, args );
	fflush( stderr );
}

/*
	print message on the stderr followed by error number and meaning.
*/
static
fperrno( va_alist )
va_dcl
{
	va_list	args;
	char	*fmt;

	resetverbcount();
	va_start( args );
	fmt = va_arg( args, char * );
	vfprintf( stderr, fmt, args );
	fprintf( stderr, gettxt("cpio:60",", errno %d, "), errno );
	fflush( stderr );
	perror("");
}


static
resetverbcount()
{
	if( Verbose == 2  &&  verbcount ) {
		fputc( '\n', verbout );
		verbcount = 0;
	}
}
			/* ident() accepts pointers to two stat structures and	  */
			/* determines if they correspond to identical files.	  */
			/* ident() assumes that if the device and inode are the	  */
			/* same then files are identical.  ident()'s purpose is to*/
			/* prevent putting the archive name in the output archive.*/
static
int
ident(in, ot)
struct stat *in, *ot;
{
	if (in->st_ino == ot->st_ino &&
	    in->st_dev == ot->st_dev)
		return(1);
	else
		return(0);
}


int
zlstat( severity, path, buf)
int	severity;
char	*path;
struct	stat *buf;
{
	int	rc;

	/* if the name is the null string convert it to "." */
	if (EQ(path, ""))
		path = ".";
	if((rc = lstat(path, buf)) == -1 )
		_errmsg("UXzlstat1", severity,
		      gettxt("cpio:82","Cannot obtain information about file:  \"%s\"."),
		       path);


	return  rc;
}
