[comp.sources.mac] MPW tar/untar tools

gz@spt.entity.com (Gail Zacharias) (05/20/88)

[MPW tar/untar tools]

These are the sources for two MPW tools:  mtar creates unix-compatible
tar archives, and muntar extracts some or all files from a tar archive.
Files are normally stored in macbinary format so that both forks, as
well as the macintosh file info, of each file are preserved.  MPW
wildcard facilities can be used to select the files to be archived.

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	makefile.hqx
#	mtar.c
#	muntar.c
# This archive created: Thu May 19 07:56:09 1988
# By:	Roger L. Long ()
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1060 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XYou should have the following files:
X
Xmtar.c - (MPW C source) An MPW tool for creating unix-compatible tar archives.
X        By default, files are stored in the so-called macbinary format, which
X	means that both forks of the macintosh file are stored, along with
X	macintosh file info (type, creator, mac filename, file dates). This
X	in turn means that you can untar the archive on unix, and download
X	selected files using the 'macbinary' option of your favorite
X	communication program.
X	Of course there is an option of storing only data forks of files.
X	Do 'mtar -H' for a short help message.
Xmuntar.c - (MPW C source) An MPW tool for extracting files from
X	unix-compatible tar archives.  You can extract individual files
X	or unpack the whole archive.  muntar will automatically recognize
X	files stored in macbinary format, and 'unmacbinary' them so you get
X	real macintosh files (both forks, etc.).  Do 'muntar -H' for a short
X	help message.
Xmakefile.hqx - A binhex'ed MPW make file for these two programs.
X
X		Enjoy,
X		gz@entity.com (a.k.a. gz@eddie.mit.edu)
SHAR_EOF
if test 1060 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1060 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'makefile.hqx'" '(1076 characters)'
if test -f 'makefile.hqx'
then
	echo shar: will not over-write existing file "'makefile.hqx'"
else
sed 's/^X//' << \SHAR_EOF > 'makefile.hqx'
X(This file must be converted with BinHex 4.0)
X
X:#'eKDf9QD@aP!&4&@&408&-J!3!!!!&@!!!"IS1&B@aX#F3*EA4KFL"YG@jdBA)
X0$@eeER4KFJR%#@eeER4KFLjM,Qm0#@aTEQXJ,@mJEA9ZG'&b)#eM)#G08&-J*b!
XYG#!R69"69#FJEA9ZG'&b,Q-ZEb"l3faTBR*KFQPPFhe$8R9ZG'PYC5j[),B0#5!
XJHd0XD@*bBA*TCA0p3dPZG'9bCQ&MC5j[)(Y$E'PLFQ&bD@9cI90dC%0-D@)ZEb"
Xl3faTBR*KFQPPFhe$8d&148aTBLj[$3eYG'&b#F3*EA4KFLjM,Qm0#@aTEQXJ,@m
XJEA4KFL!YBb!R69"6)#FJ,A3J*de38e3R)'edBA)ZBbj[)(Y$E'PLFQ&bD@9cI80
X5G@jdD@eP,QmJYJd*)#"l3faTBR*KFQPPFhe$5@jdCA*QB@0P,QmJHd0XD@*bBA*
XTCA0p8h4N3daTBLj[)(Y$E'PLFQ&bD@9cI80638j&6'PL,Qm0&%%!!!%!!!!"6!!
X!!%`!!!!bFb!T1`dJ)#!JI3dU,`dMD@CNC@BJ3dp08%&858*-43d)E@&VC@CTE'9
XX!J!!!&4&@&408&-J!!!!!!!!!!!!!!!!9%9B9%e38b!!!!!!!!!!!!!!!!!!!!!
X!!!!!!!!!!!#HXeI`!!!"9J!!!Aj&)#S[$3dJ)#!JCQPXC@aTFh3J25"QD@aPF(4
Xb)$dJ+'0SBA)J+LST+'eKE'a[BbKKFQGM)#SJFfPkC@pQ+#TKFQGf+5NT1`dJ)#!
XJ+QCTE'9XDA0d)$dJ6P9-6$X0$5!J)#"TCLJSBh!J25"bD@jNCAJSBA*RGPX`A5`
XJ*bmR+5NJ!!!!5!!*6@pZB@0[!#ed)#G08&08*b"eER4KFLjM,Qm0$A4KFJN!"J!
X)!#N!!`&6!Id"%`!!!9-"rCkc@IX!!!$d!!!!p!!!!!!"!!!!!3!!!!&-!!!!6!!
X!!$)!)@-!"'S!!!!F!$)!!%e38e)!!!!+!qhrr`!!!!!!)Q@+4Y!:
SHAR_EOF
if test 1076 -ne "`wc -c < 'makefile.hqx'`"
then
	echo shar: error transmitting "'makefile.hqx'" '(should have been 1076 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mtar.c'" '(11329 characters)'
if test -f 'mtar.c'
then
	echo shar: will not over-write existing file "'mtar.c'"
