/*
 * afio.c
 *
 * Manipulate archives and files.
 *
 * This software was written by Mark Brukhartz at Lachman Associates,
 * Inc..  Additional code was written by a large cast of people.  
 *
 * Licensing and (re)distribution
 * ------------------------------
 *
 * THE SUMMARY INFORMATION BELOW WAS WRITTEN FOR THE BENEFIT OF 
 * SOFTWARE DISTRIBUTORS
 *
 * Because of historical reasons, different parts of this software
 * package are covered by different licenses.  However:
 *
 * A) This software package as a whole may be re-distributed by any
 *    method that satisfies the conditions of both the Perl "Artistic
 *    License" and the GNU Library General Public License.
 *
 * B) According to the theory.html file of the Sunsite Archive
 *    Maintainers, this implies that the correct LSM template field
 *    is:
 *
 *          Copying-policy: LGPL
 * 
 * C) This software package can also be re-distributed under
 *    particular conditions that are _weaker_ than the Perl "Artistic
 *    License" combined with the GNU Library General Public License.
 *    Redistribution need only satisfy all four license notices below.
 *
 * Disclaimer: I am not a lawyer, and neither are the Sunsite Archive
 * Maintainers.
 *
 * END OF SUMMARY INFORMATION
 *
 * ------------------------------------------------------------------ 
 *
 * License notice 1, covering part of this software package.
 *
 * [Covers the original 1985 afio code]
 * 
 * Copyright (c) 1985 Lachman Associates, Inc..
 *
 * This software was written by Mark Brukhartz at Lachman Associates,
 * Inc.. It may be distributed within the following restrictions:
 *	(1) It may not be sold at a profit.
 *	(2) This credit and notice must remain intact.
 * This software may be distributed with other software by a commercial
 * vendor, provided that it is included at no additional charge.
 *
 *
 * [Note: it is believed that condition 5 of the Perl "Artistic
 * License" implies the intent of restriction (1) above.]
 *
 * --------
 *
 * ** License notice 2, covering part of this software package.
 *
 * [Covers the tempnam function]
 *
 * Copyright:	Copyright (c) 1989 by Monty Walls.
 *		Not derived from licensed software.
 *
 *		Permission to copy and/or distribute granted under the
 *		following conditions:
 *
 *		1). This notice must remain intact.
 *		2). The author is not responsible for the consequences of use
 *			this software, no matter how awful, even if they
 *			arise from defects in it.
 *		3). Altered version must not be represented as being the
 *			original software.
 *
 * --------
 *
 * ** License notice 3, covering part of this software package.
 *
 * [Covers the contents of the gnu.fnmatch.tar.gz file]
 *
 *  Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *  
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *  
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; see the file COPYING.LIB.  If
 *  not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 *  Cambridge, MA 02139, USA.  
 *
 * --------
 *
 * ** License notice 4, covering part of this software package.
 *
 * [Covers the remainder of this software package]
 *
 * Additional code was written by a large cast of people.
 *
 * All additional code may be redistributed under the conditions of
 * license notice 1.
 *
 * Note that the authors of the additional code retain the right to
 * allow for the re-distribution of their code under weaker (and less
 * exotic) conditions.
 *
 * --------
 *
 * ** WARRANTY NOTICE
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *
 * [End of licensing and redistribution section] 
 *
 * ---------------------------------------------------------------------
 *
 * Makefile options:
 *  o Define INDEX to use index() in place of strchr() (v7, BSD).
 *  o Define MEMCPY when an efficient memcpy() exists (SysV).
 *  o Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3).
 *  o Define NOVOID if your compiler doesn't like void casts.
 *  o Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD).
 *  o Define VOIDFIX to allow pointers to functions returning void (non-PCC).
 *  o Define CTC3B2 to support AT&T 3B2 streaming cartridge tape.
 *  o Define HAVEFCNTL if you have <fcntl.h>
 *  o Define MYTEMPNAM if you don't have tempnam()
 *  o Define UNIXPC if you are on a 3b1, 7300, etc.
 *  o Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp
 *  o Define DEFFMTCMD to being how to format the media you use the most.
 *  o Define LONGZFILE if you want .Z to be tagged on the end of a 14 char
 *
 * BUGS:
 *	See the manpage.
 *
 * Added by Jeff Buhrt:
 *	Floppy Verify/format/restart output in the middle of a set,
 *	compress files on output, extended error messages and logging
 *
 * Added by Dave Gymer:
 *	Lotsa bugfixes, Linux support, recognition of .Z files in an archive
 *	that were compressed already (and shouldn't be uncompressed).
 *	Displays compression ratios.
 *
 * See the HISTORY file for more revision info.  */

