mirror of
https://github.com/PDP-10/klh10.git
synced 2026-01-11 23:52:54 +00:00
1537 lines
43 KiB
C
1537 lines
43 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.
|
||
* More mods by Ken Harrenstien, 1992-98.
|
||
*/
|
||
/* KLH To-do:
|
||
Figure out some convention for file numbering like ss.nnn which
|
||
also accomodates designating savesets by sequential numbering from
|
||
start of dump (rather than using the saveset # in block header, which
|
||
is always 0 most of the time)
|
||
|
||
Perhaps add an interactive browse capability similar to KRSTOR.
|
||
Perhaps use a semi-permanent directory generated by initial scan.
|
||
*/
|
||
#ifdef __STDC__ /* Some compilers can't even define this correctly! */
|
||
# define READ20_STDC __STDC__
|
||
#else
|
||
# define READ20_STDC 0
|
||
#endif
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <stdio.h>
|
||
#include <time.h> /* For ctime */
|
||
#include <stdlib.h> /* For malloc */
|
||
#include <string.h> /* For strchr, strstr, memcpy, etc */
|
||
#include <ctype.h>
|
||
#include <signal.h>
|
||
#include <errno.h>
|
||
#if READ20_STDC
|
||
# include <stdarg.h> /* For punt() */
|
||
#endif
|
||
|
||
#include "dump.h"
|
||
|
||
#define LOGFILE "Logfile" /* logfile should be changeable */
|
||
|
||
char *unixname();
|
||
long getfield();
|
||
time_t unixtime();
|
||
#if READ20_STDC
|
||
void punt(int, char *, ...);
|
||
#else
|
||
void punt();
|
||
#endif
|
||
|
||
int fdTape; /* File handle for Dumper-20 format tape */
|
||
int tbrecsiz; /* Tape buffer record size */
|
||
int tblen; /* Tape buffer size (max recsize) */
|
||
char *tbuffer; /* Tape buffer location */
|
||
|
||
long nblktape = 0; /* # blocks read on entire tape so far */
|
||
long nblkfile = 0; /* # blocks read in current tape file */
|
||
long nblksset = 0; /* # blocks read in current saveset */
|
||
|
||
char *prevblock = 0;
|
||
int firstblock = 0; /* We are reading first block of a file */
|
||
|
||
FILE *fpFile; /* Output file handle on extracts */
|
||
int debug = 0;
|
||
int default_bytesize = 36; /* Default byte size for binary files */
|
||
int wordflg = 0; /* Set 1 to force 36-bit size for 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 fdbflg = 0; /* Show FDB contents for files listed/extracted*/
|
||
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 qicflg = 0; /* Non-zero if hacking QIC tape */
|
||
long tlocseek = 0; /* Hack: tapeloc to seek to (-s) */
|
||
int showtloc = 0; /* Non-zero to show tapelocs on listing */
|
||
|
||
int number; /* Current output file "number" */
|
||
|
||
#define TAPE "/dev/rmt8" /* Default input tape */
|
||
|
||
enum { OC_ASCII, OC_8BIT, OC_WORD, OC_PAGE }
|
||
outmode; /* File output conversion mode */
|
||
int outnfbpw; /* # of file bytes per word (0 if bytesize 0) */
|
||
int outnfw; /* # of file words remaining (set from fdb) */
|
||
int outnbpiw; /* # output bytes per input 36-bit file word */
|
||
int outnbleft; /* # of output bytes left to go */
|
||
|
||
int bytesize; /* FDB: Number of bits/byte in current file */
|
||
long numbytes; /* FDB: Number of bytes in current file */
|
||
int pgcount; /* FDB: Number of twenex pages in file */
|
||
|
||
long tapefmt; /* DUMPER tape format from saveset header */
|
||
long tapeno, ssno; /* Tape # and Saveset # from saveset header */
|
||
long pageno, filenum;
|
||
|
||
unsigned tprot; /* Tops-20 protection */
|
||
|
||
time_t timep[2];
|
||
#define atime (timep[0])
|
||
#define mtime (timep[1])
|
||
|
||
int offline, archived, invisible;
|
||
int apgcount, tp1, tp2, ss1, ss2, tf1, tf2;
|
||
|
||
#define DUMPFNAMLEN (0200*5) /* Max length of dumped filename */
|
||
char topsname[DUMPFNAMLEN];
|
||
char sunixname[300];
|
||
|
||
struct want {
|
||
unsigned short ssnum;
|
||
unsigned short fnum;
|
||
} want[10000]; /* limited by 20000 char arglist */
|
||
|
||
int cursswant;
|
||
int compwant();
|
||
|
||
#define CONTAINS_BOF 1 /* Block contains the beginning of a file */
|
||
#define CONTAINS_EOF 2 /* Block contains the end of a file */
|
||
#define NUL_AT_END 4 /* Put a null at the end */
|
||
#define STRING (CONTAINS_BOF|CONTAINS_EOF|NUL_AT_END) /* Make strings easy */
|
||
|
||
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();
|
||
|
||
void scan();
|
||
|
||
char helpstr[] = "\
|
||
Usage: read20 [switches] [patterns]\n\
|
||
Switches must be separated.\n\
|
||
Patterns are simple substrings of the filenames to select.\n\
|
||
-f <file> Specify tapefile. '-' uses stdin. Default is /dev/rmt8\n\
|
||
-x Extract files\n\
|
||
-t List contents (one of -t or -x must be given)\n\
|
||
-tl Show tape locations in listing\n\
|
||
-S <n> Only process saveset <n>\n\
|
||
-e <regexp> Only process filenames matching <regexp> (one -e only)\n\
|
||
-F <n> ... Only process files numbered <n> ...\n\
|
||
-V Show FDB info for files extracted or listed\n\
|
||
-s <loc> Start reading at this byte loc in tapefile (seeks)\n\
|
||
-q Say using QIC (1/4\") cartridge tape\n\
|
||
-v Verbose feedback\n\
|
||
-g Keep generation # in extracted filename\n\
|
||
-n <n> Use numeric filenames for extracts, starting with <n>\n\
|
||
-W Treat all files as 36-bit. Otherwise, 7-bit files\n\
|
||
are treated as ascii, 8-bit as 8-bit binary, and\n\
|
||
all others as 36-bit (direct copy of tape data).\n\
|
||
-T Treat 0 or 36-bit files as 7-bit ascii\n\
|
||
-B Treat 0 or 36-bit files as 8-bit binary\n\
|
||
-c Keep CRs in CRLF pairs for ascii files\n\
|
||
-d<n> Debug level (>0,>5,>10,>99) (default 0)\n\
|
||
";
|
||
|
||
main(argc, argv)
|
||
int argc;
|
||
char *argv[];
|
||
{
|
||
char *tape = TAPE; /* Pathname for tape device/file */
|
||
|
||
/* Do switch parsing */
|
||
|
||
signal(SIGINT, exit); /* Allow ^C to cause profiling to finish */
|
||
|
||
while(argc>1 && argv[1][0] == '-'){
|
||
switch(argv[1][1]){
|
||
case 's': /* Seek to byte (temporary hack) */
|
||
if (argc <= 2)
|
||
punt(0, "Need byteloc after -s\n%s", helpstr);
|
||
tlocseek = atoi(argv[2]);
|
||
argc--; argv++;
|
||
break;
|
||
case 'f':
|
||
if (argc <= 2)
|
||
punt(0, "Need filename after -f\n%s", helpstr);
|
||
tape = argv[2];
|
||
argc--; argv++;
|
||
break;
|
||
case 'W': /* Force all files to 36-bit word bytes */
|
||
wordflg = 1;
|
||
break;
|
||
case 'T': /* Force 0/36-bit files to 7-bit ascii */
|
||
default_bytesize = 7;
|
||
break;
|
||
case 'B': /* Force 0/36-bit files to 8-bit bin */
|
||
default_bytesize = 8;
|
||
break;
|
||
case 't': /* directory listing */
|
||
if (argv[1][2] == 'l')
|
||
showtloc = 1;
|
||
else
|
||
dodir = 1;
|
||
break;
|
||
case 'x': /* extract */
|
||
xflg = 1;
|
||
break;
|
||
case 'q': /* Using QIC (1/4") tape */
|
||
qicflg = 1;
|
||
break;
|
||
case 'v': /* verbosity */
|
||
verbose++;
|
||
break;
|
||
case 'V': /* Output FDB info */
|
||
fdbflg++;
|
||
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\n%s", helpstr);
|
||
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\n%s", helpstr);
|
||
if (expression)
|
||
punt(0, "Only one -e regexp allowed");
|
||
expression = argv[2];
|
||
if ((re_comp_error = re_comp(expression)) != 0)
|
||
punt(0, "error in -e from 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\n%s", helpstr);
|
||
cursswant = atoi(argv[2]);
|
||
argc--; argv++;
|
||
break;
|
||
case 'F': /* selected file numbers */
|
||
if (argc <= 2)
|
||
punt(0, "Need file number(s) after -F\n%s", helpstr);
|
||
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\n%s", argv[1], helpstr);
|
||
}
|
||
argc--; argv++;
|
||
}
|
||
|
||
if (!xflg && !dodir)
|
||
punt(0, "Need either '-x' or '-t' option.\n%s", helpstr);
|
||
|
||
if (argc > 1) {
|
||
patterns = &argv[1];
|
||
numpats = argc - 1;
|
||
}
|
||
doallflag = !(patterns || expression || nselect);
|
||
if (nselect)
|
||
qsort((char *)want, nselect, sizeof (struct want), compwant);
|
||
|
||
if (!strcmp(tape, "-")) /* stdin */
|
||
fdTape = 0;
|
||
else
|
||
fdTape = open(tape, 0); /* Open tape for read */
|
||
if (fdTape == -1)
|
||
punt(1, "Can't open 'tape' file %s", tape);
|
||
if (tlocseek) {
|
||
if (lseek(fdTape, tlocseek, 0) == -1)
|
||
punt(1, "Can't seek to %ld on 'tape' file %s",
|
||
tlocseek, tape);
|
||
}
|
||
|
||
tbrecsiz = TAPEBLK * 15; /* Default - use max block factor */
|
||
tblen = ((tbrecsiz+511)/512)*512; /* Round up to modulo-512 */
|
||
if (!(tbuffer = malloc(tblen)))
|
||
punt(1, "Can't allocate %d bytes for tape buffer", tblen);
|
||
|
||
scan(); /* Now scan tape */
|
||
}
|
||
|
||
void
|
||
scan()
|
||
{
|
||
char *tapeblock;
|
||
int rc;
|
||
int rtype;
|
||
long tloc = 0;
|
||
|
||
rc = 0;
|
||
for ( ; ; ) /* Loop till end of tape */
|
||
{
|
||
/*** Read a block ***/
|
||
if (rc == 0) {
|
||
rc = getrec(fdTape, tbuffer, (qicflg ? tblen : tbrecsiz));
|
||
if (rc > 0)
|
||
tloc += rc;
|
||
|
||
if (qicflg && rc > 0) {
|
||
if (rc == tblen) rc = tbrecsiz;
|
||
else punt(1, "Ugh, QIC rec %d long, shd be %d",
|
||
rc, tblen);
|
||
}
|
||
if (debug > 99)
|
||
printf("rc=%d\n", rc);
|
||
if ((rc % (518*5)) != 0) {
|
||
if (rc != 0)
|
||
punt(1, "Oops. Read block len = %d", rc);
|
||
}
|
||
if (rc == 0) {
|
||
if (verbose)
|
||
printf("\nEnd of tape.\n");
|
||
exit(0); /* Normal exit */
|
||
}
|
||
tapeblock = tbuffer;
|
||
rc = rc - 518*5;
|
||
}
|
||
else {
|
||
tapeblock = tapeblock + 518*5;
|
||
rc = rc - 518*5;
|
||
}
|
||
|
||
prevblock = 0;
|
||
/*** Do something with it ***/
|
||
switch(rtype = -getfield(tapeblock,
|
||
WdoffRectype, BtoffRectype, BtlenRectype))
|
||
{
|
||
case RectypeData: /* Data block */
|
||
doDatablock(tapeblock);
|
||
break;
|
||
|
||
case RectypeTphd: /* Saveset header */
|
||
nblksset = 0; /* Start of saveset */
|
||
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 block skipped\n");
|
||
break;
|
||
|
||
case RectypeCtph: /* Continued saveset hdr */
|
||
nblksset = 0; /* Start of saveset */
|
||
doSaveset(tapeblock, 1);
|
||
break;
|
||
|
||
case RectypeFill: /* Fill block */
|
||
if (verbose /* >= 3 */)
|
||
fprintf(stderr, "Fill block skipped\n");
|
||
break;
|
||
|
||
case RectypeTonext: /* FMTV6 - to next tape */
|
||
doTonext(tapeblock);
|
||
break;
|
||
|
||
default:
|
||
fprintf(stderr, "Unknown block type 0x%x, tapeloc %ld.\n",
|
||
rtype, tloc - rc);
|
||
if (debug == 0) /* Unless debugging, */
|
||
punt(0, "Aborting."); /* stop now */
|
||
break;
|
||
}
|
||
/* Done with record, bump counts */
|
||
nblktape++;
|
||
nblkfile++;
|
||
nblksset++;
|
||
}
|
||
}
|
||
|
||
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(block, wordoff, bitoff, bitlen)
|
||
char *block; /* Tape block */
|
||
int wordoff; /* 36-bit word offset */
|
||
register int bitoff; /* Bit offset of field (from msb) */
|
||
register int bitlen; /* Bit length of field */
|
||
{
|
||
register char *p; /* Used to point into block */
|
||
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);
|
||
}
|
||
|
||
/* FDB extraction stuff
|
||
** by KLH, 1992.
|
||
*/
|
||
|
||
struct fdbent {
|
||
char *fe_name; /* Symbol name */
|
||
int fe_typ; /* Value type (FBT_xxx) */
|
||
int fe_woff; /* Word offset */
|
||
int fe_boff; /* Bit offset (per MACRO, from hi-order bit) */
|
||
int fe_bits; /* Bits ((0 || 36) == full word) */
|
||
char *fe_show; /* Text to use when listing info */
|
||
|
||
long fe_lh; /* Extracted LH value */
|
||
long fe_rh; /* Extracted RH value */
|
||
long fe_val; /* Extracted value (what fits in a long) */
|
||
char *fe_str; /* String value (if one) */
|
||
time_t fe_tim; /* TAD converted to Unix timeval */
|
||
};
|
||
char festr_fname[DUMPFNAMLEN+1]; /* To hold extracted filename string */
|
||
char *festr_attrs; /* Pointer to first fn attribute if any */
|
||
char festr_aut[10*5+1]; /* To hold extracted author string */
|
||
char festr_lwr[10*5+1]; /* To hold extracted last-writer string */
|
||
|
||
enum fdbtyp {
|
||
FBT_V8, /* Octal value */
|
||
FBT_V10, /* Decimal value */
|
||
FBT_H8, /* Two octal halfwords */
|
||
FBT_H10, /* Two decimal halfwords */
|
||
FBT_FLG, /* Boolean flag */
|
||
FBT_TAD, /* T20 TAD value */
|
||
FBT_TOD, /* T20 TAD value, or interval in days */
|
||
FBT_AUT, /* Author string - at FDBend */
|
||
FBT_LWR, /* Last-Writer string - at FDBend+010 */
|
||
FBT_ACT, /* Account string - in filename */
|
||
FBT_6BT /* Possible SIXBIT value */
|
||
};
|
||
|
||
#define fedef(nam,typ,wo,bo,bs,tx) {nam,typ,wo,bo,bs,tx}
|
||
|
||
#define FBLN0 030 /* DUMPER only saves this many actual FDB words */
|
||
|
||
/* NOTE!!!! FMTV6 DUMPER writes out 037 FDB words, so all the offsets
|
||
** need to be changed if reading a format 6 tape...
|
||
*/
|
||
|
||
#define FDBAUT_Woff (WdoffFDB+FBLN0)
|
||
#define FDBLWR_Woff (WdoffFDB+FBLN0+010)
|
||
#define FDBARCF (FBLN0+010+010) /* Offset in "DumperFDB" of ARCF blk */
|
||
#define FDBARC_Woff (WdoffFDB+FDBARCF)
|
||
|
||
/* Contents of ARCF block as stored by DUMPER */
|
||
#define ARCF_AROFL 0 /* Useless flags to ARCF% */
|
||
#define ARCF_ARTP1 1 /* == .FBTP1 */
|
||
#define ARCF_ARSF1 2 /* == .FBSS1 */
|
||
#define ARCF_ARTP2 3 /* == .FBTP2 */
|
||
#define ARCF_ARSF2 4 /* == .FBSS2 */
|
||
#define ARCF_ARODT 5 /* == .FBTDT */
|
||
#define ARCF_ARPSZ 6 /* == AR%PSZ in .FBBBT */
|
||
|
||
|
||
struct fdbent fdbt[] = {
|
||
fedef(".FBHDR",FBT_H8, 0, 0,36,"Header word"),
|
||
fedef("FB%LEN",FBT_V8, 0,35, 7,"Length of FDB"),
|
||
fedef(".FBCTL",FBT_H8, 1,35,36,"Status bits"),
|
||
|
||
fedef("FB%TMP",FBT_FLG, 1, 0, 1,"File is temporary"),
|
||
fedef("FB%PRM",FBT_FLG, 1, 1, 1,"File is permanent"),
|
||
fedef("FB%NEX",FBT_FLG, 1, 2, 1,"File doesn't really exist"),
|
||
fedef("FB%DEL",FBT_FLG, 1, 3, 1,"File is deleted"),
|
||
fedef("FB%NXF",FBT_FLG, 1, 4, 1,"File is not yet closed"),
|
||
fedef("FB%LNG",FBT_FLG, 1, 5, 1,"File is longer than 512 pages"),
|
||
fedef("FB%SHT",FBT_FLG, 1, 6, 1,"DEC-reserved bit"),
|
||
fedef("FB%DIR",FBT_FLG, 1, 7, 1,"File is a directory"),
|
||
fedef("FB%NOD",FBT_FLG, 1, 8, 1,"File is not to be backed up"),
|
||
fedef("FB%BAT",FBT_FLG, 1, 9, 1,"File may have bad pages"),
|
||
fedef("FB%SDR",FBT_FLG, 1,10, 1,"Directory has subdirectories"),
|
||
fedef("FB%ARC",FBT_FLG, 1,11, 1,"File has archive status"),
|
||
fedef("FB%INV",FBT_FLG, 1,12, 1,"File is invisible"),
|
||
fedef("FB%OFF",FBT_FLG, 1,13, 1,"File is offline"),
|
||
fedef("FB%FCF",FBT_V8, 1,17, 4,"File class field"),
|
||
fedef("FB%NDL",FBT_FLG, 1,18, 1,"File is not to be deleted"),
|
||
fedef("FB%WNC",FBT_FLG, 1,19, 1,"File last write not closed"),
|
||
fedef("FB%FOR",FBT_FLG, 1,20, 1,"File has FORTRAN-style LPT chars"),
|
||
|
||
fedef(".FBEXL",FBT_V8, 02,35,36,"Link to FDB of next file type"),
|
||
fedef(".FBADR",FBT_H8, 03,35,36,"Disk address of index block"),
|
||
fedef(".FBPRT",FBT_H8, 04,35,36,"File access bits"),
|
||
fedef(".FBCRE",FBT_TAD, 05,35,36,"Time of last write"),
|
||
fedef(".FBAUT",FBT_AUT, 06,35,36,"Author of file"),
|
||
fedef(".FBGEN",FBT_H8, 07,35,36,"Generation and directory numbers"),
|
||
fedef("FB%GEN",FBT_V10, 07,17,18,"Generation number"),
|
||
fedef("FB%DRN",FBT_V8, 07,35,18,"Internal directory number"),
|
||
fedef(".FBACT",FBT_ACT,010,35,36,"Account designator"),
|
||
fedef(".FBBYV",FBT_H8, 011,35,36,"File I/O information"),
|
||
fedef("FB%RET",FBT_V10,011, 5, 6,"Retention count"),
|
||
fedef("FB%BSZ",FBT_V10,011,11, 6,"File byte size"),
|
||
fedef("FB%MOD",FBT_V10,011,17, 4,"Data mode written in"),
|
||
fedef("FB%PGC",FBT_V10,011,35,18,"Page count of file"),
|
||
fedef(".FBSIZ",FBT_V10,012,35,36,"Number of bytes in file"),
|
||
fedef(".FBCRV",FBT_TAD,013,35,36,"Creation time of file"),
|
||
fedef(".FBWRT",FBT_TAD,014,35,36,"Time of last user write"),
|
||
fedef(".FBREF",FBT_TAD,015,35,36,"Time of last nonwrite access"),
|
||
fedef(".FBCNT",FBT_H10,016,35,36,"Count of writes,,references"),
|
||
fedef(".FBBK0",FBT_H8, 017,35,36,"Backup word 1"),
|
||
fedef(".FBBK1",FBT_H8, 020,35,36,"Backup word 2"),
|
||
fedef(".FBBK2",FBT_H8, 021,35,36,"Backup word 3"),
|
||
fedef(".FBBBT",FBT_H8, 022,35,36,"Archive status bits"),
|
||
fedef("AR%RAR",FBT_FLG,022, 1, 1,"User requested archival"),
|
||
fedef("AR%RIV",FBT_FLG,022, 2, 1,"System requested migration"),
|
||
fedef("AR%NDL",FBT_FLG,022, 3, 1,"Don't delete contents"),
|
||
fedef("AR%NAR",FBT_FLG,022, 4, 1,"Resist migration"),
|
||
fedef("AR%EXM",FBT_FLG,022, 5, 1,"File is exempt from migration"),
|
||
fedef("AR%1ST",FBT_FLG,022, 6, 1,"First archive pass in progress"),
|
||
fedef("AR%RFL",FBT_FLG,022, 7, 1,"Restoral failed"),
|
||
fedef("AR%WRN",FBT_FLG,022,10, 1,"File off-line exp date approaching"),
|
||
fedef("AR%RSN",FBT_V8, 022,17, 3,"Offline reason"),
|
||
fedef("AR%PSZ",FBT_V10,022,35,18,"Page count when file went offline"),
|
||
fedef(".FBNET",FBT_TOD,023,35,36,"On-line expiration date"),
|
||
fedef(".FBUSW",FBT_H8, 024,35,36,"User-settable word"),
|
||
fedef(".FBGNL",FBT_H8, 025,35,36,"Address of FDB of next generation"),
|
||
fedef(".FBNAM",FBT_H8, 026,35,36,"Pointer to filename block"),
|
||
fedef(".FBEXT",FBT_H8, 027,35,36,"Pointer to file type block"),
|
||
/* FDB words beyond this are not actually saved by DUMPER, which instead
|
||
** uses various hacks to store the information in the rest of the block.
|
||
*/
|
||
fedef(".FBLWR",FBT_LWR,030,35,36,"Last writer to file"),
|
||
|
||
#if 0 /* Note .FBFET vanishes completely -- no equiv in DumperFDB. */
|
||
fedef(".FBTDT",FBT_TAD,031,35,36,"Date archived"),
|
||
fedef(".FBFET",FBT_TOD,032,35,36,"Offline expiration date"),
|
||
fedef(".FBTP1",FBT_6BT,033,35,36,"Tape ID for run 1"),
|
||
fedef(".FBSS1",FBT_H10,034,35,36,"Run 1 saveset number,,Tape file number"),
|
||
fedef(".FBTP2",FBT_6BT,035,35,36,"Tape ID for run 2"),
|
||
fedef(".FBSS2",FBT_H10,036,35,36,"Run 2 saveset number,,Tape file number"),
|
||
#endif
|
||
|
||
fedef(".AROFL",FBT_H8, FDBARCF+ARCF_AROFL,35,36,"ARCF% flags"),
|
||
fedef(".ARTP1",FBT_6BT,FDBARCF+ARCF_ARTP1,35,36,"Archive tape 1 ID"),
|
||
fedef(".ARSF1",FBT_H10,FDBARCF+ARCF_ARSF1,35,36,"Archive tape 1 saveset,,fileno"),
|
||
fedef(".ARTP2",FBT_6BT,FDBARCF+ARCF_ARTP2,35,36,"Archive tape 2 ID"),
|
||
fedef(".ARSF2",FBT_H10,FDBARCF+ARCF_ARSF2,35,36,"Archive tape 2 saveset,,fileno"),
|
||
fedef(".ARODT",FBT_TAD,FDBARCF+ARCF_ARODT,35,36,"Date archived"),
|
||
fedef(".ARPSZ",FBT_V10,FDBARCF+ARCF_ARPSZ,35,36,"File page count")
|
||
};
|
||
#define nfdbents ((sizeof fdbt)/(sizeof fdbt[0]))
|
||
|
||
|
||
fe_extract(blk, fe)
|
||
unsigned char *blk;
|
||
struct fdbent *fe;
|
||
{
|
||
int woff = WdoffFDB + fe->fe_woff;
|
||
|
||
switch (fe->fe_typ) {
|
||
case FBT_V8: /* Octal value */
|
||
case FBT_V10: /* Decimal value */
|
||
case FBT_FLG: /* Boolean flag */
|
||
fe->fe_val = getfield(blk, woff,
|
||
(fe->fe_boff + 1 - fe->fe_bits),
|
||
fe->fe_bits);
|
||
break;
|
||
case FBT_H8: /* Two octal halfwords */
|
||
case FBT_H10: /* Two decimal halfwords */
|
||
case FBT_6BT: /* Two SIXBIT halves */
|
||
fe->fe_lh = getfield(blk, woff, 0, 18);
|
||
fe->fe_rh = getfield(blk, woff, 18, 18);
|
||
break;
|
||
|
||
case FBT_TOD: /* T20 TAD value, or interval in days */
|
||
fe->fe_lh = getfield(blk, woff, 0, 18);
|
||
if (fe->fe_lh == 0) { /* If probably not a TAD, */
|
||
fe->fe_rh = getfield(blk, woff, 18, 18); /* get RH */
|
||
break;
|
||
} /* Else drop thru for TAD */
|
||
case FBT_TAD: /* T20 TAD value */
|
||
fe->fe_tim = unixtime(blk, woff);
|
||
break;
|
||
|
||
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
|
||
fe->fe_lh = getfield(blk, woff, 0, 18);
|
||
fe->fe_rh = getfield(blk, woff, 18, 18);
|
||
getstring(blk, festr_aut, FDBAUT_Woff,
|
||
sizeof(festr_aut)-1,STRING);
|
||
fe->fe_str = festr_aut[0] ? festr_aut : NULL;
|
||
break;
|
||
case FBT_LWR: /* Last-Writer string - ditto */
|
||
fe->fe_lh = getfield(blk, woff, 0, 18);
|
||
fe->fe_rh = getfield(blk, woff, 18, 18);
|
||
getstring(blk, festr_lwr, FDBLWR_Woff,
|
||
sizeof(festr_lwr)-1,STRING);
|
||
fe->fe_str = festr_lwr[0] ? festr_lwr : NULL;
|
||
break;
|
||
|
||
case FBT_ACT: /* Account string - in filename */
|
||
fe->fe_lh = getfield(blk, woff, 0, 18);
|
||
fe->fe_rh = getfield(blk, woff, 18, 18);
|
||
{
|
||
char *ecp, *cp = festr_attrs;
|
||
while (cp) {
|
||
if (ecp = strchr(cp, ';')) /* Tie off string first */
|
||
*ecp++ = '\0';
|
||
if (*cp++ == 'A') /* If this is acct string, */
|
||
break; /* use it! */
|
||
cp = ecp; /* Else try to continue */
|
||
}
|
||
fe->fe_str = cp;
|
||
}
|
||
break;
|
||
default:
|
||
fprintf(stderr, "Internal error - Unknown FBT_ type: %d\n",
|
||
fe->fe_typ);
|
||
break;
|
||
}
|
||
}
|
||
|
||
fe_show(f, fe)
|
||
FILE *f;
|
||
struct fdbent *fe;
|
||
{
|
||
/* First check to see if value exists, and do nothing if zero */
|
||
switch (fe->fe_typ) {
|
||
case FBT_V8: /* Octal value */
|
||
case FBT_V10: /* Decimal value */
|
||
case FBT_FLG: /* Boolean flag */
|
||
if (!fe->fe_val) return;
|
||
break;
|
||
case FBT_H8: /* Two octal halfwords */
|
||
case FBT_H10: /* Two decimal halfwords */
|
||
case FBT_6BT: /* SIXBIT */
|
||
case FBT_TOD: /* T20 TAD value, or interval in days */
|
||
if (!fe->fe_lh && !fe->fe_rh) return;
|
||
break;
|
||
case FBT_TAD: /* T20 TAD value */
|
||
if (!fe->fe_tim) return;
|
||
break;
|
||
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
|
||
case FBT_LWR: /* Last-Writer string - ditto */
|
||
case FBT_ACT: /* Account string - in filename */
|
||
if (!fe->fe_str) return;
|
||
break;
|
||
default:
|
||
fprintf(stderr, "Internal error - Unknown FBT_ type: %d\n",
|
||
fe->fe_typ);
|
||
return;
|
||
}
|
||
|
||
/* Now start printing out the value */
|
||
if (fe->fe_name[0] == '.') { /* Primary word def */
|
||
fprintf(f, "%2o %s - %s: ", fe->fe_woff, fe->fe_name, fe->fe_show);
|
||
} else { /* Secondary bit field */
|
||
fprintf(f, " %s (%oB%d) - %s: ",
|
||
fe->fe_name, masks[fe->fe_bits - 1], fe->fe_boff, fe->fe_show);
|
||
}
|
||
|
||
/* Now print the value itself */
|
||
switch (fe->fe_typ) {
|
||
case FBT_V8: fprintf(f, "%o", fe->fe_val); break; /* Octal */
|
||
case FBT_V10: fprintf(f, "%d.", fe->fe_val); break; /* Decimal */
|
||
case FBT_FLG: fprintf(f, "%s", fe->fe_val ? "Yes" : "No");/* Boolean */
|
||
break;
|
||
|
||
case FBT_H8: /* Two octal halfwords */
|
||
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
|
||
fprintf(f, "%o", fe->fe_rh);
|
||
break;
|
||
|
||
case FBT_H10: /* Two decimal halfwords */
|
||
if (fe->fe_lh) fprintf(f, "%d.,,", fe->fe_lh);
|
||
fprintf(f, "%d.", fe->fe_rh);
|
||
break;
|
||
|
||
case FBT_6BT: /* Two SIXBIT halves */
|
||
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
|
||
fprintf(f, "%o ==> ", fe->fe_rh);
|
||
if (hwd6show(f, fe->fe_lh)) /* If LH doesn't end, */
|
||
hwd6show(f, fe->fe_rh); /* show RH too */
|
||
break;
|
||
|
||
case FBT_TOD: /* T20 TAD value, or interval in days */
|
||
if (!fe->fe_lh) {
|
||
fprintf(f, "%d. days", fe->fe_rh);
|
||
break;
|
||
}
|
||
/* Fall thru to TAD case */
|
||
|
||
case FBT_TAD: /* Output TAD as "dd-Mon-yy hh:mm:ss" */
|
||
pftime(f, &fe->fe_tim);
|
||
break;
|
||
|
||
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
|
||
case FBT_ACT: /* Account string - in filename */
|
||
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
|
||
fprintf(f, "%o ", fe->fe_rh);
|
||
|
||
case FBT_LWR: /* Last-Writer string - stored in dumped block. */
|
||
/* Must skip showing actual FDB value because this is the first
|
||
** FDB word not stored by DUMPER. This and following words are
|
||
** instead filled by the strings for FBAUT, FBLWR, and FBARC.
|
||
*/
|
||
fprintf(f, "==> %s", fe->fe_str ? fe->fe_str : "?");
|
||
break;
|
||
}
|
||
putc('\n', f);
|
||
}
|
||
|
||
fdb_show(f, blk)
|
||
FILE *f;
|
||
unsigned char *blk;
|
||
{
|
||
struct fdbent *fe;
|
||
int i;
|
||
|
||
fprintf(f, "FDB for file %d.%d: %s\n\n", ssno, filenum, festr_fname);
|
||
for (fe = fdbt; fe <= &fdbt[nfdbents-1]; ++fe) {
|
||
fe_extract(blk, fe);
|
||
fe_show(f, fe);
|
||
}
|
||
}
|
||
|
||
hwd6show(f, hwd)
|
||
FILE *f;
|
||
unsigned long hwd; /* 18-bit halfword value - 3 SIXBIT chars */
|
||
{
|
||
int ch;
|
||
if (ch = (hwd>>12) & 077) {
|
||
fputc(ch + 040, f);
|
||
if (ch = (hwd>>6) & 077) {
|
||
fputc(ch + 040, f);
|
||
if (ch = hwd & 077) {
|
||
fputc(ch + 040, f);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
pftime(f, atim)
|
||
FILE *f;
|
||
time_t *atim;
|
||
{
|
||
char *cp = ctime(atim)+4;
|
||
fprintf(f, "%.2s-%.3s-%.2s %.8s", cp+4, cp, cp+18, cp+7);
|
||
}
|
||
|
||
doDatablock(block)
|
||
char *block;
|
||
{
|
||
/* max is 5 bytes per word */
|
||
static char buf[(512*5)+1]; /* A page of characters */
|
||
int ct, wct;
|
||
int maxperblock;
|
||
int nout, type;
|
||
|
||
if (debug > 10)
|
||
printf("*");
|
||
if (fpFile == NULL)
|
||
return;
|
||
|
||
maxperblock = 512 * outnbpiw; /* # output bytes per 512-wd block */
|
||
|
||
if (firstblock) type = CONTAINS_BOF;
|
||
else type = 0;
|
||
|
||
if (outnbleft > maxperblock)
|
||
ct = maxperblock, wct = 512;
|
||
else {
|
||
ct = outnbleft, wct = outnfw;
|
||
type |= CONTAINS_EOF;
|
||
}
|
||
|
||
switch (outmode) {
|
||
case OC_ASCII:
|
||
nout = getstring(block, buf, 6, ct, type);
|
||
fwrite(buf, 1, nout, fpFile);
|
||
break;
|
||
case OC_8BIT:
|
||
getbytes(block, buf, 6, ct);
|
||
fwrite(buf, 1, ct, fpFile);
|
||
break;
|
||
case OC_WORD: /* No conversion needed */
|
||
fwrite(block + (6*5), 1, ct, fpFile);
|
||
break;
|
||
case OC_PAGE: /* Cannot do anything for now */
|
||
break;
|
||
}
|
||
if (ferror(fpFile))
|
||
punt(1, "Error writing %s", sunixname);
|
||
|
||
firstblock = 0;
|
||
outnbleft -= ct;
|
||
outnfw -= wct;
|
||
}
|
||
|
||
/*ARGSUSED*/
|
||
doSaveset(block, contflag)
|
||
char *block;
|
||
int contflag;
|
||
{
|
||
int ssnoff;
|
||
long hwd;
|
||
char name[140]; /* Size must be mod 5 */
|
||
time_t t;
|
||
|
||
if (debug > 10) {
|
||
printf("\nSaveset header:\n");
|
||
showblk(stdout, block, 0, 100);
|
||
}
|
||
tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum);
|
||
ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum,
|
||
BtlenSaveSetNum);
|
||
|
||
/* Find offset to saveset name. Use same algorithm as DUMPER here.
|
||
*/
|
||
hwd = getfield(block, WdoffSSFormat, 0, 18); /* Get LH of fmt */
|
||
if (hwd & (0177L<<(18-7))) {
|
||
tapefmt = 0; /* Format wd has ASCII - old fmt! */
|
||
ssnoff = 0; /* Starts with saveset name */
|
||
} else {
|
||
/* Else use RH as format number (actually whole wd, but so what) */
|
||
tapefmt = getfield(block, WdoffSSFormat, 18, 18);
|
||
ssnoff = getfield(block, WdoffSSNamoff, 18, 18);
|
||
if (ssnoff >= 512) { /* Sanity check */
|
||
printf("Saveset header error: name offset of %ld is > 512!\n",
|
||
ssnoff);
|
||
ssnoff = 3; /* Use usual default instead */
|
||
}
|
||
}
|
||
|
||
getstring(block, name, RecHdrlen+ssnoff, sizeof(name), STRING);
|
||
name[sizeof(name)-1] = '\0'; /* Ensure terminated */
|
||
|
||
t = unixtime(block, WdoffSSDate);
|
||
if (dodir || verbose) {
|
||
if (showtloc)
|
||
printf("B%ld: ", nblktape);
|
||
printf("Tape %d: %sSaveset %d \"%s\" ",
|
||
tapeno, (contflag ? "Continued " : ""),
|
||
ssno, name);
|
||
pftime(stdout, &t);
|
||
printf("\n");
|
||
}
|
||
|
||
}
|
||
|
||
static void folddown(to, from)
|
||
register char *to, *from;
|
||
{
|
||
for ( ; *from; ++from)
|
||
*to++ = (isupper(*from) ? tolower(*from) : *from);
|
||
*to = '\0';
|
||
}
|
||
|
||
doFileHeader(block)
|
||
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, festr_fname, WdoffFLName, sizeof(festr_fname), STRING);
|
||
if (festr_attrs = strchr(festr_fname, ';')) /* Chop off ;Pprot;Aacct */
|
||
*festr_attrs++ = 0;
|
||
folddown(topsname, festr_fname);
|
||
|
||
fpFile = NULL;
|
||
|
||
if ( doallflag ||
|
||
(patterns && patternmatch()) ||
|
||
(expression && expmatch()) ||
|
||
(nselect && fmatch()) ) {
|
||
getfdbinfo(block);
|
||
pageno = getfield(block, WdoffPageNum, BtoffPageNum, BtlenPageNum);
|
||
|
||
if (dodir || verbose || fdbflg) {
|
||
if (fdbflg) {
|
||
printf("\n");
|
||
fdb_show(stdout, block);
|
||
printf("\n");
|
||
}
|
||
if (showtloc)
|
||
printf("B%ld: ", nblktape);
|
||
if (verbose)
|
||
printf("%3d %5d ", ssno, filenum);
|
||
printf("%c%c%c", prt_ar[archived], prt_off[offline],
|
||
prt_inv[invisible]);
|
||
printf(" %4d %8d %2d %06o ", offline ? apgcount : pgcount,
|
||
numbytes, bytesize, tprot);
|
||
pftime(stdout, &mtime);
|
||
printf(" %s", topsname);
|
||
#if 0 /* KLH: fix this later, currently vars are always zero */
|
||
if (archived && verbose /* >= 2 */)
|
||
printf(" %x %3d %4d %x %3d %4d", tp1, ss1, tf1, tp2, ss2, tf2);
|
||
#endif
|
||
if (pageno != 0)
|
||
printf(" Split file, part 2");
|
||
}
|
||
|
||
if (xflg) {
|
||
/* Decide bytesize (mode) for file extraction */
|
||
if (bytesize > 36)
|
||
bytesize = 36;
|
||
if (bytesize == 0 && numbytes) {
|
||
/* Probably a paged holey file, but if file len set, try 36 */
|
||
bytesize = 36; /* Cross fingers */
|
||
}
|
||
if (!bytesize) { /* If size still zero, empty file! */
|
||
outnfbpw = 0; /* # file bytes/wd */
|
||
outnfw = 0; /* # file words */
|
||
} else {
|
||
outnfbpw = 36/bytesize; /* # file bytes/wd */
|
||
outnfw = (numbytes + outnfbpw-1) / outnfbpw; /* # file wds */
|
||
}
|
||
outnbpiw = 5; /* Default output bytes per input wd */
|
||
|
||
if (!bytesize) {
|
||
outmode = OC_PAGE;
|
||
outnbpiw = outnbleft = 0;
|
||
} else if (wordflg) {
|
||
outmode = OC_WORD;
|
||
outnbpiw = 5;
|
||
outnbleft = outnbpiw * outnfw;
|
||
} else switch (bytesize) {
|
||
case 7:
|
||
outmode = OC_ASCII;
|
||
outnbpiw = 5;
|
||
outnbleft = numbytes;
|
||
break;
|
||
case 8:
|
||
outmode = OC_8BIT;
|
||
outnbpiw = 4;
|
||
outnbleft = numbytes;
|
||
break;
|
||
case 36:
|
||
if (default_bytesize == 7) {
|
||
outmode = OC_ASCII;
|
||
outnbpiw = 5;
|
||
outnbleft = outnbpiw * outnfw;
|
||
break;
|
||
} else if (default_bytesize == 8) {
|
||
outmode = OC_8BIT;
|
||
outnbpiw = 4;
|
||
outnbleft = outnbpiw * outnfw;
|
||
break;
|
||
}
|
||
default:
|
||
outmode = OC_WORD;
|
||
outnbpiw = 5;
|
||
outnbleft = outnbpiw * outnfw;
|
||
break;
|
||
}
|
||
|
||
if (outmode != OC_PAGE && !offline) {
|
||
if (pageno != 0) { /* continued file */
|
||
long missiwds = pageno * 512; /* Missing input wds */
|
||
long missob = missiwds * outnbpiw; /* Missing out bytes */
|
||
|
||
outnfw -= missiwds;
|
||
outnbleft -= missob;
|
||
if (!(dodir || verbose))
|
||
printf("%s: Split file, part 2", topsname);
|
||
printf(": %ld file words (%ld output bytes) missing.",
|
||
missiwds, missob);
|
||
if (!(dodir || verbose))
|
||
putchar('\n');
|
||
}
|
||
firstblock = 1;
|
||
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" : "paged/holey");
|
||
}
|
||
|
||
if (dodir || verbose)
|
||
putchar('\n');
|
||
}
|
||
}
|
||
|
||
/* Return 1 if topsname matches any of the "extraction" strings. */
|
||
patternmatch()
|
||
{
|
||
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. */
|
||
expmatch()
|
||
{
|
||
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);
|
||
}
|
||
|
||
int widx;
|
||
|
||
/* Return 1 if current file number matches one selected by arg line. */
|
||
fmatch()
|
||
{
|
||
|
||
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.
|
||
*/
|
||
getfdbinfo(block)
|
||
char *block;
|
||
{
|
||
|
||
mtime = unixtime(block, WdoffFDB_Wrt);
|
||
atime = unixtime(block, WdoffFDB_Ref);
|
||
|
||
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);
|
||
}
|
||
|
||
/*ARGSUSED*/
|
||
doFileTrailer(block)
|
||
char *block;
|
||
{
|
||
if (debug > 10) printf(" File trailer\n");
|
||
if (fpFile != NULL) {
|
||
if (fclose(fpFile) == EOF)
|
||
punt(1, "fclose: write error on %s", sunixname);
|
||
fpFile = NULL;
|
||
utime(sunixname, timep);
|
||
if (outnbleft || outnfw)
|
||
printf("%s: Split file, part 1: %ld file words (%ld output bytes) left\n",
|
||
topsname, outnfw, outnbleft);
|
||
}
|
||
}
|
||
|
||
/*ARGSUSED*/
|
||
doTonext(block)
|
||
char *block;
|
||
{
|
||
if (dodir || verbose) {
|
||
if (showtloc)
|
||
printf("B%ld: ", nblktape);
|
||
printf("TONEXT record - continued on next tape\n");
|
||
}
|
||
}
|
||
|
||
/*ARGSUSED*/
|
||
doTapeTrailer(block)
|
||
char *block;
|
||
{
|
||
if (dodir || verbose) {
|
||
if (showtloc)
|
||
printf("B%ld: ", nblktape);
|
||
printf("Tape trailer - Saveset End\n");
|
||
}
|
||
}
|
||
|
||
|
||
#if READ20_STDC
|
||
void punt(int prterrno, char *fmt, ...)
|
||
#else
|
||
void punt(prterrno, fmt, a,b,c,d,e)
|
||
int prterrno; /* TRUE to print errno string */
|
||
char *fmt;
|
||
#endif
|
||
{
|
||
extern int errno, sys_nerr;
|
||
|
||
fprintf(stderr, "read20: ");
|
||
#if READ20_STDC
|
||
{
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
vfprintf(stderr, fmt, ap);
|
||
va_end(ap);
|
||
}
|
||
#else
|
||
fprintf(stderr, fmt, a,b,c,d,e);
|
||
#endif
|
||
if (prterrno) {
|
||
fprintf(stderr, ": ");
|
||
if (errno >= 0 && errno < sys_nerr)
|
||
perror("");
|
||
else
|
||
fprintf(stderr, "ERRNO = %d\n", errno);
|
||
}
|
||
else
|
||
fprintf(stderr, "\n");
|
||
exit(1);
|
||
}
|
||
|
||
/*
|
||
* 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(block, s, wordoff, max, type)
|
||
register unsigned char *block; /* Tape block */
|
||
register 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 */
|
||
int type; /* Info about block we are reading (first, last
|
||
or string) */
|
||
{
|
||
register int ct; /* Number of characters loaded so far */
|
||
register char *op; /* Output pointer for <CRLF> -> <LF> hacking */
|
||
register int c;
|
||
char *orig = s; /* Save for debugging */
|
||
unsigned long top4;
|
||
static int state;
|
||
#define NORMAL 1
|
||
#define LAST_WAS_CR 2
|
||
|
||
op = s;
|
||
|
||
block += wordoff * 5;
|
||
|
||
for (ct = 0; ct < max; ct += 5)
|
||
{
|
||
/* Copy 5 7-bit bytes portably from 8-bit byte frames.
|
||
** This depends on unsigned char being implemented correctly.
|
||
*/
|
||
*op++ = block[0] >> 1;
|
||
*op++ = ((block[0] << 6) | (block[1] >> 2)) & 0177;
|
||
*op++ = ((block[1] << 5) | (block[2] >> 3)) & 0177;
|
||
*op++ = ((block[2] << 4) | (block[3] >> 4)) & 0177;
|
||
*op++ = ((block[3] << 3) | ((block[4]&017) >> 1)) & 0177;
|
||
block += 5;
|
||
}
|
||
|
||
if (keepcr) return (max);
|
||
|
||
op = s;
|
||
|
||
if (type & CONTAINS_BOF) state = NORMAL;
|
||
|
||
for (ct = 0; ct < max; ct++)
|
||
{
|
||
c = *s++;
|
||
|
||
switch (state) {
|
||
case NORMAL:
|
||
if (c != '\r')
|
||
*op++ = c;
|
||
else
|
||
state = LAST_WAS_CR;
|
||
|
||
continue;
|
||
|
||
case LAST_WAS_CR:
|
||
if (c == '\n') {
|
||
*op++ = c;
|
||
state = NORMAL;
|
||
continue;
|
||
}
|
||
if (c == '\r') {
|
||
*op++ = c;
|
||
continue;
|
||
}
|
||
*op++ = '\r';
|
||
*op++ = c;
|
||
state = NORMAL;
|
||
}
|
||
}
|
||
|
||
if (type & CONTAINS_EOF) {
|
||
if (state == LAST_WAS_CR)
|
||
*op++ = '\r';
|
||
}
|
||
if (type & NUL_AT_END) *s++ = 0;
|
||
|
||
return (op - orig);
|
||
}
|
||
|
||
/* getbytes: like getstring, but ...
|
||
1) uses 8 bit bytes
|
||
2) doesn't stop on a zero
|
||
*/
|
||
getbytes(block, s, wordoff, max)
|
||
char *block; /* Tape block */
|
||
register 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 */
|
||
|
||
block += wordoff * 5;
|
||
|
||
for (ct = 0; ct < max; ct += 4)
|
||
{
|
||
(void) memcpy(block, s, 4);
|
||
block += 5;
|
||
s += 4;
|
||
}
|
||
}
|
||
|
||
showblk(f, block, offset, nwds)
|
||
register FILE *f;
|
||
register unsigned char *block;
|
||
{
|
||
register int i;
|
||
register unsigned long lh, rh;
|
||
register int c;
|
||
char str[6];
|
||
|
||
str[5] = 0;
|
||
for (i = offset; --nwds >= 0; ++i)
|
||
{
|
||
lh = getfield(block, i, 0, 18);
|
||
rh = getfield(block, i, 18, 18);
|
||
|
||
if (!lh && !rh) {
|
||
fprintf(f, "%4o: 0\n", i);
|
||
continue;
|
||
}
|
||
|
||
/* Convert to word of ASCII */
|
||
c = (lh >> (18- 7)) & 0177;
|
||
str[0] = (isprint(c) ? c : '.');
|
||
c = (lh >> (18-14)) & 0177;
|
||
str[1] = (isprint(c) ? c : '.');
|
||
c = ((lh & 017)<<3) | (rh>>(18-3));
|
||
str[2] = (isprint(c) ? c : '.');
|
||
c = (rh >> (18-10)) & 0177;
|
||
str[3] = (isprint(c) ? c : '.');
|
||
c = (rh >> (18-17)) & 0177;
|
||
str[4] = (isprint(c) ? c : '.');
|
||
|
||
fprintf(f, "%4o: %6lo,,%6lo %s\n", i, lh, rh, str);
|
||
}
|
||
}
|
||
|
||
#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
|
||
*/
|
||
time_t
|
||
unixtime(block, wordoff)
|
||
char *block;
|
||
int wordoff;
|
||
{
|
||
long int t, s;
|
||
double d;
|
||
|
||
t = getfield(block, wordoff, 0, 18); /* LH is day */
|
||
if (t == 0) return(0); /* 0 means never referenced */
|
||
|
||
t -= DayBaseDelta; /* Switch to unix base */
|
||
/* Now has # days since */
|
||
/* Jan 1, 1970 */
|
||
if (t < 0) {
|
||
fprintf(stderr, "ERROR - Date earlier than Jan 1,1970!!!\n");
|
||
}
|
||
|
||
s = getfield(block, wordoff, 18, 18); /* RH is fraction of day */
|
||
|
||
/* Note that the following calculation must be performed in the order shown
|
||
in order to preserve precision. It should also be done in double prec.
|
||
floating point to prevent overflows.
|
||
*/
|
||
d = ((double)s * (24*60*60)) /* Make it double and pre-multiply */
|
||
/ (1<<18); /* Then divide by 1<<18 to get secs */
|
||
s = d; /* Get back into integer form */
|
||
if ((d - s) >= 0.5) /* Find remainder -- round up? */
|
||
++s; /* Yup */
|
||
|
||
return s + (t*24*60*60); /* Add day base and return */
|
||
}
|
||
|
||
char *
|
||
unixname(name)
|
||
char *name;
|
||
{
|
||
static FILE *log = NULL;
|
||
register char *t, *p;
|
||
static char lastdir[128];
|
||
struct stat stb;
|
||
int dlen;
|
||
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, strchr(name, '<') + 1); /* trim off device */
|
||
t = strrchr(sunixname, '>'); /* find end of directory */
|
||
*t = '.';
|
||
dlen = t - sunixname;
|
||
|
||
if (strncmp(lastdir, sunixname, dlen)) {/* maybe new dir */
|
||
strncpy(lastdir, sunixname, dlen); /* remember it */
|
||
lastdir[dlen] = '\0'; /* Ensure null terminated */
|
||
newdir = 1;
|
||
}
|
||
for (p = sunixname; p <= t; p++)
|
||
if (*p == '.' || *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 = '/';
|
||
}
|
||
|
||
for (p = t+1; *p; p++)
|
||
if (*p == '/') *p = '\\';
|
||
|
||
if (!genflg) {
|
||
t = strrchr(sunixname, '.'); /* find last . */
|
||
*t = 0; /* zap it out */
|
||
}
|
||
return(sunixname);
|
||
}
|
||
|
||
int
|
||
t2uprot(prot)
|
||
register unsigned 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;
|
||
}
|
||
|
||
#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
|
||
|
||
int
|
||
compwant(w1, w2)
|
||
register struct want *w1, *w2;
|
||
{
|
||
int sdif;
|
||
|
||
if (sdif = w1->ssnum - w2->ssnum)
|
||
return sdif;
|
||
return (w1->fnum - w2->fnum);
|
||
}
|
||
|
||
getrec(fd, addr, len)
|
||
int len;
|
||
char *addr;
|
||
{
|
||
register int cc, remaining = len;
|
||
|
||
while (remaining > 0)
|
||
{
|
||
cc = read(fd, addr, remaining);
|
||
|
||
/* fprintf(stderr, "read(0, %x, %d) = %d\n", addr, remaining, cc);*/
|
||
|
||
if (cc == remaining) return(len);
|
||
|
||
if (cc == 0) return(len - remaining);
|
||
|
||
if (cc < 0) {
|
||
fprintf(stderr, "Got an error, errno=%d\n", errno);
|
||
exit(1);
|
||
}
|
||
|
||
/* Hack hack, cough... If we are reading from a tape (or a file), then don't
|
||
try to pad the record...
|
||
*/
|
||
if (fd != 0) return(cc);
|
||
|
||
remaining -= cc;
|
||
addr += cc;
|
||
}
|
||
return(len);
|
||
}
|