else
sed 's/^X//' << \SHAR_EOF > 'mtar.c'
X/* Copyright 1988, Gail Zacharias.  All rights reserved.
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software provided there is no monetary profit
X * gained specifically from its use or reproduction, it is not sold,
X * rented, traded or otherwise marketed, and this copyright notice 
X * and the software version number is included prominently in any copy
X * made.
X * This is mtar version 1.0.
X *
X * Send comments, suggestions, bug reports (not bloody likely), feature
X * requests, etc. to gz@entity.com.
X *
X */
X
Xchar *Program, *Version = "1.0";
X
X#include <stdio.h>
X#include <files.h>
X#include <errors.h>
X#include <errno.h>
X#include <ioctl.h>
X
XStringPtr c2pstr();
Xchar *strchr(), *strcpy(), *strncpy(), *p2cstr();
X
X#define TEXT 0x54455854L
X
X/* The do {} while(0) is for syntactic reasons. MPW C will optimize it out. */
X#define mcopy(dest, src, len) \
X  do { char *_dp = (char *)(dest), *_sp = (char *)(src); \
X       int _i = (len); \
X       while (--_i>=0) *_dp++ = *_sp++; \
X  } while(0)
X
X#define fillmem(dest, ch, len) \
X  do { char *_dp = (char *)(dest); \
X       int _i = (len); \
X       while (--_i>=0) *_dp++ = (ch); \
X  } while (0)
X
Xstruct {
X    char name[100];
X    char mode[8];
X    char uid[8];
X    char gid[8];
X    char size[12];
X    char mtime[12];
X    char chksum[8];
X    char linkflag;
X    char linkname[100];
X    char magic[8];
X    char uname[32];
X    char gname[32];
X    char devmajor[8];
X    char devminor[8];
X    char fill[167];
X} tarh;
X
X#define tarsz(n)	(((n)+511L) & -512L)
X
Xstruct {
X    unsigned char nlen;
X    char name[63];
X    FInfo finfo;
X    char protected;
X    char zero;
X    long dflen;
X    long rflen;
X    long cdate;
X    long mdate;
X/*  char unused[29]; */
X} macbinh;
X
X#define macbinsz(n)	(((n)+127L) & -128L)
X
Xstatic HFileInfo statb;
X
Xint binary = 0,
X    dataonly = 0,
X    verbose = 0;
X
Xint dirmode = 0777,
X    filemode = 0666;
X
Xchar *uname = "root",
X     *gname = "group";
X
XFILE *tarf = (FILE *) NULL;
X
X#define writeblock(p,len)	fwrite((p), 1, (len), tarf)
X
X/* Unfortunately, the Mac keeps only local time, so this will be off by
X   the time zone... */
X#define UNIXTIME	2082844800L  /* Jan 1, 1970 00:00:00 */
X
X
Xmain (argc, argv)
X  char **argv;
X{
X  char *defalt[2];
X  Program = argv[0];
X  while (--argc > 0 && **++argv == '-') {
X    char *argp = &argv[0][1];
X    do switch (*argp++) {
X      case 'b': dataonly = 1; binary = 1; break;
X      case 'u': dataonly = 1; binary = 0; break;
X      case 'v': verbose = 1; break;
X      case 'o': if (*argp == '\0') {
X                  if (--argc == 0) goto badarg;
X		  argp = *++argv;
X		}
X		uname = argp;
X		argp = "";
X		break;
X      case 'g': if (*argp == '\0') {
X                  if (--argc == 0) goto badarg;
X		  argp = *++argv;
X		}
X		gname = argp;
X		argp = "";
X		break;
X      case 'f':	if (tarf) goto badarg;
X		if (*argp == '\0') {
X                  if (--argc == 0) goto badarg;
X		  argp = *++argv;
X		}
X		if (!strcmp(argp, "-")) tarf = stdout;
X		else if (!(tarf = fopen(argp, "w")))
X		   fatal("Cannot open %s for writing.", argp);
X		argp = "";
X		break;
X      default: goto badarg;
X    } while (*argp);
X  }
X  if (argc <= 0) {
X    defalt[0] = ":";
X    defalt[1] = (char *) NULL;
X    argv = defalt;
X  }
X  if (!tarf) tarf = stdout;
X  tarfiles(argv);
X  fflush(tarf);
X  if (ferror(tarf)) {
X     if (errno == ENOSPC) fatal("Out of disk space");
X     else fatal("Write error");
X  }
X  if (tarf != stdout) fclose(tarf);
X  return 0;
X
Xbadarg:
X  fprintf(stderr, "mtar Version %s, by gz@entity.com.  Usage: \n", Version);
X  fprintf(stderr, "%s [-vbu] [-o uname] [-g gname] [-f tarfile] files and dirs...\n", Program);
X  fprintf(stderr, "Creates a tar archive of the named files.\n\
X -v = verbose: list names of files as store them.\n\
X -b = store only data forks of files.\n\
X -u = like -b, but convert CR to LF in TEXT files.\n\
X -o = set unix owner of files to uname (default 'root').\n\
X -g = set unix group of files to gname (default 'group').\n\
X -f tarfile = write output to tarfile (instead of standard output).\n\
X -H = show this help message.\n\
XIf neither -u nor -b flags are given, stores both forks of each file (as well\n\
Xas the mac name, type, creator and dates) using macbinary format.\n\
XIf no files are specified, tars the current default directory.\n\n\
XThis is freeware.  If you paid money for this program, you got ripped off.\n");
X  return 2;
X}
X
Xtarfiles (files)
X  char **files;
X{
X  while (*files) {
X    if (strlen(*files) >= 100) nametoolong(*files);
X    statf(*files);
X    if (statb.ioFlAttrib & ioDirMask) {
X      statb.ioVRefNum = getvrefnum(*files);
X      tardir(*files);
X    }
X    else {
X      char *cp = *files+strlen(*files);
X      while (cp != *files && cp[-1] != ':') --cp;
X      if ((macbinh.nlen = strlen(cp)) >= 64) nametoolong(*files);
X      strcpy(macbinh.name, cp);
X      tarfile(*files);
X    }
X    ++files;
X  }
X  fillmem(&tarh, 0, 512);
X  writeblock(&tarh, 512);
X}
X
Xtardir (dname)
X  char *dname;
X{
X  char name[256];
X  long dirid = statb.ioDirID;
X  int index = 0;
X  int dlen = strlen(dname);
X  tarheader(dname, 0);
X  if (strchr(dname, ':')) strcpy(name, dname);
X  else {
X    name[0] = ':';
X    strcpy(&name[1], dname);
X    dlen++;
X  }
X  if (name[dlen-1] != ':') name[dlen++] = ':';
X  while (1) {
X    statb.ioFDirIndex = ++index;
X    statb.ioDirID = dirid;
X    statb.ioNamePtr = &macbinh.nlen;
X    if (PBGetCatInfo(&statb, 0) || statb.ioResult) {
X      if (statb.ioResult != fnfErr) pbsyserr(&statb);
X      break;
X    }
X    strncpy(&name[dlen], macbinh.name, macbinh.nlen);
X    name[dlen+macbinh.nlen] = '\0';
X    if (macbinh.nlen >= 64 || (dlen + macbinh.nlen) >= 100) nametoolong(name);
X    if (statb.ioFlAttrib & ioDirMask) tardir(name);
X    else tarfile(name);
X  }
X}
X
Xtarfile (fname)
X  char *fname;
X{
X  if (statb.ioFRefNum) {
X    short refnum;
X    if (ioctl(fileno(tarf), FIOREFNUM, &refnum) >= 0 && refnum == statb.ioFRefNum)
X       return 0;
X    fprintf(stderr, "%s: Warning: Somebody has \"%s\" open. You may lose.\n", Program, fname);
X  }
X  if (dataonly) tardata(fname);
X  else tarmacbin(fname);
X}
X
Xtardata (fname)
X  char *fname;
X{
X  IOParam pb;
X  tarheader(fname, statb.ioFlLgLen);
X  if (verbose) fprintf(stderr, "X %s (%ld bytes)\n", tarh.name, statb.ioFlLgLen);
X  pb.ioVRefNum = 0;
X  pb.ioVersNum = 0;
X  pb.ioPermssn = fsRdPerm;
X  pb.ioMisc = 0;
X  pb.ioNamePtr = c2pstr(fname);
X  if (PBOpen(&pb, 0)) pbsyserr(&pb);
X  writefork(&pb, statb.ioFlLgLen, 0);
X  p2cstr(fname);
X}
X
Xtarmacbin(fname)
X  char *fname;
X{
X  IOParam pb;
X  long fsize = macbinsz(statb.ioFlLgLen)+macbinsz(statb.ioFlRLgLen)+128L;
X  tarheader(fname, fsize);
X  if (verbose) 
X   fprintf(stderr, "Macbin %s (%ld+%ld bytes)\n",
X               tarh.name, statb.ioFlLgLen, statb.ioFlRLgLen);
X  macbinheader();
X  pb.ioVRefNum = 0;
X  pb.ioVersNum = 0;
X  pb.ioPermssn = fsRdPerm;
X  pb.ioMisc = 0;
X  pb.ioNamePtr = c2pstr(fname);
X  if (PBOpen(&pb, 0)) pbsyserr(&pb);
X  writefork(&pb, statb.ioFlLgLen, 1);
X  pb.ioVRefNum = 0;
X  pb.ioVersNum = 0;
X  pb.ioPermssn = fsRdPerm;
X  pb.ioMisc = 0;
X  if (PBOpenRF(&pb, 0)) pbsyserr(&pb);
X  writefork(&pb, statb.ioFlRLgLen, 1);
X  if (fsize & 511L) writeblock(&tarh, 512 - (fsize & 511L));
X  p2cstr(fname);
X}
X
Xmacbinheader()
X{
X  fillmem(&macbinh.name[macbinh.nlen], 0, 63-macbinh.nlen);
X  mcopy(&macbinh.finfo, &statb.ioFlFndrInfo, sizeof(FInfo));
X  macbinh.protected = 0;
X  macbinh.zero = 0;
X  macbinh.dflen = statb.ioFlLgLen;
X  macbinh.rflen = statb.ioFlRLgLen;
X  macbinh.cdate = statb.ioFlCrDat;
X  macbinh.mdate = statb.ioFlMdDat;
X  fillmem(&tarh, 0, 128);
X  mcopy(&tarh.name[1], &macbinh, sizeof(macbinh));
X  writeblock(&tarh, 128);
X}
X
Xwritefork (pb, fsize, macbinp)
X  IOParam *pb;
X  long fsize;
X{
X  int blocksz = (macbinp ? 128 : 512);
X  pb->ioPosMode = fsAtMark;
X  pb->ioBuffer = &tarh;
X  pb->ioReqCount = blocksz;
X  while (fsize) {
X    if (fsize < pb->ioReqCount) pb->ioReqCount = fsize;
X    fsize -= pb->ioReqCount;
X    if (PBRead(pb, 0) || pb->ioActCount != pb->ioReqCount) {
X      int err = pb->ioResult;
X      (void) PBClose(pb, 0);
X      pb->ioResult = err;
X      pbsyserr(pb);
X    }
X    if (!macbinp && !binary && statb.ioFlFndrInfo.fdType == TEXT) {
X      char *cp = pb->ioBuffer;
X      int i = pb->ioActCount;
X      while (i) {
X        if (*cp == ('M'-'@')) *cp = ('J'-'@');
X	--i, ++cp;
X      }
X    }
X    writeblock(pb->ioBuffer, blocksz);
X  }
X  if (PBClose(pb, 0)) pbsyserr(pb);
X}
X
Xtarheader (fname, fsize)
X  char *fname;
X  long fsize;
X{
X  int chksum = 0, i;
X  fillmem(&tarh, 0, 512);
X  mac2unix(tarh.name, fname);
X  if (statb.ioFlAttrib & ioDirMask) 
X    if (tarh.name[strlen(tarh.name)-1] != '/')
X      tarh.name[strlen(tarh.name)] = '/';
X  numstr(tarh.mode, ((statb.ioFlAttrib & ioDirMask) ? dirmode : filemode), 8);
X  numstr(tarh.uid, 0, 8);
X  numstr(tarh.gid, 0, 8);
X  numstr(tarh.size, fsize, 12);
X  numstr(tarh.mtime, statb.ioFlMdDat - UNIXTIME, 12);
X  fillmem(tarh.chksum, ' ', 8);
X  tarh.linkflag = ((statb.ioFlAttrib & ioDirMask) ? '5' : '0');
X  strcpy(tarh.magic, "ustar  ");
X  strcpy(tarh.uname, uname);
X  strcpy(tarh.gname, gname);
X  for(i=0; i<512; ++i) chksum += (unsigned char)tarh.name[i];
X  numstr(tarh.chksum, chksum, 8);
X  writeblock(&tarh, 512);
X}
X
X/* Don't allow absolute names.   "HD:foo:bar" becomes "./foo/bar" */
Xmac2unix(uname, mname)
X char *uname, *mname;
X{
X  char *dp = uname, *cp;
X  if (cp = strchr(mname, ':')) *dp++ = '.', *dp++ = '/', ++cp;
X  else cp = mname;
X  while (1) {
X    while (*cp == ':') *dp++ = '.', *dp++ = '.', *dp++ = '/', ++cp;
X    while (*cp && *cp != ':') if ((*dp++ = *cp++) == '/') dp[-1] = ':';
X    if (!*cp++) break;
X    *dp++ = '/';
X  }
X  for (dp = uname; *dp; ++dp) if (*dp == ' ') *dp = '_';
X  if (strlen(uname) >= 100) nametoolong(mname);
X}
X
Xnumstr (p, num, count)
X  char *p;
X{
X  p[--count] = '\0';
X  if (count != 11) p[--count] = ' ';
X  if (num < 0) {
X    *p++ = '-';
X    --count;
X    num = -num;
X  }
X  while (count) {
X    p[--count] = '0' + (num & 7);
X    num >>= 3;
X  }
X}
X
Xstatf (fname)
X  char *fname;
X{
X  char name[256];
X  statb.ioNamePtr = c2pstr(strcpy(name, fname));
X  statb.ioFVersNum = 0;
X  statb.ioVRefNum = 0;
X  statb.ioFDirIndex = 0;
X  statb.ioDirID = 0;
X  if (PBGetCatInfo(&statb, 0) || statb.ioResult) pbsyserr(&statb);
X  statb.ioNamePtr = NULL;
X}
X
Xgetvrefnum (fname)
X  char *fname;
X{
X  HVolumeParam pb;
X  char name[256];
X  pb.ioNamePtr = c2pstr(strcpy(name, fname));
X  pb.ioVRefNum = 0;
X  pb.ioVolIndex = 0;
X  if (PBHGetVInfo(&pb, 0) || pb.ioResult) pbsyserr(&pb);
X  return pb.ioVRefNum;
X}
X
Xnametoolong (name)
X  char *name;
X{
X  fatal("\"%s\" - name too long", name);
X}
X
Xpbsyserr (pb)
X  IOParam *pb;
X{
X  int err = pb->ioResult;
X  char *name = pb->ioNamePtr;
X  p2cstr(name);
X  if (err == fnfErr) fatal("%s - File not found", name);
X  else if (err == nsvErr) fatal("%s - No such volume", name);
X  else if (err == tmfoErr) fatal("%s - Too many files open", name);
X  else if (err == permErr) fatal("%s - Permissions error", name);
X  else if (err == dupFNErr) fatal("%s - Duplicate filename", name);
X  else if (err == eofErr) fatal("%s - Premature end of file", name);
X  else fatal("%s - Error #%d", name, err);
X}
X
Xfatal(fmt, arg1, arg2, arg3, arg4, arg5, arg6)
X  char *fmt, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6;
X{
X  if (tarf && tarf != stdout) fclose(tarf);
X  fprintf(stderr, "%s: ", Program);
X  fprintf(stderr, fmt, arg1, arg2, arg3, arg4, arg5, arg6);
X  fprintf(stderr, "\n");
X  exit(2);
X}
X
SHAR_EOF
if test 11329 -ne "`wc -c < 'mtar.c'`"
then
	echo shar: error transmitting "'mtar.c'" '(should have been 11329 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'muntar.c'" '(13396 characters)'
