/* * More or less portable version of DUMPER written for 32 bit * machines. DUMPER is a DEC-10/20 tape reader/writer * (This program doesn't write tapes.) used for * backing up files and exchanging tapes with other 10/20 sites. * This version was written on a VAX-11/780 in VAX11C. * DUMPER.C is the only file needed to build the program. * * Usage: * DUMPER tape [logfile] [-p] [-rdestination] [-sselector] * Where: * tape Name of the tape drive on which tape is mounted. * -p Print file names. * -R Destination area of the form dev:[dir], dev: or logical:. * -s File name selection pattern for retrieval, default "*". * Any part of the selector may be wild carded. * * Example run: * $ DUMPER:==$disk:[dir]DUMPER.EXE ! Setup symbol on VAX * $ mount mfa0:/foriegn/blocksize=25900/record=2590 * $! mount the tape. note the blocksize and recordsize parameters. * $! recodrsize must always be 2590. Blocksize must be set to the * $! number of records per block as specified by the dump command on * $! the 10/20 system. * $ dumper mfa0: tapelog.txt -p * $! put listing of files on tape in tapelog.txt * $ dumper mfa0: -p -rdra1:[cpm] "-s**" * $! Retrieve all files in the "CPM" directory * $ dumper mfa0: -p -rdra1: * $! retrieve all files from tape. note: the program doesn't create * $! directories you must do this ahead of time. * * Written by: * Pieter Bowman * James Ray Westmoreland * Salt Lake City, Utah */ #include #include #define VERSION "V1.3" typedef unsigned char BYTE; /* definitions for tape info. */ /* Format for DEC-20 word encoded on standard DUMPER */ /* tape. (This is called CORE-DUMP.) */ /* 1 1 2 2 3 3 3 */ /* 0 7 8 5 6 3 4 1 2 5 */ /* |--------|--------|--------|--------|----| */ /* | | | | | | */ /* |--------|--------|--------|--------|----| */ #define CHKSUM 0 /* | Checksum for the record | */ #define ACCESS 1 /* | Access for the record (not used) | */ #define TAPNO 2 /* | SSNO (3 - 17) TPNO (18 - 35) | */ #define PAGNO 3 /* | FILNO (2 - 17) PGNO (18 - 35) | */ #define TYP 4 /* | -TYP record type | */ #define SEQ 5 /* | Sequence number | */ /* record type definitions */ #define DATA 0 /* File page */ #define TPHD 1 /* Tape header */ #define FLHD 2 /* File header (FDB info) */ #define FLTR 3 /* File trailer */ #define TPTR 4 /* Tape trailer (only on continued tapes) */ #define USR 5 /* Directory info (DDB) */ #define CTPH 6 /* Continued tape header */ #define FILL 7 /* Filler record */ #define FDB 134 /* Decimal word number in tape record */ /* NOTE: This is word, not byte number. */ #define NBYTES 2590 /* 518 36 bit words core dump format */ BYTE taperec[NBYTES]; FILE *ofil, /* Output file descriptor pointer */ *rfp; int rfd; /* Restore file descriptor */ int tape; /* tape file number (returned from open) */ char *rfile, /* Pointer to partial file spec for restore */ *selectfile[10] = { "*"}; /* Default select all files */ /* Switchs from command line */ int sfn, /* Number of files selected (Max. 10) */ prnt, /* Print file names when found */ restore, /* Restoring a file */ donearg, /* Finished current command line argument */ ascfil; /* restored file is ascii */ char rfnam[100], /* Restore file name */ fname[140], /* File name from tape */ ssname[80], /* Save set name from tape */ sstime[16]; /* Save set time from tape (after conversion) */ int bytesize, /* Byte size of current file */ bytes, /* Number of bytes (size bytesize) in file */ bytes_written=0,/* Number of bytes written to output file. */ pages, /* Number of pages (512 words) in file */ rstrcrrnt; /* Restore current file */ int ssno, /* Save set number from tape */ tpno, /* Tape number from tape */ flno, /* File number from tape */ pgno, /* Page number in file from tape */ seq, /* sequence number from tape */ oldseq, /* old seq. number last record */ rectyp; /* Record type */ main(argc, argv) int argc; char *argv[]; { char *p; char *infile = 0, /* Pointer to input file name (eg. MTA0:) */ *outfile = 0; /* & output file name (output file names...) */ int sw, flg, i, ii, fndfl; /* Misc. stuff */ fprintf(stderr,"TOPS-10/20 DUMPER-tape reader program version %s\n\n\n",VERSION); if( argc < 2 ) { fprintf(stderr, "Usage: dumper tape {log} {-s(file)} {-p} {-r(file)}\n"); fprintf(stderr, "-s = select files to restore\n"); fprintf(stderr, "-p = print filenames from tape to log file\n"); fprintf(stderr, "-r = restore files & partial filename for restore\n"); fprintf(stderr, "Note: no spaces allowed between switch and file\n"); exit(1); } else infile = argv[1]; if( argc > 2 ) if( argv[2][0] != '-' ) outfile = argv[2]; prnt = restore = FALSE; sfn = 0; for(i = 2; i < argc; i++) { donearg = FALSE; if(argv[i][0] == '-' ) for( ii = 1; argv[i][ii] && !donearg; ii++) switch(argv[i][ii]) { case 's': case 'S': if(sfn < 10){ selectfile[sfn++] = &argv[i][ii+1]; p = &argv[i][ii+1]; while(*p){ if(isalpha(*p)) *p = toupper(*p); p++; } } else fprintf(stderr,"Too many select file patterns.\n"); donearg = TRUE; break; case 'p': case 'P': prnt = TRUE; donearg = TRUE; break; case 'r': case 'R': if(!restore){ restore = TRUE; rfile = &argv[i][ii+1]; } else fprintf(stderr,"Restore file area already specified.\n"); donearg = TRUE; break; default: fprintf(stderr, "?Unrecognized switch \"%s\"\n", argv[i]); exit(1); } } sfn += sfn ? 0 : 1; /* Have at least default */ if( (tape = open(infile, 0)) <= 0 ) { fprintf(stderr, "Can't open %s\n", infile); exit(1); } if( outfile==0 ) ofil = stdout; else if( (ofil = fdopen(creat(outfile,0,"mrs=0","rfm=var","rat=cr"),"w")) <= 0 ) { fprintf(stderr, "Can't open %s\n", outfile); exit(1); } flg = TRUE; rstrcrrnt = FALSE; while( flg ) { if( !rdrec() ) { flg = FALSE; continue; } switch( rectyp ) { case DATA: if( rstrcrrnt ) wrtrec(); break; case TPHD: fprintf(ofil, "DUMPER Tape %d, Volid %s, %s, %s\n\n", tpno, infile, ssname, sstime); fflush( ofil ); if( ofil != stdout ) printf("DUMPER Tape %d, Volid %s, %s, %s\n\n", tpno, infile, ssname, sstime); break; case FLHD: getfname(); getfsize(); fndfl = FALSE; for(i = 0; i < sfn; i++) if( !wild(selectfile[i], fname) ) fndfl = TRUE; if( fndfl ) { if( prnt ) fprintf(ofil, "%s\t%d %d(%d)\n", fname, pages, bytes, bytesize); if(bytesize == 36){ bytesize = 7; bytes *= 5; } if(bytesize == 7) ascfil = TRUE; if( (rstrcrrnt=restore) ) { if( (bytesize==7 || bytesize==8)) { makfilnam(); fprintf(ofil, "\t(as) %s", rfnam); fflush( ofil ); if(ascfil){ if( (rfp = fdopen(creat(rfnam,0,"mrs=0","rfm=var","rat=cr"),"w")) < 0) { fprintf(stderr, "Can't open '%s'\n", rfnam); rstrcrrnt = FALSE; } } else { if( (rfd = creat(rfnam,0,"mrs=0","rfm=var")) < 0) { fprintf(stderr, "Can't open '%s'\n", rfnam); rstrcrrnt = FALSE; } } } else { fprintf(ofil, "\tIllegal byte size.\n"); rstrcrrnt = FALSE; } } } break; case FLTR: if( rstrcrrnt ) { rstrcrrnt = FALSE; fprintf(ofil, "\t[Ok]\n"); if(ascfil){ fclose( rfp ); ascfil = FALSE; } else { close( rfd ); } bytes_written = 0; } break; case TPTR: break; case USR: break; case CTPH: break; case FILL: break; default: printf("Unknown record type %d\n", i); break; } } close(tape); if( ofil != stdout ) fclose(ofil); } long int getbits(wrd, s, f) int wrd, s, f; { register int j, sbyte, fbyte; register long int i; if( f-s > 31 ) { fprintf(stderr, "getbits: Can't use more than 32 bits of the 36.\n"); exit(1); } i = 0; sbyte = s/8; fbyte = f/8; for( j = sbyte; j <= fbyte; j++ ) i = (i << (j>3?4:8)) | taperec[wrd*5+j]; i >>= 7 - (f>31 ? f+4 : f) % 8; i &= ~((unsigned long) 0) >> (31+s-f); return( i ); } getfname() { cvtasz( 06, fname, sizeof fname ); } /* Convert from 5 7bit bytes packed in 36 bits to 5 8 bit bytes */ /* with (ASCIZ) zero (nul) termination. */ cvtasz( wrd, s, ml) int wrd; char *s; int ml; { int b; b = 0; while( (*s++ = getbits( wrd+b/5, b%5*7, b%5*7+6 )) && b < ml ) b++; } /* Get file sizes */ getfsize() { bytesize = (int) getbits(FDB+011, 6, 11); bytes = (int) getbits(FDB+012, 4, 35); pages = (int) getbits(FDB+011, 18, 35); } rdrec() { int status; status = read(tape, taperec, NBYTES); if( status <= 0 ) return( FALSE ); ssno = getbits(TAPNO, 3, 17); tpno = getbits(TAPNO, 18, 35); flno = getbits(PAGNO, 2, 17); pgno = getbits(PAGNO, 18, 35); rectyp = -getbits(TYP, 4, 35); seq = getbits(SEQ, 4, 35); if( seq <= oldseq ) { fprintf(stderr, "?Sequence error at record %d, continuing.\n", seq); } oldseq = seq; return( TRUE ); } wrtrec() { static int bcnt=0; static char recbuf[512]; char c; int i, j; for(i = 6; i < 518; i++) for(j = 0; j < 36-bytesize; j+=bytesize){ c = (BYTE)getbits(i, j, j+bytesize-1); if(ascfil){ if(c != 015) fputc(c, rfp); } else { recbuf[bcnt] = c; if(bcnt > 508 || bytes_written >= bytes){ write(rfd, &recbuf, (bcnt<509)?bcnt:bcnt+1); bcnt = -1; if(bytes_written >= bytes){ bcnt = 0; return; } } bcnt++; } if(bytes_written >= bytes) return; bytes_written++; } } /* * Take a 20ish filename and make it a Vaxish filename. First copy the * structure verbatim (useless). Next copy the first 9 characters of the * directory name; alpha-numeric plus periods. If a period is encountered * end of first directory name and can have another 9 chars. Then copy the * first 9 chars. of the file name; alpha-numeric only (ie. no quoted (^V) * characters). Copy the first 3 characters of the file extension (type); * "alnum". Lastly copy the generation number; can be as high as 262143. */ makfilnam() { char rsa[100], str[100], *strchr(), *p, *r; int rflen, fnlen, rfnlen, rsalen, i, j; p = fname; r = str; while( (*r++ = *p++) != '<' ) /* Copy structure */ ; for( j = 0; *p != '>'; j++, p++ ) /* Copy first 9 chs of dir */ if( j < 9 && isalnum(*p) ) *r++ = *p; else if( *p == '.' ) { /* If period we have a subdir*/ *r++ = *p; /* Move the "." */ j = 0; /* So start count over */ } *r++ = *p++; /* Copy ">" */ for( j = 0; *p != '.'; j++, p++ ) /* First 9 chs. of file name */ if( j < 9 && isalnum(*p) ) *r++ = *p; *r++ = *p++; /* Copy "." */ for( j = 0; *p != '.'; j++, p++ ) /* First 3 chs of file type */ if( j < 3 && isalnum(*p) ) *r++ = *p; *r++ = *p++; /* Copy "." */ while( (*r++ = *p++) != ';' ) /* Copy until first ";" */ ; *(--r) = '\0'; /* Replace ";" with null */ rflen = strlen(rfile); fnlen = strlen(str); rfnlen = sizeof rfnam; rsalen = sizeof rsa; for(i = 0; i < rfnlen; i++) /* Clear restore file name string */ rfnam[i] = '\0'; parse(rfile, rflen, str, fnlen, rfnam, rfnlen, rsa, rsalen); } /* * wild: Compare two strings: * The first of which can contain the characters * "*" (match any number of characters, zero or more) * and "%" (match any single character), this is the * wild string (ws). * The second is a plain text string, which is * compared to the first. * * This is the same sort of routine as used by * many operating systems for wild-carded file searching. * * This implimentation written in "C" on a VAX-11/780 * uses a recursive algorithim to accomplish the task. * Therefore for strings of any length the stack must be * fairly large to avoid stack overflow problems. * * Author: Pieter Bowman 1 Aug 84 */ wild( ws, s ) char *ws, *s; { switch( *ws ) { case '*': if( !wild( ws+1, s+1 ) ) return( 0 ); if( *s ) return( wild( ws, s+1 ) ); break; default : /* Note: this "if" falls through to use */ /* the same code as the "%" wild card. */ if( *ws != *s ) break; case '%': if( *ws && *s ) return( wild( ws+1, s+1 ) ); break; } return( *ws - *s ); } /* * Program to accept wild card characters in input (partial) file * specification and display full file specification. */ #include fab #include nam parse(fna, fns, dna, dns, esa, ess, rsa, rss) char *fna, *dna, *esa, *rsa; int fns, dns, ess, rss; { static struct FAB fab_blk; static struct NAM nam_blk; int status; fab_blk = cc$rms_fab; nam_blk = cc$rms_nam; fab_blk.fab$l_fop = FAB$M_NAM; fab_blk.fab$l_nam = &nam_blk; fab_blk.fab$l_fna = fna; fab_blk.fab$b_fns = fns; fab_blk.fab$l_dna = dna; fab_blk.fab$b_dns = dns; nam_blk.nam$l_esa = esa; nam_blk.nam$b_ess = ess; nam_blk.nam$l_rsa = rsa; nam_blk.nam$b_rss = rss; status = SYS$PARSE(&fab_blk,0,0); return(status); }