#ifdef LINT
static char *ident = "$Header: /u/buhrt/src/afio/RCS/afio.c,v 2.3 1991/09/25 20:08:33 buhrt Exp $";
#endif

#include <stdio.h>
#include <errno.h>
#ifdef sun
/* fix SunOS errno.h not declaring what the manpage says it declares 
   bogosity. */
 extern int sys_nerr;
 extern char *sys_errlist[];
#endif
#ifdef hpux
 /* Fix that HPUX dosent have sys_nerr or sys_errlist 
    Added by Daniel Andersson, daniel.andersson@sto.sema.se
  */
extern int sys_nerr;
extern char *sys_errlist[];
#endif

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include "patchlevel.h"

#ifdef linux

#define linux_tstamp 1

#include <utime.h>
#include <sys/wait.h>

/* for flushing floppy cache */
#include <linux/fd.h>
#endif

/* compatibility fixes for IRIX native c compiler. */
#ifdef irix_cc_compatibility
#define linux_tstamp 1
#include <strings.h>
#endif

#ifndef	major
#ifdef sun
#include <sys/mkdev.h>
#else
#include <sys/sysmacros.h>
#endif
#endif /* major */

#include "afio.h"


/* define 1 to enable file descriptor leak debugging code */
#define FDDEBUG 0

     /*
      * Static variables.
      */
     STATIC short Fflag;	/*
				 * floppy flag (write when buf full)
				 * set -sdisk_size as well
				 */
     STATIC short Zflag;	/* compress the files that we can */
     STATIC short verifyflag;	/* Verify (floppy) flag */
     STATIC short verifycnt;
#ifdef	CTC3B2
     STATIC short Cflag;	/* Enable 3B2 CTC streaming (kludge) */
