mirror of
https://github.com/open-simh/simtools.git
synced 2026-04-25 11:41:23 +00:00
simh tools
This commit is contained in:
636
extracters/backup/backup.c
Normal file
636
extracters/backup/backup.c
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user