1
0
mirror of https://github.com/brouhaha/tapeutils.git synced 2026-01-11 23:53:18 +00:00
Björn Victor 84a3a78d2c
Read saveset name from the right place
Support DUMPER format 6, which has saveset name at different offset. Also check dumper tape format and reject too old or invalid format codes.
2025-01-22 17:31:32 +01:00

1042 lines
27 KiB
C

/*
* Program to read Tops-20 Dumper format tapes
*
* Jim Guyton, Rand Corporation
* Original 10/20/82
* jdg: -n added 6/11/83
* jdg: can now extract 8-bit-byte files 2/9/86
*
* Lot of mods by Jay Lepreau, Univ of Utah, 1-2/87.
* See the RCS log for details.
*
* Modified by Eric Smith <eric@brouhaha.com>, 4-DEC-2000, to use
* tapeio library, in order to use tape image files. Also changed
* to use environment variable TAPE if set, otherwise default to /dev/nst0.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define _REGEX_RE_COMP
#include <regex.h>
#include "dumper.h"
#include "tapeio.h"
#define LOGFILE "Logfile" /* logfile should be changeable */
tape_handle_t tape_handle;
char tapeblocka[TAPEBLK]; /* One logical record from tape */
FILE *fpFile; /* Output file handle on extracts */
int debug = 0;
int textflg = 0; /* Non-zero if retr binary files as text */
int binflg = 0; /* Non-zero if retr all files */
int numflg = 0; /* Non-zero if using numeric filenames */
int keepcr = 0; /* Keep CR's in CRLF pairs in text files */
int dodir = 0; /* directory listing */
int xflg = 0; /* extract */
int verbose = 0;
int genflg; /* keep generation number */
int nselect; /* number of files still to be selected by number */
int doallflag; /* act on all files cause no args given */
int label; /* there was a tape label */
int number; /* Current output file "number" */
#define TAPE "/dev/nst0" /* Default input tape */
int bytesize; /* Number of bits/byte in current file */
long numbytes; /* Number of bytes in current file */
long truncate_length = 0; /* For -b, truncate output to this. */
int pgcount; /* Number of twenex pages in file */
long pageno, tapeno, ssno, filenum;
unsigned tprot; /* Tops-20 protection */
char *timeptr;
struct utimbuf timep;
int offline, archived, invisible;
int apgcount, tp1, tp2, ss1, ss2, tf1, tf2;
char topsname[130];
char sunixname[300];
struct want {
unsigned short ssnum;
unsigned short fnum;
} want[10000]; /* limited by 20000 char arglist */
int cursswant;
char **patterns = 0; /* Filename match patterns */
int numpats = 0; /* Number of patterns */
char *expression = 0;
char *re_comp_error; /* Error message from re_comp() */
extern char *re_comp();
#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__CYGWIN__)
static regex_t re_regexp;
char *re_comp(char *s)
{
if (regcomp(&re_regexp, s, 0) == 0)
return NULL;
else
return "error";
}
static int re_exec(char *s)
{
return regexec(&re_regexp, s, 0, 0, 0);
}
#endif
/*
read20 [-f tapefile] [-t] [-c] [-T] [-n number] pattern
no tapefile ==> /dev/rmt8
-t == directory listing
-n == use numeric filenames in extracts, number is 1st name
-T == pretend 36 bit files are 7-bit ascii
-c == keep CR's in CRLF pairs.
-g == keep generation numbers
*/
void punt (int prterrno, char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf(stderr, fmt, ap);
va_end (ap);
if (prterrno) {
fprintf(stderr, ": %s\n", strerror(errno));
}
else
fprintf(stderr, "\n");
exit(1);
}
/* fold -- perform case folding
*
* Usage: p = fold (out,in,whichway);
* p = foldup (out,in);
* p = folddown (out,in);
* char *p,*in,*out;
* enum {FOLDUP, FOLDDOWN} whichway;
*
* Fold performs case-folding, moving string "in" to
* "out" and folding one case to another en route.
* Folding may be upper-to-lower case (folddown) or
* lower-to-upper case.
* Foldup folds to upper case; folddown folds to lower case.
* The same string may be specified as both "in" and "out".
* The address of "out" is returned for convenience.
*
* HISTORY
* 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University
* Rewritten for VAX; now uses enumerated type for fold(). The
* foldup() and folddown() routines are new.
*
*/
typedef enum {
FOLDUP, FOLDDOWN}
FOLDMODE;
char *fold (char *out, char *in, FOLDMODE whichway)
{
register char *i,*o;
register char lower;
char upper;
int delta;
switch (whichway)
{
case FOLDUP:
lower = 'a'; /* lower bound of range to change */
upper = 'z'; /* upper bound of range */
delta = 'A' - 'a'; /* amount of change */
break;
case FOLDDOWN:
default:
lower = 'A';
upper = 'Z';
delta = 'a' - 'A';
}
i = in;
o = out;
do {
if (*i >= lower && *i <= upper) *o++ = *i++ + delta;
else *o++ = *i++;
}
while (*i);
*o = '\0';
return (out);
}
char *foldup (char *out, char *in)
{
return (fold(out,in,FOLDUP));
}
char *folddown (char *out, char *in)
{
return (fold(out,in,FOLDDOWN));
}
int masks[32] = /* bitmasks for different length fields */
{ 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff
};
long getfield (char *block, /* Tape block record */
int wordoff, /* 36-bit word offset */
int bitoff, /* Bit offset of field (from msb) */
int bitlen) /* Bit length of field */
{
register char *p; /* Used to point into record */
register long w32; /* First 32 bits of the 36 bit word */
int w4; /* Last 4 bits of the 36 bit word */
long w = 0; /* the word to return */
/* First, the "illegal" kludge */
if (bitoff == 0 && bitlen == 36) {
bitoff = 4;
bitlen = 32;
}
if (bitlen > 32)
punt(0, "Can't get that large a field = %d!", bitlen);
/* A PDP-10 (or 20) 36-bit word is laid out with the first 32 bits
as the first 4 bytes and the last 4 bits are the low order 4 bits
of the 5th byte. The high 4 bits of that byte should be zero */
p = block + (5*wordoff); /* Get ptr to word of interest */
w32 = *p++ & 0377; /* First byte */
w32 = (w32 << 8) | (*p++ & 0377); /* 2nd */
w32 = (w32 << 8) | (*p++ & 0377); /* 3rd */
w32 = (w32 << 8) | (*p++ & 0377); /* 4th */
w4 = *p; /* 5th */
if (w4 > 017)
punt(0, "Not a PDP-10 tape! w4 = octal %o", w4);
/* Get the field right justified in the word "w".
There are three cases that I have to handle:
[1] field is contained in w32
[2] field crosses w32 and w4
[3] field is contained in w4
*/
if (bitoff+bitlen <= 32) /* [1] field is contained in w32 */
{
w = w32 >> (32 - (bitoff+bitlen));
}
else if (bitoff <= 32) /* [2] field crosses boundary */
{
w = (w32 << (bitoff+bitlen-32))
| (w4 >> (36 - (bitoff+bitlen)));
}
else /* [3] field is contained in w4 */
{
w = w4 >> (36 - (bitoff+bitlen));
}
w = w & masks[bitlen-1]; /* Trim to proper size */
return(w);
}
int lastc = 0;
/*
* Unpack into buffer 's' the 7 bit string stored in 'block' and
* append a null char. Optionally strip CR's from CRLF pairs. 'max'
* is the max number of 7-bit chars to unpack from 'block', not the
* max to put into 's' (that's important!). This only works if
* getstring() is always called with 'max' mod 5 == 0, except for the
* last call on "contiguous" blocks.
* Returns number of chars stored in output buffer.
*/
int getstring (char *block, /* Tape block */
char *s /* Destination string buffer */,
int wordoff, /* 36-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of characters loaded so far */
char *orig = s; /* Save for debugging */
int c;
while (ct < max)
{
for (i = 0; i < 5; i++)
{
c = getfield(block, wordoff, i*7, 7);
if (lastc == '\r' && c != '\n')
*s++ = '\r';
if (c != '\r' || keepcr)
*s++ = c;
if (!keepcr)
lastc = c;
if ((ct + i + 1) == max)
return (s - orig);
}
wordoff++;
ct += 5;
}
printf("Fall thru in getfield\n");
fflush(stdout);
*s = '\0';
return (s - orig);
}
/*
* pendstring - return any character pending output after
* last call to getstring(). Also zeros `lastc'.
* Can only return '\r' or 0 for none.
*/
int pendstring (void)
{
int olastc = lastc;
lastc = 0;
return (olastc == '\r') ? '\r' : 0;
}
/* getbytes: like getstring, but ...
1) uses 8 bit bytes
2) doesn't stop on a zero
*/
void getbytes (char *block, /* Tape block */
char *s, /* Destination string buffer */
int wordoff, /* 36-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of characters loaded so far */
/* char *orig = s; */ /* Save for debugging */
while (ct < max)
{
for (i = 0; i < 4; i++)
{
*s = getfield(block, wordoff, i*8, 8);
/* if (*s == 0) return; */
s++;
}
wordoff++;
ct += 4;
}
/** punt(0, "String greater than %d characters.", max); **/
}
/* getwords: like getstring, but ...
extracts 5 7-bit characters from a 36-bit word, and swizzles the
remaining 1 bit into the last output character.
*/
void getwords (char *block, /* Tape block */
char *s, /* Destination string buffer */
int wordoff, /* 36-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of words loaded so far */
while (ct < max)
{
for (i = 0; i < 5; i++)
*s++ = getfield(block, wordoff, i*7, 7);
s[-1] |= getfield(block, wordoff, 35, 1) << 7;
wordoff++;
ct++;
}
}
/*
* Unpack into buffer 's' the 8 bit string stored in 'block' and
* append a null char.
* Returns number of chars stored in output buffer.
*/
int getascii (char *block, /* Tape block */
char *s /* Destination string buffer */,
int off, /* 8-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
memcpy (s, block + off, max);
s[max] = 0;
return max;
}
#define SecPerTick (24.*60.*60.)/0777777
#define DayBaseDelta 0117213 /* Unix day 0 in Tenex format */
/*
* This screws up on some of the atime's we see, including, yielding, e.g.
* Fri Dec 23 23:28:16 1994
* Fri Dec 23 23:28:16 1994
* Tue Jan 13 07:57:03 1987
*/
long unixtime(char *block, int wordoff)
{
long int t, s;
t = getfield(block, wordoff, 0, 18); /* First half is day */
t -= DayBaseDelta; /* Switch to unix base */
/* Now has # days since */
/* Jan 1, 1970 */
s = getfield(block, wordoff, 18, 18); /* 2nd half is fraction day */
s = s * SecPerTick; /* Turn into seconds */
s += t*24*60*60; /* Add day base */
return(s);
}
char *unixname (char *name)
{
static FILE *log = NULL;
register char *t, *p;
static char lastdir[64];
struct stat stb;
int mask;
register int newdir = 0;
if (numflg) { /* If numeric filenames */
if (log == NULL) log = fopen(LOGFILE, "a");
fprintf(log, "%d is %s\n", number, name);
sprintf(sunixname, "%d", number++);
return(sunixname);
}
strcpy(sunixname, index(name, '<') + 1); /* trim off device */
t = rindex(sunixname, '>'); /* find end of directory */
*t = '.';
if (strncmp(lastdir, sunixname, t - sunixname)) {/* maybe new dir */
strncpy(lastdir, sunixname, t - sunixname); /* remember it */
newdir = 1;
}
for (p = sunixname; p <= t; p++)
if (*p == '.') {
if (newdir) {
*p = '\0'; /* temporarily null it off */
if (stat(sunixname, &stb) < 0) {
mask = umask(2);
if (mkdir(sunixname, 0777) < 0)
punt(1, "mkdir %s failed", sunixname);
umask(mask);
}
}
*p = '/';
}
if (!genflg) {
t = rindex(sunixname, '.'); /* find last . */
*t = 0; /* zap it out */
}
return(sunixname);
}
void doDatablock (char *block)
{
/* max is 5 bytes per word */
static char buf[(512*5)+1]; /* A page of characters */
int ct;
int maxperblock;
int nout;
if (debug > 10)
printf("*");
if (fpFile == NULL)
return;
switch (bytesize) { /* only handle 7 and 8 bit bytes */
case 7: maxperblock = 512*5; break;
case 8: maxperblock = 512*4; break;
case 36: maxperblock = 512; break;
default: return;
}
if (numbytes > maxperblock)
ct = maxperblock;
else
ct = numbytes;
if (binflg) {
getwords(block, buf, 6, ct);
fwrite(buf, 5, ct, fpFile);
}
else if (bytesize == 7) {
nout = getstring(block, buf, 6, ct);
fwrite(buf, 1, nout, fpFile);
}
else { /* if not 7, then 8bit */
getbytes(block, buf, 6, ct);
fwrite(buf, 1, ct, fpFile);
}
if (ferror(fpFile))
punt(1, "Error writing %s", sunixname);
numbytes -= ct;
}
void doSaveset (char *block, int contflag)
{
static char name[102];
static char ss[2];
long ssfmt, ssptr;
long t;
if (debug > 10) printf("\nSaveset header:");
tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum);
ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum,
BtlenSaveSetNum);
ssfmt = getfield(block, WdoffSSFmt, BtoffWord, BtlenWord); /* Get format */
ssptr = getfield(block, WdoffSSPtr, BtoffWord, BtlenWord); /* Get pointer */
// Check tape format! Otherwise breaks e.g. on Install tapes (which aren't in dumper format).
if ((ssfmt < 4) || (ssfmt > 6)) {
// Formats older than 4 not supported, and 6 was the highest (TOPS-20 v6-7).
// If you want to support older fmts, write the code. :-)
fprintf (stderr, "Bad dumper tape format %012lo\n", ssfmt);
exit(1);
}
if (verbose) {
printf("Saveset format %ld, name pointer %ld; tape %ld, saveset %ld\n", ssfmt, ssptr, tapeno, ssno);
}
if (ssptr == 0) {
/* If there is no pointer, use default offset: for format 5-6 (T20 v6-7), SS.MSG, otherwise (T20 v4-5) BFMSG */
getstring(block, name, ssfmt > 4 ? WdoffSSMsg : WdoffSSName, sizeof(name));
} else {
getstring(block, name, ssptr+WdoffSSstart, sizeof(name));
}
ss[0] = pendstring(); /* superfluous */
(void) strcat(name, ss);
t = unixtime(block, WdoffSSDate);
if (dodir || verbose)
printf("%sSaveset '%s' %s\n", contflag ? "Continued " : "",
name, ctime(&t));
}
/* Return 1 if topsname matches any of the "extraction" strings. */
int patternmatch (void)
{
register int i;
for (i = 0; i < numpats; i++)
if (strstr(topsname, patterns[i]))
return (1);
return (0);
}
/* Return 1 if topsname matches the regular expression. */
int expmatch (void)
{
register int match;
if (expression) {
if ((match = re_exec(topsname)) == -1)
punt(0, "re_exec: internal error on %s", topsname);
else
return (match);
}
return (0);
}
/* Return 1 if current file number matches one selected by arg line. */
int fmatch (void)
{
static int widx;
while (want[widx].ssnum < ssno)
widx++;
if (want[widx].ssnum > ssno)
return 0;
while (want[widx].fnum < filenum)
widx++;
if (want[widx].fnum > filenum)
return 0;
return 1;
}
/*
* Sets a bunch of global variables to info from the fdb.
* For some reason the archive tape info is garbage.
*/
void getfdbinfo (char *block)
{
timep.modtime = unixtime(block, WdoffFDB_Wrt);
timep.actime = unixtime(block, WdoffFDB_Ref);
timeptr = ctime(& timep.modtime) + 4; /* Skip over day-name field */
timeptr[20] = '\0'; /* Chop off \n at end */
bytesize = getfield(block, WdoffFDB_BSZ, BtoffFDB_BSZ, BtlenFDB_BSZ);
numbytes = getfield(block, WdoffFDB_Size, BtoffFDB_Size,BtlenFDB_Size);
pgcount = getfield(block, WdoffFDB_PGC, BtoffFDB_PGC, BtlenFDB_PGC);
tprot = getfield(block, WdoffFDB_PRT, BtoffFDB_PRT, BtlenFDB_PRT);
archived = getfield(block, WdoffFDB_CTL, BtoffFDB_Arc, BtlenFDB_Arc);
invisible = getfield(block, WdoffFDB_CTL, BtoffFDB_Inv, BtlenFDB_Inv);
offline = getfield(block, WdoffFDB_CTL, BtoffFDB_Off, BtlenFDB_Off);
apgcount = getfield(block, WdoffFDB_PGC_A, BtoffFDB_PGC, BtlenFDB_PGC);
/* The rest is bogus. */
tp1 = getfield(block, WdoffFDB_TP1, 0, 36);
tp2 = getfield(block, WdoffFDB_TP2, 0, 36);
ss1 = getfield(block, WdoffFDB_SS1, BtoffFDB_SS, BtlenFDB_SS);
ss2 = getfield(block, WdoffFDB_SS2, BtoffFDB_SS, BtlenFDB_SS);
tf1 = getfield(block, WdoffFDB_TF1, BtoffFDB_TF, BtlenFDB_TF);
tf2 = getfield(block, WdoffFDB_TF2, BtoffFDB_TF, BtlenFDB_TF);
}
int t2uprot (unsigned int prot)
{
register unsigned tprot, uprot;
register int tshift;
#ifdef notdef
if (f->FB_dir) { /* THIS WON'T WORK! */
punt(0, "Can't handle directory %s", topsname);
prot = gtdirprot(_dirnm(jfn)); /* returns 20 fmt protection */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift; /* pick up next field */
uprot <<= 3;
if (tprot & DP_rd)
uprot |= WREAD|WEXEC; /* world read, world execute */
if (tprot & (DP_cn|DP_cf)) /* allow write for either conn. */
uprot |= WWRITE; /* access or add files access */
}
}
else
#endif
{ /* do it this way so easily modified-- i know it could be faster */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift;
uprot <<= 3;
uprot |= (tprot >> 3) & 07; /* just r,w,x */
}
}
return uprot;
}
/* Compute the number of 8-bit host bytes needed to store a PDP-10
file, using the swizzled format with five octets per 36 bits. */
static long host_octets (long file_bytes, int byte_size)
{
int bytes_per_word = 36 / byte_size;
long words = file_bytes / bytes_per_word;
int remaining_bytes = file_bytes % bytes_per_word;
return 5 * words + (remaining_bytes * byte_size + 6) / 7;
}
void doFileHeader (char *block)
{
char *ts;
static char prt_ar[2] = {'-', 'A'};
static char prt_inv[2] = {'-', 'I'};
static char prt_off[2] = {'-', 'O'};
if (debug > 5)
printf("File Header block:\n");
filenum = getfield(block, WdoffFileNum, BtoffFileNum, BtlenFileNum);
getstring(block, topsname, WdoffFLName, sizeof(topsname));
ts = index(topsname, ';'); /* Chop off ;Pprotection;Aacct */
*ts++ = pendstring(); /* superfluous */
*ts = 0;
folddown(topsname, topsname);
fpFile = NULL;
if ( doallflag ||
(patterns && patternmatch()) ||
(expression && expmatch()) ||
(nselect && fmatch()) ) {
getfdbinfo(block);
pageno = getfield(block, WdoffPageNum, BtoffPageNum, BtlenPageNum);
if (dodir || verbose) {
if (verbose)
printf("%3ld%6ld ", ssno, filenum);
printf("%c%c%c", prt_ar[archived], prt_off[offline],
prt_inv[invisible]);
printf("%5d%9ld %2d %o %s %s", offline ? apgcount : pgcount,
numbytes, bytesize, tprot, timeptr, topsname);
if (archived && verbose >= 2)
printf(" %x%4d%5d %x%4d%5d", tp1, ss1, tf1, tp2, ss2, tf2);
if (pageno != 0)
printf(" Split file, part 2");
}
if (xflg) {
if (binflg) {
if (bytesize == 0)
bytesize = 36;
truncate_length = host_octets (numbytes, bytesize);
numbytes = (numbytes + (36/bytesize) - 1) / (36 / bytesize);
bytesize = 36;
}
/* Special hack for bad files */
else if (textflg && bytesize != 7) {
if (bytesize == 0 || bytesize == 36) {
bytesize = 7;
numbytes *= 5;
}
}
if ((bytesize == 7 || bytesize == 8 || binflg) && !offline) {
if (pageno != 0) { /* continued file */
int missing = pageno * 512 * (bytesize == 7 ? 5 : 4);
numbytes -= missing;
if (!(dodir || verbose))
printf("%s: Split file, part 2", topsname);
printf(": %d raw bytes missing.", missing);
if (!(dodir || verbose))
putchar('\n');
}
fpFile = fopen(unixname(topsname), "w");
if (fpFile == NULL)
punt(1, "Can't open %s for write", sunixname);
else if (verbose)
printf(" Extracted.");
if (fchmod(fileno(fpFile), t2uprot(tprot) & ~0111) < 0)
punt(1, "fchmod on %s", sunixname);
} else if (verbose)
printf(" Skipping -- %s file.",
offline ? "offline" : "binary");
}
if (dodir || verbose)
putchar('\n');
}
}
/*ARGSUSED*/
void doFileTrailer (char *block)
{
if (debug > 10) printf(" File trailer\n");
if (fpFile != NULL) {
if (pendstring() == '\r')
putc('\r', fpFile);
if (truncate_length) {
fflush (fpFile);
ftruncate (fileno (fpFile), truncate_length);
}
if (fclose(fpFile) == EOF)
punt(1, "fclose: write error on %s", sunixname);
fpFile = NULL;
utime(sunixname, & timep);
if (numbytes != 0)
printf("%s: Split file, part 1: %ld raw bytes left\n",
topsname, numbytes);
}
}
/*ARGSUSED*/
void doTapeTrailer (char *block)
{
if (debug > 10) printf("Tape Trailer");
}
void doAnsiLabel (char *block)
{
char string[80];
getascii (block, string, 0, 4);
if (debug > 10) printf("ANSI Label %s\n", string);
if (strncmp (block, "VOL1", 4) == 0) {
getascii (block, string, 4, 6);
printf ("ANSI Volume Identifier: %s\n", string);
} else if (strncmp (block, "HDR1", 4) == 0) {
if (verbose) {
getascii (block, string, 4, 17);
printf ("ANSI File Indentifier: %s\n", string);
getascii (block, string, 21, 6);
printf ("File Set: %s\n", string);
getascii (block, string, 27, 4);
printf ("File Section: %s\n", string);
}
} else if (strncmp (block, "HDR2", 4) == 0) {
if (verbose) {
getascii (block, string, 4, 1);
printf ("Record Format: %s\n", string);
getascii (block, string, 5, 5);
printf ("Block Length: %s\n", string);
getascii (block, string, 10, 5);
printf ("Record Length: %s\n", string);
}
}
}
int compwant(const void *wa1, const void *wa2)
{
const struct want *w1 = wa1;
const struct want *w2 = wa2;
int sdif;
if ((sdif = w1->ssnum - w2->ssnum))
return sdif;
return (w1->fnum - w2->fnum);
}
int main (int argc, char *argv[])
{
char *tape; /* Pathname for tape device/file */
char *tapeblock;
int rc;
int rtype;
if (! (tape = getenv ("TAPE")))
tape = TAPE;
/* Do switch parsing */
while(argc>1 && argv[1][0] == '-'){
switch(argv[1][1]){
case 'f':
if (argc <= 2)
punt(0, "Need filename after -f");
tape = argv[2];
argc--; argv++;
break;
case 'T': /* Force text mode on "binary" files */
textflg = 1;
break;
case 'b': /* Force extracting all files */
binflg = 1;
break;
case 't': /* directory listing */
dodir = 1;
break;
case 'x': /* extract */
xflg = 1;
break;
case 'v': /* verbosity */
verbose++;
break;
case 'g': /* keep gen number */
genflg++;
break;
case 'd':
debug = atoi(&argv[1][2]);
fprintf(stderr, "Debug value set to %d\n", debug);
break;
case 'n': /* numeric output filenames */
if (argc <= 2)
punt(0, "Need number after -n");
number = atoi(argv[2]); /* First file name */
numflg = 1;
argc--; argv++;
break;
case 'c': /* keep CR`s in CR/LF pairs */
keepcr++;
break;
case 'e': /* regular expression */
if (argc <= 2)
punt(0, "Need expression after -e");
if (expression)
punt(0, "Only one regexp allowed");
expression = argv[2];
if ((re_comp_error = re_comp(expression)) != 0)
punt(0, "re_comp: %s", re_comp_error);
argc--; argv++;
break;
case 'S': /* selected save set number */
if (argc <= 2)
punt(0, "Need save set number after -S");
cursswant = atoi(argv[2]);
argc--; argv++;
break;
case 'F': /* selected file numbers */
if (argc <= 2)
punt(0, "Need file number(s) after -F");
for (argc -= 2, argv += 2;
argc && isdigit(**argv);
argc--, argv++, nselect++) {
want[nselect].ssnum = cursswant;
want[nselect].fnum = atoi(*argv);
}
argc += 2; argv -= 2;
break;
default:
punt(0, "unknown flag %s", argv[1]);
}
argc--; argv++;
}
if (!xflg && !dodir)
punt(0, "Need either '-x' or '-t' option.");
if (textflg && binflg)
punt(0, "'-T' and '-b' are mutually exclusive.");
if (argc > 1) {
patterns = &argv[1];
numpats = argc - 1;
}
doallflag = !(patterns || expression || nselect);
if (nselect)
qsort((char *)want, nselect, sizeof (struct want), compwant);
tape_handle = opentape (tape, 0, 0);
if (! tape_handle)
punt(1, "Can't open tape '%s'", tape);
label = 0;
rc = 0;
for ( ; ; ) /* Loop till end of tape */
{
/*** Read a block ***/
if (rc == 0) {
rc = getrec (tape_handle, tapeblocka, TAPEBLK);
if (debug > 99)
printf("rc=%d\n", rc);
if ((rc % (518*5)) != 0) {
if (rc == 80) {
doAnsiLabel(tapeblocka);
label = 1;
rc = 0;
continue;
}
if (rc != 0)
punt(1, "Oops. Read block len = %d", rc);
}
if (rc == 0) {
if (label) {
label = 0;
continue;
}
if (verbose)
printf("\nEnd of tape.\n");
exit(0); /* Normal exit */
}
tapeblock = tapeblocka;
rc = rc - 518*5;
}
else {
tapeblock = tapeblock + 518*5;
rc = rc - 518*5;
}
/*** Do something with it ***/
switch(rtype = -getfield(tapeblock,
WdoffRectype, BtoffRectype, BtlenRectype))
{
case RectypeData: /* Data block */
doDatablock(tapeblock);
break;
case RectypeTphd: /* Saveset header */
doSaveset(tapeblock, 0);
break;
case RectypeFlhd: /* File header */
doFileHeader(tapeblock);
break;
case RectypeFltr: /* File trailer */
doFileTrailer(tapeblock);
break;
case RectypeTptr: /* Tape trailer */
doTapeTrailer(tapeblock);
break;
case RectypeUsr: /* User directory info ? */
if (verbose >= 3)
fprintf(stderr, "Directory record skipped\n");
break;
case RectypeCtph: /* Continued saveset hdr */
doSaveset(tapeblock, 1);
break;
case RectypeFill: /* Fill record */
if (verbose >= 3)
fprintf(stderr, "Fill record skipped\n");
break;
default:
punt(0, "Unknown record type 0x%x", rtype);
break;
}
}
}
#ifdef notdef
#define HOUR 3600
#define DAY (HOUR*24)
#define DAY0 40587 /* number of days between tops20 0 day and Unix 0 day */
#define makeword(l, r) ( ((l) << 18) | (r) )
#define getright(b) ( (b) & 0777777 )
#define getleft(b) ( (b) >> 18 )
/* Convert Tops-20 to Unix time -- curently incomplete due to 32 < 36 bits */
int
_t2utim(t)
unsigned t;
{
register ticks, rh, secs;
ticks = t - makeword(DAY0, 0);
rh = getright(ticks) * DAY;
secs = rh >> 18;
if (rh % makeword(1,0) > 0400000)
secs++; /* round up */
return (getleft(ticks) * DAY) + secs;
}
#endif