#endif /* CTC3B2 */
     STATIC short aflag;	/* Preserve atime (while munging ctime) */
     STATIC short dflag;	/* Don't create missing directories */
     STATIC short fflag;	/* Fork before writing to archive */
     STATIC short gflag;	/* Change to input file directories */
            short hflag;	/* Follow symbolic links */
     STATIC short jflag;	/* Don't generate sparse filesystem blocks */
     STATIC short kflag;	/* Skip initial junk to find a header */
            short lflag;	/* Link rather than copying (when possible) */
     STATIC short mflag;	/* Ignore archived timestamps */
     STATIC short nflag;	/* Keep newer existing files */
     STATIC short uflag;	/* Report files with unseen links */
     STATIC short vflag;	/* Verbose */
     STATIC short xflag;	/* Retain file ownership */
     STATIC short zflag;	/* Print final statistics */
     STATIC short flag0;	/* Input names terminated with '\0' */
     STATIC short Jflag=0;	/* Keep going after archive write error */
     STATIC short hidequit;	/* show the quit option? */
     STATIC short abspaths;	/* allow absolute path names? */
     STATIC uint arbsize = BLOCK;	/* Archive block size */
     STATIC short areof;	/* End of input volume reached */
     STATIC int arfd = -1;	/* Archive file descriptor */
     STATIC ulonglong arleft;	/* Space remaining within current volume */
     STATIC char *arname;	/* Expanded archive name */
     STATIC uint arpad;		/* Final archive block padding boundary */
     STATIC char arspec[PATHSIZE];	/* Specified archive name */
     STATIC ulonglong aruntil;	/* Volume size limit */
     STATIC int roundaruntil=1; /* Round aruntil to multiple of arbsize? */
     STATIC ulonglong maxsizetocompress=200L*1024L*1024L; /* ==0, then no max */
     STATIC int askfornext=0;	/* Ask for next disk on input eof? */
     STATIC uint arvolume = 1;	/* Volume number */
     STATIC off_t buflen;	/* Archive buffer length */
     STATIC char *buffer;	/* Archive buffer */
     STATIC char *bufidx;	/* Archive buffer index */
     STATIC char *bufend;	/* End of data within archive buffer */
     STATIC Child *children;	/* Child processes */
     STATIC char *formatcmd = DEFFMTCMD;	/* how to format */
     STATIC ushort gid;		/* Group ID */
     STATIC Link *linkbase[256];/* Unresolved link information */
     STATIC ino_t freshino = 0xffff; /* counter to make fresh inos */
     STATIC FILE *logfile = NULL;	/* log same errors as stderr would */
     STATIC ushort mask;	/* File creation mask */
     STATIC char *myname;	/* Arg0 */
     extern char *optarg;	/* Option argument */
     extern int optind;		/* Command line index */
     STATIC int outpid;		/* Process ID of outstanding outflush() */
     STATIC char pwd[PATHSIZE];	/* Working directory (with "-g") */
     STATIC int pipepid;	/* Pipeline process ID */
     STATIC time_t timenow;	/* Current time */
     STATIC time_t timewait;	/* Time spent awaiting new media */
     STATIC ulonglong total;	/* Total number of bytes transferred */
     STATIC int ttyf = -1;	/* For interactive queries (yuk) */
     STATIC ushort uid;		/* User ID */
     int uncompressrun = 0;	/* is uncompress running? its pid if so */
     char uncompto[PATHSIZE];	/* name we uncompressed to */
     STATIC int anycorrupt = 0; /* if any data is corrupted */
     STATIC int warnings = 0; /* if any data is corrupted */
     int printbytepos = 0;      /* print position of each file in archive */
     ulonglong bytepos;            /* position of first byte of current file */
     STATIC char *controlscript=NULL;  /* script to pipe control files to */
     STATIC char *promptscript=NULL; /* name of disk-change script */
     STATIC ushort extfmt = 0;  /* Use extended ASCII format */
     STATIC ushort hugefmt = 0;  /* Use second extended ASCII format */
     STATIC char *email=NULL;        /* email address to notify of volume change requests */
     STATIC int rewindfd = -1;  /* file descriptor to rewind before
                                   (un)compress invocations */
     STATIC char *ignorewarnings="m"; /* data for -1 option */
     STATIC char *aruntil_string; /* -s option string given by user */

