home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / ansitape / part01 / ansitape.c
Encoding:
C/C++ Source or Header  |  1987-03-02  |  29.5 KB  |  1,431 lines

  1. /*
  2.  * ANSITAPE.C 
  3.  *
  4.  * This program creates, reads, writes, and takes directory listings of
  5.  * ANSI-formatted tapes. 
  6.  *
  7.  * This program was developed at 
  8.  *
  9.  * HQDA Army Artificial Intelligence Center
  10.  * Pentagon Attn: DACS-DMA
  11.  * Washington, DC  20310-0200 
  12.  *
  13.  * Phone: (202) 694-6900 
  14.  *
  15.  ********************************************
  16.  *
  17.  * THIS PROGRAM IS IN THE PUBLIC DOMAIN 
  18.  *
  19.  ********************************************
  20.  *
  21.  * Modification History:
  22.  *
  23.  * 28 January 1987    David S. Hayes Cleaned up for Usenet,
  24.     and changed switch formats.
  25.   
  26.  * 6 August 1986    David S. Hayes Added -st I/O switch. 
  27.   
  28.  * 28 July 1986        David S. Hayes Changed to stream-style tape
  29.     access, added IBM labels and EBCDIC code conversion. 
  30.   
  31.  * 18 July 1986        David S. Hayes Changed to correct problem with
  32.     files spanning more than one block. 
  33.   
  34.  * 17 June 1986        David S. Hayes Original version. 
  35.  */
  36.  
  37. #define SYSID "UNIXTAPEV2.0"    /* version 2.0 patch level 0 */
  38.  
  39. #include <stdio.h>
  40. #include <strings.h>
  41. #include <ctype.h>
  42. #include <sys/file.h>
  43. #include <sys/types.h>
  44. #include <sys/ioctl.h>
  45. #include <sys/mtio.h>
  46. #include <sys/time.h>
  47. #include <sys/stat.h>
  48.  
  49. #include "ansitape.h"
  50.  
  51. #define DEFAULT_TAPE "/dev/rmt8"
  52. #define IGN ' '            /* empty constant for hdr space fill */
  53.  
  54. /*
  55.  * The largest file that we will try to determine recordsize for.
  56.  * Anything larger than this, we just use recordsize = blocksize. 
  57.  */
  58. #define READMAX 100000
  59.  
  60.  
  61. #define LIST        1
  62. #define EXTRACT        2
  63. #define WRITE        3
  64. #define CREATE        4
  65.  
  66.  
  67. #define makelower(c)    ( isupper(c) ? tolower(c) : c)
  68. #define makeupper(c)    ( islower(c) ? toupper(c) : c)
  69. #define max(a,b)    ( a >= b ? a : b )
  70. #define min(a,b)    ( a < b ? a : b )
  71.  
  72. #define SAME 0
  73.  
  74.  
  75. typedef unsigned char FLAG;    /* boolean values only */
  76. #define TRUE        '\001'
  77. #define FALSE        '\000'
  78.  
  79.  
  80. extern int      errno;
  81.  
  82. char           *upstring();
  83. char           *downstring();
  84. char           *tail();
  85. char           *malloc();
  86. char           *alloca();
  87. FLAG            match();
  88. FLAG            eot();
  89.  
  90.  
  91.  
  92. char           *labels[] = {"", "HDR", "EOV", "EOF", ""};
  93. #define TOP_OF_FILE    1
  94. #define END_OF_TAPE    2
  95. #define END_OF_FILE    3
  96. #define END_OF_SET    4
  97. #define UNKNOWN_LABEL    -1
  98.  
  99.  
  100. struct ansi_vol1 vol1;
  101. struct ansi_hdr1 hdr1;
  102. struct ansi_hdr2 hdr2;
  103. struct ansi_hdr3 hdr3;
  104. struct ansi_hdr4 hdr4;
  105.  
  106. struct tape_control_block {
  107.     /* Stuff pertaining to the tape set. */
  108.     FLAG            ebcdic;
  109.     FLAG            ibm_format;
  110.     int             reel_switch;
  111.     char            set_name[6];
  112.     char            tapesys[13];
  113.     char            owner[14];
  114.     char            vol_access;
  115.     int             ansi_level;
  116.     int             reel_count;
  117.  
  118.     /* Stuff pertaining to an individual volume. */
  119.     char            vol_name[6];
  120.     FLAG            mounted;
  121.     int             file_count;
  122.  
  123.     /* Stuff for an individual file. */
  124.     char            tape_filename1[17];
  125.     char            tape_filename2[63];
  126.     char            unix_filename[80];
  127.     int             blk_size;
  128.     int             rec_size;
  129.     char            rec_format;
  130.     char            car_control;
  131.     int             generation;
  132.     int             genversion;
  133.     char            file_access;
  134.     long            created;
  135.     long            expires;
  136.     int             density;
  137.     int             blk_count;
  138.     int             blk_offset;
  139.  
  140.     /* Stuff to be able to read/write on the tape. */
  141.     int             fd;
  142.     char           *buffer;
  143.     int             char_count;
  144.     int             next;
  145.     FLAG            eof;
  146.     FLAG            open;
  147.     int             rw_mode;
  148. }               tcb;
  149.  
  150.  
  151. int             func;        /* What we're supposed to do */
  152. char           *tapename = DEFAULT_TAPE;
  153. FLAG            verbose = FALSE,/* be wordy */
  154.                 query = FALSE,    /* ask before doing */
  155.                 no_hdr3 = FALSE,/* don't write HDR3 or HDR4 */
  156.                 ibm_format = FALSE,    /* use IBM standard label
  157.                      * format */
  158.                 ebcdic = FALSE,    /* tape should be EBCDIC */
  159.                 flag_bs = FALSE,/* blocksize was given */
  160.                 flag_rs = FALSE,/* recordsize was given */
  161.                 flag_stdio = FALSE;    /* do file i/o using stdin/out */
  162.  
  163. char            main_set_name[6] = "UNIX";    /* file set id */
  164. char            main_vol_name[6] = "UNIX";    /* currently-mounted
  165.                          * volume */
  166. char           *username,
  167.                *progname;
  168. int             main_blk_size = 2048,
  169.                 main_rec_size;
  170. char            main_rec_format = REC_VARIABLE;    /* fixed or variable */
  171. char            main_car_control = CC_IMPLIED;    /* carriage control */
  172.  
  173. char          **main_file_list;
  174. int             main_file_count;
  175.  
  176.  
  177. /*
  178.  * Here's the code. 
  179.  */
  180.  
  181. main(argc, argv)
  182.     int             argc;
  183.     char           *argv[];
  184. {
  185.     int             i;
  186.     char           *getlogin();
  187.  
  188.     setbuf(stderr, NULL);    /* unbuffer error output */
  189.  
  190.     username = getlogin();
  191.     progname = argv[0];
  192.     if (username) {
  193.     downstring(username, main_vol_name, 6);
  194.     downstring(username, main_set_name, 6);
  195.     };
  196.  
  197.     if (argc >= 2) {
  198.     /* Now 1-character option names. */
  199.     for (i = 0; argv[1][i]; i++) {
  200.         if (argv[1][i] == '-')
  201.         continue;    /* minus is optional on first keylist */
  202.         switch (argv[1][i]) {
  203.           case 't':
  204.         func = LIST;
  205.         break;
  206.  
  207.           case 'x':
  208.         func = EXTRACT;
  209.         break;
  210.  
  211.           case 'c':
  212.         func = CREATE;
  213.         break;
  214.  
  215.           case 'r':
  216.         if (func != CREATE)
  217.             func = WRITE;
  218.         break;
  219.  
  220.           case 'v':
  221.         verbose = TRUE;
  222.         break;
  223.  
  224.           case 'q':
  225.         query = TRUE;
  226.         break;
  227.  
  228.         /* Do file I/O to stdio instead. */
  229.           case 'f':
  230.         flag_stdio = TRUE;
  231.         break;
  232.  
  233.         /*
  234.          * Suppress HDR3 headers for systems that can't handle
  235.          * them. 
  236.          */
  237.           case '3':
  238.         no_hdr3 = TRUE;
  239.         break;
  240.  
  241.         /* Tape is to be written in ASCII. */
  242.           case 'a':
  243.         ebcdic = FALSE;
  244.         break;
  245.  
  246. #ifdef EBCDIC
  247.           case 'e':
  248.         ebcdic = TRUE;
  249.         break;
  250.  
  251.           case 'i':
  252.         ibm_format = TRUE;
  253.         ebcdic = TRUE;
  254.         break;
  255. #endif
  256.  
  257.           default:
  258.         fprintf(stderr, "unknown key %c - ignored\n", argv[1][i]);
  259.         };
  260.     };
  261.     };
  262.  
  263.     for (i = 2; i < argc; i++) {
  264.     /* Do 2-character options */
  265.  
  266.     if (strncmp(argv[i], "cc=", 3) == SAME) {
  267.         switch (argv[i][3]) {
  268.           case 'f':
  269.         main_car_control = CC_FORTRAN;
  270.         break;
  271.           case 'i':
  272.         main_car_control = CC_IMPLIED;
  273.         break;
  274.           case 'e':
  275.         main_car_control = CC_EMBEDDED;
  276.         break;
  277.           default:
  278.         fprintf(stderr, "unknown carriage control option %s\n", argv[i]);
  279.         };
  280.         continue;
  281.     };
  282.  
  283.     if (strncmp(argv[i], "rs=", 3) == SAME) {
  284.         if (strcmp(argv[i], "rs=r") == SAME) {
  285.         main_rec_size = 0;
  286.         main_rec_format = REC_VARIABLE;
  287.         } else {
  288.         flag_rs = TRUE;
  289.         sscanf(argv[i], "rs=%d", &main_rec_size);
  290.         main_rec_format = REC_FIXED;
  291.         };
  292.         continue;
  293.     };
  294.  
  295.     if (strncmp(argv[i], "bs=", 3) == SAME) {
  296.         flag_bs = TRUE;
  297.         sscanf(argv[i], "bs=%d", &main_blk_size);
  298.         continue;
  299.     };
  300.  
  301.     if (strncmp(argv[i], "vo=", 3) == SAME) {
  302.         downstring(&argv[i][3], main_set_name, 6);
  303.         downstring(&argv[i][3], main_vol_name, 6);
  304.         continue;
  305.     };
  306.  
  307.     if (strncmp(argv[i], "rf=", 3) == SAME) {
  308.         switch (argv[i][3]) {
  309.           case 'f':
  310.         main_rec_format = REC_FIXED;
  311.         break;
  312.           case 'v':
  313.         main_rec_format = REC_VARIABLE;
  314.         break;
  315.           default:
  316.         fprintf(stderr, "unknown record format %c - ignored\n",
  317.             argv[i][3]);
  318.         };
  319.         continue;
  320.     };
  321.  
  322.     if (strncmp(argv[i], "mt=", 3) == SAME) {
  323.         tapename = &argv[i][3];
  324.         continue;
  325.     };
  326.  
  327.     /* That's not an option, assume it's a file name. */
  328.     break;
  329.     };
  330.  
  331.     /* Figure out where and how many file names we have. */
  332.     main_file_count = argc - i;
  333.     main_file_list = argc ? &argv[i] : (char **) 0;
  334.  
  335.     if (main_rec_size > main_blk_size) {
  336.     if (flag_rs) {
  337.         main_blk_size = main_rec_size;
  338.         fprintf(stderr, "blocksize adjusted up to recordsize (%d)\n",
  339.             main_rec_size);
  340.     } else {
  341.         main_rec_size = main_blk_size;
  342.         fprintf(stderr, "recordsize adjusted down to blocksize (%d)\n",
  343.             main_rec_size);
  344.     };
  345.     };
  346.  
  347.     switch (func) {
  348.       case WRITE:
  349.     tcb.fd = open(tapename, O_RDWR, 0666);
  350.     break;
  351.  
  352.       case CREATE:
  353.     tcb.fd = open(tapename, O_RDWR | O_CREAT | O_TRUNC, 0666);
  354.     break;
  355.  
  356.       default:
  357.     tcb.fd = open(tapename, O_RDONLY, 0666);
  358.     }
  359.  
  360.     if (errno) {
  361.     perror("can't open tape device");
  362.     exit(-1);
  363.     };
  364.  
  365.     switch (func) {
  366.       case LIST:
  367.     list_volume_set();
  368.     break;
  369.  
  370.       case CREATE:
  371.       case WRITE:
  372.     write_volume_set();
  373.     break;
  374.  
  375.       case EXTRACT:
  376.     read_volume_set();
  377.     break;
  378.  
  379.       default:
  380.     fprintf(stderr, "specify one of -t, -x, -c, -r\n");
  381.     exit(-1);
  382.     };
  383.     exit(0);
  384. }
  385.  
  386.  
  387. /* List contents of a full volume set. */
  388.  
  389. list_volume_set()
  390. {
  391.     mount_tape();
  392.     for (; ansi_open(FREAD) == TOP_OF_FILE;) {
  393.     ansi_close();
  394.     list_file();
  395.     };
  396. }
  397.  
  398.  
  399. /* List one file, possibly with maximized verbosity. */
  400.  
  401. list_file()
  402. {
  403.     printf("%-22s", tcb.unix_filename);
  404.  
  405.     if (verbose) {
  406.     printf(" %5d blocks", tcb.blk_count);
  407.     printf(", rec fmt %s %d bytes",
  408.            tcb.rec_format == REC_FIXED ? "fixed" : "var max",
  409.            tcb.rec_size);
  410.     switch (tcb.car_control) {
  411.       case CC_IMPLIED:
  412.         printf(", cc implied");
  413.         break;
  414.       case CC_EMBEDDED:
  415.         printf(", cc embedded");
  416.         break;
  417.       case CC_FORTRAN:
  418.         printf(", cc fortran");
  419.         break;
  420.       default:
  421.         printf(", cc code '%c'", tcb.car_control);
  422.     };
  423.     };
  424.     putchar('\n');
  425. }
  426.  
  427.  
  428.  
  429. /*
  430.  * Write files to a volume set.  Uses the ansi_write buffered output
  431.  * function, which automatically chains to new tapes if it has to. 
  432.  */
  433.  
  434. write_volume_set()
  435. {
  436.     int             j;
  437.  
  438.     /*
  439.      * First, make ready to write the tape.  If this is create, then we
  440.      * must init the tape.  If not, mount the tape, and advance to the
  441.      * logical end-of-volume-set. 
  442.      */
  443.  
  444.     if (func == CREATE) {
  445.     tcb.reel_count = 0;
  446.     strncpy(tcb.vol_name, main_vol_name, 6);
  447.     strncpy(tcb.set_name, main_set_name, 6);
  448.     strncpy(tcb.owner, username, 14);
  449.     init_tape();
  450.     } else {
  451.     mount_tape();
  452.  
  453.     do {
  454.         j = ansi_open(FREAD);
  455.         ansi_close();
  456.     } while (j != END_OF_SET);
  457.  
  458.     tape(MTBSF, 1);        /* back up past last tape mark */
  459.     };
  460.  
  461.     for (j = 0; j < main_file_count; j++)
  462.     write_file(main_file_list[j]);
  463.     tape(MTWEOF, 2);
  464. }
  465.  
  466. /*
  467.  * Write from a stream to a tape.  Lots of special stuff, like record
  468.  * and block sizes, are imported implicitly from static variables that
  469.  * were initialized by command-line options. 
  470.  */
  471.  
  472. write_file(fn)
  473.     char           *fn;
  474. {
  475.     int             stat;
  476.     FILE           *fp;
  477.     struct stat     sb;        /* check on our input file */
  478.     int             rlen;    /* length of a single record */
  479.     FLAG            disk_file;    /* our input is from a rewind-able
  480.                  * source */
  481.  
  482.     if (query) {
  483.     fprintf(stderr, "r %s ? ", fn);
  484.     if (!yes_or_no())
  485.         return END_OF_FILE;
  486.     };
  487.  
  488.     if (flag_stdio)
  489.     fp = stdin;
  490.     else
  491.     fp = fopen(fn, "r");
  492.     if (fp == NULL) {
  493.     fprintf(stderr, "can't read %s", fn);
  494.     perror(" - ignored");
  495.     return END_OF_FILE;
  496.     };
  497.  
  498.     strncpy(tcb.unix_filename, tail(fn), 80);
  499.     if (verbose)
  500.     fprintf(stderr, "r %-17s", tcb.unix_filename);
  501.  
  502.     tcb.ibm_format = ibm_format;
  503.     tcb.ebcdic = ebcdic;
  504.     tcb.rec_format = main_rec_format;
  505.     tcb.car_control = main_car_control;
  506.     tcb.generation = 1;
  507.     tcb.genversion = 0;
  508.     tcb.file_access = NOPROTECT;
  509.     tcb.blk_offset = 0;
  510.     tcb.blk_size = main_blk_size;
  511.     tcb.rec_size = flag_rs ? main_rec_size : main_blk_size;
  512.  
  513.     errno = 0;
  514.     fstat(fileno(fp), &sb);
  515.     disk_file = (errno == 0) && (sb.st_mode & S_IFMT) == S_IFREG;
  516.     tcb.created = sb.st_ctime;
  517.  
  518.     if (!flag_rs && disk_file && main_rec_format == REC_VARIABLE) {
  519.     if (sb.st_size < READMAX || main_rec_size == 0) {
  520.         tcb.rec_size = 0;
  521.         rlen = 4;        /* line length field */
  522.         while (!feof(fp)) {
  523.         if (getc(fp) == '\n') {
  524.             tcb.rec_size = max(tcb.rec_size, rlen);
  525.             rlen = 4;    /* line length field */
  526.         } else
  527.             rlen++;
  528.         };
  529.         rewind(fp);
  530.     };
  531.     };
  532.  
  533.     if (tcb.rec_size > tcb.blk_size) {
  534.     fprintf(stderr, "%s recordsize (%d) is too big for blocksize - ignored\n", fn, tcb.rec_size);
  535.     return END_OF_FILE;
  536.     };
  537.  
  538.     ansi_open(FWRITE);
  539.     do {
  540.     stat = write_record(fp);
  541.     } while (stat != END_OF_FILE);
  542.  
  543.     ansi_close();
  544.     if (!flag_stdio)
  545.     fclose(fp);
  546.     if (verbose)
  547.     fprintf(stderr, "  %8d blocks\n", tcb.blk_count);
  548.     return END_OF_FILE;
  549. }
  550.  
  551.  
  552. /* Write a block of data */
  553.  
  554. write_record(fp)
  555.     FILE           *fp;
  556. {
  557.     char           *linebuf;
  558.     int             rl;
  559.  
  560.     linebuf = alloca(tcb.blk_size);    /* goes away on function exit */
  561.  
  562.     if (tcb.rec_format == REC_FIXED) {
  563.     rl = fread(linebuf, sizeof(char), tcb.rec_size, fp);
  564.     if (rl == 0)
  565.         return END_OF_FILE;
  566.     } else {
  567.     /* format is variable-length records */
  568.     if (fgets(linebuf, tcb.rec_size, fp) == NULL)
  569.         return END_OF_FILE;
  570.     rl = strlen(linebuf);
  571.     };
  572.  
  573.     ansi_write(linebuf, rl);
  574.     return 0;
  575. }
  576.  
  577.  
  578.  
  579. /*
  580.  * Read from a tape.  The var main_file_list is a list of files that we
  581.  * should extract.  We apply filename wildcarding using ? and * to the
  582.  * names.  (These must be quoted to prevent shell interpretation.)  If
  583.  * no filenames are supplied, then we extract everything. 
  584.  */
  585.  
  586. read_volume_set()
  587. {
  588.     int             i;
  589.     FLAG            extract;
  590.  
  591.     for (; ansi_open(FREAD) != END_OF_SET;) {
  592.     extract = main_file_count == 0;
  593.     for (i = 0; i < main_file_count && !extract; i++) {
  594.         extract = (match(tcb.unix_filename, main_file_list[i]));
  595.     };
  596.  
  597.     if (extract) {
  598.         if (query) {
  599.         fprintf(stderr, "x  %s ? ", tcb.unix_filename);
  600.         if (!yes_or_no())
  601.             continue;
  602.         };
  603.         if (verbose)
  604.         fprintf(stderr, "x  %-17s", tcb.unix_filename);
  605.         read_file();
  606.     };
  607.  
  608.     ansi_close();
  609.     scan_labels();
  610.     if (extract && verbose)
  611.         fprintf(stderr, "  %8d blocks\n", tcb.blk_count);
  612.     };
  613. }
  614.  
  615.  
  616. read_file()
  617. {
  618.     int             max_reclen,
  619.                     reclen;
  620.     char           *record;
  621.     FILE           *fp;
  622.  
  623.     record = malloc(max_reclen = tcb.rec_size + 20);
  624.     /* a little extra room */
  625.  
  626.     if (!flag_stdio)
  627.     fp = fopen(tcb.unix_filename, "w");
  628.     else
  629.     fp = stdout;
  630.  
  631.     while ((reclen = ansi_read(record, max_reclen)) != NULL) {
  632.     fwrite(record, sizeof(char), reclen, fp);
  633.     };
  634.     if (!flag_stdio)
  635.     fclose(fp);
  636. }
  637.  
  638.  
  639.  
  640. /***************************************************
  641.  *                                                 *
  642.  *   A N S I   S U P P O R T   R O U T I N E S     *
  643.  *                                                 *
  644.  ***************************************************/
  645.  
  646.  
  647. /* ANSI_OPEN.  Open up a tape file, and prepare to read it. */
  648.  
  649. ansi_open(mode)
  650.     int             mode;
  651. {
  652.     int             err;
  653.  
  654.     err = 0;
  655.     if (!tcb.mounted) {
  656.     if (mode == FREAD)
  657.         mount_tape();
  658.     else
  659.         init_tape();
  660.     };
  661.  
  662.     if (mode == FREAD) {
  663.     /* We are going to read a tape. */
  664.     err = read_labels();
  665.     if (err != TOP_OF_FILE)
  666.         return err;
  667.     scan_labels();
  668.     } else {
  669.     /* We are writing a tape. */
  670.     tcb.reel_switch = FALSE;
  671.     tcb.file_count++;
  672.     tcb.blk_count = 0;
  673.  
  674.     make_labels(TOP_OF_FILE);
  675.     write_labels();
  676.     err = TOP_OF_FILE;
  677.     };
  678.  
  679.     if (err != UNKNOWN_LABEL) {
  680.     tcb.open = TRUE;
  681.     tcb.eof = FALSE;
  682.     tcb.rw_mode = mode;
  683.     tcb.blk_count = 0;
  684.     tcb.next = 0;
  685.     tcb.char_count = 0;
  686.     };
  687.  
  688.     return err;
  689. }
  690.  
  691.  
  692. /*
  693.  * ANSI_READ.  Read in a record from an buffered, open tape file. We
  694.  * assume that nothing perverted has happened, like the recordsize
  695.  * being longer than the buffersize. We do automatic reel switching if
  696.  * needed. Return is number of charcters read, or END_OF_TAPE. 
  697.  */
  698.  
  699. ansi_read(bufp, bufl)
  700.     char            bufp[];
  701. int             bufl;
  702. {
  703.     char            rl[5];
  704.     int             read_min,
  705.                     read_count;
  706.  
  707.     if (tcb.eof || !tcb.open)
  708.     return NULL;
  709.  
  710.     read_min = (tcb.rec_format == REC_VARIABLE) ? 4
  711.     : tcb.rec_size;
  712.  
  713.     if (tcb.next + read_min > tcb.char_count) {
  714.     while (tcb.next < tcb.char_count)
  715.         ansi_getc();
  716.     };
  717.  
  718.     if (tcb.rec_format == REC_VARIABLE) {
  719.     do {
  720.         rl[0] = ansi_getc();
  721.     } while (!isdigit(rl[0]) && !tcb.eof);
  722.     rl[1] = ansi_getc();
  723.     rl[2] = ansi_getc();
  724.     rl[3] = ansi_getc();
  725.     rl[4] = '\0';
  726.     read_min = atoi(rl) - 4;
  727.     };
  728.  
  729.     if (tcb.eof)
  730.     return NULL;
  731.  
  732.     read_count = 0;
  733.     for (; bufl && read_count < read_min && !tcb.eof;) {
  734.     *(bufp++) = ansi_getc();
  735.     bufl--, read_count++;
  736.     };
  737.  
  738.     if (bufl && tcb.car_control == CC_IMPLIED) {
  739.     *(bufp++) = '\n';
  740.     bufl--, read_count++;
  741.     };
  742.     if (bufl)
  743.     *bufp = '\0';
  744.  
  745.     return read_count;
  746. }
  747.  
  748.  
  749. /* Read a single character from the tape buffer. */
  750.  
  751. ansi_getc()
  752. {
  753.     int             err;
  754.  
  755. try:
  756.     if (tcb.eof)
  757.     return END_OF_FILE;
  758.  
  759.     if (tcb.buffer && tcb.open) {
  760.     if (tcb.char_count <= tcb.next) {
  761.         tcb.next = 0;
  762.         tcb.char_count = read_tape(tcb.buffer, tcb.blk_size);
  763.         if (tcb.char_count == 0)
  764.         goto do_eof;
  765.     };
  766.     return tcb.buffer[tcb.next++];
  767.     };
  768.  
  769.     if (!tcb.buffer) {
  770.     tcb.buffer = malloc(tcb.blk_size);
  771.     tcb.next = 0;
  772.     goto try;
  773.     };
  774.  
  775. do_eof:
  776.     tcb.eof = TRUE;
  777.     tcb.open = FALSE;
  778.     err = read_labels();
  779.     if (err == END_OF_TAPE) {
  780.     next_volume();
  781.     tcb.eof = FALSE;
  782.     tcb.open = TRUE;
  783.     };
  784.     goto try;
  785. }
  786.  
  787.  
  788. /*
  789.  * ANSI_WRITE.  Write a record, given a pointer and a length. We do all
  790.  * the formatting necessary.  Flush the tape buffer if we run off the
  791.  * end, and start a new one. 
  792.  */
  793.  
  794. ansi_write(rec, len)
  795.     char            rec[];
  796. int             len;
  797. {
  798.     int             overhead = 0;
  799.     char            scratch[10];
  800.  
  801.     if (!tcb.buffer) {
  802.     tcb.buffer = malloc(tcb.blk_size);
  803.     tcb.next = 0;
  804.     tcb.char_count = tcb.blk_size;
  805.     fill(tcb.buffer, tcb.blk_size, '^');
  806.     };
  807.  
  808.     if (tcb.car_control == CC_IMPLIED && len > 0 && iscntrl(rec[len - 1]))
  809.     rec[--len] = '\0';
  810.  
  811.     /* Check to see if we will run off the end of the block. */
  812.     overhead = (tcb.rec_format == REC_VARIABLE) ? 4 : 0;
  813.     if (len + overhead + tcb.next > tcb.blk_size) {
  814.  
  815.     /* Write the old tape block. */
  816.     write_tape(tcb.buffer, tcb.blk_size);
  817.     tcb.blk_count++;
  818.  
  819.     /* Check for physical end of tape. */
  820.     if (eot()) {
  821.         make_labels(END_OF_TAPE);
  822.         write_labels();
  823.         next_volume();
  824.     };
  825.  
  826.     /* Initialize a new tape block. */
  827.     fill(tcb.buffer, tcb.blk_size, '^');
  828.     tcb.next = tcb.blk_offset;
  829.     };
  830.  
  831.     if (tcb.rec_format == REC_VARIABLE) {
  832.     sprintf(scratch, "%04d", len + 4);
  833.     strncpy(&tcb.buffer[tcb.next], scratch, 4);
  834.     tcb.next += 4;
  835.     };
  836.  
  837.     bcopy(rec, &tcb.buffer[tcb.next], len);
  838.     tcb.next += len;
  839.  
  840.     return 0;
  841. }
  842.  
  843.  
  844. /*
  845.  * ANSI_CLOSE.  Close out a file.  We may not have read all the data in
  846.  * the file yet.  If that's so, we skip over the intervening data.
  847.  * Remember that we may have to switch reels. 
  848.  */
  849.  
  850. ansi_close()
  851. {
  852.     int             err;
  853.  
  854.     if (!tcb.open)
  855.     return 0;
  856.  
  857.     if (tcb.rw_mode == FWRITE) {
  858.     if (tcb.next > tcb.blk_offset) {
  859.         write_tape(tcb.buffer, tcb.next);
  860.         tcb.next = tcb.blk_offset;
  861.         tcb.blk_count++;
  862.     };
  863.     tape(MTWEOF, 1);
  864.     make_labels(END_OF_FILE);
  865.     write_labels();
  866.     goto closed;
  867.     };
  868.  
  869.     for (;;) {
  870.     if (!tcb.eof)
  871.         skip_past_marks(1);
  872.     err = read_labels();
  873.     if (err == END_OF_FILE)
  874.         break;
  875.     next_volume();
  876.     tcb.eof = 0;
  877.     };
  878.     scan_labels();
  879.  
  880. closed:
  881.     tcb.eof = tcb.open = FALSE;
  882.     if (tcb.buffer)
  883.     free(tcb.buffer);
  884.     tcb.buffer = 0;
  885.     tcb.char_count = tcb.next = 0;
  886.     return 0;
  887. }
  888.  
  889.  
  890.  
  891. /*
  892.  * Support routines. 
  893.  */
  894.  
  895.  
  896. next_volume()
  897. {
  898.     tape(MTOFFL, 1);
  899.     fprintf(stderr, "Mount continuation tape and push return.  ");
  900.     yes_or_no();
  901.     if (func == LIST || func == EXTRACT) {
  902.     mount_tape();
  903.     read_labels();        /* skip the file continuation HDRn
  904.                  * records */
  905.     } else {
  906.     init_tape();
  907.     ansi_open(tcb.rw_mode);
  908.     };
  909. }
  910.  
  911.  
  912. /*
  913.  * Write a volume header record.  We only write VOL1, since VAX/VMS
  914.  * does not use the VOL2 record. 
  915.  */
  916.  
  917. init_tape()
  918. {
  919.     char            scratch[90];
  920.  
  921.     tcb.ebcdic = ebcdic;
  922.     tcb.ibm_format = ibm_format;
  923.     tcb.ansi_level = 3;
  924.     strncpy(tcb.tapesys, SYSID, 13);
  925.  
  926.     fill(scratch, 90, ' ');
  927.     sprintf(scratch, "VOL1%-6.6s%c",
  928.         tcb.vol_name,    /* volume name */
  929.         NOPROTECT        /* access protection */
  930.     );
  931.  
  932.     if (tcb.ibm_format)
  933.     sprintf(&scratch[41], "%-10.10s%29c",
  934.         tcb.owner,    /* owner of volume set */
  935.         IGN        /* ignored 3 and ansi_level */
  936.         );
  937.     else
  938.     sprintf(&scratch[37], "%-14.14s%28c%1d",
  939.         tcb.owner,    /* owner of volume set */
  940.         IGN,        /* ignored 3 */
  941.         tcb.ansi_level    /* ansi label standard level */
  942.         );
  943.  
  944.     fillnull(scratch, ' ', sizeof(vol1));
  945.     upstring(scratch, (char *) &vol1, sizeof(vol1));
  946.     write_tape((char *) &vol1, sizeof(vol1));
  947.  
  948.     tcb.reel_count++;
  949.     tcb.file_count = 0;
  950.     tcb.mounted = TRUE;
  951.  
  952.     if (verbose)
  953.     fprintf(stderr, "volume %-6s initialized\n", tcb.vol_name);
  954. }
  955.  
  956.  
  957. /* Read a volume header.  Save the volume name for later. */
  958.  
  959. mount_tape()
  960. {
  961.     tcb.ebcdic = ebcdic;
  962.     tcb.ibm_format = ibm_format;
  963.  
  964.     read_tape((char *) &vol1, sizeof(vol1));
  965.     downstring((char *) &vol1, (char *) &vol1, sizeof(vol1));
  966.     if (strncmp(vol1.header, "vol1", 4) != 0) {
  967.     fprintf(stderr, "no VOL1 header record\n");
  968.     exit(-1);
  969.     };
  970.  
  971.     tcb.mounted = TRUE;
  972.     sscanf((char *) &vol1, "vol1%6c%c",
  973.        tcb.vol_name,
  974.        &tcb.vol_access
  975.     );
  976.  
  977.     if (tcb.ibm_format)
  978.     sscanf(&vol1.owner[4], "%10c", tcb.owner);
  979.     else
  980.     sscanf(vol1.owner, "%14c%*28c%1d",
  981.            tcb.owner,
  982.            &tcb.ansi_level
  983.         );
  984.  
  985.     if (verbose)
  986.     fprintf(stderr, "volume %-6s mounted\n", tcb.vol_name);
  987. }
  988.  
  989.  
  990. /*
  991.  * Extract the information from a set of headers fetched by
  992.  * read_labels(). 
  993.  */
  994.  
  995. scan_labels()
  996. {
  997.     sscanf((char *) &hdr4, "%*3c4%63c", tcb.tape_filename2);
  998.  
  999.     sscanf((char *) &hdr1, "%*3c1%17c%6c%4d%4d%4d%2d",
  1000.        tcb.tape_filename1,
  1001.        tcb.set_name,
  1002.        &tcb.reel_count,
  1003.        &tcb.file_count,
  1004.        &tcb.generation,
  1005.        &tcb.genversion);
  1006.     sscanf(hdr1.created, " %5d %5d%c%6d%17c",
  1007.        &tcb.created,
  1008.        &tcb.expires,
  1009.        &tcb.file_access,
  1010.        &tcb.blk_count,
  1011.        tcb.tapesys);
  1012.  
  1013.     sscanf((char *) &hdr2, "%*3c2%c%5d%5d%1d%1d%*19c%c%*15c%2d",
  1014.        &tcb.rec_format,
  1015.        &tcb.blk_size,
  1016.        &tcb.rec_size,
  1017.        &tcb.density,
  1018.        &tcb.reel_switch,
  1019.        &tcb.car_control,
  1020.        &tcb.blk_offset);
  1021.  
  1022.     if (tcb.ibm_format && hdr2.recfmt == IBM_VARIABLE)
  1023.     tcb.rec_format = REC_VARIABLE;
  1024.  
  1025.     bzero(tcb.unix_filename, 80);
  1026.     sprintf(tcb.unix_filename, "%.17s%.63s",
  1027.         tcb.tape_filename1,
  1028.         tcb.tape_filename2);
  1029.  
  1030.     sscanf(tcb.unix_filename, "%80s", tcb.unix_filename);
  1031.  
  1032.     return 0;
  1033. }
  1034.  
  1035.  
  1036. /*
  1037.  * Write the header for a file whose particulars are in our global
  1038.  * static variables. 
  1039.  */
  1040.  
  1041. make_labels(type)
  1042.     int             type;
  1043. {
  1044.     char            scratch[90];
  1045.     int             namelen;
  1046.  
  1047.     /*
  1048.      * Split up unix_filename into two parts. First is rightmost 17
  1049.      * characters of name, second is other characters, up to 63. 
  1050.      */
  1051.  
  1052.     namelen = strlen(tcb.unix_filename);
  1053.     namelen = min(17, namelen);
  1054.     strncpy(tcb.tape_filename1, tcb.unix_filename, 17);
  1055.     strncpy(tcb.tape_filename2, &tcb.unix_filename[namelen], 63);
  1056.  
  1057.     sprintf(scratch, "%3s1%-17.17s%-6.6s%04d%04d%04d%02d",
  1058.         labels[type],
  1059.         tcb.tape_filename1,
  1060.         tcb.set_name,
  1061.         tcb.reel_count,
  1062.         tcb.file_count,
  1063.         tcb.generation,
  1064.         tcb.genversion);
  1065.     sprintf(&scratch[41], " %05d %05d%1c%06d%-13.13s%7c",
  1066.         ansi_date(tcb.created),
  1067.         ansi_date(tcb.expires),
  1068.         tcb.file_access,
  1069.         tcb.blk_count,
  1070.         tcb.tapesys,
  1071.         IGN);
  1072.     upstring(scratch, (char *) &hdr1, sizeof(hdr1));
  1073.  
  1074.     sprintf(scratch, "%3s2%c%05d%05d%1d%1d%-8.8s/%-8.8s  %1c B%11c%02d%28c",
  1075.         labels[type],
  1076.         tcb.rec_format,
  1077.         tcb.blk_size,
  1078.         tcb.rec_size,
  1079.         tcb.density,
  1080.         tcb.reel_switch,
  1081.         username, progname,    /* job name & job step */
  1082.         tcb.car_control,
  1083.         IGN,
  1084.         tcb.blk_offset,
  1085.         IGN);
  1086.     upstring(scratch, (char *) &hdr2, sizeof(hdr2));
  1087.  
  1088.     /* IBM uses different code for variable-length records. */
  1089.     if (tcb.ibm_format && tcb.rec_format == REC_VARIABLE)
  1090.     hdr2.recfmt = IBM_VARIABLE;
  1091.  
  1092.     if (!tcb.ibm_format && !no_hdr3) {
  1093.     sprintf(scratch, "%3s3%76c",
  1094.         labels[type],
  1095.         IGN);
  1096.     upstring(scratch, (char *) &hdr3, sizeof(hdr3));
  1097.     sprintf(scratch, "%3s4%-63.63s00%11c",
  1098.         labels[type],
  1099.         tcb.tape_filename2,
  1100.         IGN);
  1101.     upstring(scratch, (char *) &hdr4, sizeof(hdr4));
  1102.     };
  1103. }
  1104.  
  1105.  
  1106. /*
  1107.  * Write out tape headers that have already been made by make_labels(). 
  1108.  */
  1109.  
  1110. write_labels()
  1111. {
  1112.     upstring((char *) &hdr1, (char *) &hdr1, sizeof(hdr1));
  1113.     upstring((char *) &hdr2, (char *) &hdr2, sizeof(hdr2));
  1114.     upstring((char *) &hdr3, (char *) &hdr3, sizeof(hdr3));
  1115.     upstring((char *) &hdr4, (char *) &hdr4, sizeof(hdr4));
  1116.  
  1117.     write_tape((char *) &hdr1, sizeof(hdr1));
  1118.     write_tape((char *) &hdr2, sizeof(hdr2));
  1119.  
  1120.     if (!tcb.ibm_format && tcb.ansi_level >= 3 && !no_hdr3) {
  1121.     write_tape((char *) &hdr3, sizeof(hdr3));
  1122.     write_tape((char *) &hdr4, sizeof(hdr4));
  1123.     };
  1124.  
  1125.     tape(MTWEOF, 1);
  1126.     return 0;
  1127. }
  1128.  
  1129.  
  1130. /* Read a tape header.  Set it up for scan_labels(). */
  1131.  
  1132. read_labels()
  1133. {
  1134.     int             l;
  1135.     int             label_type;
  1136.     struct {
  1137.     char            header[3];
  1138.     char            seq;
  1139.     char            data[76];
  1140.     }               inhdr;
  1141.  
  1142.     fill((char *) &hdr1, sizeof(hdr1), ' ');
  1143.     fill((char *) &hdr2, sizeof(hdr2), ' ');
  1144.     fill((char *) &hdr3, sizeof(hdr3), ' ');
  1145.     fill((char *) &hdr4, sizeof(hdr4), ' ');
  1146.  
  1147.     l = read_tape((char *) &inhdr, sizeof(inhdr));
  1148.     if (l == 0)
  1149.     return END_OF_SET;
  1150.  
  1151.     for (label_type = 1; label_type <= 4; label_type++)
  1152.     if (strncmp(inhdr.header, labels[label_type], 3) == 0)
  1153.         break;
  1154.  
  1155.     if (label_type > 4)
  1156.     label_type = UNKNOWN_LABEL;
  1157.  
  1158.     for (; l; l = read_tape((char *) &inhdr, sizeof(inhdr))) {
  1159.     if (label_type == UNKNOWN_LABEL || strncmp(inhdr.header, labels[label_type], 3) != 0)
  1160.         continue;
  1161.     switch (inhdr.seq) {
  1162.       case '1':
  1163.         downstring((char *) &inhdr, (char *) &hdr1, sizeof(hdr1));
  1164.         break;
  1165.       case '2':
  1166.         downstring((char *) &inhdr, (char *) &hdr2, sizeof(hdr2));
  1167.         break;
  1168.       case '3':
  1169.         downstring((char *) &inhdr, (char *) &hdr3, sizeof(hdr3));
  1170.         break;
  1171.       case '4':
  1172.         downstring((char *) &inhdr, (char *) &hdr4, sizeof(hdr4));
  1173.     };
  1174.     };
  1175.  
  1176.     return label_type;
  1177. }
  1178.  
  1179.  
  1180. /*
  1181.  * Return an integer with an ANSI date for the time of creation
  1182.  * (actually, last modification) of a file. 
  1183.  */
  1184.  
  1185. ansi_date(mtime)
  1186.     long            mtime;
  1187. {
  1188.     struct tm      *tm,
  1189.                    *gmtime();
  1190.  
  1191.     if (mtime == 0)
  1192.     mtime = time((long *) 0);
  1193.     tm = gmtime(&mtime);
  1194.     return 1000 * tm->tm_year + tm->tm_yday;
  1195. }
  1196.  
  1197. /*
  1198.  * Write a block of data to the tape drive.  If the tape should be
  1199.  * operated as an EBCDIC device, do the conversion. 
  1200.  */
  1201.  
  1202. write_tape(buffer, buflen)
  1203.     char           *buffer;
  1204.     int             buflen;
  1205. {
  1206.     int             err;
  1207.  
  1208. #ifdef EBCDIC
  1209.     if (tcb.ebcdic)
  1210.     to_ebcdic(buffer, buffer, buflen);
  1211. #endif
  1212.  
  1213.     err = write(tcb.fd, buffer, buflen);
  1214.     return err;
  1215. }
  1216.  
  1217.  
  1218. /*
  1219.  * Read a block from the tape drive.  If the tape should be operated as
  1220.  * an EBCDIC device, do the conversion. 
  1221.  */
  1222.  
  1223. read_tape(buffer, buflen)
  1224.     char           *buffer;
  1225.     int             buflen;
  1226. {
  1227.     int             inlen;
  1228.  
  1229.     inlen = read(tcb.fd, buffer, buflen);
  1230. #ifdef EBCDIC
  1231.     if (tcb.ebcdic)
  1232.     to_ascii(buffer, buffer, inlen);
  1233. #endif
  1234.  
  1235.     return inlen;
  1236. }
  1237.  
  1238.  
  1239. /*
  1240.  * Skip past some number of files on a tape drive. This isn't really
  1241.  * needed on anything but streaming drives. 
  1242.  */
  1243.  
  1244. skip_past_marks(count)
  1245.     int             count;
  1246. {
  1247.     int             i;
  1248.     static char     ignore[20];
  1249.  
  1250.     if (count > 1) {
  1251.     tape(MTFSF, count);
  1252.     return;
  1253.     };
  1254.  
  1255.     for (i = 0; i < 6; i++) {
  1256.     if (read(tcb.fd, ignore, 20) == 0)
  1257.         return;
  1258.     };
  1259.  
  1260.     tape(MTFSF, 1);
  1261. }
  1262.  
  1263.  
  1264. /* Do a special operation on a tape drive. */
  1265.  
  1266. tape(op, count)
  1267.     int             op,
  1268.                     count;
  1269. {
  1270.     struct mtop     mt;
  1271.  
  1272.     mt.mt_op = op;
  1273.     mt.mt_count = count;
  1274.     ioctl(tcb.fd, MTIOCTOP, &mt);
  1275. }
  1276.  
  1277.  
  1278. /* Find the last pathname component of a pathname. */
  1279.  
  1280. char           *
  1281. tail(path)
  1282.     char            path[];
  1283. {
  1284.     int             ix = strlen(path);
  1285.  
  1286.     ix = strlen(path) - 1;
  1287.     for (; ix >= 0; --ix) {
  1288.     if (path[ix] == '/')
  1289.         return &path[++ix];
  1290.     };
  1291.  
  1292.     return path;
  1293. }
  1294.  
  1295.  
  1296. /*
  1297.  * Match a string (s) against a wildcard key.  Returns 1 if they match,
  1298.  * or 0 if they don't. 
  1299.  */
  1300.  
  1301. FLAG
  1302. match(s, key)
  1303.     char           *s,
  1304.                    *key;
  1305. {
  1306.     if (*s == '\0') {
  1307.     switch (*key) {
  1308.       case '\0':
  1309.         return TRUE;
  1310.  
  1311.       case '*':
  1312.         return match(s, ++key);
  1313.  
  1314.       default:
  1315.         return FALSE;
  1316.     };
  1317.     };
  1318.  
  1319.     if (*key == '?')
  1320.     return match(++s, ++key);
  1321.  
  1322.     if (*key == '*')
  1323.     return match(s, (key + 1)) || match((s + 1), key);
  1324.  
  1325.     if (*s == *key)
  1326.     return match(++s, ++key);
  1327.  
  1328.     return FALSE;
  1329. }
  1330.  
  1331.  
  1332. /*
  1333.  * Determine if we are off the physical end of the tape.  For now, just
  1334.  * say we haven't got there yet.  There is no device-independent way to
  1335.  * know when we hit EOT, so if we do this for real, we have a program
  1336.  * that requires a pre-programmed knowledge of the tape controller in
  1337.  * use. 
  1338.  */
  1339.  
  1340. FLAG
  1341. eot()
  1342. {
  1343.     return FALSE;
  1344. }
  1345.  
  1346.  
  1347. /*
  1348.  * Ask the user something, and return ok if we get anything starting
  1349.  * with the letter 'y'. 
  1350.  */
  1351.  
  1352. yes_or_no()
  1353. {
  1354.     char            buf[256];
  1355.     static FILE    *user = NULL;
  1356.  
  1357.     if (user == NULL) {
  1358.     if (flag_stdio)
  1359.         user = fopen("/dev/tty", "r");
  1360.     else
  1361.         user = stdin;
  1362.     };
  1363.  
  1364.     fgets(buf, 256, user);
  1365.     return makelower(buf[0]) == 'y';
  1366. }
  1367.  
  1368.  
  1369. /* Convert a string to upper-case, for at most n characters */
  1370.  
  1371. char           *
  1372. upstring(from, to, len)
  1373.     char           *from,
  1374.                    *to;
  1375.     int             len;
  1376. {
  1377.     char           *to1 = to;
  1378.  
  1379.     for (; len--;) {
  1380.     *to = makeupper(*from);
  1381.     if (*from == '\0')
  1382.         break;
  1383.     to++, from++;
  1384.     };
  1385.  
  1386.     return to1;
  1387. }
  1388.  
  1389. char           *
  1390. downstring(from, to, len)
  1391.     char           *from,
  1392.                    *to;
  1393.     int             len;
  1394. {
  1395.     char           *to1 = to;
  1396.  
  1397.     for (; len--;) {
  1398.     *to = makelower(*from);
  1399.     if (*from == '\0')
  1400.         break;
  1401.     to++, from++;
  1402.     };
  1403.  
  1404.     return to1;
  1405. }
  1406.  
  1407.  
  1408. /* Fill an area with some character. */
  1409.  
  1410. fill(area, size, c)
  1411.     char           *area;
  1412.     int             size;
  1413.     char            c;
  1414. {
  1415.     for (; size--;)
  1416.     *(area++) = c;
  1417. }
  1418.  
  1419.  
  1420. /* Fill in nulls left by sprintf. */
  1421.  
  1422. fillnull(area, fill, len)
  1423.     char           *area,
  1424.                     fill;
  1425.     int             len;
  1426. {
  1427.     for (; len--; area++)
  1428.     if (*area == '\0')
  1429.         *area = fill;
  1430. }
  1431.