| /* 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; | |
| } | |
| } | |