if test -f 'muntar.c'
then
	echo shar: will not over-write existing file "'muntar.c'"
else
sed 's/^X//' << \SHAR_EOF > 'muntar.c'
X/* Copyright 1988, Gail Zacharias.  All rights reserved.
X * Permission is hereby granted to copy, reproduce, redistribute or
X * otherwise use this software provided there is no monetary profit
X * gained specifically from its use or reproduction, it is not sold,
X * rented, traded or otherwise marketed, and this copyright notice 
X * and the software version number is included prominently in any copy
X * made.
X * This is muntar version 1.0.
X *
X * Send comments, suggestions, bug reports (not bloody likely), feature
X * requests, etc. to gz@entity.com.
X *
X */
X
Xchar *Program, *Version = "1.0";
X
X#include <stdio.h>
X#include <files.h>
X#include <errors.h>
X
X#define MPS 0x4D505320L
X#define TEXT 0x54455854L
X
X/* The do {} while(0) is for syntactic reasons. MPW C will optimize it out. */
X#define mcopy(dest, src, len) \
X  do { char *_dp = (char *)(dest), *_sp = (char *)(src); \
X       int _i = (len); \
X       while (--_i>=0) *_dp++ = *_sp++; \
X  } while(0)
X
Xstruct {
X    char name[100];
X    char mode[8];
X    char uid[8];
X    char gid[8];
X    char size[12];
X    char mtime[12];
X    char chksum[8];
X    char linkflag;
X    char linkname[100];
X    char magic[8];
X    char uname[32];
X    char gname[32];
X    char devmajor[8];
X    char devminor[8];
X    char fill[167];
X} tarh;
X#define tarsz(n)    (((n)+511L) & -512L)
X
Xstruct {
X    unsigned char nlen;
X    char name[63];
X    FInfo finfo;
X    char protected;
X    char zero;
X    long dflen;
X    long rflen;
X    long cdate;
X    long mdate;
X    char unused[29];
X} macbinh;
X
X#define macbinsz(n)    (((n)+127L) & -128L)
X
XFILE *tarf = (FILE *) NULL;
X
X#define readblock(buf, size) ((size) == fread((char *) buf, 1, (size), tarf))
X
Xchar *malloc(), *strrchr();
XStringPtr c2pstr();
X
XOSType filecreator = MPS;
X
Xint listonly = 0,
X    binary = 0,
X    dataonly = 0,
X    verbose = 0,
X    ignore_cdate = 0,
X    ignore_mdate = 0;
X
Xchar **files = (char **) NULL;
Xint *ffound = (int *) NULL;
X
Xmain (argc, argv)
X  char **argv;
X{
X    Program = argv[0];
X    while (--argc > 0 && **++argv == '-') {
X      char *argp = &argv[0][1];
X      do switch (*argp++) {
X        case 'l': verbose = listonly = 1; break;
X	case 'v': verbose = 1; break;
X        case 'b': binary = 1; break;
X        case 'u': dataonly = 1; break;
X        case 'm': ignore_mdate = 1; break;
X        case 'd': ignore_cdate = 1; break;
X        case 'c': if (*argp == '\0') {
X                    if (--argc == 0) goto badarg;
X                    argp = *++argv;
X                  }
X		  if (*argp == '\0' || strlen(argp) > 4) goto badarg;
X		  filecreator = 0x20202020L;
X                  { char *cp = (char *)&filecreator;
X                     while (*argp) *cp++ = *argp++;
X                  }
X                  break;
X        case 'f': if (tarf) goto badarg;
X                  if (*argp == '\0') {
X                      if (--argc == 0) goto badarg;
X                      argp = *++argv;
X                  }
X                  if (!strcmp(argp, "-")) tarf = stdin;
X		  else if (!(tarf = fopen(argp, "r")))
X		      fatal("Cannot open %s for reading.", argp);
X		  argp = "";
X		  break;
X        default: goto badarg;
X      } while (*argp);
X    }
X    if (!tarf) tarf = stdin;
X    if (argc) {
X      files = argv;
X      files[argc] = (char *) NULL;
X      ffound = malloc(argc * sizeof(int));
X      if (!ffound) fatal("Out of memory.");
X      while (argc) ffound[--argc] = 0;
X    }
X    untar();
X    if (tarf != stdin) fclose(tarf);
X    if (ffound) free(ffound);
X    return 0;
X
Xbadarg:
X  fprintf(stderr, "muntar Version %s by gz@entity.com.  Usage: \n", Version);
X  fprintf(stderr, "%s [-lvubdm] [-c XXXX] [-f tarfile] filenames...\n", Program);
X  fprintf(stderr, "Extracts the named files from a tar archive.\n\
X -l = just list file names, do not actually extract any files.\n\
X -v = verbose: list names of files as extract them.\n\
X -u = do not try to detect and handle macbinary files.\n\
X -b = do not convert LF to CR in non-macbinary files.\n\
X -d = do not restore creation dates in macbinary files.\n\
X -m = do not restore modification dates in macbinary files.\n\
X -c XXXX = set file creator to XXXX (default is MPS) in non-macbinary files.\n\
X -f tarfile = read input from tarfile (instead of standard input).\n\
X -H = show this help message.\n\
XIf no filenames are specified, all files in the archive will be\n\
Xextracted (or listed).\n\n\
XThis is freeware.  If you paid money for this program, you got ripped off.\n");
X  return 2;
X}
X
Xuntar ()
X{
X  long size, chksum, hchksum;
X  int i;
X  while (readblock(&tarh, sizeof(tarh))) {
X    for (i=0; i < sizeof(tarh) && tarh.name[i]=='\0'; ++i) ;
X    if (i == sizeof(tarh)) break;
X    hchksum = untar_number(tarh.chksum);
X    for (i=0; i < 8; ++i) tarh.chksum[i] = ' ';
X    for (i=chksum=0; i < sizeof(tarh); ++i) chksum += (unsigned char)tarh.name[i];
X    if (chksum != hchksum)
X      fatal("Bad header checksum (got 0%lo, expected 0%lo) for %s",
X            chksum, hchksum, tarh.name);
X    switch (tarh.linkflag) {
X      case '\0': case '0': untar_file(); break;
X      case '1': case '2':  untar_link(); break;
X      case '5': untar_directory(); break;
X      default: fatal("Unknown header type (0%o)", tarh.linkflag);
X    }
X  }
X  if (!feof(tarf) && ferror(tarf)) readerr();
X 
X  for(i=0; files[i]; ++i) if (!ffound[i])
X    fprintf(stderr,"%s: Warning - File %s not found.\n", Program, files[i]);
X}
X
Xuntar_file ()
X{
X  long fsize = untar_number(tarh.size);
X  if (tarh.name[strlen(tarh.name)-1] == '/') return untar_directory();
X  unix2mac(tarh.name);
X  if (!dataonly && chkmacbin(fsize)) untar_macbin(fsize);
X  else untar_data(fsize);
X}
X
Xchkmacbin (fsize)
X  long fsize;
X{
X  char buf[128];
X  if (fsize < 128) return 0;
X  if (!readblock(buf, 128)) readerr();
X  mcopy(&macbinh, &buf[1], sizeof(macbinh));
X  if (buf[0] == 0 && macbinh.zero == 0 &&
X      macbinh.nlen < 64 && macbinh.nlen > 0 &&
X      macbinh.dflen >= 0 && macbinh.rflen >= 0 &&
X      (unsigned) (macbinsz(macbinh.dflen) + macbinsz(macbinh.rflen) + 128L)
X          <= (unsigned) fsize) return 1;
X  fseek(tarf, -128L, 1);
X  return 0;
X}
X
Xuntar_data (fsize)
X  long fsize;
X{
X  IOParam pb;
X  char fname[110];
X  int ignore = ignore_file(tarh.name);
X
X  if (verbose && !ignore) {
X    printf("X %s (%ld bytes)\n", tarh.name, fsize);
X    fflush(stdout);
X  }
X  if (listonly || ignore) {
X    fseek(tarf, tarsz(fsize), 1);
X    return;
X  }
X  create_file(tarh.name);
X  openpb(&pb, strcpy(fname, tarh.name), fsWrPerm);
X  pb.ioReqCount = sizeof(tarh);
X  writefork(&pb, fsize, binary);
X}
X
Xuntar_macbin (fsize)
X  long fsize;
X{
X  IOParam pb;
X  char fname[170], *cp;
X  long endpos = ftell(tarf) - 128 + tarsz(fsize);
X  int ignore;
X  for(cp = tarh.name+strlen(tarh.name); cp != tarh.name && cp[-1] != ':'; --cp);
X  mcopy(cp, macbinh.name, macbinh.nlen);
X  cp[macbinh.nlen] = '\0';
X  ignore = ignore_file(tarh.name);
X  if (verbose && !ignore) {
X    printf("Macbin %s (%ld+%ld bytes)\n", tarh.name, macbinh.dflen, macbinh.rflen);
X    fflush(stdout);
X  }
X  if (!listonly && !ignore) {
X    create_file(tarh.name);
X    strcpy(fname, tarh.name);  /* For error messages */
X    openpb(&pb, fname, fsWrPerm);
X    pb.ioReqCount = 128;
X    writefork(&pb, macbinh.dflen, 1);
X    pb.ioNamePtr = fname;
X    pb.ioVRefNum = 0;
X    pb.ioVersNum = 0;
X    pb.ioPermssn = fsWrPerm;
X    pb.ioMisc = 0;
X    if (PBOpenRF(&pb, 0)) pbsyserr(&pb);
X    pb.ioReqCount = 128;
X    writefork(&pb, macbinh.rflen, 1);
X    setmacbin(fname);
X  }
X  fseek(tarf, endpos, 0);
X}
X
Xwritefork(pb, size, binp)
X  IOParam *pb;
X  long size;
X{
X  int i;
X  pb->ioPosMode = fsAtMark;
X  pb->ioPosOffset = 0;
X  pb->ioBuffer = &tarh;
X  while (size) {
X    if (!readblock(&tarh, pb->ioReqCount)) {
X      PBClose(pb, 0);
X      readerr();
X    }
X    if (!binp)
X      for (i=0; i < pb->ioReqCount; ++i)
X        if (tarh.name[i] == 'J'-'@') tarh.name[i] = 'M'-'@';
X    if (size < pb->ioReqCount) pb->ioReqCount = size;
X    size -= pb->ioReqCount;
X    if (PBWrite(pb, 0) || pb->ioActCount != pb->ioReqCount) {
X      int err = pb->ioResult;
X      PBClose(pb, 0);
X      pb->ioResult = err;
X      pbsyserr(pb);
X    }
X  }
X  PBClose(pb, 0);
X}
X
X
Xsetmacbin (fname)
X  char *fname;
X{
X  FileParam pb;
X  pb.ioNamePtr = fname;
X  pb.ioVRefNum = 0;
X  pb.ioFVersNum = 0;
X  pb.ioFDirIndex = 0;
X  if (PBGetFInfo(&pb, 0)) pbsyserr(&pb);
X  mcopy(&pb.ioFlFndrInfo, &macbinh.finfo, sizeof(FInfo));
X  if (!ignore_cdate) pb.ioFlCrDat = macbinh.cdate;
X  if (!ignore_mdate) pb.ioFlMdDat = macbinh.mdate;
X  if (PBSetFInfo(&pb, 0)) pbsyserr(&pb);
X}
X
Xuntar_directory ()
X{
X  int len = strlen(tarh.name);
X
X  if (len && tarh.name[len-1] != '/') {
X    tarh.name[len] = '/';
X    tarh.name[len+1] = '\0';
X  }
X  unix2mac(tarh.name);
X  if (!tarh.name[1] || ignore_file(tarh.name)) return;
X  if (listonly) {
X    printf("Directory \"%s\"\n", tarh.name);
X    fflush(stdout);
X    return;
X  }
X  create_directory(tarh.name);
X}
X
X/*  fname may contain a filename after the last :, it will be ignored */
Xcreate_directory (fname)
X  char *fname;
X{
X  HFileParam pb;
X  char name[128], *cp, *bp;
X  
X  cp = fname + strlen(fname);
X  for (bp = fname; *bp == ':'; ++bp);
X
X  pb.ioNamePtr = (StringPtr) name;
X  do {
X    while (cp[-1] != ':') --cp;
X    if (cp == bp) fatal("%s - Bad directory name", fname);
X    strncpy(name+1, fname, name[0] = --cp - fname);
X    pb.ioVRefNum = 0;
X    pb.ioDirID = 0;
X  } while (PBDirCreate(&pb, 0) == dirNFErr);
X  if (pb.ioResult) pbsyserr(&pb);
X  while (1) {
X    while (*++cp != ':') if (!*cp) return;
X    strncpy(name+1, fname, name[0] = cp - fname);
X    pb.ioVRefNum = 0;
X    pb.ioDirID = 0;
X    if (PBDirCreate(&pb, 0)) pbsyserr(&pb);
X  }
X}
X  
Xcreate_file (fname)
X  char *fname;
X{
X  FileParam pb;
X  char name[128];
X  pb.ioNamePtr = c2pstr(strcpy(name, fname));
X  pb.ioVRefNum = 0;
X  pb.ioFVersNum = 0;
X  if (PBCreate(&pb, 0)) {
X     if (pb.ioResult != dirNFErr) pbsyserr(&pb);
X     create_directory(fname);
X     pb.ioNamePtr = c2pstr(strcpy(name, fname));
X     pb.ioVRefNum = 0;
X     pb.ioFVersNum = 0;
X     if (PBCreate(&pb, 0)) pbsyserr(&pb);
X  }
X  pb.ioNamePtr = c2pstr(strcpy(name, fname));
X  pb.ioVRefNum = 0;
X  pb.ioFVersNum = 0;
X  pb.ioFDirIndex = 0;
X  if (PBGetFInfo(&pb, 0)) pbsyserr(&pb);
X  pb.ioNamePtr = c2pstr(strcpy(name, fname));
X  pb.ioFlFndrInfo.fdType = TEXT;
X  pb.ioFlFndrInfo.fdCreator = filecreator;
X  if (PBSetFInfo(&pb, 0)) pbsyserr(&pb);
X}
X
Xuntar_link ()
X{
X  unix2mac(tarh.name);
X  unix2mac(tarh.linkname);
X  fprintf(stderr, "%s: Warning: Link %s -> %s (ignored)\n", Program, tarh.name, tarh.linkname);
X}
X
X/* Convert Unix pathname to a mac pathname.  Do not allow absolute
X   names - "/foo/bar/baz" is treated as if it were "foo/bar/baz".
X */
Xunix2mac (name)
X  char *name;
X{
X  char buf[102];
X  char *cp = name, *bp = buf, *op;
X  *bp++ = ':';
X  op = bp;
X  while (1) {
X    if (cp[0] == '/') ++cp;
X    else if (cp[0] == '.' && cp[1] == '/') cp += 2;
X    else if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') {
X      if (op == bp) *bp++ = ':', op = bp;
X      else for (--op; op != bp && op[-1] != ':'; --op);
X      cp += 3;
X    }
X    else {
X      while (*cp && *cp != '/') if ((*op++ = *cp++) == ':') op[-1] = '/';
X      if (!*cp++) break;
X      *op++ = ':';
X    }
X  }
X  *op = '\0';
X  strcpy(name, buf);
X}
X
Xignore_file (file)
X  char *file;
X{
X  char **fp = files;
X  int *ip = ffound;
X  if (!fp) return 0;
X  for (; *fp; ++fp, ++ip)
X    if (match_file(*fp, file)) {
X      ++ip[0];
X      return 0;
X    }
X  return 1;
X}
X
X/* Name is the user-specified name, file is the actual tarred file */
Xmatch_file (name, file)
X  char *name, *file;
X{
X  char *cp;
X  if (!strdiff(name, file)) return 1;
X  if ((cp = strrchr(file, ':')) && !strdiff(cp+1, name)) return 1;
X  if (name[strlen(name)-1] == ':' && strdiff(name, file) >= 0) return 1;
X  return 0;
X}
X
X/* return 0 if the same, 1 if s1 is a substring of s2, -1 otherwise */
Xstrdiff (s1, s2)
X  char *s1, *s2;
X{
X  while (*s1) {
X    if (*s1 != *s2) {
X      char c1 = *s1;
X      if ('A' <= c1 && c1 <= 'Z') c1 |= 0x20;
X      if ('a' > c1 || c1 > 'z' || c1 != (*s2 | 0x20)) return -1;
X    }
X    ++s1, ++s2;
X  }
X  return (*s2 ? 1 : 0);
X}
X
Xuntar_number (cp)
X  char *cp;
X{
X  int neg = 0, num = 0;
X  while (*cp == ' ') cp++;
X  if (*cp == '-') neg++, cp++;
X  while ('0' <= *cp && *cp <= '7') num = (num<<3) + (*cp++ - '0');
X  if (neg) num = -num;
X  return num;
X}
X
Xopenpb(pb, name, perm)
X  IOParam *pb;
X  char *name;
X  int perm;
X{
X  pb->ioVRefNum = 0;
X  pb->ioVersNum = 0;
X  pb->ioPermssn = perm;
X  pb->ioMisc = 0;
X  pb->ioRefNum = 0;
X  pb->ioNamePtr = c2pstr(name);
X  if (PBOpen(pb, 0)) pbsyserr(pb);
X}
X
X
Xreaderr ()
X{
X  if (feof(tarf)) fatal("Premature end of file");
X  else fatal("Read error");
X}
X
Xpbsyserr (pb)
X  IOParam *pb;
X{
X  int err = pb->ioResult;
X  char *name = pb->ioNamePtr;
X  p2cstr(name);
X  if (err == fnfErr) fatal("%s - File not found", name);
X  else if (err == nsvErr) fatal("%s - No such volume", name);
X  else if (err == tmfoErr) fatal("%s - Too many files open", name);
X  else if (err == permErr) fatal("%s - Permissions error", name);
X  else if (err == dupFNErr) fatal("%s - Duplicate filename", name);
X  else if (err == eofErr) fatal("%s - Premature end of file", name);
X  else fatal("%s - Error #%d", name, err);
X}
X
Xfatal(fmt, arg1, arg2, arg3, arg4, arg5, arg6)
X  char *fmt, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6;
X{
X  if (tarf && tarf != stdin) fclose(tarf);
X  if (ffound) free(ffound);
X  fflush(stdout);
X  fprintf(stderr, "%s: ", Program);
X  fprintf(stderr, fmt, arg1, arg2, arg3, arg4, arg5, arg6);
X  fprintf(stderr, "\n");
X  exit(2);
X}
X
SHAR_EOF
if test 13396 -ne "`wc -c < 'muntar.c'`"
then
	echo shar: error transmitting "'muntar.c'" '(should have been 13396 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
---