Import version 0.6 from http://www.36bit.org/dec/software/unix-util/
This commit is contained in:
parent
86e5f36943
commit
a9d00fca34
20
Makefile
20
Makefile
@ -20,11 +20,11 @@
|
||||
# options
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
CFLAGS = -O2
|
||||
LDFLAGS =
|
||||
# CFLAGS = -O2 -Wall
|
||||
# LDFLAGS =
|
||||
|
||||
# CFLAGS = -g
|
||||
# LDFLAGS = -g
|
||||
CFLAGS = -g -Wall
|
||||
LDFLAGS = -g
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# You shouldn't have to change anything below this point, but if you do please
|
||||
@ -32,13 +32,13 @@ LDFLAGS =
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = tapeutils
|
||||
VERSION = 0.4
|
||||
VERSION = 0.6
|
||||
DSTNAME = $(PACKAGE)-$(VERSION)
|
||||
|
||||
PROGRAMS = tapecopy tapedump
|
||||
PROGRAMS = tapecopy tapedump t10backup read20
|
||||
|
||||
HEADERS = tapeio.h
|
||||
SOURCES = tapeio.c tapecopy.c tapedump.c
|
||||
HEADERS = tapeio.h t10backup.h dumper.h
|
||||
SOURCES = tapeio.c tapecopy.c tapedump.c t10backup.c read20.c
|
||||
MISC = COPYING
|
||||
|
||||
DISTFILES = $(MISC) Makefile $(HEADERS) $(SOURCES)
|
||||
@ -60,6 +60,10 @@ tapecopy: tapecopy.o tapeio.o
|
||||
|
||||
tapedump: tapedump.o tapeio.o
|
||||
|
||||
t10backup: t10backup.o tapeio.o
|
||||
|
||||
read20: read20.o tapeio.o
|
||||
|
||||
|
||||
include $(SOURCES:.c=.d)
|
||||
|
||||
|
||||
133
dumper.h
Normal file
133
dumper.h
Normal file
@ -0,0 +1,133 @@
|
||||
/* 5 bytes per 36-bit word */
|
||||
/* 518 word logical blocks */
|
||||
#define TAPEBLK 518*5*15
|
||||
|
||||
/* Checksum is first word */
|
||||
#define WdoffChecksum 0
|
||||
#define BtoffChecksum 0
|
||||
#define BtlenChecksum 36
|
||||
/* Page access bits is second word */
|
||||
#define WdoffAccess 1
|
||||
#define BtoffAccess 0
|
||||
#define BtlenAccess 36
|
||||
/* SCD, first 3 bits in next word */
|
||||
#define WdoffSCD 2
|
||||
#define BtoffSCD 0
|
||||
#define BtlenSCD 3
|
||||
/* Number of saveset on tape */
|
||||
#define WdoffSaveSetNum 2
|
||||
#define BtoffSaveSetNum 3
|
||||
#define BtlenSaveSetNum 15
|
||||
/* Tape number of dump */
|
||||
#define WdoffTapeNum 2
|
||||
#define BtoffTapeNum 18
|
||||
#define BtlenTapeNum 18
|
||||
/* F1, F2 Flag bits */
|
||||
#define WdoffF1F2 3
|
||||
#define BtoffF1F2 0
|
||||
#define BtlenF1F2 2
|
||||
/* File Number in Set (new format only) */
|
||||
#define WdoffFileNum 3
|
||||
#define BtoffFileNum 2
|
||||
#define BtlenFileNum 16
|
||||
/* Page Number in file */
|
||||
#define WdoffPageNum 3
|
||||
#define BtoffPageNum 18
|
||||
#define BtlenPageNum 18
|
||||
/* Record type (2's complement) */
|
||||
#define WdoffRectype 4
|
||||
#define BtoffRectype 0
|
||||
#define BtlenRectype 36
|
||||
/* Record sequence number */
|
||||
#define WdoffRecseq 5
|
||||
#define BtoffRecseq 0
|
||||
#define BtlenRecseq 36
|
||||
|
||||
|
||||
/* SCD Values */
|
||||
#define SCDNormal 0
|
||||
#define SCDCollection 1
|
||||
#define SCDArchive 2
|
||||
#define SCDMigration 3
|
||||
|
||||
/* F1, F2 Values */
|
||||
#define F1F2Old 0
|
||||
#define F1F2OldContinue 3
|
||||
#define F1F2New 1
|
||||
#define F1F2NewContinue 2
|
||||
|
||||
/* Record type values */
|
||||
#define RectypeData 0
|
||||
#define RectypeTphd 1
|
||||
#define RectypeFlhd 2
|
||||
#define RectypeFltr 3
|
||||
#define RectypeTptr 4
|
||||
#define RectypeUsr 5
|
||||
#define RectypeCtph 6
|
||||
#define RectypeFill 7
|
||||
|
||||
char *rectypes[] = {
|
||||
"DATA",
|
||||
"ISSH",
|
||||
"FLHD",
|
||||
"FLTR",
|
||||
"TPTR",
|
||||
"UDIR",
|
||||
"CSSH",
|
||||
"FILL",
|
||||
};
|
||||
|
||||
#define BtoffWord 0
|
||||
#define BtlenWord 36
|
||||
|
||||
#define WdoffSSDate 8 /* Saveset date offset (type 1, 6) */
|
||||
#define WdoffSSName 9 /* Saveset name offset (type 1, 6) */
|
||||
#define WdoffFLName 6 /* Filename offset (type 2) */
|
||||
#define WdoffFDB 134 /* FDB offset (type 2) */
|
||||
|
||||
#define WdoffFDB_CTL 01+WdoffFDB /* Control word .FBCTL */
|
||||
|
||||
#define BtoffFDB_Arc 11 /* archived */
|
||||
#define BtlenFDB_Arc 1
|
||||
|
||||
#define BtoffFDB_Inv 12 /* invisible */
|
||||
#define BtlenFDB_Inv 1
|
||||
|
||||
#define BtoffFDB_Off 13 /* offline */
|
||||
#define BtlenFDB_Off 1
|
||||
|
||||
#define WdoffFDB_PRT 04+WdoffFDB /* protection */
|
||||
#define BtoffFDB_PRT 18
|
||||
#define BtlenFDB_PRT 18
|
||||
|
||||
#define WdoffFDB_BSZ 011+WdoffFDB /* Number of bits per byte */
|
||||
#define BtoffFDB_BSZ 6
|
||||
#define BtlenFDB_BSZ 6
|
||||
|
||||
#define WdoffFDB_PGC 011+WdoffFDB /* Number of pages in the file */
|
||||
#define BtoffFDB_PGC 18
|
||||
#define BtlenFDB_PGC 18
|
||||
|
||||
#define WdoffFDB_Size 012+WdoffFDB /* Number of bytes in the file */
|
||||
|
||||
#define BtoffFDB_Size 0
|
||||
#define BtlenFDB_Size 36
|
||||
|
||||
#define WdoffFDB_Wrt 014+WdoffFDB /* Date of last write to file */
|
||||
|
||||
#define WdoffFDB_Ref 015+WdoffFDB /* read time */
|
||||
|
||||
#define WdoffFDB_PGC_A 022+WdoffFDB /* Pagecount before archive */
|
||||
|
||||
#define WdoffFDB_TP1 033+WdoffFDB /* Tape ID for archive run 1 */
|
||||
|
||||
#define WdoffFDB_SS1 034+WdoffFDB /* Saveset # for archive run 1 */
|
||||
#define BtoffFDB_SS 0
|
||||
#define BtlenFDB_SS 18
|
||||
#define WdoffFDB_TF1 034+WdoffFDB /* Tape file # for archive run 1 */
|
||||
#define BtoffFDB_TF 18
|
||||
#define BtlenFDB_TF 18
|
||||
|
||||
#define WdoffFDB_TP2 035+WdoffFDB /* Tape ID for archive run 2 */
|
||||
#define WdoffFDB_SS2 036+WdoffFDB /* Saveset # for archive run 2 */
|
||||
#define WdoffFDB_TF2 036+WdoffFDB /* Tape file # for archive run 2 */
|
||||
892
read20.c
Normal file
892
read20.c
Normal file
@ -0,0 +1,892 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#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 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 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 */
|
||||
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();
|
||||
|
||||
/*
|
||||
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, ...)
|
||||
{
|
||||
extern int errno, sys_nerr;
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
if (prterrno) {
|
||||
fprintf(stderr, ": ");
|
||||
if (errno >= 0 && errno < sys_nerr)
|
||||
perror("");
|
||||
else
|
||||
fprintf(stderr, "ERRNO = %d\n", 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); **/
|
||||
}
|
||||
|
||||
|
||||
#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;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (numbytes > maxperblock)
|
||||
ct = maxperblock;
|
||||
else
|
||||
ct = numbytes;
|
||||
|
||||
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 t;
|
||||
|
||||
if (debug > 10) printf("\nSaveset header:");
|
||||
tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum);
|
||||
ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum,
|
||||
BtlenSaveSetNum);
|
||||
getstring(block, name, WdoffSSName, 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;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
/* Special hack for bad files */
|
||||
if (textflg && bytesize != 7) {
|
||||
if (bytesize == 0 || bytesize == 36) {
|
||||
bytesize = 7;
|
||||
numbytes *= 5;
|
||||
}
|
||||
}
|
||||
if ((bytesize == 7 || bytesize == 8) && !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 (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");
|
||||
}
|
||||
|
||||
|
||||
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 '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 (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);
|
||||
|
||||
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 != 0)
|
||||
punt(1, "Oops. Read block len = %d", rc);
|
||||
}
|
||||
if (rc == 0) {
|
||||
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
|
||||
|
||||
777
t10backup.c
Normal file
777
t10backup.c
Normal file
@ -0,0 +1,777 @@
|
||||
/*
|
||||
Dump contents of Tops-10 BACKUP tapes, or tape images.
|
||||
|
||||
Original author unknown.
|
||||
Modified by Eric Smith to use John Wilson's tapeio library.
|
||||
|
||||
Copyright 1999 Eric Smith
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as published
|
||||
by the Free Software Foundation. Note that permission is not granted
|
||||
to redistribute this program under the terms of any other version of the
|
||||
General Public License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "t10backup.h"
|
||||
#include "tapeio.h"
|
||||
|
||||
#define bool long
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
#define MAX_FILE_SPEC 1024
|
||||
|
||||
#define RAWSIZE (5*(32+512))
|
||||
|
||||
#define endof(s) (strchr(s, (char) 0))
|
||||
|
||||
char *progname;
|
||||
|
||||
|
||||
tape_handle_t tape; /* Source "tape". */
|
||||
|
||||
|
||||
bool eightbit = false; /* Status of -8 (eight-bit) flag. */
|
||||
bool buildtree = false; /* Status of -d (build trees) flag. */
|
||||
bool interchange = false; /* Status of -i (interchange) flag. */
|
||||
long verbose = 0; /* Status of -v (verbose) flag. */
|
||||
|
||||
|
||||
char* argfiles [MAX_FILE_SPEC]; /* File spec's to extract. */
|
||||
long argcount; /* Number of them. */
|
||||
|
||||
|
||||
unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */
|
||||
|
||||
long headlh[32], headrh[32]; /* Header block from tape. */
|
||||
long datalh[512], datarh[512]; /* Data block from tape. */
|
||||
|
||||
long prevSEQ; /* SEQ number of previous block. */
|
||||
long currentfilenumber;
|
||||
|
||||
long defercount; /* Count of defered output bytes. */
|
||||
|
||||
bool extracting;
|
||||
FILE* destination;
|
||||
|
||||
/* Tape information: */
|
||||
|
||||
char systemname[100];
|
||||
char savesetname[100];
|
||||
|
||||
/* File information: */
|
||||
|
||||
long a_bsiz; /* For now. */
|
||||
long a_alls;
|
||||
long a_mode;
|
||||
long a_leng;
|
||||
|
||||
char filedev[100]; /* Device: */
|
||||
char filedir[100]; /* [ufd] */
|
||||
char filename[100]; /* file name. */
|
||||
char fileext[100]; /* extension. */
|
||||
|
||||
char filespec[7][100]; /* [0]: device:ufd. */
|
||||
/* [1-5]: sfd's, stored directly here. */
|
||||
/* [6]: file.ext */
|
||||
|
||||
char cname[100]; /* Canonical name. */
|
||||
|
||||
|
||||
void print_usage (FILE *f)
|
||||
{
|
||||
fprintf (f, "Usage: %s [options] [filespecs...]\n", progname);
|
||||
fprintf (f, "Options:\n"
|
||||
" -t list directory\n"
|
||||
" -x extract files\n"
|
||||
" -f <file> read input from file\n"
|
||||
" /dev/* tape drive\n"
|
||||
" host:file rmt server\n"
|
||||
" file tape image file\n"
|
||||
" - tape image from stdin\n"
|
||||
" -s <range> use save sets in range\n"
|
||||
" e.g. '-s 2' for only save set 2\n"
|
||||
" '-s 2,' for save set 2 through EOT\n"
|
||||
" '-s 3,5' for save sets 3 through 5\n"
|
||||
" -v verbose\n"
|
||||
" -vv very verbose\n"
|
||||
" -8 eight bit mode\n"
|
||||
" -i interchange mode\n"
|
||||
" -d build directory tree\n");
|
||||
}
|
||||
|
||||
|
||||
void fatal (int retval, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (fmt)
|
||||
{
|
||||
fprintf (stderr, "%s: ", progname);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
if (retval == 1)
|
||||
print_usage (stderr);
|
||||
|
||||
exit (retval);
|
||||
}
|
||||
|
||||
|
||||
void warning (char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf (stderr, "%s: ", progname);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
|
||||
/* unpackheader unpacks the header block from the raw stream. */
|
||||
|
||||
void unpackheader (void)
|
||||
{
|
||||
unsigned char* rawptr;
|
||||
long i, left, right;
|
||||
unsigned char c;
|
||||
|
||||
rawptr = & rawdata [0];
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
left = * (rawptr++) << 10;
|
||||
left |= * (rawptr++) << 2;
|
||||
left |= (c = * (rawptr++)) >> 6;
|
||||
right = (c & 077) << 12;
|
||||
right |= * (rawptr++) << 4;
|
||||
right |= * (rawptr++) & 017;
|
||||
headlh [i] = left;
|
||||
headrh [i] = right;
|
||||
if (verbose > 1)
|
||||
{
|
||||
printf("\n%li l=%ld, r=%ld", i, left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* unpackdata unpacks the data block from the raw stream. */
|
||||
|
||||
void unpackdata (void)
|
||||
{
|
||||
unsigned char* rawptr;
|
||||
long i, left, right;
|
||||
unsigned char c;
|
||||
|
||||
rawptr = & rawdata [32*5];
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
{
|
||||
left = * (rawptr++) << 10;
|
||||
left |= * (rawptr++) << 2;
|
||||
left |= (c = * (rawptr++)) >> 6;
|
||||
right = (c & 077) << 12;
|
||||
right |= * (rawptr++) << 4;
|
||||
right |= * (rawptr++) & 017;
|
||||
datalh [i] = left;
|
||||
datarh [i] = right;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* pars_5chars reads five ASCII chars from a machine word. */
|
||||
|
||||
void pars_5chars (long index, char *store)
|
||||
{
|
||||
long l, r;
|
||||
|
||||
l = datalh [index];
|
||||
r = datarh [index];
|
||||
|
||||
store [0] = (0177 & (l >> 11));
|
||||
store [1] = (0177 & (l >> 4));
|
||||
store [2] = (0177 & ((l << 3) | ((r >> 15) & 017)));
|
||||
store [3] = (0177 & (r >> 8));
|
||||
store [4] = (0177 & (r >> 1));
|
||||
}
|
||||
|
||||
|
||||
/* pars_asciz stores asciz text from data */
|
||||
|
||||
void pars_asciz (long index, char *store)
|
||||
{
|
||||
long words;
|
||||
|
||||
words = datarh [index++];
|
||||
while ((words--) > 0)
|
||||
{
|
||||
pars_5chars (index++, store);
|
||||
store += 5;
|
||||
}
|
||||
* store = (char) 0;
|
||||
}
|
||||
|
||||
|
||||
/* pars_o_name parses an o$name block from data. */
|
||||
|
||||
void pars_o_name (long index)
|
||||
{
|
||||
long lastw;
|
||||
|
||||
lastw = index + datarh [index];
|
||||
++index;
|
||||
while (index < lastw)
|
||||
{
|
||||
switch (datalh [index])
|
||||
{
|
||||
case 0: index = lastw; break;
|
||||
case 1: pars_asciz (index, filedev); break;
|
||||
case 2: pars_asciz (index, filename); break;
|
||||
case 3: pars_asciz (index, fileext); break;
|
||||
case 32: pars_asciz (index, filedir); break;
|
||||
case 33: pars_asciz (index, filespec [1]); break;
|
||||
case 34: pars_asciz (index, filespec [2]); break;
|
||||
case 35: pars_asciz (index, filespec [3]); break;
|
||||
case 36: pars_asciz (index, filespec [4]); break;
|
||||
case 37: pars_asciz (index, filespec [5]); break;
|
||||
}
|
||||
index += datarh [index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pars_o_attr (long index)
|
||||
{
|
||||
/* parse off file attribute block */
|
||||
++index;
|
||||
a_bsiz = datarh [index + A_BSIZ]; /* for now... */
|
||||
a_alls = datarh [index + A_ALLS]; /* for now... */
|
||||
a_mode = datarh [index + A_MODE]; /* for now... */
|
||||
a_leng = datarh [index + A_LENG]; /* for now... */
|
||||
}
|
||||
|
||||
|
||||
void pars_o_dirt (long index)
|
||||
{
|
||||
/* parse off directory attribute block */
|
||||
}
|
||||
|
||||
|
||||
void pars_o_sysn (long index)
|
||||
{
|
||||
pars_asciz (index, systemname);
|
||||
}
|
||||
|
||||
|
||||
void pars_o_ssnm (long index)
|
||||
{
|
||||
pars_asciz (index, savesetname);
|
||||
}
|
||||
|
||||
|
||||
void zerotapeinfo (void)
|
||||
{
|
||||
systemname [0] = (char) 0;
|
||||
savesetname [0] = (char) 0;
|
||||
}
|
||||
|
||||
|
||||
void zerofileinfo (void)
|
||||
{
|
||||
filedev [0] = (char) 0;
|
||||
filedir [0] = (char) 0;
|
||||
filename [0] = (char) 0;
|
||||
fileext [0] = (char) 0;
|
||||
|
||||
filespec [0][0] = (char) 0;
|
||||
filespec [1][0] = (char) 0;
|
||||
filespec [2][0] = (char) 0;
|
||||
filespec [3][0] = (char) 0;
|
||||
filespec [4][0] = (char) 0;
|
||||
filespec [5][0] = (char) 0;
|
||||
filespec [6][0] = (char) 0;
|
||||
|
||||
cname [0] = (char) 0;
|
||||
}
|
||||
|
||||
|
||||
/* unpackinfo picks non-data information from data block. */
|
||||
|
||||
void unpackinfo (void)
|
||||
{
|
||||
long index;
|
||||
|
||||
unpackdata ();
|
||||
|
||||
index = 0;
|
||||
while (index < headrh [G_LND])
|
||||
{
|
||||
switch (datalh [index])
|
||||
{
|
||||
case 1: pars_o_name (index); break;
|
||||
case 2: pars_o_attr (index); break;
|
||||
case 3: pars_o_dirt (index); break;
|
||||
case 4: pars_o_sysn (index); break;
|
||||
case 5: pars_o_ssnm (index); break;
|
||||
}
|
||||
index += datarh [index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printtapeinfo (void)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
if (* savesetname != (char) 0)
|
||||
printf ("Saveset name: %s\n", savesetname);
|
||||
if (* systemname != (char) 0)
|
||||
printf ("Written on: %s\n", systemname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void downcase (char *s)
|
||||
{
|
||||
while (*s != (char) 0) {
|
||||
if (isupper(*s)) *s = tolower(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void buildfilenames (void)
|
||||
{
|
||||
long i;
|
||||
|
||||
if (* filedev != (char) 0)
|
||||
sprintf (filespec [0], "%s:%s", filedev, filedir);
|
||||
else
|
||||
sprintf (filespec [0], "%s", filedir);
|
||||
|
||||
sprintf (filespec [6], "%s.%s", filename, fileext);
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
downcase (filespec [i]);
|
||||
|
||||
sprintf (cname, "%s", filespec [0]);
|
||||
for (i = 1; i < 6; i++)
|
||||
{
|
||||
if (* filespec [i] != (char) 0)
|
||||
sprintf (endof (cname), ".%s", filespec [i]);
|
||||
}
|
||||
if (* cname != (char) 0)
|
||||
sprintf (endof (cname), "..%s", filespec [6]);
|
||||
else
|
||||
sprintf (cname, "%s", filespec [6]);
|
||||
}
|
||||
|
||||
|
||||
void printfileinfo (void)
|
||||
{
|
||||
buildfilenames ();
|
||||
printf ("%3ld %s", currentfilenumber, cname);
|
||||
if (verbose)
|
||||
{
|
||||
printf (" (%ld) alloc:%ld, mode:%lo, len:%ld", a_bsiz, a_alls, a_mode, a_leng);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
|
||||
/* readblock reads one logical block from the input stream. */
|
||||
/* The header is unpacked into head{l,r}; the data is not. */
|
||||
|
||||
int readblock (void)
|
||||
{
|
||||
long i;
|
||||
i = getrec (tape, rawdata, RAWSIZE);
|
||||
if (i == 0)
|
||||
return (0);
|
||||
if (i != RAWSIZE)
|
||||
{
|
||||
fprintf (stderr, "record length %ld, expected %d\n", i, RAWSIZE);
|
||||
while (i++ < RAWSIZE) rawdata [i] = (char) 0;
|
||||
}
|
||||
unpackheader ();
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/* Disk file output routines: */
|
||||
|
||||
void WriteBlock (void)
|
||||
{
|
||||
char buffer [5*512];
|
||||
long bufpos, index;
|
||||
|
||||
unpackdata ();
|
||||
|
||||
for (index = headrh [G_LND], bufpos = 0;
|
||||
index < (headrh [G_LND] + headrh [G_SIZE]); index++)
|
||||
{
|
||||
pars_5chars (index, &buffer [bufpos]);
|
||||
bufpos += 5;
|
||||
}
|
||||
|
||||
if (headlh [G_FLAGS] & GF_EOF)
|
||||
{
|
||||
for (index = 1; index < (eightbit ? 4 : 5); index++)
|
||||
{
|
||||
if (buffer[bufpos - 1] == (char) 0)
|
||||
bufpos--;
|
||||
}
|
||||
}
|
||||
|
||||
(void) fwrite (buffer, sizeof (char), bufpos, destination);
|
||||
}
|
||||
|
||||
|
||||
/* OpenOutput opens the output file, according to -d and -i flags. */
|
||||
|
||||
bool OpenOutput (void)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char oname [100];
|
||||
long i;
|
||||
|
||||
defercount = 0;
|
||||
|
||||
if (interchange)
|
||||
destination = fopen (filespec [6], "w");
|
||||
else if (! buildtree)
|
||||
destination = fopen(cname, "w");
|
||||
else
|
||||
{
|
||||
for (i = 0, oname [0] = (char) 0; i < 6; i++)
|
||||
{
|
||||
if (* filespec [i] == (char) 0)
|
||||
break;
|
||||
sprintf (endof (oname), "%s", filespec [i]);
|
||||
if (stat (oname, & statbuf) != 0)
|
||||
{
|
||||
if (mkdir (oname, 0777) != 0)
|
||||
{
|
||||
warning ("cannot create %s/\n", oname);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
sprintf (endof (oname), "/");
|
||||
}
|
||||
sprintf (endof (oname), "%s", filespec [6]);
|
||||
destination = fopen (oname, "w");
|
||||
}
|
||||
|
||||
return (destination != NULL);
|
||||
}
|
||||
|
||||
void CloseOutput (void)
|
||||
{
|
||||
/* Close output file after us. */
|
||||
}
|
||||
|
||||
/* Argmatch checks if the current file matches the given argument: */
|
||||
|
||||
bool argmatch (char *arg)
|
||||
{
|
||||
long target;
|
||||
char* f;
|
||||
char* p;
|
||||
char* s;
|
||||
|
||||
if (*arg == '#')
|
||||
{
|
||||
(void) sscanf (arg, "#%ld", & target);
|
||||
return (target == currentfilenumber);
|
||||
}
|
||||
|
||||
if (*arg == '*')
|
||||
return (1);
|
||||
|
||||
for (f = cname; *f != (char) 0; f++)
|
||||
{
|
||||
for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++)
|
||||
;
|
||||
if (*s == (char) 0)
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* doextract performs the job of "backup -x ..." */
|
||||
|
||||
int doextract (void)
|
||||
{
|
||||
int got_blocks = 0;
|
||||
long i;
|
||||
|
||||
currentfilenumber = 0;
|
||||
extracting = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (! readblock ())
|
||||
return (got_blocks);
|
||||
|
||||
got_blocks = 1;
|
||||
|
||||
if (headrh [G_SEQ] == prevSEQ)
|
||||
continue;
|
||||
|
||||
if (headrh [G_TYPE] == T_FILE)
|
||||
{
|
||||
if (headlh [G_FLAGS] & GF_SOF)
|
||||
{
|
||||
currentfilenumber++;
|
||||
zerofileinfo ();
|
||||
unpackinfo ();
|
||||
buildfilenames ();
|
||||
for (i = 0; i < argcount; i++)
|
||||
{
|
||||
if (argmatch (argfiles [i]))
|
||||
{
|
||||
if (*argfiles[i] == '#')
|
||||
{
|
||||
/* Maybe do a pure shift here? */
|
||||
argfiles [i] = argfiles [--argcount];
|
||||
}
|
||||
extracting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (extracting)
|
||||
{
|
||||
if (OpenOutput ())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
printf ("Extracting %s", cname);
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning ("can't open %s for output\n", cname);
|
||||
extracting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extracting)
|
||||
{
|
||||
WriteBlock ();
|
||||
if (headlh [G_FLAGS] & GF_EOF)
|
||||
{
|
||||
(void) fclose (destination);
|
||||
extracting = false;
|
||||
if (verbose)
|
||||
printf ("\n");
|
||||
if (argcount == 0)
|
||||
{
|
||||
skipfile (tape, 1);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prevSEQ = headrh[G_SEQ];
|
||||
}
|
||||
}
|
||||
|
||||
/* dodirectory performs the job of "backup -t ..." */
|
||||
|
||||
int dodirectory (void)
|
||||
{
|
||||
int got_blocks = 0;
|
||||
currentfilenumber = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (! readblock ())
|
||||
return (got_blocks);
|
||||
|
||||
got_blocks = 1;
|
||||
|
||||
if (headrh [G_SEQ] == prevSEQ)
|
||||
continue;
|
||||
|
||||
if (headrh [G_TYPE] == T_BEGIN)
|
||||
{
|
||||
zerotapeinfo ();
|
||||
unpackinfo ();
|
||||
printtapeinfo ();
|
||||
}
|
||||
if (headrh [G_TYPE] == T_FILE)
|
||||
{
|
||||
if (headlh [G_FLAGS] & GF_SOF)
|
||||
{
|
||||
++currentfilenumber;
|
||||
zerofileinfo();
|
||||
unpackinfo();
|
||||
printfileinfo();
|
||||
}
|
||||
}
|
||||
prevSEQ = headrh [G_SEQ];
|
||||
}
|
||||
}
|
||||
|
||||
/* command decoder and dispatcher */
|
||||
|
||||
void checkarg (char *arg)
|
||||
{
|
||||
long i;
|
||||
char c;
|
||||
|
||||
if (*arg == '#')
|
||||
{
|
||||
if (sscanf (arg, "#%ld%c", & i, & c) != 1)
|
||||
fatal (1, "bad argument: '%s'\n", arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void parse_range (char *s, int *first, int *last)
|
||||
{
|
||||
char *s2 = s;
|
||||
|
||||
(* first) = (* last) = strtol (s, & s, 0);
|
||||
if (! s)
|
||||
return;
|
||||
if (*(s++) != ',')
|
||||
goto badrange;
|
||||
if (! *s)
|
||||
{
|
||||
(* last) = INT_MAX;
|
||||
return;
|
||||
}
|
||||
(* last) = strtol (s, & s, 0);
|
||||
if (! s)
|
||||
goto badrange;
|
||||
if ((* first) > (* last))
|
||||
goto badrange;
|
||||
return;
|
||||
|
||||
badrange:
|
||||
fatal (1, "bad range syntax: '%s'\n", s2);
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
long i;
|
||||
char action = '\0';
|
||||
char* inputname = NULL;
|
||||
char *arg;
|
||||
int first_save_set = 0;
|
||||
int last_save_set = 0;
|
||||
int current_save_set = 0;
|
||||
|
||||
progname = argv [0];
|
||||
|
||||
argcount = 0;
|
||||
|
||||
while (--argc > 0)
|
||||
{
|
||||
++argv;
|
||||
arg = argv [0];
|
||||
if (arg [0] == '-')
|
||||
{
|
||||
while (*++arg)
|
||||
switch (*arg)
|
||||
{
|
||||
case '8':
|
||||
eightbit = true; break;
|
||||
case 'd':
|
||||
buildtree = true; break;
|
||||
case 'f':
|
||||
if (--argc < 0)
|
||||
fatal (1, "input file name missing\n");
|
||||
inputname = (++argv) [0];
|
||||
break;
|
||||
case 'i':
|
||||
interchange = true; break;
|
||||
case 's':
|
||||
if ((--argc < 0) || ((++argv)[0][0] == '-'))
|
||||
fatal (1, "file skip count missing\n");
|
||||
parse_range (argv [0], & first_save_set, & last_save_set);
|
||||
/*
|
||||
fprintf (stderr, "save set range %d..%d\n",
|
||||
first_save_set, last_save_set);
|
||||
*/
|
||||
break;
|
||||
case 't':
|
||||
case 'x':
|
||||
action = *arg; break;
|
||||
case 'v':
|
||||
verbose++; break;
|
||||
default:
|
||||
fatal (1, "bad option %c\n", arg [1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argcount >= MAX_FILE_SPEC)
|
||||
fatal (1, "too many file specs\n");
|
||||
argfiles [argcount++] = argv [0];
|
||||
}
|
||||
}
|
||||
|
||||
if (! action)
|
||||
fatal (1, "either -t or -x must be specified\n");
|
||||
|
||||
for (i = 0; i < argcount; i++)
|
||||
checkarg (argfiles[i]);
|
||||
|
||||
if (inputname == NULL)
|
||||
fatal (1, "no input file given\n");
|
||||
|
||||
tape = opentape (inputname, 0, 0);
|
||||
|
||||
if (! tape)
|
||||
fatal (1, "can't open %s for input\n", inputname);
|
||||
|
||||
if (first_save_set > 0)
|
||||
{
|
||||
skipfile (tape, first_save_set);
|
||||
current_save_set = first_save_set;
|
||||
}
|
||||
|
||||
while (current_save_set <= last_save_set)
|
||||
{
|
||||
int ok = 0;
|
||||
switch (action)
|
||||
{
|
||||
case 't': ok = dodirectory (); break;
|
||||
case 'x': ok = doextract (); break;
|
||||
}
|
||||
if (! ok)
|
||||
break;
|
||||
current_save_set++;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
80
t10backup.h
Normal file
80
t10backup.h
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
/* Record types: */
|
||||
|
||||
#define T_LABEL 1 /* Label. */
|
||||
#define T_BEGIN 2 /* Start of SaveSet. */
|
||||
#define T_END 3 /* End of SaveSet. */
|
||||
#define T_FILE 4 /* File data. */
|
||||
#define T_UFD 5 /* UFD data. */
|
||||
#define T_EOV 6 /* End of volume. */
|
||||
#define T_COMM 7 /* Comment. */
|
||||
#define T_CONT 8 /* Continuation. */
|
||||
|
||||
/* Offsets into header block: */
|
||||
|
||||
#define G_TYPE 0 /* Record type. */
|
||||
#define G_SEQ 1 /* Sequence #. */
|
||||
#define G_RTNM 2 /* Relative tape #. */
|
||||
#define G_FLAGS 3 /* Flags: */
|
||||
#define GF_EOF 0400000 /* End of file. */
|
||||
#define GF_RPT 0200000 /* Repeat of last record. */
|
||||
#define GF_NCH 0100000 /* Ignore checksum. */
|
||||
#define GF_SOF 0040000 /* Start of file. */
|
||||
#define G_CHECK 4 /* Checksum. */
|
||||
#define G_SIZE 5 /* Size of data in this block. */
|
||||
#define G_LND 6 /* Length of non-data block. */
|
||||
|
||||
/* Non-data block types: */
|
||||
|
||||
#define O_NAME 1 /* File name. */
|
||||
#define O_ATTR 2 /* File attributes. */
|
||||
#define O_DIRECT 3 /* Directory attributes. */
|
||||
#define O_SYSNAME 4 /* System name block. */
|
||||
#define O_SAVESET 5 /* SaveSet name block. */
|
||||
|
||||
/* Offsets in attribute block: */
|
||||
|
||||
#define A_FHLN 0 /* header length word */
|
||||
#define A_FLGS 1 /* flags */
|
||||
#define A_WRIT 2 /* creation date/time */
|
||||
#define A_ALLS 3 /* allocated size */
|
||||
#define A_MODE 4 /* mode */
|
||||
#define A_LENG 5 /* length */
|
||||
#define A_BSIZ 6 /* byte size */
|
||||
#define A_VERS 7 /* version */
|
||||
#define A_PROT 8 /* protection */
|
||||
#define A_ACCT 9 /* byte pointer account string */
|
||||
#define A_NOTE 10 /* byte pointer to anonotation string */
|
||||
#define A_CRET 11 /* creation date/time of this generation */
|
||||
#define A_REDT 12 /* last read date/time of this generation */
|
||||
#define A_MODT 13 /* monitor set last write date/time */
|
||||
#define A_ESTS 14 /* estimated size in words */
|
||||
#define A_RADR 15 /* requested disk address */
|
||||
#define A_FSIZ 16 /* maximum file size in words */
|
||||
#define A_MUSR 17 /* byte ptr to id of last modifier */
|
||||
#define A_CUSR 18 /* byte ptr to id of creator */
|
||||
#define A_BKID 19 /* byte ptr to save set of previous backup */
|
||||
#define A_BKDT 20 /* date/time of last backup */
|
||||
#define A_NGRT 21 /* number of generations to retain */
|
||||
#define A_NRDS 22 /* nbr opens for read this generation */
|
||||
#define A_NWRT 23 /* nbr opens for write this generation */
|
||||
#define A_USRW 24 /* user word */
|
||||
#define A_PCAW 25 /* privileged customer word */
|
||||
#define A_FTYP 26 /* file type and flags */
|
||||
#define A_FBSZ 27 /* byte sizes */
|
||||
#define A_FRSZ 28 /* record and block sizes */
|
||||
#define A_FFFB 29 /* application/customer word */
|
||||
|
||||
/* T_BEGIN, T_END & T_CONT header offsets: */
|
||||
|
||||
#define S_DATE 12
|
||||
#define S_FORMAT 13
|
||||
#define S_BVER 14
|
||||
#define S_MONTYP 15
|
||||
#define S_SVER 16
|
||||
#define S_APR 17
|
||||
#define S_DEVICE 18
|
||||
#define S_MTCHAR 19
|
||||
#define S_REELID 20
|
||||
#define S_LTYPE 21
|
||||
|
||||
@ -26,10 +26,12 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "tapeio.h"
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "stdarg.h"
|
||||
|
||||
#include "tapeio.h"
|
||||
|
||||
#define MAX_REC_LEN 32768
|
||||
|
||||
char *progname;
|
||||
@ -71,8 +73,8 @@ int main (int argc, char *argv[])
|
||||
int verbose = 0;
|
||||
char *srcfn = NULL;
|
||||
char *destfn = NULL;
|
||||
tape_handle src = NULL;
|
||||
tape_handle dest = NULL;
|
||||
tape_handle_t src = NULL;
|
||||
tape_handle_t dest = NULL;
|
||||
|
||||
progname = argv [0];
|
||||
|
||||
|
||||
239
tapedump.c
239
tapedump.c
@ -1,13 +1,6 @@
|
||||
/*
|
||||
tapedump
|
||||
|
||||
This program uses a modified version of John Wilson's tapeio library to
|
||||
copy tapes between any of the following:
|
||||
|
||||
local tape drives - pathname starting with /dev/
|
||||
remote tape drives (rmt) - pathname containing a colon
|
||||
Wilson-format tape image files - any other pathname
|
||||
|
||||
Copyright 1999, 2000 Eric Smith
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -26,12 +19,19 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "tapeio.h"
|
||||
#include "stdio.h"
|
||||
#include "stdarg.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
#include "tapeio.h"
|
||||
|
||||
#define MAX_REC_LEN 32768
|
||||
|
||||
|
||||
typedef unsigned int u32; /* non-portable!!! */
|
||||
typedef unsigned char uchar;
|
||||
|
||||
|
||||
char *progname;
|
||||
|
||||
void print_usage (FILE *f)
|
||||
@ -60,9 +60,9 @@ void fatal (int retval, char *fmt, ...)
|
||||
|
||||
#define BYTES_PER_LINE 16
|
||||
|
||||
void dump (FILE *f, char *buf, int len)
|
||||
void dump (FILE *f, uchar *buf, u32 len)
|
||||
{
|
||||
int i, j;
|
||||
u32 i, j;
|
||||
|
||||
for (i = 0; i < len; i += BYTES_PER_LINE)
|
||||
{
|
||||
@ -75,9 +75,9 @@ void dump (FILE *f, char *buf, int len)
|
||||
for (j = 0; j < BYTES_PER_LINE; j++)
|
||||
if ((i + j) < len)
|
||||
{
|
||||
char c = buf [i+j] & 0x7f;
|
||||
if ((buf [i+j] >= ' ') && (buf [i+j] <= '~'))
|
||||
fprintf (f, "%c", buf [i+j]);
|
||||
uchar c = buf [i+j] & 0x7f;
|
||||
if ((c >= ' ') && (c <= '~'))
|
||||
fprintf (f, "%c", c);
|
||||
else
|
||||
fprintf (f, ".");
|
||||
}
|
||||
@ -92,36 +92,202 @@ void dump (FILE *f, char *buf, int len)
|
||||
|
||||
#define MAX_FN_LEN 6
|
||||
|
||||
void dump_hp_2000_file_header (FILE *f, char *buf)
|
||||
u32 get16 (uchar *buf, u32 offset)
|
||||
{
|
||||
unsigned long uid;
|
||||
return ((buf [offset] & 0xff) << 8) | (buf [offset+1] & 0xff);
|
||||
}
|
||||
|
||||
char * file_flag_names [16] =
|
||||
{
|
||||
"unrestricted",
|
||||
"protected",
|
||||
"locked",
|
||||
"private",
|
||||
"unused",
|
||||
"unused",
|
||||
"unused",
|
||||
"unused",
|
||||
"unused",
|
||||
"unused",
|
||||
"unused",
|
||||
"fcp",
|
||||
"mwa",
|
||||
"pfa",
|
||||
"output",
|
||||
"input"
|
||||
};
|
||||
|
||||
void dump_hp_2000_directory_entry (FILE *f, uchar *buf)
|
||||
{
|
||||
u32 uid;
|
||||
u32 flags;
|
||||
char uids [6];
|
||||
char filename [MAX_FN_LEN + 1];
|
||||
int i;
|
||||
uid = ((buf [0] & 0xff) << 8) | (buf [1] & 0xff);
|
||||
|
||||
uid = get16 (buf, 0);
|
||||
if (uid == 0xffff)
|
||||
{
|
||||
fprintf (f, "End of directory\n");
|
||||
return;
|
||||
}
|
||||
uids [0] = (uid / 1024) + '@';
|
||||
sprintf (& uids [1], "%03d", uid & 0x3ff);
|
||||
for (i = 0; i < MAX_FN_LEN; i++)
|
||||
filename [i] = buf [i+2] & 0x7f;
|
||||
filename [sizeof (filename) - 1] = '\0';
|
||||
fprintf (f, " id %s filename '%s'\n", uids, filename);
|
||||
fprintf (f, "ID %s filename '%s'", uids, filename);
|
||||
if (buf [2] & 0x80)
|
||||
fprintf (f, " ASCII");
|
||||
if (buf [4] & 0x80)
|
||||
fprintf (f, " file");
|
||||
if (buf [2] & 0x80)
|
||||
fprintf (f, " semi-compiled");
|
||||
flags = get16 (buf, 14);
|
||||
for (i = 15; i >= 0; i--)
|
||||
{
|
||||
if (flags & (1 << i))
|
||||
fprintf (f, " %s", file_flag_names [i]);
|
||||
}
|
||||
fprintf (f, "\n");
|
||||
}
|
||||
|
||||
void dump_hp_2000_hibernate_file_header (FILE *f, uchar *buf, u32 len)
|
||||
{
|
||||
if (len < 24)
|
||||
{
|
||||
fprintf (f, "file header too short for directory entry\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dump_hp_2000_directory_entry (f, buf);
|
||||
}
|
||||
|
||||
void dump_hp_2000_hibernate (FILE *f, u32 file, u32 record, uchar *buf, u32 len)
|
||||
{
|
||||
if ((file > 0) && (record == 0))
|
||||
dump_hp_2000_hibernate_file_header (f, buf, len);
|
||||
}
|
||||
|
||||
|
||||
static int mcp_file_type = 0;
|
||||
|
||||
void dump_hp_2000_mcp_file_header (FILE *f, uchar *buf, u32 len)
|
||||
{
|
||||
u32 checksum = 0;
|
||||
u32 info;
|
||||
int i;
|
||||
|
||||
if (len != 10)
|
||||
{
|
||||
fprintf (f, "wrong MCP file header length\n");
|
||||
return;
|
||||
}
|
||||
if ((get16 (buf, 0) != 01000) || (get16 (buf, 2) != 02001))
|
||||
{
|
||||
fprintf (f, "wrong MCP file header magic numbers\n");
|
||||
return;
|
||||
}
|
||||
fprintf (f, "MCP file ID word %d ", get16 (buf, 4));
|
||||
info = get16 (buf, 6);
|
||||
switch (info)
|
||||
{
|
||||
case 0x0000:
|
||||
fprintf (f, "abs");
|
||||
break;
|
||||
case 0x8000:
|
||||
fprintf (f, "rel");
|
||||
break;
|
||||
default:
|
||||
fprintf (f, "info word %d", info);
|
||||
break;
|
||||
}
|
||||
mcp_file_type = info;
|
||||
for (i = 2; i < 8; i += 2)
|
||||
checksum += get16 (buf, i);
|
||||
checksum &= 0xffff;
|
||||
if (checksum != get16 (buf, 8))
|
||||
fprintf (f, ", bad MCP file header checksum");
|
||||
fprintf (f, "\n");
|
||||
}
|
||||
|
||||
void dump_hp_2000_abs_record (FILE *f, uchar *buf, u32 len)
|
||||
{
|
||||
u32 l;
|
||||
u32 checksum = 0;
|
||||
u32 tape_checksum;
|
||||
u32 i;
|
||||
|
||||
l= buf [0] * 2 + 6;
|
||||
if ((l != len) || (buf [1] != 0))
|
||||
{
|
||||
fprintf (f, "first word of record is not length (expected %d)\n", l);
|
||||
return;
|
||||
}
|
||||
for (i = 2; i < len - 2; i += 2)
|
||||
checksum += get16 (buf, i);
|
||||
checksum &= 0xffff;
|
||||
tape_checksum = get16 (buf, len - 2);
|
||||
if (checksum != tape_checksum)
|
||||
fprintf (f, "computed checksum %04x, tape checksum %04x\n", checksum,
|
||||
tape_checksum);
|
||||
}
|
||||
|
||||
void dump_hp_2000_rel_record (FILE *f, uchar *buf, u32 len)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l= buf [0] * 2;
|
||||
if ((l != len) || (buf [1] != 0))
|
||||
{
|
||||
fprintf (f, "first word of record is not length (expected %d)\n", l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_hp_2000_mcp (FILE *f, u32 file, u32 record, uchar *buf, u32 len)
|
||||
{
|
||||
if ((len == 10) && (buf [0] == 0x02) && (buf [1] == 0x00) &&
|
||||
(buf [2] == 0x04) && (buf [3] == 0x01))
|
||||
{
|
||||
dump_hp_2000_mcp_file_header (f, buf, len);
|
||||
}
|
||||
else switch (mcp_file_type)
|
||||
{
|
||||
case 0x0000:
|
||||
dump_hp_2000_abs_record (f, buf, len);
|
||||
break;
|
||||
case 0x8000:
|
||||
dump_hp_2000_rel_record (f, buf, len);
|
||||
break;
|
||||
default:
|
||||
fprintf (f, "unknown file type\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HP_2000_SUPPORT */
|
||||
|
||||
|
||||
typedef enum {
|
||||
generic,
|
||||
#ifdef HP_2000_SUPPORT
|
||||
hp_2000_hibernate,
|
||||
hp_2000_mcp,
|
||||
#endif /* HP_2000_SUPPORT */
|
||||
} t_tape_type;
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int file = 0;
|
||||
int record = 0;
|
||||
unsigned long filebytes = 0;
|
||||
unsigned long tapebytes = 0;
|
||||
int len;
|
||||
u32 file = 0;
|
||||
u32 record = 0;
|
||||
u32 filebytes = 0;
|
||||
u32 tapebytes = 0;
|
||||
u32 len;
|
||||
char *srcfn = NULL;
|
||||
tape_handle src = NULL;
|
||||
char *buf;
|
||||
#ifdef HP_2000_SUPPORT
|
||||
int hp_2000_hibernate = 0;
|
||||
#endif /* HP_2000_SUPPORT */
|
||||
tape_handle_t src = NULL;
|
||||
uchar *buf;
|
||||
t_tape_type tape_type = generic;
|
||||
|
||||
progname = argv [0];
|
||||
|
||||
@ -131,7 +297,9 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
#ifdef HP_2000_SUPPORT
|
||||
if (argv [0][1] == 'h')
|
||||
hp_2000_hibernate++;
|
||||
tape_type = hp_2000_hibernate;
|
||||
else if (argv [0][1] == 'm')
|
||||
tape_type = hp_2000_mcp;
|
||||
else
|
||||
#endif /* HP_2000_SUPPORT */
|
||||
fatal (1, "unrecognized option '%s'\n", argv [0]);
|
||||
@ -145,7 +313,7 @@ int main (int argc, char *argv[])
|
||||
if (! srcfn)
|
||||
fatal (1, NULL);
|
||||
|
||||
buf = (char *) malloc (MAX_REC_LEN);
|
||||
buf = (uchar *) malloc (MAX_REC_LEN);
|
||||
if (! buf)
|
||||
fatal (2, "can't allocate buffer\n");
|
||||
|
||||
@ -170,10 +338,19 @@ int main (int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
printf ("file %d record %d: length %d\n", file, record, len);
|
||||
switch (tape_type)
|
||||
{
|
||||
#ifdef HP_2000_SUPPORT
|
||||
if (hp_2000_hibernate && (file > 0) && (record == 0) && (len >= 24))
|
||||
dump_hp_2000_file_header (stdout, buf);
|
||||
case hp_2000_hibernate:
|
||||
dump_hp_2000_hibernate (stdout, file, record, buf, len);
|
||||
break;
|
||||
case hp_2000_mcp:
|
||||
dump_hp_2000_mcp (stdout, file, record, buf, len);
|
||||
break;
|
||||
#endif /* HP_2000_SUPPORT */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dump (stdout, buf, len);
|
||||
fflush (stdout);
|
||||
filebytes += len;
|
||||
|
||||
28
tapeio.c
28
tapeio.c
@ -138,7 +138,7 @@ static void doread (int handle, char *buf, int len)
|
||||
|
||||
|
||||
/* get response from "rmt" server */
|
||||
static int response (tape_handle mtape)
|
||||
static int response (tape_handle_t mtape)
|
||||
{
|
||||
char c, rc;
|
||||
int n;
|
||||
@ -177,7 +177,7 @@ static int response (tape_handle mtape)
|
||||
|
||||
|
||||
/* send ioctl() command to local or remote tape drive */
|
||||
static int doioctl (tape_handle mtape, struct mtop *op)
|
||||
static int doioctl (tape_handle_t mtape, struct mtop *op)
|
||||
{
|
||||
int len;
|
||||
|
||||
@ -195,15 +195,15 @@ static int doioctl (tape_handle mtape, struct mtop *op)
|
||||
|
||||
/* open the tape drive (or whatever) */
|
||||
/* "create" =1 to create if file, "writable" =1 to open with write access */
|
||||
tape_handle opentape (char *name, int create, int writable)
|
||||
tape_handle_t opentape (char *name, int create, int writable)
|
||||
{
|
||||
char *p, *user, *port;
|
||||
int len;
|
||||
char *host = NULL;
|
||||
|
||||
tape_handle mtape = NULL;
|
||||
tape_handle_t mtape = NULL;
|
||||
|
||||
mtape = (tape_handle) calloc (1, sizeof (struct mtape_t));
|
||||
mtape = (tape_handle_t) calloc (1, sizeof (struct mtape_t));
|
||||
if (! mtape)
|
||||
FAIL ("?can't allocate mtape struct\n");
|
||||
|
||||
@ -320,7 +320,7 @@ tape_handle opentape (char *name, int create, int writable)
|
||||
|
||||
|
||||
/* close the tape drive */
|
||||
void closetape (tape_handle mtape)
|
||||
void closetape (tape_handle_t mtape)
|
||||
{
|
||||
if (mtape->waccess)
|
||||
{ /* opened for create/append */
|
||||
@ -346,7 +346,7 @@ void closetape (tape_handle mtape)
|
||||
|
||||
|
||||
/* rewind tape */
|
||||
void posnbot (tape_handle mtape)
|
||||
void posnbot (tape_handle_t mtape)
|
||||
{
|
||||
if (mtape->tape_type == TT_IMAGE)
|
||||
{ /* image file */
|
||||
@ -368,7 +368,7 @@ void posnbot (tape_handle mtape)
|
||||
|
||||
|
||||
/* position tape at EOT (between the two tape marks) */
|
||||
void posneot (tape_handle mtape)
|
||||
void posneot (tape_handle_t mtape)
|
||||
{
|
||||
if (mtape->tape_type == TT_IMAGE)
|
||||
{ /* image file */
|
||||
@ -408,7 +408,7 @@ void posneot (tape_handle mtape)
|
||||
|
||||
|
||||
/* read a tape record, return actual length (0=tape mark) */
|
||||
int getrec (tape_handle mtape, char *buf, int len)
|
||||
int getrec (tape_handle_t mtape, char *buf, int len)
|
||||
{
|
||||
unsigned char byte [4]; /* 32 bits for length field(s) */
|
||||
unsigned long l; /* at least 32 bits */
|
||||
@ -465,7 +465,7 @@ int getrec (tape_handle mtape, char *buf, int len)
|
||||
|
||||
|
||||
/* write a tape record */
|
||||
void putrec (tape_handle mtape, char *buf, int len)
|
||||
void putrec (tape_handle_t mtape, char *buf, int len)
|
||||
{
|
||||
unsigned char l [4];
|
||||
|
||||
@ -495,7 +495,7 @@ void putrec (tape_handle mtape, char *buf, int len)
|
||||
|
||||
|
||||
/* write a tape mark */
|
||||
void tapemark (tape_handle mtape)
|
||||
void tapemark (tape_handle_t mtape)
|
||||
{
|
||||
static char zero [4] = { 0, 0, 0, 0 };
|
||||
|
||||
@ -516,7 +516,7 @@ void tapemark (tape_handle mtape)
|
||||
|
||||
|
||||
/* skip records (negative for reverse) */
|
||||
void skiprec (tape_handle mtape, int count)
|
||||
void skiprec (tape_handle_t mtape, int count)
|
||||
{
|
||||
unsigned char byte [4]; /* 32 bits for length field(s) */
|
||||
unsigned long l; /* at least 32 bits */
|
||||
@ -566,7 +566,7 @@ void skiprec (tape_handle mtape, int count)
|
||||
|
||||
/* skip forward to the next file mark, and leave the tape positioned
|
||||
after the mark */
|
||||
static void skip_to_mark (tape_handle mtape)
|
||||
static void skip_to_mark (tape_handle_t mtape)
|
||||
{
|
||||
unsigned char byte [4]; /* 32 bits for length field(s) */
|
||||
unsigned long l; /* at least 32 bits */
|
||||
@ -627,7 +627,7 @@ static void skip_to_mark (tape_handle mtape)
|
||||
|
||||
|
||||
/* skip files (negative for reverse) */
|
||||
void skipfile (tape_handle mtape, int count)
|
||||
void skipfile (tape_handle_t mtape, int count)
|
||||
{
|
||||
if (mtape->tape_type != TT_IMAGE)
|
||||
{
|
||||
|
||||
20
tapeio.h
20
tapeio.h
@ -21,33 +21,33 @@
|
||||
*/
|
||||
|
||||
|
||||
typedef struct mtape_t *tape_handle; /* opaque type */
|
||||
typedef struct mtape_t *tape_handle_t; /* opaque type */
|
||||
|
||||
|
||||
/* open a tape drive */
|
||||
tape_handle opentape (char *name, int create, int writable);
|
||||
tape_handle_t opentape (char *name, int create, int writable);
|
||||
|
||||
/* close a tape drive */
|
||||
void closetape (tape_handle h);
|
||||
void closetape (tape_handle_t h);
|
||||
|
||||
/* rewind tape */
|
||||
void posnbot (tape_handle h);
|
||||
void posnbot (tape_handle_t h);
|
||||
|
||||
/* position tape at EOT (between the two tape marks) */
|
||||
void posneot (tape_handle h);
|
||||
void posneot (tape_handle_t h);
|
||||
|
||||
/* read a tape record, return actual length (0=tape mark) */
|
||||
int getrec (tape_handle h, char *buf, int len);
|
||||
int getrec (tape_handle_t h, char *buf, int len);
|
||||
|
||||
/* write a tape record */
|
||||
void putrec (tape_handle h, char *buf, int len);
|
||||
void putrec (tape_handle_t h, char *buf, int len);
|
||||
|
||||
/* write a tape mark */
|
||||
void tapemark (tape_handle h);
|
||||
void tapemark (tape_handle_t h);
|
||||
|
||||
/* skip records (negative for reverse) */
|
||||
void skiprec (tape_handle h, int count);
|
||||
void skiprec (tape_handle_t h, int count);
|
||||
|
||||
/* skip files (negative for reverse) */
|
||||
void skipfile (tape_handle h, int count);
|
||||
void skipfile (tape_handle_t h, int count);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user