/* TAPEMAP Purpose: scan a 9-track magnetic tape, reporting some of the characteristics of its contents, such as the number of files, the number of records, the number of bytes, the contents of some sample records in hex, ascii, or ebcdic format. copied from a similar program available on an IBM VM/CMS machine at SLAC Programmer: Patrick Nolan September 1990 */ /* known inadequacies: doesn't really deal with different tape densities incomplete error handling for command line */ #include #include #include #include #include #include #include #include #include #include #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) unsigned char ebcdic_to_ascii[256] = { 0x00, 0x01, 0x02, 0x03, 0x7F, 0x09, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x7F, 0x7F, 0x08, 0x7F, 0x18, 0x19, 0x7F, 0x7F, 0x1C, 0x1D, 0x1E, 0x1F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x0A, 0x17, 0x1B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x05, 0x06, 0x07, 0x7F, 0x7F, 0x16, 0x7F, 0x7F, 0x7F, 0x7F, 0x04, 0x7F, 0x7F, 0x7F, 0x7F, 0x14, 0x15, 0x7F, 0x1A, 0x20, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x5E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, 0x26, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x7E, 0x2D, 0x2F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, 0x7F, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x7F, 0x7B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x7F, 0x7D, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7F, 0x7F, 0x7F, 0x5B, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x5D, 0x7F, 0x7F, 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x5C, 0x7F, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F }; enum tape_density {d800, d1600, d6250}; enum alphabet {ascii, ebcdic}; enum outformat {standard, hex, character}; #define NAMESIZE 80 #define BUFSIZE 65536 #define MAXERROR 10 int density = d6250; /* probably irrelevant for 1/2 inch tape */ int chartype = ascii; int format = standard; /* combine hex and character in output */ int first = 1; /* start at beginning of tape */ int last = 9999; /* read the whole tape */ int blocks = 5; /* print contents of 5 records per file */ int passtm = 0; /* don't ignore any double EOF marks */ int maxerror = MAXERROR; /* how many tape errors can be tolerated */ char unit[NAMESIZE] = ""; char tapename[NAMESIZE] = ""; char default_tape[] = "/dev/rst20"; unsigned char printable(); main(argc, argv) int argc; char *argv[]; { int i; if (argc < 2) usage(); if ((argc==2) && (*argv[1]=='h')) usage(); for (i=1; i < argc; i++) { if (*argv[i] != '-') { printf ("Error in argument %s\n",argv[i]); exit(-1); } switch (argv[i][1]) { case 'd' : i++; if (!strcmp(argv[i],"800")) density = d800; else if (!strcmp(argv[i],"1600")) density = d1600; else if (!strcmp(argv[i],"6250")) density = d6250; else { printf("Invalid density %s\n",argv[i]); exit(-2); } break; case 'e' : chartype = ebcdic; break; case 's' : format = standard; break; case 'h' : format = hex; break; case 'c' : format = character; break; case 'b' : blocks = atoi(argv[++i]); break; case 'f' : first = atoi(argv[++i]); break; case 'l' : last = atoi(argv[++i]); break; case 'm' : maxerror = atoi(argv[++i]); break; case 'p' : passtm = atoi(argv[++i]); break; case 'u' : strcpy (unit, argv[++i]); break; case 't' : strcpy (tapename, argv[++i]); break; default : printf("Invalid argument %s\n", argv[i]); exit(-3); } /* end of switch */ } /* end of for loop */ if (strlen(unit) == 0) { char *tape; tape = getenv("TAPE"); if (tape) strcpy(unit, tape); } if (strlen(unit) == 0) strcpy(unit, default_tape); map_the_tape(); return 0; } usage() { printf("Usage:\n"); printf("tapemap [-d density] [-e] [-s | -h | -c] [-b blocks] [-f first]"); printf(" [-l last ]\n [-m maxerror] [-p passtm] [-u unit] [-t tapename]\n"); exit(0); } map_the_tape() { unsigned char buffer[BUFSIZE]; int quit, neof, nrec, nfile, nread, fd, lasteof, nbytes, recinfile; int maxrec, minrec, filebytes, nerror; print_header(); fd = open(unit, O_RDONLY); if (fd < 0) { perror("tapemap open error"); exit(-4); } neof = 0; nrec = 0; lasteof = 0; nbytes = 0; recinfile = 0; maxrec = 0; minrec = 100000; filebytes = 0; nerror = 0; while (!quit) { nread = read(fd, buffer, BUFSIZE); if (nread < 0) { /* read error */ nrec++; nerror++; recinfile++; printf("Read error at record #%d.\n"); fflush(stdout); if (nerror > maxerror) { quit = 1; printf("Terminating program because of excessive tape errors."); } } else if (nread == 0) { /* EOF */ neof++; nrec++; if (neof >= first) printf("EOF #%d at record #%d.\n",neof,nrec); fflush(stdout); if (lasteof) { /* We've hit a double EOF */ if (neof >= passtm+2) quit = 1; } else { /* This is an ordinary EOF, not EOT (yet) */ lasteof = 1; if (neof >= last) /* Stop if we've seen the requested files */ quit = 1; if (neof >= first) { /* Print file summary */ printf("File contains %d records, %d bytes.", recinfile, filebytes); printf(" Shortest=%d, longest=%d\n\n", minrec, maxrec); } maxrec = 0; minrec = 100000; } recinfile = 0; filebytes = 0; } else { /* regular data record */ nrec++; recinfile++; nbytes += nread; filebytes += nread; maxrec = MAX(nread,maxrec); minrec = MIN(nread,minrec); lasteof = 0; if ((neof >= first-1) && (recinfile <= blocks)) outline(buffer, nread, nrec); } } printf("\nThis tape contains %d bytes of data, %d records, %d files.\n", nbytes, nrec, neof-1); if (nerror > 0) printf ("There were %d tape read errors.\n",nerror); fflush(stdout); close(fd); } print_header() { time_t tim; time(&tim); printf("TAPEMAP %s", asctime(localtime(&tim))); printf("\nTape name: %s\n", tapename); printf("Tape drive: %s\n", unit); printf("First file = %d. Last file = %d.\n", first, last); if (chartype == ascii) printf("Character type = ASCII\n"); else printf("Character type = EBCDIC\n"); printf("Display: blocks/file = %d", blocks); if (format == hex) printf(" format = hexadecimal\n"); else if (format == character) printf(" format = character\n"); else printf(" format = standard\n"); printf("\n"); printf(" Rec Bytes Contents\n"); printf("---- ----- ------------------------------------"); printf("----------------------------\n"); fflush(stdout); } outline(buffer, nread, nrec) int nread, nrec; unsigned char *buffer; { int i; printf("%4i %5i ",nrec,nread); switch(format) { case hex : for (i = 0; i < MIN(nread,32); i++) printf("%2.2x", buffer[i]); break; case character : for (i = 0; i < MIN(nread,64); i++) printf("%1c", printable(buffer[i])); break; case standard : for (i = 0; i < MIN(nread,21); i++) printf("%1c", printable(buffer[i])); printf(" "); for (i = 0; i < MIN(nread,21); i++) printf("%2.2x", buffer[i]); break; } printf("\n"); fflush(stdout); } unsigned char printable(inchar) unsigned char inchar; { unsigned char c; c = inchar; if (chartype == ebcdic) c = ebcdic_to_ascii[c]; if ((isgraph(c)) && (isascii(c))) return c; else return '.'; }