mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-17 16:45:16 +00:00
637 lines
14 KiB
C
637 lines
14 KiB
C
|
|
/* 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;
|
|
}
|
|
|
|
}
|
|
|