int main (int ac, char **av)
{
  reg int c;
  reg uint group = 1;
  VOIDFN (*fn)(char **) = NULL;
  time_t timedone;
  auto char remote[PATHSIZE];
  char *exitmsg;
  int status;

#if 0
  printf("sizeof off_t = %d\n",sizeof(off_t));
  printf("sizeof size_t = %d\n",sizeof(size_t));
#endif

  if ((myname = strrchr (*av, '/')))
    ++myname;
  else
    myname = *av;
  mask = umask (0);
  uid = getuid ();
  gid = getgid ();
  if (uid == 0)
    xflag = 1;

  /* ignore SIGPIPE to deal with gzip -d exiting prematurely */
  VOID signal (SIGPIPE, SIG_IGN);

  /* All letters have been used as options, now eating into the numbers....
   */

  while ((c = options (ac, av, 
         "aioprtIOVCb:c:de:fghjklmns:uvxXy:Y:zFKZL:R:qAE:G:M:w:W:T:SBD:P:Q:U4JH:0@:N:3:1:92:5"))
        )
    {
      switch (c)
	{
	case 'r':
	  if (fn)
	    usage ();
	  fn = readcheck;
	  break;
	case 'i':
	  if (fn)
	    usage ();
	  fn = in;
	  break;
	case 'o':
	  if (fn)
	    usage ();
	  fn = out;
	  break;
	case 'p':
	  if (fn)
	    usage ();
	  fn = pass;
	  break;
	case 't':
	  if (fn)
	    usage ();
	  fn = toc;
	  break;
	case 'I':
	  if (fn)
	    usage ();
	  fn = copyin;
	  break;
	case 'O':
	  if (fn)
	    usage ();
	  fn = copyout;
	  break;
	case 'V':
	  VOID printf ("%s: Version %s dated %s\n",
		       myname, VERSION, DATE);
	  exit (0);
#ifdef	CTC3B2
	case 'C':
	  ++Cflag;
	  arbsize = 31 * 512;
	  group = 10;
	  aruntil = 1469 * 31 * 512;
	  break;
#endif /* CTC3B2 */
	case 'a':
	  ++aflag;
	  break;
	case 'b':
	  if ((arbsize = (uint) optsize (optarg)) == 0)
	    fatal (optarg, "Bad block size");
	  break;
	case 'c':
	  if ((group = (uint) optsize (optarg)) == 0)
	    fatal (optarg, "Bad buffer count");
	  break;
	case 'd':
	  ++dflag;
	  break;
	case 'e':
	  arpad = (uint) optsize (optarg);
	  break;
	case 'f':
	  ++fflag;
	  break;
	case 'g':
	  ++gflag;
	  break;
	case 'h':
	  ++hflag;
	  break;
	case 'j':
	  ++jflag;
	  break;
	case 'k':
	  ++kflag;
	  break;
	case 'l':
	  ++lflag;
	  break;
	case 'm':
	  ++mflag;
	  break;
	case 'n':
	  ++nflag;
	  break;
	case 's':
	  /* Do a 'dry run' to check all values for syntax errors */
	  aruntil_string = strdup(optarg);
          while(aruntil_string) update_aruntil();
	  /* Now the real initialisation */
          aruntil_string = optarg;
          update_aruntil();
	  if (aruntil == 0)  askfornext = 1;
	  break;
	case 'F':
	  ++Fflag;
	  break;
	case 'Z':
	  ++Zflag;
	  break;
	case 'K':
	  ++verifyflag;
	  break;
	case 'u':
	  ++uflag;
	  break;
	case 'v':
	  ++vflag;
	  break;
	case 'x':
	  xflag = 1;
	  break;
	case 'X':
	  xflag = 0;
	  break;
	case 'y':
	  nameadd (optarg, 0);
	  break;
	case 'Y':
	  nameadd (optarg, 1);
	  break;
	case 'z':
	  ++zflag;
	  break;
	case 'L':
	  if ((logfile = fopen (optarg, "a")) == (FILE *) 0)
	    {
	      fprintf (stderr,
		       "Can't open %s to append, get help\n",
		       optarg);
	      exit (1);
	    }
	  break;
	case 'R':
	  formatcmd = optarg;
	  break;
	case 'q':
	  hidequit = TRUE;
	  break;
	case 'A':
	  abspaths = TRUE;
	  break;
        case 'E':
          if(!readcompexts(optarg))
	    {
	      fprintf (stderr,
		       "Can't read configuration file %s\n",
		       optarg);
	      exit (1);
	    }
          break;
        case 'G':
          gzipfactor=(uint) optsize(optarg);
          if((gzipfactor <1) || (gzipfactor >9))
	    {
	      fprintf (stderr,
		       "%s: Illegal gzip speed factor (Must be 1--9)\n",
		       optarg);
	      exit (1);
	    }
          break;
        case 'M':
          maxmem=(off_t) optsize(optarg);
          break;
        case 'T':
          compthreshold=(uint) optsize(optarg);
          break;
        case 'w':
          if(!nameaddfile(optarg,0))
	    {
	      fprintf (stderr,
		       "Can't read configuration file %s\n",
		       optarg);
	      exit (1);
	    }
          break;
        case 'W':
          if(!nameaddfile(optarg,1))
	    {
	      fprintf (stderr,
		       "Can't read configuration file %s\n",
		       optarg);
	      exit (1);
	    }
          break;
        case 'S':
          ignoreslash=0;
          break;
        case 'B':
          printbytepos=1;
          break;
        case 'D':
          controlscript=optarg;
          break;
  	case 'Q':
	  compressargs=1;
  	  add_arg(optarg);
  	  break;
  	case 'P':
  	  compressprog=optarg;
  	  break;
  	case 'U':	/* compress All files */
  	  forceZflag=1;
	  lflag=1; /* Due to internal limitations we need to set this */
   	  break;
 	case '0':
 	  flag0 = 1;
 	  break;
 	case '4':       /* Use extended ASCII format */
 	  extfmt = 1;
 	  break;
 	case '5':       /* Use second extended ASCII format */
 	  hugefmt = 1;
 	  break;
 	case 'J':
 	  Jflag = 1;
 	  break;
       case 'H':
         promptscript=optarg;  /* name or definition of the promptscript */
         break;
       case '@':
         email=optarg;
         break;
       case '3':
         rewindfd=(int)optsize(optarg);
         break;
       case '1':
         ignorewarnings=optarg;
         break;
       case '9':
	 roundaruntil=0;
         break;
       case '2':
	 maxsizetocompress=optsize(optarg);
         break;

	default:
	  usage ();
	}
    }
  if (fn == NULL || av[optind] == NULL)
    usage ();

 if (extfmt && hugefmt) 
   {
     warn(av[0], "Both -4 and -5 can not be specified at the same time."); 
     usage ();
   }

  if(fflag && aruntil==0)
  {
      fprintf (stderr,"Fatal: must supply nonzero -s [volsize] to use -f\n");
      usage();
  }

  if(compressprog && (rewindfd==-1))
      if(strstr(compressprog,"pgp") || strstr(compressprog,"gpg"))
      {
	  fatal(compressprog,"Must use -3 flag if -P program matches 'pgp' or 'gpg', see the afio manual page.");
      }

  if(!compressprog) compressprog = PRG_COMPRESS;
  compress_arg_list[0] = compressprog;

  if (Fflag)
    {      
      if ((buflen = (off_t) aruntil) == 0)
	usage ();
    }
  else
    buflen = arbsize * group;

  if( roundaruntil )
    {
      /* round aruntil down to a multiple of arbsize: some devices
         (like ftape) puke on a smaller-than-blocksize last write to
         the volume */
      aruntil = ( aruntil / (ulonglong) arbsize );
      aruntil = aruntil * arbsize;
    }

  if (aruntil && (aruntil < arbsize))
    {
#ifdef hpux 
      /* HPUX gcc dosent like the (ulong) fixed by Daniel Andersson
         daniel.andersson@sto.sema.se  */
      fprintf (stderr, "Media size %ld is less than block size %d\n",
               aruntil, arbsize);    
#else
      fprintf (stderr, "Media size %ld is less than block size %d\n",
	       (ulong) aruntil, arbsize);
#endif
   usage ();
    }
  if (arpad == 0)
    arpad = arbsize;
  if (fn != pass)
    {
      reg char *colon;
      reg char *perc;
      reg char *equal;
      char  *host;
      reg int isoutput = (fn == out || fn == copyout);

      arname = strcpy (arspec, av[optind++]);
      if ((colon = strchr (arspec, ':')))
	{
	  *colon++ = '\0';
	  if ((perc = strchr (arspec, '%')))
	    *perc++ = '\0';
	  if ((equal = strchr ((perc ? perc : arspec), '=')))
	    *equal++ = '\0';
	  if ((host=strchr(arspec,'@')))
	      *host++ = 0;  
	  VOID sprintf (arname = remote,
			"!%s %s%s %s '%s -%c -b %u -c %u %s'",
			perc ? perc : "rsh",
			host ? "-l ":"",
			host ? arspec : "",
			host ? host : arspec,
			equal ? equal : myname,
			isoutput ? 'O' : 'I', arbsize,
			group, colon);
          if( host )
            *--host = '@';			
	  if (equal)
	    *--equal = '=';
	  if (perc)
	    *--perc = '%';
	  *--colon = ':';
	}
      if (gflag && *arname != '/' && *arname != '!')
	fatal (arspec, "Relative pathname");
      VOID signal (SIGINT, goodbye);
      /*
       * +BLOCK is added to make sure we don't overrun buffer on a
       * read (internal read(1) length is thus met)
       */

      if ((buffer = bufidx = bufend = malloc (buflen + BLOCK)) == NULL)
	fatal (arspec, "Cannot allocate enough memory for I/O buffer");

      /*
       * open a floppy at the last moment (if output), otherwise now
       * note we set arleft prematurely so we don't have to open the
       * disk now
       */
      if (!Fflag || !isoutput)
	{
	  if (nextopen (isoutput ? O_WRONLY : O_RDONLY) < 0)
	    goodbye (1);
	}
      else
	arleft = aruntil;
    }
  timenow = time ((time_t *) NULL);
  (*fn) (av + optind);
  timedone = time ((time_t *) NULL);
  if (uflag)
    linkleft ();
  if (vflag || (fn == toc))
    fflush(stdout);

  exitmsg="The operation was successful.";
  if(warnings) 
  {
      exitmsg=malloc(80);
      if(exitmsg==NULL) 
	  exitmsg="The operation HAD WARNINGS ABOUT ERRORS.";
      else
	  sprintf(exitmsg,"The operation HAD %d WARNING%s ABOUT ERRORS.",warnings,warnings==1?"":"S");
  }
  if(anycorrupt) exitmsg="The operation FAILED.";

  if (zflag)
    {
      reg FILE *stream;

      stream = fn == toc || fn == copyin || arfd == STDOUT ? stderr : stdout;
      VOID fprintf (stream, "%s: ", myname);
      prsize (stream, total);
      VOID fprintf (stream, " bytes %s in %lu seconds. %s\n",
                    fn == pass
                    ? "transferred"
                    : fn == out || fn == copyout
                    ? "written"
                    : "read",
                    timedone - timenow - timewait,
                    exitmsg);     
    }
  if (logfile != (FILE *) 0)
    {
      VOID fprintf (logfile, "%s: Final count: ", myname);
      prsize (logfile, total);
      VOID fprintf (logfile,
		    " bytes %s in %lu seconds (+waited %lu seconds for disk swapping (%u disks)) finished at %s",
		    (fn == pass ? "transferred" : (fn == out
						   || fn == copyout ? "written" : "read")),
		    timedone - timenow - timewait,
		    timewait, arvolume, ctime (&timedone));
      VOID fprintf (logfile,"%s\n",exitmsg);
    }
  nextclos ();

  /* Added to lower chance of race condition (?) in rsh processing
     when reading remote files */
  if(fn==copyin) { fflush(stdout);  fclose(stdout); sleep(2);  } 
  if (email) mail(email,-1,arspec);

  status=0;
  if(anycorrupt) status=1;
  if(warnings) if(index(ignorewarnings,(int)'a')==NULL) status=1;

  goodbye (status);
  /* NOTREACHED */
  return 0;	/* silence gcc -Wall */
}



