*BSD News Article 20434


Return to BSD News archive

Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!howland.reston.ans.net!noc.near.net!ceylon!genesis!steve2
From: steve2@genesis.nred.ma.us
Newsgroups: comp.os.386bsd.development
Subject: ft dist0.2 part 03/03
Message-ID: <CCtK7G.3E5@genesis.nred.ma.us>
Date: 4 Sep 93 07:45:10 GMT
Organization: Genesis Public Access Unix  +1 508 664 0149
Lines: 1589

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	qtar
#	qtar/Makefile
#	qtar/action.c
#	qtar/fileset.c
#	qtar/ftecc.c
#	qtar/func.c
#	qtar/header.c
#	qtar/qtar.c
#	qtar/qtar.h
#	qtar/volume.c
#	tools
#	tools/getseg.c
#	tools/postest.c
#
echo c - qtar
mkdir qtar > /dev/null 2>&1
echo x - qtar/Makefile
sed 's/^X//' >qtar/Makefile << 'END-of-qtar/Makefile'
X#
X#  Copyright (c) 1993 Steve Gerakines
X#
X#  This is freely redistributable software.  You may do anything you
X#  wish with it, so long as the above notice stays intact.
X#
X#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X#  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X#  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X#  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X#  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X#  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X#  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X#  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X#  POSSIBILITY OF SUCH DAMAGE.
X#
X#  Makefile - QIC tape archiver makefile
X#  07/22/93 v0.1
X#  Initial revision.
X#
X
XCC = cc
XCFLAGS = -O
X
XOBJ = qtar.o ftecc.o header.o action.o func.o fileset.o volume.o
X
Xqtar:	$(OBJ)
X	$(CC) -o qtar $(OBJ)
X
Xclean:
X	rm -f qtar $(OBJ) core core.*
X
X$(OBJ): qtar.h
END-of-qtar/Makefile
echo x - qtar/action.c
sed 's/^X//' >qtar/action.c << 'END-of-qtar/action.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  action.c - QIC tape archiver actions
X *  07/22/93 v0.1
X *  Initial revision.
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <errno.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X#include <ctype.h>
X
Xint tfd;			/* file descriptor for tape */
X
X
X/* List volume names on tape (volume list) */
Xvoid getvols()
X{
X  if (!check_stat(tfd,0)) {
X	ioctl(tfd, QIOREWIND);
X	if (!get_header(tfd)) vol_list();
X  }
X}
X
X/* Retrieve files from tape (extract or list) */
Xvoid getfiles()
X{
X  QIC_VTbl *v;
X
X  if (check_stat(tfd,0)) return;
X  ioctl(tfd, QIOREWIND);
X  if (get_header(tfd)) return;
X  if ((v = vol_select(label)) == NULL) {
X	fprintf(stderr, "No volumes selected\n");
X	return;
X  }
X
X  /* Retrieve the file set directory from tape. */
X  fs_retrieve(v);
X
X  /* Traverse the file set data and list/extract files. */
X  fs_perform();
X}
X
X
X/* Put files to tape */
Xputfiles()
X{
X}
X
X
X/*
X *  Either get files or put files
X */
Xdoaction()
X{
X  /* Open the tape device */
X  tfd = open(ftdev, 2);
X  if (tfd < 2) {
X	perror(ftdev);
X	exit(0);
X  }
X
X  switch(action) {
X     case ACT_LISTVOL:
X	getvols();
X	break;
X
X     case ACT_LIST:
X     case ACT_EXTRACT:
X	getfiles();
X	break;
X
X     case ACT_APPEND:
X     case ACT_CREATE:
X	putfiles();
X	break;
X
X     default:
X	fprintf(stderr, "qtar: invalid action code\n");
X	break;
X  }
X
X  close(tfd);
X}
END-of-qtar/action.c
echo x - qtar/fileset.c
sed 's/^X//' >qtar/fileset.c << 'END-of-qtar/fileset.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  fileset.c - QIC tape archiver fileset functions
X *  08/14/93 v0.1
X *  Initial revision.
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <errno.h>
X#include <time.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <grp.h>
X#include <string.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X#include <ctype.h>
X
XULONG sg_cpos;			/* Absolute byte position on tape */
XULONG sg_bpos;			/* Byte offset within data block */
XULONG sg_avail;			/* Total bytes available in current segment */
XULONG sg_foffset;		/* First block offset found */
XULONG sg_boffset;		/* Byte offset from segment structure */
XULONG sg_bcount;		/* Byte count from segment structure */
Xint sg_iscpr;			/* TRUE if this block of data is compressed */
Xint sg_first;			/* TRUE if first segment read */
X
XFSNode fsroot;			/* file set root directory */
Xchar rootname[2];		/* root directory string "" or "/" */
Xint curuid, curgid;		/* uid and gid of current user */
X
X/* Initialize for I/O processing on a fileset. */
Xfs_init(QIC_VTbl *v)
X{
X  sg_cpos = (ULONG)v->vt_first * QCV_SEGSIZE;
X  curuid = getuid();
X  curgid = getgid();
X}
X
X
X/* Read single characters, buffer by segment */
Xint fs_getc()
X{
X  static int curseg = -1;
X  static int curtrk = -1;
X  int bpos, sno, tno, cno, r;
X  QIC_Segment s;
X
X  tno = sg_cpos / (QCV_SEGSIZE * geo.g_segtrk);
X  sno = (sg_cpos / QCV_SEGSIZE) % geo.g_segtrk;
X  cno = sg_cpos % QCV_SEGSIZE;
X
X  if (sg_first) {
X	sg_first = 0;
X	goto nextseg;
X  }
X
X  /* If current character exceeds amount available, next segment. */
X  if (cno >= sg_avail) {
Xcompute_next:
X	sg_cpos &= ~0x7fff;
X	sg_cpos += QCV_SEGSIZE;
X	tno = sg_cpos / (QCV_SEGSIZE * geo.g_segtrk);
X	sno = (sg_cpos / QCV_SEGSIZE) % geo.g_segtrk;
X	cno = sg_cpos % QCV_SEGSIZE;
X	goto nextseg;
X  }
X
X  /* If byte position exceeds amount in this block, process next block. */
X  if (vf_strat && sg_bpos == sg_bcount) {
X	if ((sg_avail - cno) <= 12) goto compute_next;
X	sg_bcount = US_VAL(buff,cno);
X	if (!sg_bcount) goto compute_next;
X	sg_bpos = 0;
X	cno += 2;
X	sg_cpos += 2;
X  }
X
X  if (tno != curtrk || sno != curseg) {
Xnextseg:
X	curseg = sno;
X	curtrk = tno;
X	s.sg_trk = tno;
X	s.sg_seg = sno;
X	s.sg_badmap = BADMAP(tno,sno);
X	s.sg_data = buff;
X	if (ioctl(tfd, QIOREAD, &s) < 0) perror("read");
X	r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
X	sg_avail = sect_bytes(s.sg_badmap) - QCV_ECCSIZE;
X	switch (vf_strat) {
X	    case 0:		/* No compression used */
X		bpos = 0;
X		sg_cpos = tno * QCV_SEGSIZE * geo.g_segtrk + sno * QCV_SEGSIZE;
X		sg_boffset = sg_cpos;
X		sg_bcount = sg_avail;
X		sg_iscpr = 0;
X		cno = 0;
X/*printf("trk %d segment %d sg_boffset=%d\n", tno, sno, sg_boffset);*/
X		break;
X	    case 1:		/* Compression, no segment spanning */
X		sg_foffset = 0;
X		break;
X	    case 2:
X		sg_foffset = US_VAL(s.sg_data,0);
X		cno += 2;
X		sg_cpos += 2;
X		break;
X	}
X  }
X
X  if (vf_strat && cno == sg_foffset) {
X	sg_boffset = UL_VAL(buff,cno);
X	sg_bcount = US_VAL(buff,cno+4) & 0x7fff;
X	sg_iscpr = (US_VAL(buff,cno+4) & 0x8000) == 0;
X	cno += 6;
X	sg_cpos += 6;
X  }
X
X  r = buff[cno++];
X  sg_cpos++;
X
X  return(r);
X}
X
X
X/* Retrieve file set directory from tape for a given volume. */
Xint fs_retrieve(QIC_VTbl *v)
X{
X  char *name;
X
X  /* Initialize for processing a volume */
X  vol_init(v);
X  sg_first = 1;
X
X  /* Initialize the root node */
X  rootname[0] = (isabsolute) ? '/' : '\0';
X  rootname[1] = '\0';
X  fsroot.name = rootname;
X  bzero(fsroot.fixed, 10);
X  bzero(fsroot.sys, 32);
X  fsroot.hsize = fsroot.dsize = 0;
X  fsroot.nxosi = 0;
X  fsroot.xosi = NULL;
X  fsroot.next = fsroot.prev = fsroot.parent = NULL;
X  fsroot.dirh = fsroot.dirt = NULL;
X
X  /* Recurse and gather file info if root not empty. */
X  if (UL_VAL(v->vt_dtasize,0)) return(fs_gather(&fsroot, rootname));
X  return(0);
X}
X
X
X/* Gather file info recursively. */
Xfs_gather(FSNode *n, char *dname)
X{
X  FSNode *dh, *dt;
X  int i, siz, psiz;
X  FSNode *nn;
X  char curdir[80];
X  char tmpstr[128];
X
X  dh = dt = NULL;
X
X  psiz = strlen(dname);
X  if (psiz && dname[psiz-1] == '/') psiz--;
X  if (dname[0] == '/') psiz--;
X
X  /* First, read all entries for the current directory. */
X  for (;;) {
X	/* Allocate a new node */
X	if ((nn = (FSNode *)malloc(sizeof(FSNode))) == NULL) {
X		fprintf(stderr, "qtar: Out of memory!\n");
X		exit(1);
X	}
X	for (i = 0; i < 10; i++) nn->fixed[i] = fs_getc();
X	siz = nn->fixed[0] - 9;
X	for (i = 0; siz > 0; siz--, i++) nn->sys[i] = fs_getc();
X	siz = fs_getc();
X	if ((nn->name = malloc(siz+1)) == NULL) {
X		fprintf (stderr, "qtar: Out of memory!\n");
X		exit(1);
X	}
X	if (islowering) {
X		for (i = 0; siz > 0; siz--, i++)
X			nn->name[i] = tolower(fs_getc());
X	} else {
X		for (i = 0; siz > 0; siz--, i++)
X			nn->name[i] = fs_getc();
X	}
X	nn->name[i] = '\0';
X	nn->hsize = nn->dsize = 0;
X	if (UL_VAL(nn->fixed,6)) {
X		nn->hsize = nn->fixed[0] + i + psiz + 7;
X		nn->dsize = UL_VAL(nn->fixed,6) - nn->hsize;
X	}
X
X	nn->nxosi = 0;
X	nn->xosi = NULL;
X	nn->next = nn->prev = NULL;
X	nn->parent = n;
X	nn->dirh = nn->dirt = NULL;
X	if (dh != NULL) {
X		nn->prev = dt;
X		dt->next = nn;
X		dt = nn;
X	} else
X		dh = dt = nn;
X	if (nn->fixed[1] & 0xc0) break;
X  }
X
X  /* Save the list head and tail for the given parent. */
X  n->dirh = dh;
X  n->dirt = dt;
X
X  /* Next scan the list and recurse on non-empty sub directories. */
X  for (; dh != NULL; dh = dh->next) {
X	if ((dh->fixed[1] & 0x20) == 0) continue;
X	sprintf(curdir, "%s%s/", dname, dh->name);
X	if (UL_VAL(dh->fixed,6)) continue;
X	fs_gather(dh, curdir);
X  }
X  return(0);
X}
X
X/* Kick off tree traversal. */
Xfs_perform()
X{
X  fs_traverse(&fsroot, rootname, nfiles ? 1 : 0);
X}
X
X/* Locate files in the tree recursively. */
Xfs_traverse(FSNode *n, char *path, int docmp)
X{
X  FSNode *dh;
X  int i;
X  char item[256];
X
X  dh = n->dirh;
X  for (; dh != NULL; dh = dh->next) {
X	if (dh->dirh != NULL) continue;
X	strcpy(item, path);
X	strcat(item, dh->name);
X	if (docmp) {
X		for (i = 0; i < nfiles; i++)
X			if (!strcmp(item, filelist[i])) break;
X		if (i == nfiles) continue;
X	}
X	if (action == ACT_EXTRACT)
X		fs_extract(dh, item);
X	else
X		fs_list(dh, item);
X  }
X
X  dh = n->dirh;
X  for (; dh != NULL; dh = dh->next) {
X	if (dh->dirh == NULL) continue;
X	strcpy(item, path);
X	strcat(item, dh->name);
X/*printf("docmp=%d nfiles=%d item=%s\n", docmp, nfiles, item);*/
X	if (docmp) {
X		for (i = 0; i < nfiles; i++)
X			if (!strcmp(item, filelist[i])) break;
X		if (i == nfiles) {
X			strcat(item, "/");
X			fs_traverse(dh, item, 1);
X			continue;
X		}
X	}
X	if (action == ACT_EXTRACT)
X		fs_extract(dh, item);
X	else
X		fs_list(dh, item);
X	strcat(item, "/");
X	fs_traverse(dh, item, 0);
X  }
X}
X
X/* Extract an item from tape */
Xfs_extract(FSNode *n, char *item)
X{
X}
X
X/* List an item stored on tape */
Xfs_list(FSNode *n, char *item)
X{
X  int uid, gid, perm, size;
X  int i, uextend;
X  char str[32], str2[16], pbits[11], owner[32], *cp;
X  const char xwr[] = "xwr";
X  static struct passwd *p;
X  static struct group *g;
X  struct tm *mtime;
X  static int omax = 3;
X  static int smax = 5;
X  static int lastuid = -1;	/* cache last uid and gid entries */
X  static int lastgid = -1;
X
X  if (!isverbose) {
X	printf("%s%s\n", item, (n->fixed[1] & 0x20) ? "/" : "");
X	return(0);
X  }
X
X  /* Decide if the unix extension area exists */
X  uextend = (n->fixed[0] > 9 && n->sys[0] == 1) ? 1 : 0;
X
X  /* Figure out permissions and file type */
X  perm = (n->fixed[1] & 0x7) << 6;
X  if (uextend) {
X	perm |= n->sys[1] & 0x3f;
X	perm |= (n->sys[2] & 0x01) ? S_ISUID : 0;
X	perm |= (n->sys[2] & 0x02) ? S_ISGID : 0;
X	perm |= (n->sys[2] & 0x04) ? S_ISTXT : 0;
X	perm |= (n->sys[2] & 0x10) ? S_IFCHR : 0;
X	perm |= (n->sys[2] & 0x20) ? S_IFBLK : 0;
X	perm |= (n->sys[2] & 0x40) ? S_IFREG : 0;
X	perm |= (n->sys[2] & 0x80) ? S_IFIFO : 0;
X  }
X
X  size = 0;
X  if ((n->fixed[1] & 0x20) == 0) {
X	if (!uextend) perm |= S_IFREG;
X	size = n->dsize;
X  } else
X	perm |= S_IFDIR;
X
X  /* Get file uid and gid, if any */
X  uid = (uextend) ? UL_VAL(n->sys,15) : curuid;
X  gid = (uextend) ? UL_VAL(n->sys,19) : curgid;
X
X  /* Modification time */
X  mtime = qtime(&n->fixed[2]);
X
X  /* Now display what we've got. */
X  strcpy(pbits, "----------");
X  if ((perm & S_IFBLK) == S_IFBLK)
X	pbits[0] = 'b';
X  else if ((perm & S_IFDIR) == S_IFDIR)
X	pbits[0] = 'd';
X  else if ((perm & S_IFCHR) == S_IFCHR)
X	pbits[0] = 'c';
X  else if ((perm & S_IFIFO) == S_IFIFO)
X	pbits[0] = 'p';
X  for (i = 0; i < 9; i++)
X	if (perm & (1 << i)) pbits[9 - i] = xwr[i % 3];
X
X  if (uid != lastuid) {
X	if ((p = getpwuid(uid)) == NULL) {
X		uid = curuid;
X		p = getpwuid(curuid);
X	}
X	lastuid = uid;
X  }
X  if (gid != lastgid) {
X	if ((g = getgrgid(gid)) == NULL) {
X		gid = curgid;
X		g = getgrgid(curgid);
X	}
X	lastgid = gid;
X  }
X  strcpy(owner, (p == NULL) ? "nobody" : p->pw_name);
X  strcat(owner, "/");
X  strcat(owner, (p == NULL) ? "nobody" : g->gr_name);
X  if (omax < (i=strlen(owner))) omax = i;
X  sprintf(str, "%d", size);
X  if (smax < (i=strlen(str))) smax = i;
X  strcpy(str, asctime(mtime));
X  if ((cp = strchr(str, '\n')) != NULL) *cp = '\0';
X
X  if (pbits[0] == 'b' || pbits[0] == 'c') {
X	sprintf(str2, "%d, %d", n->sys[23], n->sys[24]);
X	printf("%s %-*.*s %*.*s %s %s%s\n",
X		pbits, omax,omax,owner, smax,smax,str2,
X		str, item, (n->fixed[1]&0x20) ? "/":"");
X  } else
X	printf("%s %-*.*s %*d %s %s%s\n",
X		pbits, omax,omax,owner, smax,size,
X		str, item, (n->fixed[1]&0x20) ? "/":"");
X}
END-of-qtar/fileset.c
echo x - qtar/ftecc.c
sed 's/^X//' >qtar/ftecc.c << 'END-of-qtar/ftecc.c'
X/*
X *  ftecc.c  07/22/93
X *  Handle error correction for floppy tape drives.
X *
X *  File contents are copyrighted by David L. Brown and falls under the
X *  terms of the GNU Public License.  See his original release for the
X *  specific terms.
X *
X *  Steve Gerakines
X *  steve2@genesis.nred.ma.us
X *  Modified slightly to fit with my tape driver.  I'm not at all happy
X *  with this module and will have it replaced with a more functional one
X *  in the next release.  I am close, but progress will continue to be
X *  slow until I can find a book on the subject where the translator
X *  understands both the to and from languages. :-(  For now it will
X *  suffice.
X */
X#include <sys/ftape.h>
X#include "qtar.h"
X
X/*
X *  In order to speed up the correction and adjustment, we can compute
X *  a matrix of coefficients for the multiplication.
X */
Xstruct inv_mat {
X  UCHAR log_denom;      /* The log z of the denominator. */
X  UCHAR zs[3][3];       /* The coefficients for the adjustment matrix. */
X};
X
X/* This array is a table of powers of x, from 0 to 254. */
Xstatic UCHAR alpha_power[] = {
X  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
X  0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
X  0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
X  0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
X  0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
X  0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
X  0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
X  0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
X  0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
X  0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
X  0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
X  0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
X  0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
X  0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
X  0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
X  0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
X  0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
X  0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
X  0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
X  0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
X  0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
X  0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
X  0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
X  0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
X  0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
X  0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
X  0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
X  0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
X  0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
X  0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
X  0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
X  0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3
X};
X
X/*
X * This is the reverse lookup table.  There is no log of 0, so the
X * first element is not valid.
X */
Xstatic UCHAR alpha_log[] = {
X  0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
X  0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
X  0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
X  0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
X  0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
X  0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
X  0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
X  0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
X  0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
X  0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
X  0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
X  0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
X  0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
X  0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
X  0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
X  0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
X  0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
X  0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
X  0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
X  0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
X  0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
X  0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
X  0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
X  0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
X  0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
X  0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
X  0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
X  0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
X  0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
X  0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
X  0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
X  0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
X};
X
X/* Multiply two numbers in the field. */
Xstatic UCHAR multiply(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (a == 0 || b == 0) return(0);
X  tmp = (alpha_log[a] + alpha_log[b]);
X  if (tmp > 254) tmp -= 255;
X  return (alpha_power[tmp]);
X}
X
Xstatic UCHAR divide(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (a == 0 || b == 0) return(0);
X  tmp = (alpha_log[a] - alpha_log[b]);
X  if (tmp < 0) tmp += 255;
X  return (alpha_power[tmp]);
X}
X
X/*
X * This is just like divide, except we have already looked up the log
X * of the second number.
X */
Xstatic UCHAR divide_out(UCHAR a, UCHAR b)
X{
X  int tmp;
X
X  if (a == 0) return 0;
X  tmp = alpha_log[a] - b;
X  if (tmp < 0) tmp += 255;
X  return (alpha_power[tmp]);
X}
X
X/* This returns the value z^{a-b}. */
Xstatic UCHAR z_of_ab(UCHAR a, UCHAR b)
X{
X  int tmp = (int)a - (int)b;
X
X  if (tmp < 0)
X	tmp += 255;
X  else if (tmp >= 255)
X	tmp -= 255;
X  return(alpha_power[tmp]);
X}
X
X/*  Calculate the inverse matrix.  Returns 1 if the matrix is valid, or
X *  zero if there is no inverse.  The i's are the indices of the bytes
X *  to be corrected.
X */
Xstatic int calculate_inverse (UCHAR *pblk, struct inv_mat *inv)
X{
X  /* First some variables to remember some of the results. */
X  UCHAR z20, z10, z21, z12, z01, z02;
X  UCHAR i0, i1, i2;
X
X  i0 = pblk[0]; i1 = pblk[1]; i2 = pblk[2];
X
X  z20 = z_of_ab (i2, i0); z10 = z_of_ab (i1, i0);
X  z21 = z_of_ab (i2, i1); z12 = z_of_ab (i1, i2);
X  z01 = z_of_ab (i0, i1); z02 = z_of_ab (i0, i2);
X  inv->log_denom = (z20 ^ z10 ^ z21 ^ z12 ^ z01 ^ z02);
X  if (inv->log_denom == 0) return 0;
X  inv->log_denom = alpha_log[inv->log_denom];
X
X  /* Calculate all of the coefficients on the top. */
X  inv->zs[0][0] = alpha_power[i1] ^ alpha_power[i2];
X  inv->zs[0][1] = z21 ^ z12;
X  inv->zs[0][2] = alpha_power[255-i1] ^ alpha_power[255-i2];
X
X  inv->zs[1][0] = alpha_power[i0] ^ alpha_power[i2];
X  inv->zs[1][1] = z20 ^ z02;
X  inv->zs[1][2] = alpha_power[255-i0] ^ alpha_power[255-i2];
X
X  inv->zs[2][0] = alpha_power[i0] ^ alpha_power[i1];
X  inv->zs[2][1] = z10 ^ z01;
X  inv->zs[2][2] = alpha_power[255-i0] ^ alpha_power[255-i1];
X  return(1);
X}
X
X/*
X * Determine the error values for a given inverse matrix and syndromes.
X */
Xstatic void determine3(struct inv_mat *inv, UCHAR *es, UCHAR *ss)
X{
X  UCHAR tmp;
X  int i, j;
X
X  for (i = 0; i < 3; i++) {
X	tmp = 0;
X	for (j = 0; j < 3; j++) tmp ^= multiply (ss[j], inv->zs[i][j]);
X	es[i] = divide_out(tmp, inv->log_denom);
X  }
X}
X
X
X/*
X *  Compute the 3 syndrome values.  The data pointer should point to
X *  the offset within the first block of the column to calculate.  The
X *  count of blocks is in blocks.  The three bytes will be placed in
X *  ss[0], ss[1], and ss[2].
X */
Xstatic void compute_syndromes(UCHAR *data, int nblks, int col, UCHAR *ss)
X{
X  int i;
X  UCHAR v;
X
X  ss[0] = 0; ss[1] = 0; ss[2] = 0;
X  for (i = (nblks-1)*QCV_BLKSIZE; i >= 0; i -= QCV_BLKSIZE) {
X	v = data[i+col];
X	if (ss[0] & 0x01) { ss[0] >>= 1; ss[0] ^= 0xc3;	} else ss[0] >>= 1;
X	ss[0] ^= v;
X	ss[1] ^= v;
X	if (ss[2] & 0x80) { ss[2] <<= 1; ss[2] ^= 0x87; } else ss[2] <<= 1;
X	ss[2] ^= v;
X  }
X}
X
X/*
X *  Calculate the parity bytes for a segment.  Returns 0 on success.
X */
Xint set_parity (UCHAR *data, ULONG badmap)
X{
X  int col;
X  struct inv_mat inv;
X  UCHAR ss[3], es[3], pblk[3];
X  int nblks;
X
X  nblks = sect_count(badmap);
X  pblk[0] = nblks-3; pblk[1] = nblks-2; pblk[2] = nblks-1;
X  if (!calculate_inverse(pblk, &inv)) return(1);
X  pblk[0] *= QCV_BLKSIZE; pblk[1] *= QCV_BLKSIZE; pblk[2] *= QCV_BLKSIZE;
X  for (col = 0; col < QCV_BLKSIZE; col++) {
X	compute_syndromes (data, nblks, col, ss);
X	determine3(&inv, es, ss);
X	data[pblk[0]+col] = es[0];
X	data[pblk[1]+col] = es[1];
X	data[pblk[2]+col] = es[2];
X  }
X  return(0);
X}
X
X
X/*
X *  Check and correct errors in a block.  Returns 0 on success,
X *  1 if failed.
X */
Xint check_parity(UCHAR *data, ULONG badmap, ULONG crcmap)
X{
X  int i, j, col, crcerrs, r, tries, nblks;
X  struct inv_mat inv;
X  UCHAR i1, i2, ss[3], es[3], eblk[3];
X
X  nblks = sect_count(badmap);
X  crcerrs = 0;
X  for (i = 0; crcerrs < 3 && i < nblks; i++)
X	if (crcmap & (1 << i)) eblk[crcerrs++] = i;
X
X  for (i = 1, j = crcerrs; j < 3 && i < nblks; i++)
X	if ((crcmap & (1 << i)) == 0) eblk[j++] = i;
X
X  if (!calculate_inverse (eblk, &inv)) return(1);
X
X  eblk[0] *= QCV_BLKSIZE; eblk[1] *= QCV_BLKSIZE; eblk[2] *= QCV_BLKSIZE;
X  r = 0;
X  for (col = 0; col < QCV_BLKSIZE; col++) {
X	compute_syndromes (data, nblks, col, ss);
X
X	if (!ss[0] && !ss[1] && !ss[2]) continue;
X	if (crcerrs) {
X		determine3 (&inv, es, ss);
X		for (j = 0; j < crcerrs; j++)
X			data[eblk[j] + col] ^= es[j];
X		compute_syndromes (data, nblks, col, ss);
X		if (!ss[0] && !ss[1] && !ss[2]) {
X			r = 1;
X			continue;
X		}
X	}
X	determine3 (&inv, es, ss);
X	i1 = alpha_log[divide(ss[2], ss[1])];
X	i2 = alpha_log[divide(ss[1], ss[0])];
X	if (i1 != i2 || ((QCV_BLKSIZE * i1) + col) > QCV_SEGSIZE)
X		r = 1;
X	else
X		data[QCV_BLKSIZE * i1 + col] ^= ss[1];
X  }
X
X  return(r);
X}
END-of-qtar/ftecc.c
echo x - qtar/func.c
sed 's/^X//' >qtar/func.c << 'END-of-qtar/func.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  func.c - QIC tape archiver misc support functions
X *  07/22/93 v0.1
X *  Initial revision.
X */
X#include <stdio.h>
X#include <errno.h>
X#include <time.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X
X
X/* Check status of tape drive */
Xint check_stat(int fd, int wr)
X{
X  int r, s;
X
X  r = ioctl(fd, QIOSTATUS, &s);
X  if (r < 0) {
X	fprintf(stderr, "Couldn't get drive status\n");
X	return(1);
X  }
X
X  if ((s & QS_FMTOK) == 0) {
X	fprintf(stderr, "Tape is not formatted\n");
X	return(2);
X  }
X
X  if (wr && (s & QS_RDONLY) != 0) {
X	fprintf(stderr, "Tape is write protected\n");
X	return(3);
X  }
X
X  return(0);
X}
X
X/* Return number of sectors available in a segment. */
Xint sect_count(ULONG badmap)
X{
X  int i, amt;
X
X  for (amt = QCV_BLKSEG, i = 0; i < QCV_BLKSEG; i++)
X	if (badmap & (1 << i)) amt--;
X  return(amt);
X}
X
X/* Return number of bytes available in a segment. */
Xint sect_bytes(ULONG badmap)
X{
X  int i, amt;
X
X  for (amt = QCV_SEGSIZE, i = 0; i < QCV_BLKSEG; i++)
X	if (badmap & (1 << i)) amt -= QCV_BLKSIZE;
X  return(amt);
X}
X
X/* Return tm struct from QIC date format. */
Xstruct tm *qtime(UCHAR *qt)
X{
X  ULONG *vp = (ULONG *)qt;
X  static struct tm t;
X  ULONG v;
X
X  v = *vp;
X  t.tm_year = ((v >> 24) & 0xff)+47; v &= 0xffffff;
X  t.tm_sec = v % 60; v /= 60;
X  t.tm_min = v % 60; v /= 60;
X  t.tm_hour = v % 24; v /= 24;
X  t.tm_mday = (v % 31)+1; v /= 31;
X  t.tm_mon = v % 12; v /= 12;
X  t.tm_wday = 0;
X  t.tm_yday = 0;
X  t.tm_isdst = 0;
X  t.tm_gmtoff = 0;
X  t.tm_zone = NULL;
X  return(&t);
X}
X
END-of-qtar/func.c
echo x - qtar/header.c
sed 's/^X//' >qtar/header.c << 'END-of-qtar/header.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  header.c - QIC tape archiver header processing
X *  07/22/93 v0.1
X *  Initial revision.
X */
X#include <stdio.h>
X#include <errno.h>
X#include <time.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X
Xint gothdr = 0;					/* TRUE if valid header found */
Xint shdr = -1;					/* Header Segment */
Xint sdhdr = -1;					/* Duplicate header Segment */
Xint sdcms = -1;					/* Compression map Segment */
Xint svtbl = -1;					/* Volume table Segment */
X
XUCHAR buff[QCV_SEGSIZE];			/* Scratch buffer */
X
Xstatic UCHAR hbuff[QCV_SEGSIZE];		/* Header buffer */
Xstatic UCHAR vbuff[QCV_SEGSIZE];		/* VTBL buffer (packed) */
Xstatic UCHAR cbuff[QCV_SEGSIZE];		/* Compression map (packed) */
XQIC_Header *hptr = (QIC_Header *)hbuff;		/* Pointer to header */
XQIC_VTbl *vptr = (QIC_VTbl *)vbuff;		/* Pointer to vtbl */
XQIC_DCMap *cptr = (QIC_DCMap *)cbuff;		/* Pointer to dcms */
Xint vbytes = 0;					/* VTBL size in bytes */
Xint cbytes = 0;					/* Compression map size */
XQIC_Geom geo;					/* Tape geometry */
X
X
X/* Read header from tape */
Xget_header(int fd)
X{
X  int r, sn, bytes;
X  QIC_Segment s;
X
X  if (ioctl(fd, QIOGEOM, &geo) < 0) {
X	fprintf(stderr, "Couldn't determine tape geometry\n");
X	return(1);
X  }
X
X  /* First get the header and duplicate */
X  for (sn = 0; sn < 16; sn++) {
X	s.sg_trk = 0;
X	s.sg_seg = sn;
X	s.sg_badmap = 0;
X	s.sg_data = buff;
X	ioctl(fd, QIOREAD, &s);
X	r = check_parity(s.sg_data, 0, s.sg_crcmap);
X
X	if (s.sg_data[0] == 0x55 && s.sg_data[1] == 0xaa &&
X	    s.sg_data[2] == 0x55 && s.sg_data[3] == 0xaa) {
X		if (shdr >= 0) {
X			sdhdr = sn;
X			if (!r && !gothdr) {
X				fprintf(stderr, "Using secondary header\n");
X				bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
X				gothdr = 1;
X			}
X			break;
X		}
X		shdr = sn;
X		if (!r) {
X			bcopy(s.sg_data, hbuff, QCV_SEGSIZE);
X			gothdr = 1;
X		} else {
X			fprintf(stderr, "Too many errors in primary header\n");
X		}
X	}
X  }
X
X  if (!gothdr) {
X	fprintf(stderr, "Couldn't read header segment\n");
X	ioctl(fd, QIOREWIND);
X	return(1);
X  }
X
X  for (sn = sdhdr+1; sn < 32; sn++) {
X	s.sg_trk = 0;
X	s.sg_seg = sn;
X	s.sg_badmap = BADMAP(0,sn);
X	s.sg_data = buff;
X	ioctl(fd, QIOREAD, &s);
X	r = check_parity(s.sg_data, s.sg_badmap, s.sg_crcmap);
X	bytes = sect_bytes(s.sg_badmap);
X
X	if (s.sg_data[0] == 'D' && s.sg_data[1] == 'C' &&
X	    s.sg_data[2] == 'M' && s.sg_data[3] == 'S') {
X		if (r) fprintf(stderr, "Compression map failed CRC check\n");
X		sdcms = sn;
X		cbytes = bytes;
X		bcopy(s.sg_data, cbuff, cbytes);
X		break;
X	}
X	if (svtbl >= 0) break;	/* if no compression map, stop */
X
X	if (s.sg_data[0] == 'V' && s.sg_data[1] == 'T' &&
X	    s.sg_data[2] == 'B' && s.sg_data[3] == 'L') {
X		if (r) fprintf(stderr, "Volume table failed CRC check\n");
X		svtbl = sn;
X		vbytes = bytes;
X		bcopy(s.sg_data, vbuff, vbytes);
X		continue;
X	}
X  }
X  return(0);
X}
END-of-qtar/header.c
echo x - qtar/qtar.c
sed 's/^X//' >qtar/qtar.c << 'END-of-qtar/qtar.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  qtar.c - QIC tape archiver
X *  07/22/93 v0.1
X *  Initial groundwork for a QIC format tape archiver.
X */
X#include <stdio.h>
X#include <errno.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X
Xint action = ACT_NONE;		/* action to perform */
Xint isverbose = 0;		/* list verbosely */
Xchar *ftdev = "/dev/ft0a";	/* device to use */
Xint ismultivol = 0;		/* multi-volume archive */
Xint ispreserve = 0;		/* preserve permissions */
Xint isabsolute = 0;		/* absolute filenames */
Xint islowering = 0;		/* convert to lowercase filenames */
Xint isstdout = 0;		/* extract to stdout */
Xchar *label = NULL;		/* volume label to use (default = first) */
Xchar **filelist;
Xint nfiles = 0;
X
X/* Display usage */
Xusage()
X{
X  printf("usage: qtar [-clrtx][LMOPpv] [-f dev] [-V vol] [file ...]\n");
X  printf("One of the following actions:\n");
X  printf("    -c         create new archive\n");
X  printf("    -l         list tape volumes only\n");
X  printf("    -r         append files to archive\n");
X  printf("    -t         list files in archive\n");
X  printf("    -x         extract files from archive\n");
X  printf("Options:\n");
X  printf("    -L         lowercase filenames\n");
X  printf("    -M         multi-volume archive\n");
X  printf("    -O         extract to stdout\n");
X  printf("    -P         don't remove leading '/'\n");
X  printf("    -p         preserve permissions on extract\n");
X  printf("    -v         verbose listing of files\n");
X  printf("    -f device  use device instead of %s\n", ftdev);
X  printf("    -V volume  specify volume name\n");
X  exit(0);
X}
X
X
X/* Main entry point */
Xmain(int argc, char *argv[])
X{
X  int i, j;
X  char *cp;
X
X  if (argc < 2) usage();
X  for (i = 1; i < argc; i++) {
X	cp = argv[i];
X	if (i > 1 && *cp != '-') break;
X	if (*cp == '-') cp++;
X
X	while (*cp != '\0') {
X		switch(*cp) {
X		    case 'c':
X			if (action != ACT_NONE) usage();
X			action = ACT_CREATE;
X			break;
X		    case 'l':
X			if (action != ACT_NONE) usage();
X			action = ACT_LISTVOL;
X			break;
X		    case 'r':
X			if (action != ACT_NONE) usage();
X			action = ACT_APPEND;
X			break;
X		    case 't':
X			if (action != ACT_NONE) usage();
X			action = ACT_LIST;
X			break;
X		    case 'x':
X			if (action != ACT_NONE) usage();
X			action = ACT_EXTRACT;
X			break;
X		    case 'L':
X			islowering++;
X			break;
X		    case 'M':
X			ismultivol++;
X			break;
X		    case 'O':
X			isstdout++;
X			break;
X		    case 'P':
X			isabsolute++;
X			break;
X		    case 'p':
X			ispreserve++;
X			break;
X		    case 'v':
X			isverbose++;
X			break;
X		    case 'f':
X			if (i == (argc - 1) || argv[i+1][0] == '-') usage();
X			cp[1] = '\0';
X			ftdev = argv[++i];
X			break;
X		    case 'V':
X			if (i == (argc - 1) || argv[i+1][0] == '-') usage();
X			cp[1] = '\0';
X			label = argv[++i];
X			break;
X		    default:
X			usage();
X			/* NOTREACHED */
X		}
X		cp++;
X	}
X  }
X  if (action == ACT_NONE) usage();
X  nfiles = argc - i;
X  filelist = &argv[i];
X  if ((action == ACT_APPEND || action == ACT_CREATE) && !nfiles) usage();
X  if (action == ACT_LISTVOL && nfiles) usage();
X
X  doaction();
X
X  exit(0);
X}
END-of-qtar/qtar.c
echo x - qtar/qtar.h
sed 's/^X//' >qtar/qtar.h << 'END-of-qtar/qtar.h'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  qtar.h - QIC tape archiver header file
X *  07/22/93 v0.1
X *  Initial revision.
X */
X
X/* Action requested */
Xenum {
X	ACT_NONE,		/* No action */
X	ACT_LIST,		/* List files in volume */
X	ACT_LISTVOL,		/* List tape volumes */
X	ACT_EXTRACT,		/* Extract from volume */
X	ACT_APPEND,		/* Append new volume */
X	ACT_CREATE		/* Create new volume, erasing old */
X};
X
X/* Fileset structure */
Xtypedef struct fsnode {		/* file set tree */
X	char *name;		/* file/directory name */
X	UCHAR fixed[10];	/* fixed info */
X	UCHAR sys[32];		/* system specific info */
X	int hsize;		/* Total header size */
X	int dsize;		/* Total data size */
X	int nxosi;		/* number of applicable xosi records */
X	UCHAR **xosi;		/* the actual XOSI records */
X	struct fsnode *next;	/* next directory entry */
X	struct fsnode *prev;	/* previous directory entry */
X	struct fsnode *parent;	/* pointer to parent directory entry */
X	struct fsnode *dirh;	/* head of sub-directory */
X	struct fsnode *dirt;	/* tail of sub-directory */
X} FSNode;
X
Xextern int action;		/* action to perform */
Xextern int isverbose;		/* list verbosely */
Xextern char *ftdev;		/* device to use */
Xextern int ismultivol;		/* multi-volume archive */
Xextern int ispreserve;		/* preserve permissions */
Xextern int isabsolute;		/* absolute filenames */
Xextern int islowering;		/* convert to lowercase filenames */
Xextern int isstdout;		/* extract to stdout */
Xextern char *label;		/* volume label to use */
Xextern char **filelist;		/* list of files to process */
Xextern int nfiles;		/* number of files to process */
Xextern int tfd;			/* file descriptor for tape */
Xextern QIC_VTbl *vptr;		/* VTBL pointer */
Xextern QIC_Header *hptr;	/* Header pointer */
Xextern QIC_DCMap *cptr;		/* DCMS pointer */
Xextern UCHAR buff[QCV_SEGSIZE];	/* Scratch buffer */
Xextern QIC_Geom geo;		/* Current tape's geometry */
Xextern int vf_strat;		/* Compression strategy */
X
Xextern int vbytes;		/* VTBL size in bytes */
Xextern int cbytes;		/* DCMS size in bytes */
Xextern int gothdr;		/* TRUE if header found */
Xextern int shdr;		/* Header segment number */
Xextern int sdhdr;		/* Duplicate header segment number */
Xextern int svtbl;		/* Volume table segment number */
Xextern int sdcms;		/* Compresion map segment number */
X
X/* header.c */
Xextern int get_header();
X
X/* func.c */
Xextern int check_stat();
Xextern struct tm *qtime();
Xextern int sect_count();
Xextern int sect_bytes();
X
X/* ftecc.c */
Xextern int set_parity();
Xextern int check_parity();
X
X/* volume.c */
Xextern int vol_init();
Xextern int vol_list();
Xextern QIC_VTbl *vol_select();
X
X/* fileset.c */
Xextern int fs_init();
Xextern int fs_retrieve();
Xextern int fs_gather();
Xextern int fs_traverse();
Xextern int fs_getc();
X
X/* Lookup the badmap for a given track and segment. */
X#define BADMAP(t,s)	hptr->qh_badmap[(t)*geo.g_segtrk+(s)]
X
X/* Retrieve values from a character array. */
X#define UL_VAL(s,p)	(*((ULONG *)&(s)[p]))
X#define US_VAL(s,p)	(*((USHORT *)&(s)[p]))
END-of-qtar/qtar.h
echo x - qtar/volume.c
sed 's/^X//' >qtar/volume.c << 'END-of-qtar/volume.c'
X/*
X *  Copyright (c) 1993 Steve Gerakines
X *
X *  This is freely redistributable software.  You may do anything you
X *  wish with it, so long as the above notice stays intact.
X *
X *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
X *  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
X *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
X *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X *  POSSIBILITY OF SUCH DAMAGE.
X *
X *  volume.c - QIC tape archiver volume support functions
X *  08/14/93 v0.1
X *  Initial revision.
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <errno.h>
X#include <time.h>
X#include <sys/ftape.h>
X#include "qtar.h"
X#include <ctype.h>
X
X/* Volume flags */
Xint vf_compress;		/* TRUE if compression used */
Xint vf_cptype;			/* Compression method */
Xint vf_segspan;			/* TRUE if segment spanning used */
Xint vf_strat;			/* Volume I/O strategy */
X
X/* Retrieve the name of a volume, use default if none given. */
Xchar *vol_name(QIC_VTbl *v)
X{
X  static char str[45];
X
X  /* Grab this volume's name; use default name if none */
X  strncpy(str, v->vt_vname, 44);
X  str[44] = '\0';
X  if (str[0] == '\0' || str[0] == ' ')
X	sprintf(str, "Volume_%d\n", (int)(v - vptr) / sizeof(QIC_VTbl));
X  return(str);
X}
X
X
X/* List volumes in vtbl */
Xvol_list()
X{
X  char str[80];
X  int i;
X  int maxent = vbytes / sizeof(QIC_VTbl);
X  struct tm *t;
X  QIC_VTbl *v;
X
X  if (isverbose) {
X	printf(
X"Volume                                       Seq OS   Saved    Label\n");
X	printf(
X"============================================ === ==== ======== ================\n");
X  }
X
X  for (i = 0; i < maxent; i++) {
X	v = &vptr[i];
X	if (v->vt_sig[0] != 'V' || v->vt_sig[1] != 'T' ||
X	    v->vt_sig[2] != 'B' || v->vt_sig[3] != 'L') break;
X	if (!isverbose) {
X		printf("%s\n", vol_name(v));
X		continue;
X	}
X	printf("%-44.44s ", vol_name(v));
X	printf("%3d ", v->vt_multi);
X	switch (v->vt_ostype) {
X	    case 1:
X		printf("DOS  ");
X		break;
X	    case 2:
X		printf("Unix ");
X	        break;
X	    case 4:
X		printf("OS/2 ");
X		break;
X	    case 8:
X		printf("Mac  ");
X		break;
X	    case 16:
X		printf("NetW ");
X		break;
X	    case 32:
X		printf("LanM ");
X		break;
X	    default:
X		printf("Unk  ");
X		break;
X	}
X
X	t = qtime(v->vt_savdate);
X	printf("%02d/%02d/%02d ", t->tm_mon+1, t->tm_mday, t->tm_year % 100);
X
X	strncpy(str, v->vt_label, 16);
X	str[16] = '\0';
X	printf("%s\n",str);
X  }
X  if (!i && isverbose) printf("(no volumes)\n");
X}
X
X
X/* Locate the requested volume label by name. */
XQIC_VTbl *vol_select(char *which)
X{
X  QIC_VTbl *v;
X  int maxent = vbytes / sizeof(QIC_VTbl);
X  char str[45];
X  int i;
X
X  /* If no volume segment found, return error. */
X  if (svtbl < 0) return(NULL);
X  
X  for (i = 0; i < maxent; i++) {
X	v = &vptr[i];
X	if (v->vt_sig[0] != 'V' || v->vt_sig[1] != 'T' ||
X	    v->vt_sig[2] != 'B' || v->vt_sig[3] != 'L') break;
X	if (which == NULL) return(v);
X	if (!strncmp(vol_name(v), which, 44)) return(v);
X  }
X  return(NULL);
X}
X
X
X/* Initialize for I/O on a given volume. */
Xvol_init(QIC_VTbl *v)
X{
X
X  /* Initialize flags for this volume */
X  vf_compress = v->vt_cprtype & 0x80;
X  vf_cptype = v->vt_cprtype & 0x3f;
X  vf_segspan = v->vt_flags & 0x10;
X  vf_strat = 0;
X  if (vf_compress) {
X	vf_strat = 1;
X	if (vf_segspan) vf_strat = 2;
X  }
X  fs_init(v);
X}
END-of-qtar/volume.c
echo c - tools
mkdir tools > /dev/null 2>&1
echo x - tools/getseg.c
sed 's/^X//' >tools/getseg.c << 'END-of-tools/getseg.c'
X#include <stdio.h>
X#include <sys/ftape.h>
X#include <errno.h>
X
XQIC_Segment s;
XUCHAR buff[QCV_SEGSIZE];
X
Xmain(int argc, char *argv[])
X{
X  int i, j, r, fd;
X  int seg, trk;
X  QIC_Geom g;
X
X  if (argc != 3) {
X	fprintf(stderr, "usage: getseg track# seg#\n");
X	exit(1);
X  }
X
X  if ((fd = open("/dev/ft0a",0)) < 0) {
X	perror("/dev/ft0a");
X	exit(1);
X  }
X  trk = atoi(argv[1]);
X  seg = atoi(argv[2]);
X
X  r = ioctl(fd, QIOGEOM, &g);
X  if (r < 0) {
X	perror("QIOGEOM");
X	exit(1);
X  }
X  fprintf(stderr, "Current tape format is: %s/%s\n", g.g_fmtdesc,g.g_lendesc);
X
X  if (trk < 0 || trk >= g.g_trktape ||
X	seg < 0 || seg >= g.g_segtrk) {
X	fprintf(stderr, "bad parameters for tape; tracks %d segments %d\n",
X		g.g_trktape, g.g_segtrk);
X	exit(1);
X  }
X
X  s.sg_trk = trk;
X  s.sg_seg = seg;
X  s.sg_data = buff;
X  fprintf(stderr, "reading %d %d ... ", trk, seg);
X  r = ioctl(fd, QIOREAD, &s);
X  if (r < 0) perror("QIOREAD");
X  fprintf(stderr, "badmap is %08lx\n", s.sg_crcmap);
X  close(fd);
X  fwrite(buff, 1, QCV_SEGSIZE, stdout);
X  exit(0);
X}
END-of-tools/getseg.c
echo x - tools/postest.c
sed 's/^X//' >tools/postest.c << 'END-of-tools/postest.c'
X#include <stdio.h>
X#include <sys/ftape.h>
X#include <errno.h>
X
XQIC_Segment s;
XUCHAR buff[QCV_SEGSIZE];
X
Xmain()
X{
X  int i, j, r, fd;
X  int seg, trk;
X
X  srand(time(NULL));
X
X  if ((fd = open("/dev/ft0a",0)) < 0) {
X	perror("/dev/ft0a");
X	exit(1);
X  }
X
X  for (i = 0; i < 20; i++) {
X	j = rand() % 10;
X	seg = rand() % 150;
X	trk = rand() % 28;
X	while (j--) {
X		s.sg_trk = trk;
X		s.sg_seg = seg;
X		s.sg_data = buff;
X		printf("==> reading %d %d\n", trk, seg);
X		r = ioctl(fd, QIOREAD, &s);
X		if (r < 0) perror("QIOREAD");
X		seg = (seg + 1) % 150;
X		if (!seg) trk++;
X	}
X  }
X
X  printf("all done.");
X  close(fd);
X  exit(0);
X}
END-of-tools/postest.c
exit