sources-request@mirror.UUCP (03/04/87)
Submitted by: David S. Hayes <sundc!hqda-ai!merlin> Mod.sources: Volume 8, Issue 99 Archive-name: ansitape/Part01 This is a program that I've had around for some time, but never really did anything with before. I use it for transferring data between VAX/VMS, IBM MVS, and Unix 4.2BSD. It works well for me, but someone out there is likely to find a bug. In particular, I could not get a full copy of the ANSI magtape standards, so I did the best I could by testing to see what would work. I think this does enough to be useful in most cases, though. I'm not in the support business, but I would like to hear about bugs. As time permits, I'll try to fix them. Updates will be published in mod.sources, using 'patch' formats. I'd like to do the 'official' patches here, so everyone doesn't end up with custom (incompatible) changes. This software was developed on Government time, and is therefore freely available to the public. (It's even covered under the Freedom of Information Act, if anyone ever cared to ask for it.) Hope someone finds this useful. David S. Hayes, The Merlin of Avalon PhoneNet: (202) 694-6900 ARPA: merlin%hqda-ai.uucp@brl-smoke UUCP: ...!seismo!sundc!hqda-ai!merlin #!/bin/sh # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # ansitape.c echo x - ansitape.c cat > "ansitape.c" << '//E*O*F ansitape.c//' /* * ANSITAPE.C * * This program creates, reads, writes, and takes directory listings of * ANSI-formatted tapes. * * This program was developed at * * HQDA Army Artificial Intelligence Center * Pentagon Attn: DACS-DMA * Washington, DC 20310-0200 * * Phone: (202) 694-6900 * ******************************************** * * THIS PROGRAM IS IN THE PUBLIC DOMAIN * ******************************************** * * Modification History: * * 28 January 1987 David S. Hayes Cleaned up for Usenet, and changed switch formats. * 6 August 1986 David S. Hayes Added -st I/O switch. * 28 July 1986 David S. Hayes Changed to stream-style tape access, added IBM labels and EBCDIC code conversion. * 18 July 1986 David S. Hayes Changed to correct problem with files spanning more than one block. * 17 June 1986 David S. Hayes Original version. */ #define SYSID "UNIXTAPEV2.0" /* version 2.0 patch level 0 */ #include <stdio.h> #include <strings.h> #include <ctype.h> #include <sys/file.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/mtio.h> #include <sys/time.h> #include <sys/stat.h> #include "ansitape.h" #define DEFAULT_TAPE "/dev/rmt8" #define IGN ' ' /* empty constant for hdr space fill */ /* * The largest file that we will try to determine recordsize for. * Anything larger than this, we just use recordsize = blocksize. */ #define READMAX 100000 #define LIST 1 #define EXTRACT 2 #define WRITE 3 #define CREATE 4 #define makelower(c) ( isupper(c) ? tolower(c) : c) #define makeupper(c) ( islower(c) ? toupper(c) : c) #define max(a,b) ( a >= b ? a : b ) #define min(a,b) ( a < b ? a : b ) #define SAME 0 typedef unsigned char FLAG; /* boolean values only */ #define TRUE '\001' #define FALSE '\000' extern int errno; char *upstring(); char *downstring(); char *tail(); char *malloc(); char *alloca(); FLAG match(); FLAG eot(); char *labels[] = {"", "HDR", "EOV", "EOF", ""}; #define TOP_OF_FILE 1 #define END_OF_TAPE 2 #define END_OF_FILE 3 #define END_OF_SET 4 #define UNKNOWN_LABEL -1 struct ansi_vol1 vol1; struct ansi_hdr1 hdr1; struct ansi_hdr2 hdr2; struct ansi_hdr3 hdr3; struct ansi_hdr4 hdr4; struct tape_control_block { /* Stuff pertaining to the tape set. */ FLAG ebcdic; FLAG ibm_format; int reel_switch; char set_name[6]; char tapesys[13]; char owner[14]; char vol_access; int ansi_level; int reel_count; /* Stuff pertaining to an individual volume. */ char vol_name[6]; FLAG mounted; int file_count; /* Stuff for an individual file. */ char tape_filename1[17]; char tape_filename2[63]; char unix_filename[80]; int blk_size; int rec_size; char rec_format; char car_control; int generation; int genversion; char file_access; long created; long expires; int density; int blk_count; int blk_offset; /* Stuff to be able to read/write on the tape. */ int fd; char *buffer; int char_count; int next; FLAG eof; FLAG open; int rw_mode; } tcb; int func; /* What we're supposed to do */ char *tapename = DEFAULT_TAPE; FLAG verbose = FALSE,/* be wordy */ query = FALSE, /* ask before doing */ no_hdr3 = FALSE,/* don't write HDR3 or HDR4 */ ibm_format = FALSE, /* use IBM standard label * format */ ebcdic = FALSE, /* tape should be EBCDIC */ flag_bs = FALSE,/* blocksize was given */ flag_rs = FALSE,/* recordsize was given */ flag_stdio = FALSE; /* do file i/o using stdin/out */ char main_set_name[6] = "UNIX"; /* file set id */ char main_vol_name[6] = "UNIX"; /* currently-mounted * volume */ char *username, *progname; int main_blk_size = 2048, main_rec_size; char main_rec_format = REC_VARIABLE; /* fixed or variable */ char main_car_control = CC_IMPLIED; /* carriage control */ char **main_file_list; int main_file_count; /* * Here's the code. */ main(argc, argv) int argc; char *argv[]; { int i; char *getlogin(); setbuf(stderr, NULL); /* unbuffer error output */ username = getlogin(); progname = argv[0]; if (username) { downstring(username, main_vol_name, 6); downstring(username, main_set_name, 6); }; if (argc >= 2) { /* Now 1-character option names. */ for (i = 0; argv[1][i]; i++) { if (argv[1][i] == '-') continue; /* minus is optional on first keylist */ switch (argv[1][i]) { case 't': func = LIST; break; case 'x': func = EXTRACT; break; case 'c': func = CREATE; break; case 'r': if (func != CREATE) func = WRITE; break; case 'v': verbose = TRUE; break; case 'q': query = TRUE; break; /* Do file I/O to stdio instead. */ case 'f': flag_stdio = TRUE; break; /* * Suppress HDR3 headers for systems that can't handle * them. */ case '3': no_hdr3 = TRUE; break; /* Tape is to be written in ASCII. */ case 'a': ebcdic = FALSE; break; #ifdef EBCDIC case 'e': ebcdic = TRUE; break; case 'i': ibm_format = TRUE; ebcdic = TRUE; break; #endif default: fprintf(stderr, "unknown key %c - ignored\n", argv[1][i]); }; }; }; for (i = 2; i < argc; i++) { /* Do 2-character options */ if (strncmp(argv[i], "cc=", 3) == SAME) { switch (argv[i][3]) { case 'f': main_car_control = CC_FORTRAN; break; case 'i': main_car_control = CC_IMPLIED; break; case 'e': main_car_control = CC_EMBEDDED; break; default: fprintf(stderr, "unknown carriage control option %s\n", argv[i]); }; continue; }; if (strncmp(argv[i], "rs=", 3) == SAME) { if (strcmp(argv[i], "rs=r") == SAME) { main_rec_size = 0; main_rec_format = REC_VARIABLE; } else { flag_rs = TRUE; sscanf(argv[i], "rs=%d", &main_rec_size); main_rec_format = REC_FIXED; }; continue; }; if (strncmp(argv[i], "bs=", 3) == SAME) { flag_bs = TRUE; sscanf(argv[i], "bs=%d", &main_blk_size); continue; }; if (strncmp(argv[i], "vo=", 3) == SAME) { downstring(&argv[i][3], main_set_name, 6); downstring(&argv[i][3], main_vol_name, 6); continue; }; if (strncmp(argv[i], "rf=", 3) == SAME) { switch (argv[i][3]) { case 'f': main_rec_format = REC_FIXED; break; case 'v': main_rec_format = REC_VARIABLE; break; default: fprintf(stderr, "unknown record format %c - ignored\n", argv[i][3]); }; continue; }; if (strncmp(argv[i], "mt=", 3) == SAME) { tapename = &argv[i][3]; continue; }; /* That's not an option, assume it's a file name. */ break; }; /* Figure out where and how many file names we have. */ main_file_count = argc - i; main_file_list = argc ? &argv[i] : (char **) 0; if (main_rec_size > main_blk_size) { if (flag_rs) { main_blk_size = main_rec_size; fprintf(stderr, "blocksize adjusted up to recordsize (%d)\n", main_rec_size); } else { main_rec_size = main_blk_size; fprintf(stderr, "recordsize adjusted down to blocksize (%d)\n", main_rec_size); }; }; switch (func) { case WRITE: tcb.fd = open(tapename, O_RDWR, 0666); break; case CREATE: tcb.fd = open(tapename, O_RDWR | O_CREAT | O_TRUNC, 0666); break; default: tcb.fd = open(tapename, O_RDONLY, 0666); } if (errno) { perror("can't open tape device"); exit(-1); }; switch (func) { case LIST: list_volume_set(); break; case CREATE: case WRITE: write_volume_set(); break; case EXTRACT: read_volume_set(); break; default: fprintf(stderr, "specify one of -t, -x, -c, -r\n"); exit(-1); }; exit(0); } /* List contents of a full volume set. */ list_volume_set() { mount_tape(); for (; ansi_open(FREAD) == TOP_OF_FILE;) { ansi_close(); list_file(); }; } /* List one file, possibly with maximized verbosity. */ list_file() { printf("%-22s", tcb.unix_filename); if (verbose) { printf(" %5d blocks", tcb.blk_count); printf(", rec fmt %s %d bytes", tcb.rec_format == REC_FIXED ? "fixed" : "var max", tcb.rec_size); switch (tcb.car_control) { case CC_IMPLIED: printf(", cc implied"); break; case CC_EMBEDDED: printf(", cc embedded"); break; case CC_FORTRAN: printf(", cc fortran"); break; default: printf(", cc code '%c'", tcb.car_control); }; }; putchar('\n'); } /* * Write files to a volume set. Uses the ansi_write buffered output * function, which automatically chains to new tapes if it has to. */ write_volume_set() { int j; /* * First, make ready to write the tape. If this is create, then we * must init the tape. If not, mount the tape, and advance to the * logical end-of-volume-set. */ if (func == CREATE) { tcb.reel_count = 0; strncpy(tcb.vol_name, main_vol_name, 6); strncpy(tcb.set_name, main_set_name, 6); strncpy(tcb.owner, username, 14); init_tape(); } else { mount_tape(); do { j = ansi_open(FREAD); ansi_close(); } while (j != END_OF_SET); tape(MTBSF, 1); /* back up past last tape mark */ }; for (j = 0; j < main_file_count; j++) write_file(main_file_list[j]); tape(MTWEOF, 2); } /* * Write from a stream to a tape. Lots of special stuff, like record * and block sizes, are imported implicitly from static variables that * were initialized by command-line options. */ write_file(fn) char *fn; { int stat; FILE *fp; struct stat sb; /* check on our input file */ int rlen; /* length of a single record */ FLAG disk_file; /* our input is from a rewind-able * source */ if (query) { fprintf(stderr, "r %s ? ", fn); if (!yes_or_no()) return END_OF_FILE; }; if (flag_stdio) fp = stdin; else fp = fopen(fn, "r"); if (fp == NULL) { fprintf(stderr, "can't read %s", fn); perror(" - ignored"); return END_OF_FILE; }; strncpy(tcb.unix_filename, tail(fn), 80); if (verbose) fprintf(stderr, "r %-17s", tcb.unix_filename); tcb.ibm_format = ibm_format; tcb.ebcdic = ebcdic; tcb.rec_format = main_rec_format; tcb.car_control = main_car_control; tcb.generation = 1; tcb.genversion = 0; tcb.file_access = NOPROTECT; tcb.blk_offset = 0; tcb.blk_size = main_blk_size; tcb.rec_size = flag_rs ? main_rec_size : main_blk_size; errno = 0; fstat(fileno(fp), &sb); disk_file = (errno == 0) && (sb.st_mode & S_IFMT) == S_IFREG; tcb.created = sb.st_ctime; if (!flag_rs && disk_file && main_rec_format == REC_VARIABLE) { if (sb.st_size < READMAX || main_rec_size == 0) { tcb.rec_size = 0; rlen = 4; /* line length field */ while (!feof(fp)) { if (getc(fp) == '\n') { tcb.rec_size = max(tcb.rec_size, rlen); rlen = 4; /* line length field */ } else rlen++; }; rewind(fp); }; }; if (tcb.rec_size > tcb.blk_size) { fprintf(stderr, "%s recordsize (%d) is too big for blocksize - ignored\n", fn, tcb.rec_size); return END_OF_FILE; }; ansi_open(FWRITE); do { stat = write_record(fp); } while (stat != END_OF_FILE); ansi_close(); if (!flag_stdio) fclose(fp); if (verbose) fprintf(stderr, " %8d blocks\n", tcb.blk_count); return END_OF_FILE; } /* Write a block of data */ write_record(fp) FILE *fp; { char *linebuf; int rl; linebuf = alloca(tcb.blk_size); /* goes away on function exit */ if (tcb.rec_format == REC_FIXED) { rl = fread(linebuf, sizeof(char), tcb.rec_size, fp); if (rl == 0) return END_OF_FILE; } else { /* format is variable-length records */ if (fgets(linebuf, tcb.rec_size, fp) == NULL) return END_OF_FILE; rl = strlen(linebuf); }; ansi_write(linebuf, rl); return 0; } /* * Read from a tape. The var main_file_list is a list of files that we * should extract. We apply filename wildcarding using ? and * to the * names. (These must be quoted to prevent shell interpretation.) If * no filenames are supplied, then we extract everything. */ read_volume_set() { int i; FLAG extract; for (; ansi_open(FREAD) != END_OF_SET;) { extract = main_file_count == 0; for (i = 0; i < main_file_count && !extract; i++) { extract = (match(tcb.unix_filename, main_file_list[i])); }; if (extract) { if (query) { fprintf(stderr, "x %s ? ", tcb.unix_filename); if (!yes_or_no()) continue; }; if (verbose) fprintf(stderr, "x %-17s", tcb.unix_filename); read_file(); }; ansi_close(); scan_labels(); if (extract && verbose) fprintf(stderr, " %8d blocks\n", tcb.blk_count); }; } read_file() { int max_reclen, reclen; char *record; FILE *fp; record = malloc(max_reclen = tcb.rec_size + 20); /* a little extra room */ if (!flag_stdio) fp = fopen(tcb.unix_filename, "w"); else fp = stdout; while ((reclen = ansi_read(record, max_reclen)) != NULL) { fwrite(record, sizeof(char), reclen, fp); }; if (!flag_stdio) fclose(fp); } /*************************************************** * * * A N S I S U P P O R T R O U T I N E S * * * ***************************************************/ /* ANSI_OPEN. Open up a tape file, and prepare to read it. */ ansi_open(mode) int mode; { int err; err = 0; if (!tcb.mounted) { if (mode == FREAD) mount_tape(); else init_tape(); }; if (mode == FREAD) { /* We are going to read a tape. */ err = read_labels(); if (err != TOP_OF_FILE) return err; scan_labels(); } else { /* We are writing a tape. */ tcb.reel_switch = FALSE; tcb.file_count++; tcb.blk_count = 0; make_labels(TOP_OF_FILE); write_labels(); err = TOP_OF_FILE; }; if (err != UNKNOWN_LABEL) { tcb.open = TRUE; tcb.eof = FALSE; tcb.rw_mode = mode; tcb.blk_count = 0; tcb.next = 0; tcb.char_count = 0; }; return err; } /* * ANSI_READ. Read in a record from an buffered, open tape file. We * assume that nothing perverted has happened, like the recordsize * being longer than the buffersize. We do automatic reel switching if * needed. Return is number of charcters read, or END_OF_TAPE. */ ansi_read(bufp, bufl) char bufp[]; int bufl; { char rl[5]; int read_min, read_count; if (tcb.eof || !tcb.open) return NULL; read_min = (tcb.rec_format == REC_VARIABLE) ? 4 : tcb.rec_size; if (tcb.next + read_min > tcb.char_count) { while (tcb.next < tcb.char_count) ansi_getc(); }; if (tcb.rec_format == REC_VARIABLE) { do { rl[0] = ansi_getc(); } while (!isdigit(rl[0]) && !tcb.eof); rl[1] = ansi_getc(); rl[2] = ansi_getc(); rl[3] = ansi_getc(); rl[4] = '\0'; read_min = atoi(rl) - 4; }; if (tcb.eof) return NULL; read_count = 0; for (; bufl && read_count < read_min && !tcb.eof;) { *(bufp++) = ansi_getc(); bufl--, read_count++; }; if (bufl && tcb.car_control == CC_IMPLIED) { *(bufp++) = '\n'; bufl--, read_count++; }; if (bufl) *bufp = '\0'; return read_count; } /* Read a single character from the tape buffer. */ ansi_getc() { int err; try: if (tcb.eof) return END_OF_FILE; if (tcb.buffer && tcb.open) { if (tcb.char_count <= tcb.next) { tcb.next = 0; tcb.char_count = read_tape(tcb.buffer, tcb.blk_size); if (tcb.char_count == 0) goto do_eof; }; return tcb.buffer[tcb.next++]; }; if (!tcb.buffer) { tcb.buffer = malloc(tcb.blk_size); tcb.next = 0; goto try; }; do_eof: tcb.eof = TRUE; tcb.open = FALSE; err = read_labels(); if (err == END_OF_TAPE) { next_volume(); tcb.eof = FALSE; tcb.open = TRUE; }; goto try; } /* * ANSI_WRITE. Write a record, given a pointer and a length. We do all * the formatting necessary. Flush the tape buffer if we run off the * end, and start a new one. */ ansi_write(rec, len) char rec[]; int len; { int overhead = 0; char scratch[10]; if (!tcb.buffer) { tcb.buffer = malloc(tcb.blk_size); tcb.next = 0; tcb.char_count = tcb.blk_size; fill(tcb.buffer, tcb.blk_size, '^'); }; if (tcb.car_control == CC_IMPLIED && len > 0 && iscntrl(rec[len - 1])) rec[--len] = '\0'; /* Check to see if we will run off the end of the block. */ overhead = (tcb.rec_format == REC_VARIABLE) ? 4 : 0; if (len + overhead + tcb.next > tcb.blk_size) { /* Write the old tape block. */ write_tape(tcb.buffer, tcb.blk_size); tcb.blk_count++; /* Check for physical end of tape. */ if (eot()) { make_labels(END_OF_TAPE); write_labels(); next_volume(); }; /* Initialize a new tape block. */ fill(tcb.buffer, tcb.blk_size, '^'); tcb.next = tcb.blk_offset; }; if (tcb.rec_format == REC_VARIABLE) { sprintf(scratch, "%04d", len + 4); strncpy(&tcb.buffer[tcb.next], scratch, 4); tcb.next += 4; }; bcopy(rec, &tcb.buffer[tcb.next], len); tcb.next += len; return 0; } /* * ANSI_CLOSE. Close out a file. We may not have read all the data in * the file yet. If that's so, we skip over the intervening data. * Remember that we may have to switch reels. */ ansi_close() { int err; if (!tcb.open) return 0; if (tcb.rw_mode == FWRITE) { if (tcb.next > tcb.blk_offset) { write_tape(tcb.buffer, tcb.next); tcb.next = tcb.blk_offset; tcb.blk_count++; }; tape(MTWEOF, 1); make_labels(END_OF_FILE); write_labels(); goto closed; }; for (;;) { if (!tcb.eof) skip_past_marks(1); err = read_labels(); if (err == END_OF_FILE) break; next_volume(); tcb.eof = 0; }; scan_labels(); closed: tcb.eof = tcb.open = FALSE; if (tcb.buffer) free(tcb.buffer); tcb.buffer = 0; tcb.char_count = tcb.next = 0; return 0; } /* * Support routines. */ next_volume() { tape(MTOFFL, 1); fprintf(stderr, "Mount continuation tape and push return. "); yes_or_no(); if (func == LIST || func == EXTRACT) { mount_tape(); read_labels(); /* skip the file continuation HDRn * records */ } else { init_tape(); ansi_open(tcb.rw_mode); }; } /* * Write a volume header record. We only write VOL1, since VAX/VMS * does not use the VOL2 record. */ init_tape() { char scratch[90]; tcb.ebcdic = ebcdic; tcb.ibm_format = ibm_format; tcb.ansi_level = 3; strncpy(tcb.tapesys, SYSID, 13); fill(scratch, 90, ' '); sprintf(scratch, "VOL1%-6.6s%c", tcb.vol_name, /* volume name */ NOPROTECT /* access protection */ ); if (tcb.ibm_format) sprintf(&scratch[41], "%-10.10s%29c", tcb.owner, /* owner of volume set */ IGN /* ignored 3 and ansi_level */ ); else sprintf(&scratch[37], "%-14.14s%28c%1d", tcb.owner, /* owner of volume set */ IGN, /* ignored 3 */ tcb.ansi_level /* ansi label standard level */ ); fillnull(scratch, ' ', sizeof(vol1)); upstring(scratch, (char *) &vol1, sizeof(vol1)); write_tape((char *) &vol1, sizeof(vol1)); tcb.reel_count++; tcb.file_count = 0; tcb.mounted = TRUE; if (verbose) fprintf(stderr, "volume %-6s initialized\n", tcb.vol_name); } /* Read a volume header. Save the volume name for later. */ mount_tape() { tcb.ebcdic = ebcdic; tcb.ibm_format = ibm_format; read_tape((char *) &vol1, sizeof(vol1)); downstring((char *) &vol1, (char *) &vol1, sizeof(vol1)); if (strncmp(vol1.header, "vol1", 4) != 0) { fprintf(stderr, "no VOL1 header record\n"); exit(-1); }; tcb.mounted = TRUE; sscanf((char *) &vol1, "vol1%6c%c", tcb.vol_name, &tcb.vol_access ); if (tcb.ibm_format) sscanf(&vol1.owner[4], "%10c", tcb.owner); else sscanf(vol1.owner, "%14c%*28c%1d", tcb.owner, &tcb.ansi_level ); if (verbose) fprintf(stderr, "volume %-6s mounted\n", tcb.vol_name); } /* * Extract the information from a set of headers fetched by * read_labels(). */ scan_labels() { sscanf((char *) &hdr4, "%*3c4%63c", tcb.tape_filename2); sscanf((char *) &hdr1, "%*3c1%17c%6c%4d%4d%4d%2d", tcb.tape_filename1, tcb.set_name, &tcb.reel_count, &tcb.file_count, &tcb.generation, &tcb.genversion); sscanf(hdr1.created, " %5d %5d%c%6d%17c", &tcb.created, &tcb.expires, &tcb.file_access, &tcb.blk_count, tcb.tapesys); sscanf((char *) &hdr2, "%*3c2%c%5d%5d%1d%1d%*19c%c%*15c%2d", &tcb.rec_format, &tcb.blk_size, &tcb.rec_size, &tcb.density, &tcb.reel_switch, &tcb.car_control, &tcb.blk_offset); if (tcb.ibm_format && hdr2.recfmt == IBM_VARIABLE) tcb.rec_format = REC_VARIABLE; bzero(tcb.unix_filename, 80); sprintf(tcb.unix_filename, "%.17s%.63s", tcb.tape_filename1, tcb.tape_filename2); sscanf(tcb.unix_filename, "%80s", tcb.unix_filename); return 0; } /* * Write the header for a file whose particulars are in our global * static variables. */ make_labels(type) int type; { char scratch[90]; int namelen; /* * Split up unix_filename into two parts. First is rightmost 17 * characters of name, second is other characters, up to 63. */ namelen = strlen(tcb.unix_filename); namelen = min(17, namelen); strncpy(tcb.tape_filename1, tcb.unix_filename, 17); strncpy(tcb.tape_filename2, &tcb.unix_filename[namelen], 63); sprintf(scratch, "%3s1%-17.17s%-6.6s%04d%04d%04d%02d", labels[type], tcb.tape_filename1, tcb.set_name, tcb.reel_count, tcb.file_count, tcb.generation, tcb.genversion); sprintf(&scratch[41], " %05d %05d%1c%06d%-13.13s%7c", ansi_date(tcb.created), ansi_date(tcb.expires), tcb.file_access, tcb.blk_count, tcb.tapesys, IGN); upstring(scratch, (char *) &hdr1, sizeof(hdr1)); sprintf(scratch, "%3s2%c%05d%05d%1d%1d%-8.8s/%-8.8s %1c B%11c%02d%28c", labels[type], tcb.rec_format, tcb.blk_size, tcb.rec_size, tcb.density, tcb.reel_switch, username, progname, /* job name & job step */ tcb.car_control, IGN, tcb.blk_offset, IGN); upstring(scratch, (char *) &hdr2, sizeof(hdr2)); /* IBM uses different code for variable-length records. */ if (tcb.ibm_format && tcb.rec_format == REC_VARIABLE) hdr2.recfmt = IBM_VARIABLE; if (!tcb.ibm_format && !no_hdr3) { sprintf(scratch, "%3s3%76c", labels[type], IGN); upstring(scratch, (char *) &hdr3, sizeof(hdr3)); sprintf(scratch, "%3s4%-63.63s00%11c", labels[type], tcb.tape_filename2, IGN); upstring(scratch, (char *) &hdr4, sizeof(hdr4)); }; } /* * Write out tape headers that have already been made by make_labels(). */ write_labels() { upstring((char *) &hdr1, (char *) &hdr1, sizeof(hdr1)); upstring((char *) &hdr2, (char *) &hdr2, sizeof(hdr2)); upstring((char *) &hdr3, (char *) &hdr3, sizeof(hdr3)); upstring((char *) &hdr4, (char *) &hdr4, sizeof(hdr4)); write_tape((char *) &hdr1, sizeof(hdr1)); write_tape((char *) &hdr2, sizeof(hdr2)); if (!tcb.ibm_format && tcb.ansi_level >= 3 && !no_hdr3) { write_tape((char *) &hdr3, sizeof(hdr3)); write_tape((char *) &hdr4, sizeof(hdr4)); }; tape(MTWEOF, 1); return 0; } /* Read a tape header. Set it up for scan_labels(). */ read_labels() { int l; int label_type; struct { char header[3]; char seq; char data[76]; } inhdr; fill((char *) &hdr1, sizeof(hdr1), ' '); fill((char *) &hdr2, sizeof(hdr2), ' '); fill((char *) &hdr3, sizeof(hdr3), ' '); fill((char *) &hdr4, sizeof(hdr4), ' '); l = read_tape((char *) &inhdr, sizeof(inhdr)); if (l == 0) return END_OF_SET; for (label_type = 1; label_type <= 4; label_type++) if (strncmp(inhdr.header, labels[label_type], 3) == 0) break; if (label_type > 4) label_type = UNKNOWN_LABEL; for (; l; l = read_tape((char *) &inhdr, sizeof(inhdr))) { if (label_type == UNKNOWN_LABEL || strncmp(inhdr.header, labels[label_type], 3) != 0) continue; switch (inhdr.seq) { case '1': downstring((char *) &inhdr, (char *) &hdr1, sizeof(hdr1)); break; case '2': downstring((char *) &inhdr, (char *) &hdr2, sizeof(hdr2)); break; case '3': downstring((char *) &inhdr, (char *) &hdr3, sizeof(hdr3)); break; case '4': downstring((char *) &inhdr, (char *) &hdr4, sizeof(hdr4)); }; }; return label_type; } /* * Return an integer with an ANSI date for the time of creation * (actually, last modification) of a file. */ ansi_date(mtime) long mtime; { struct tm *tm, *gmtime(); if (mtime == 0) mtime = time((long *) 0); tm = gmtime(&mtime); return 1000 * tm->tm_year + tm->tm_yday; } /* * Write a block of data to the tape drive. If the tape should be * operated as an EBCDIC device, do the conversion. */ write_tape(buffer, buflen) char *buffer; int buflen; { int err; #ifdef EBCDIC if (tcb.ebcdic) to_ebcdic(buffer, buffer, buflen); #endif err = write(tcb.fd, buffer, buflen); return err; } /* * Read a block from the tape drive. If the tape should be operated as * an EBCDIC device, do the conversion. */ read_tape(buffer, buflen) char *buffer; int buflen; { int inlen; inlen = read(tcb.fd, buffer, buflen); #ifdef EBCDIC if (tcb.ebcdic) to_ascii(buffer, buffer, inlen); #endif return inlen; } /* * Skip past some number of files on a tape drive. This isn't really * needed on anything but streaming drives. */ skip_past_marks(count) int count; { int i; static char ignore[20]; if (count > 1) { tape(MTFSF, count); return; }; for (i = 0; i < 6; i++) { if (read(tcb.fd, ignore, 20) == 0) return; }; tape(MTFSF, 1); } /* Do a special operation on a tape drive. */ tape(op, count) int op, count; { struct mtop mt; mt.mt_op = op; mt.mt_count = count; ioctl(tcb.fd, MTIOCTOP, &mt); } /* Find the last pathname component of a pathname. */ char * tail(path) char path[]; { int ix = strlen(path); ix = strlen(path) - 1; for (; ix >= 0; --ix) { if (path[ix] == '/') return &path[++ix]; }; return path; } /* * Match a string (s) against a wildcard key. Returns 1 if they match, * or 0 if they don't. */ FLAG match(s, key) char *s, *key; { if (*s == '\0') { switch (*key) { case '\0': return TRUE; case '*': return match(s, ++key); default: return FALSE; }; }; if (*key == '?') return match(++s, ++key); if (*key == '*') return match(s, (key + 1)) || match((s + 1), key); if (*s == *key) return match(++s, ++key); return FALSE; } /* * Determine if we are off the physical end of the tape. For now, just * say we haven't got there yet. There is no device-independent way to * know when we hit EOT, so if we do this for real, we have a program * that requires a pre-programmed knowledge of the tape controller in * use. */ FLAG eot() { return FALSE; } /* * Ask the user something, and return ok if we get anything starting * with the letter 'y'. */ yes_or_no() { char buf[256]; static FILE *user = NULL; if (user == NULL) { if (flag_stdio) user = fopen("/dev/tty", "r"); else user = stdin; }; fgets(buf, 256, user); return makelower(buf[0]) == 'y'; } /* Convert a string to upper-case, for at most n characters */ char * upstring(from, to, len) char *from, *to; int len; { char *to1 = to; for (; len--;) { *to = makeupper(*from); if (*from == '\0') break; to++, from++; }; return to1; } char * downstring(from, to, len) char *from, *to; int len; { char *to1 = to; for (; len--;) { *to = makelower(*from); if (*from == '\0') break; to++, from++; }; return to1; } /* Fill an area with some character. */ fill(area, size, c) char *area; int size; char c; { for (; size--;) *(area++) = c; } /* Fill in nulls left by sprintf. */ fillnull(area, fill, len) char *area, fill; int len; { for (; len--; area++) if (*area == '\0') *area = fill; } //E*O*F ansitape.c// echo Possible errors detected by \'wc\' [hopefully none]: temp=/tmp/shar$$ trap "rm -f $temp; exit" 0 1 2 3 15 cat > $temp <<\!!! 1430 3772 30164 ansitape.c !!! wc ansitape.c | sed 's=[^ ]*/==' | diff -b $temp - exit 0