/*
 * update_aruntil()
 *
 * Sets the next aruntil-value out of the options-list
 */
void update_aruntil()
{    
    char *next_aruntil_string = aruntil_string;

    if(aruntil_string)
      {
	aruntil_string = strchr (aruntil_string, ',');
	if(aruntil_string) *aruntil_string++='\0';

	aruntil = optsize (next_aruntil_string);
      }
}

/*
 * mail()
 *
 * Mail tape change message
 */
void mail(char *who,int vol,char *archive)
{
	FILE *fp;
	char cmd[1024];
	char hostname[256];
	gethostname(hostname,sizeof(hostname));
	sprintf(cmd,"sendmail %s",who);
	fp = popen(cmd,"w");
	if (fp == NULL ) {
		perror(cmd);
		return;
	}
	fprintf(fp,"From: Afio archiver\n");
	fprintf(fp,"Subject: %s %s: %s\n\n",hostname,archive,vol<0?"operation complete":"volume change needed");
	fprintf(fp,"Hostname: %s\n",hostname);
	fprintf(fp,"Archive : %s\n\n",archive);
	if(vol>=0) 
	    fprintf(fp,"Need change to volume #%d.\n\n",vol);
	else
	    fprintf(fp,"Operation complete.\n\n");
	pclose(fp);
}


