simh tools

This commit is contained in:
Bob Supnik
2003-12-28 17:18:00 -08:00
committed by Mark Pizzolato
commit abf806277d
52 changed files with 23924 additions and 0 deletions

636
extracters/backup/backup.c Normal file
View File

@@ -0,0 +1,636 @@
/* Dump contents of Tops-10 BACKUP tapes, which have been read
into a disk file. The "known good" way to do this is to use the
unix utility "dd", and a command line something like this:
dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block
the key thing is that this program expects a fixed block size of
2720 bytes. If the tape actually has some other format, this
program probably won't succeed. You can use the unix utility "tcopy"
to inspect the contents of the tape.
Here's the tcopy output from a good tape:
tcopy /dev/rmt0
file 0: block size 2720: 9917 records
file 0: eof after 9917 records: 26974240 bytes
eot
total length: 26974240 bytes
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "backup.h"
#define bool long
#define false 0
#define true 1
#define RAWSIZE (5*(32+512))
#define endof(s) (strchr(s, (char) 0))
FILE* source; /* Source "tape". */
bool eightbit = false; /* Status of -8 (eight-bit) flag. */
bool copytape = false; /* Status of -c (copytape fmt) flag. */
bool buildtree = false; /* Status of -d (build trees) flag. */
bool interchange = false; /* Status of -i (interchange) flag. */
bool binary = false; /* Status of -b (binary) flag. */
bool timfmt = false; /* Status of -m (mts format) flag. */
long verbose = 0; /* Status of -v (verbose) flag. */
char** argfiles; /* 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;
char deferbyte; /* Defered byte for output. */
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. */
/* unpackheader unpacks the header block from the raw stream. */
void unpackheader() {
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%i l=%d, r=%d",i,left,right);}
}
}
/* unpackdata unpacks the data block from the raw stream. */
void unpackdata() {
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_36bits reads 36 bits from a machine word. */
void pars_36bits(index, store)
long index;
char *store;
{
long l, r;
l = datalh[index];
r = datarh[index];
store[0] = r & 0377;
store[1] = (r >> 8) & 0377;
store[2] = ((r >> 16) & 03) | ((l << 2) & 0374);
store[3] = (l >> 6) & 0377;
store[4] = (l >> 14) & 017;
store[5] = store[6] = store[7] = 0;
}
/* pars_5chars reads five ASCII chars from a machine word. */
void pars_5chars(index, store)
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(index, store)
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(index)
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(index)
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(index)
long index;
{
/* parse off directory attribute block */
}
void pars_o_sysn(index)
long index;
{
pars_asciz(index, systemname);
}
void pars_o_ssnm(index)
long index;
{
pars_asciz(index, savesetname);
}
void zerotapeinfo() {
systemname[0] = (char) 0;
savesetname[0] = (char) 0;
}
void zerofileinfo() {
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() {
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() {
if (verbose) {
if (*savesetname != (char) 0) printf("Saveset name: %s\n", savesetname);
if (*systemname != (char) 0) printf("Written on: %s\n", systemname);
}
}
void downcase(s)
char* s;
{
while (*s != (char) 0) {
if (isupper(*s)) *s = tolower(*s);
s++;
}
}
void buildfilenames() {
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() {
buildfilenames();
printf("%3d %s", currentfilenumber, cname);
if (verbose) {
printf(" (%d) alloc:%d, mode:%o, len:%d", 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. */
long blockn=0;
void readblock() {
long i, bytes;
unsigned char bc[4];
i = fread(bc, sizeof(char), 4, source);
if (i == 0) return;
bytes = ((long) bc[1] << 8) | (bc[0]);
if (bytes == 0) return;
if (bytes != RAWSIZE)
fprintf(stderr, "backup: incorrect block size = %d\n", bytes);
i = fread(rawdata, sizeof(char), RAWSIZE, source);
blockn++;
while (i++ < RAWSIZE) rawdata[i] = (char) 0;
fread(bc, sizeof(char), 4, source);
unpackheader();
}
/* Disk file output routines: */
void WriteBlock() {
char buffer[5*512];
char binbuf[8*512];
long bufpos, index;
for (index = 0; index < 5*512; index++) buffer[index] = 0;
for (index = 0; index < 8*512; index++) binbuf[index] = 0;
unpackdata();
if (binary) {
for (index = headrh[G_LND], bufpos = 0;
index < (headrh[G_LND] + headrh[G_SIZE]); index++) {
pars_36bits(index, &binbuf[bufpos]);
bufpos += 8;
}
(void) fwrite(binbuf, sizeof(char), bufpos, destination);
}
else {
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)) && (bufpos > 0); 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() {
struct stat statbuf;
char oname[100];
long i;
defercount = 0;
if (interchange) {
destination = fopen(filespec[6], (binary? "wb": "w"));
} else if (!buildtree) {
for (i = 0; (i < sizeof (cname)) && cname[i]; i++)
if (cname[i] == ':') cname[i] = '.';
destination = fopen(cname, (binary? "wb": "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) {
fprintf(stderr, "backup: cannot create %s/\n", oname);
return(false);
}
}
sprintf(endof(oname), "/");
}
sprintf(endof(oname), "%s", filespec[6]);
destination = fopen(oname, (binary? "wb": "w")); */
fprintf(stderr, "backup: tree mode not supported\n");
return(false);
}
return(destination != NULL);
}
void CloseOutput() {
/* Close output file after us. */
}
/* Argmatch checks if the current file matches the given argument: */
bool argmatch(arg)
char* arg;
{
long target;
char* f;
char* p;
char* s;
if (*arg == '#') {
(void) sscanf(arg, "#%d", &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 ..." */
void doextract() {
long i;
currentfilenumber = 0;
extracting = false;
while (!feof(source)) {
readblock();
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 {
fprintf(stderr, "backup: 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)
break;
}
}
}
prevSEQ = headrh[G_SEQ];
}
}
/* dodirectory performs the job of "backup -t ..." */
void dodirectory() {
currentfilenumber = 0;
while (!feof(source)) {
readblock();
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 */
bool checkarg(arg)
char* arg;
{
long i;
char c;
if (*arg == '#') {
if (sscanf(arg, "#%d%c", &i, &c) != 1) {
fprintf(stderr, "backup: bad argument: %s\n", arg);
return(true);
}
}
return(false);
}
int main(argc, argv)
long argc;
char* argv[];
{
long i;
char* s, tapetype[4];
bool namenext = false;
bool actgiven = false;
char action;
char* inputname = NULL;
if (--argc > 0) {
for (s = *(++argv); *s != (char) 0; s++)
switch(*s) {
case '-':
break;
case '8':
eightbit = true; break;
case 'b':
binary = true; break;
case 'c':
copytape = true; break;
case 'd':
buildtree = true; break;
case 'f':
namenext = true; break;
case 'i':
interchange = true; break;
case 'm':
timfmt = true; break;
case 't':
case 'x':
action = *s; actgiven = true; break;
case 'v':
verbose++; break;
default:
fprintf(stderr, "backup: bad option %c\n", *s);
return 0;
}
}
if (namenext) {
if (--argc > 0)
inputname = *(++argv);
else {
fprintf(stderr, "backup: input file name missing\n");
return 0;
}
}
argfiles = ++argv; /* Keep rest of arguments. */
argcount = --argc; /* ... and count 'em. */
for (i = 0; i < argcount; i++) {
if (checkarg(argfiles[i])) {
fprintf(stderr, "backup: error in argument %d = %s\n", i, argfiles[i]);
return 0; } }
if (inputname == NULL) {
/* Use environment var. TAPE here? */
fprintf(stderr, "backup: no input file given\n");
return 0;
}
if (strcmp(inputname, "-") != 0) {
if ((source = fopen(inputname, "rb")) == NULL) {
fprintf(stderr, "backup: can't open %s for input\n", inputname);
return 0;
}
fprintf (stderr, "backup: opening %s for input\n", inputname);
if (timfmt) fread (tapetype, sizeof(char), 4, source);
} else {
source = stdin;
}
switch (action) {
case 't': dodirectory(); break;
case 'x': doextract(); break;
default:
fprintf(stderr, "backup: internal error in program\n");
return 0;
}
}

View 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

View File

@@ -0,0 +1,91 @@
Note: This program has no attribution for authorship. I modified it
to deal with binary files.
Bob Supnik
Description:
backup is a program that can list the contents of, and extract
files from a TOPS-10 backup tape. The command syntax is some-
what resembling that of tar. The command
backup -t[v][f tape]
lists the contents of 'tape'. The command
backup -x[cdimv8][f tape] file1 file2 ...
extracts all files that match either of the file arguments given.
The names used for the created files will be the canonical names
from the tape, unless -d or -i are in effect. The canonical name
is a string of the format: device.p_pn.sfd1.sfd2..file.ext
Arguments:
'tape' is the name of a tape drive, the name of a file or the
single character '-', meaning stdin. If omitted, the environment
variable TAPE will be consulted.
If the "tape" argument is actually a file, which was captured
from a tape (say, on a different machine). The "known good" way
to capture the contents of the tape is to use the unix utility
"dd", and a command line something like this:
dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block
the key thing is that this program expects a fixed block size of
2720 bytes. If the tape actually has some other format, this
program probably won't succeed. You can use the unix utility "tcopy"
to inspect the contents of the tape.
Here's the tcopy output from a good tape:
tcopy /dev/rmt0
file 0: block size 2720: 9917 records
file 0: eof after 9917 records: 26974240 bytes
eot
total length: 26974240 bytes
File arguments are either any substring of the canonical name
printed with 'backup -t ...', or a hash mark, and the number
of the file on the tape, or "*" to simply extract all the files.
Options:
-b Extract files in binary format. The files on tape have one
36b word stored in five 8b frames, as specified in PDP-10
"core dump" mode. The extracted files have one 36b word
stored, right justified, on one little-endian 64b word, as
expected by the simh simulators.
-c The 'tape' is a disk file, in copytape format.
-d Create a directory structure when restoring, giving files named
device:p_pn/sfd1/sfd2/file.ext, instead of the flat name space
resulting from using the canonical names.
-f The disk image file is the next argument.
-i Ignore device and directory info when building output file names.
-m The disk image file has a 4 byte header specifying density, which
must be ignored.
-v Verbose. Does the obvious. -vv does even more.
-8 Tops-10 file was in eight-bit mode.
Bugs:
We don't handle multiple tape savesets any good.
We don't check for bad format of input.
We don't handle checksums, or repeated blocks any good.