/*
 * copyin()
 *
 * Copy directly from the archive to the standard output.
 */
STATIC VOIDFN
copyin (char **av)
{
  reg int got;
  reg uint have;

  if (*av)
    fatal (*av, "Extraneous argument");
  while (!areof || askfornext)
  { 
      VOID infill ();
      while ((have = bufend - bufidx))
	  if ((got = writeall (STDOUT, bufidx, have)) < 0)
	      fatal ("<stdout>", syserr ());
	  else 
	  {
	      total+=have;
	      if (got > 0)
		  bufidx += got;
	      else
	        return;
	  }
  }
}

/*
 * copyout()
 *
 * Copy directly from the standard input to the archive.
 */
STATIC VOIDFN
copyout (char **av)
{
  reg int got;
  reg uint want;

  if (*av)
    fatal (*av, "Extraneous argument");
  for (;;)
    { 
      while ((want = bufend - bufidx) == 0)
	outflush (NOTDONE);
      if ((got = read (STDIN, bufidx, want)) < 0)
	fatal ("<stdin>", syserr ());
      else if (got == 0)
	break;
      else
      {
	  bufidx += got;
	  total += got; /* actually a bit too early for bytes written count */
      } 
    }
  outflush (DONE);
  if (fflag)
    outwait ();
}

/*
 * dirchg()
 *
 * Change to the directory containing a given file.
 */
STATIC int
dirchg (char *name, char *local)
{
  reg char *last;
  reg int len;
  auto char dir[PATHSIZE];

  if (*name != '/')
    return (warn (name, "Relative pathname"));
  for (last = name + strlen (name); last[-1] != '/'; --last)
    ;
  len = last - name;
  strncpy (dir, name, len)[len] = '\0';
  VOID strcpy (local, *last ? last : ".");
  if (strcmp (dir, pwd) == 0)
    return (0);
  if (chdir (dir) < 0)
    return (warn (name, syserr ()));
  VOID strcpy (pwd, dir);
  return (0);
}

/*
 * dirmake()
 *
 * Make a directory. Returns zero if successful, -1 otherwise.
 */
STATIC int
dirmake (char *name, Stat *asb) 
{
  if (mkdir (name, asb->sb_mode & S_IPOPN) < 0)
    return (-1);
/* First do the chown, then the chmod, because the chown may clear
   the suid/sgid bits we want to set.
*/
  if (xflag)
    VOID chown (name,
		uid == 0 ? ush (asb->sb_uid) : uid,
		ush (asb->sb_gid));
  if (asb->sb_mode & S_IPEXE)
    VOID chmod (name, asb->sb_mode & S_IPERM);
  return (0);
}

/*
 * dirneed()
 *
 * Recursively create missing directories (with the same permissions
 * as their first existing parent). Temporarily modifies the 'name'
 * argument string. Returns zero if successful, -1 otherwise.
 */
STATIC int
dirneed (char *name)
{
  reg char *cp;
  reg char *last;
  reg int ok;
  static Stat sb;

  last = NULL;
  for (cp = name; *cp;)
    if (*cp++ == '/')
      last = cp;
  if (last == NULL)
    return (STAT (".", &sb));
  *--last = '\0';
  ok = STAT (*name ? name : "/", &sb) == 0
    ? ((sb.sb_mode & S_IFMT) == S_IFDIR)
    : (!dflag && dirneed (name) == 0 && dirmake (name, &sb) == 0);
  *last = '/';
  return (ok ? 0 : -1);
}

/*
 * fatal()
 *
 * Print fatal message and exit.
 */
STATIC void
fatal (char *what, char *why) 
{
  /* print position in archive if some data was transferred */
  if(total>0)
    VOID warnarch ("Fatal error:",0);
  VOID warn (what, why);
  goodbye (1);
}



/*
 * writeall()
 *
 * Write all bytes in buf or return -1.  Used to fix invalud assumptions
 * about write() elsewhere.
 */
STATIC
int writeall(int fd, const char *buf, int count)
{
 int put,totalput;

 totalput=0;
 while(totalput<count)
 {
     put=write(fd,buf+totalput,count-totalput);

     if(put<0) return put;
     totalput+=put;
 }

 return count;
}


/*
 * mayberewind()
 *
 * implement -3 option
 */
STATIC
void mayberewind()
{
  if(rewindfd>=1) 
  {
    if(lseek(rewindfd,(off_t)0,SEEK_SET)<0)
    {
	fatal("-3 option",syserr());
    }
