home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / read-vms-backs / vmsbackup.c < prev   
Encoding:
C/C++ Source or Header  |  1987-01-19  |  16.2 KB  |  833 lines

  1. /*
  2.  *
  3.  *  Title:
  4.  *    Backup
  5.  *
  6.  *  Decription:
  7.  *    Program to read VMS backup tape
  8.  *
  9.  *  Author:
  10.  *    John Douglas CAREY.
  11.  *    Sven-Ove Westberg    (version 3.0)
  12.  *
  13.  *  Net-addess:
  14.  *    john%monu1.oz@seismo.ARPA
  15.  *    luthcad!sow@enea.UUCP
  16.  *
  17.  *  History:
  18.  *    Version 1.0 - September 1984
  19.  *        Can only read variable length records
  20.  *    Version 1.1
  21.  *        Cleaned up the program from the original hack
  22.  *        Can now read stream files
  23.  *    Version 1.2
  24.  *        Now convert filename from VMS to UNIX
  25.  *            and creates sub-directories
  26.  *    Version 1.3
  27.  *        Works on the Pyramid if SWAP is defined
  28.  *    Version 1.4
  29.  *        Reads files spanning multiple tape blocks
  30.  *    Version 1.5
  31.  *        Always reset reclen = 0 on file open
  32.  *        Now output fixed length records
  33.  *
  34.  *      Version 2.0 - July 1985
  35.  *        VMS Version 4.0 causes a rethink !!
  36.  *        Now use mtio operations instead of opening and closing file
  37.  *        Blocksize now grabed from the label
  38.  *
  39.  *    Version 2.1 - September 1985
  40.  *        Handle variable length records of zero length.
  41.  *
  42.  *    Version 2.2 - July 1986
  43.  *        Handle FORTRAN records of zero length.
  44.  *        Inserted exit(0) at end of program.
  45.  *        Distributed program in aus.sources
  46.  *
  47.  *    Version 2.3 - August 1986
  48.  *        Handle FORTRAN records with record length fields
  49.  *        at the end of a block
  50.  *        Put debug output to a file.
  51.  *        Distributed program in net.sources
  52.  *
  53.  *    Version 3.0 - December 1986
  54.  *        Handle multiple saveset 
  55.  *        Remote tape
  56.  *        Interactive mode
  57.  *        File name selection with meta-characters
  58.  *        Convert ; to : in VMS filenames
  59.  *        Flag for usage of VMS directory structure
  60.  *        Flag for "useless" files  eg. *.exe
  61.  *        Flag for use VMS version in file names
  62.  *        Flag for verbose mode
  63.  *        Flag to list the contents of the tape
  64.  *        Distributed to mod.sources
  65.  *
  66.  *
  67.  *  Installation:
  68.  *
  69.  *    Computer Centre
  70.  *    Monash University
  71.  *    Wellington Road
  72.  *    Clayton
  73.  *    Victoria    3168
  74.  *    AUSTRALIA
  75.  *
  76.  */
  77. #include    <stdio.h>
  78. #include    <ctype.h>
  79.  
  80. #include    <sys/ioctl.h>
  81. #include    <sys/types.h>
  82. #ifdef REMOTE
  83. #include    <local/rmt.h>
  84. #include    <sys/stat.h>
  85. #endif
  86. #include    <sys/mtio.h>
  87. #include    <sys/file.h>
  88.  
  89. #ifdef pyr
  90. #define SWAP
  91. #endif pyr
  92.  
  93. #ifdef sun
  94. #define SWAP
  95. #endif
  96.  
  97. struct bbh {
  98.     short    bbh_dol_w_size;
  99.     short    bbh_dol_w_opsys;
  100.     short    bbh_dol_w_subsys;
  101.     short    bbh_dol_w_applic;
  102.     long    bbh_dol_l_number;
  103.     char    bbh_dol_t_spare_1[20];
  104.     short    bbh_dol_w_struclev;
  105.     short    bbh_dol_w_volnum;
  106.     long    bbh_dol_l_crc;
  107.     long    bbh_dol_l_blocksize;
  108.     long    bbh_dol_l_flags;
  109.     char    bbh_dol_t_ssname[32];
  110.     short    bbh_dol_w_fid[3];
  111.     short    bbh_dol_w_did[3];
  112.     char    bbh_dol_t_filename[128];
  113.     char    bbh_dol_b_rtype;
  114.     char    bbh_dol_b_rattrib;
  115.     short    bbh_dol_w_rsize;
  116.     char    bbh_dol_b_bktsize;
  117.     char    bbh_dol_b_vfcsize;
  118.     short    bbh_dol_w_maxrec;
  119.     long    bbh_dol_l_filesize;
  120.     char    bbh_dol_t_spare_2[22];
  121.     short    bbh_dol_w_checksum;
  122. } *block_header;
  123.  
  124. struct brh {
  125.     short    brh_dol_w_rsize;
  126.     short    brh_dol_w_rtype;
  127.     long    brh_dol_l_flags;
  128.     long    brh_dol_l_address;
  129.     long    brh_dol_l_spare;
  130. } *record_header;
  131.  
  132. /* define record types */
  133.  
  134. #define    brh_dol_k_null    0
  135. #define    brh_dol_k_summary    1
  136. #define    brh_dol_k_volume    2
  137. #define    brh_dol_k_file    3
  138. #define    brh_dol_k_vbn    4
  139. #define brh_dol_k_physvol    5
  140. #define brh_dol_k_lbn    6
  141. #define    brh_dol_k_fid    7
  142.  
  143. struct bsa {
  144.     short    bsa_dol_w_size;
  145.     short    bsa_dol_w_type;
  146.     char    bsa_dol_t_text[1];
  147. } *data_item;
  148.  
  149. #ifdef    STREAM
  150. char    *def_tapefile = "/dev/rts8";
  151. #else
  152. char    *def_tapefile = "/dev/rmt8";
  153. #endif
  154. char    *tapefile;
  155.  
  156. char    filename[128];
  157. int    filesize;
  158.  
  159. char    recfmt;        /* record format */
  160.  
  161. #define            FAB_dol_C_UDF    0    /* undefined */
  162. #define            FAB_dol_C_FIX    1    /* fixed-length record */
  163. #define            FAB_dol_C_VAR    2    /* variable-length record */
  164. #define            FAB_dol_C_VFC    3    /* variable-length with fixed-length control record */
  165. #define         FAB_dol_C_STM    4    /* RMS-11 stream record (valid only for sequential org) */
  166. #define            FAB_dol_C_STMLF    5    /* stream record delimited by LF (sequential org only) */
  167. #define         FAB_dol_C_STMCR    6    /* stream record delimited by CR (sequential org only) */
  168. #define            FAB_dol_C_MAXRFM    6    /* maximum rfm supported */
  169.  
  170. char    recatt;        /* record attributes */
  171.  
  172. #define            FAB_dol_V_FTN    0    /* FORTRAN carriage control character */
  173. #define            FAB_dol_V_CR    1    /* line feed - record -carriage return */
  174. #define            FAB_dol_V_PRN    2    /* print-file carriage control */
  175. #define            FAB_dol_V_BLK    3    /* records don't cross block boundaries */
  176.  
  177. #define    FANO    20
  178.  
  179. #ifdef    pyr
  180. static struct    bsa    *file_table[FANO];
  181. #else
  182. struct    bsa    *file_table[FANO];
  183. #endif
  184.  
  185. FILE    *f    = NULL;
  186. int    file_count;
  187. short    reclen;
  188. short    fix;
  189. short    recsize;
  190. int    vfcsize;
  191.  
  192. #ifdef    NEWD
  193. FILE    *lf;
  194. #endif    NEWD
  195.  
  196. int    fd;        /* tape file descriptor */
  197. int     cflag, dflag, eflag, sflag, tflag, vflag, wflag, xflag;
  198. int    setnr;
  199. char    **gargv;
  200. int     goptind, gargc;
  201.  
  202. #define    LABEL_SIZE    80
  203. char    label[LABEL_SIZE];
  204.  
  205. char    *block;
  206. int    blocksize;
  207.  
  208. struct    mtop    op;
  209.  
  210. FILE *
  211. openfile(fn)
  212. char    *fn;
  213. {
  214.     char    ufn[256];
  215.     char    ans[80];
  216.     char    *p, *q, s, *ext;
  217.     int    procf;
  218.  
  219.     procf = 1;
  220.     /* copy fn to ufn and convert to lower case */
  221.     p = fn;
  222.     q = ufn;
  223.     while (*p) {
  224.         if (isupper(*p))
  225.             *q = *p - 'A' + 'a';
  226.         else
  227.             *q = *p;
  228.         p++;
  229.         q++;
  230.     }
  231.     *q = '\0';
  232.  
  233.     /* convert the VMS to UNIX and make the directory path */
  234.     p = ufn;
  235.     q = ++p;
  236.     while (*q) {
  237.         if (*q == '.' || *q == ']') {
  238.             s = *q;
  239.             *q = '\0';
  240.             if(procf && dflag) mkdir(p, 0777);
  241.             *q = '/';
  242.             if (s == ']')
  243.                 break;
  244.         }
  245.         *q++;
  246.     }
  247.     *q++;
  248.     if(!dflag) p=q;
  249.     /* strip off the version number */
  250.     while (*q && *q != ';') {
  251.         if( *q == '.') ext = q;
  252.         q++;
  253.     }
  254.     if (cflag) {
  255.         *q = ':';
  256.     }
  257.     else {
  258.         *q = '\0';
  259.     }
  260.     if(!eflag && procf) procf = typecmp(++ext);
  261.     if(procf && wflag) {
  262.         printf("extract %s [ny]",filename);
  263.         fflush(stdout);
  264.         gets(ans);
  265.         if(*ans != 'y') procf = NULL;
  266.     }
  267.     if(procf)
  268.         /* open the file for writing */
  269.         return(fopen(p, "w"));
  270.     else
  271.         return(NULL);
  272. }
  273.  
  274. typecmp(str)    /* Compare the filename type in str with our list
  275.                    of file type to be ignored.  Return 0 if the
  276.                    file is to be ignored, return 1 if the
  277.                    file is not in our list and should not be ignored. */
  278. register char   *str;
  279. {
  280.         static char *type[] = {
  281.                 "exe",          /* vms executable image */
  282.                 "lib",          /* vms object library */
  283.                 "obj",          /* rsx object file */
  284.                 "odl",          /* rsx overlay description file */
  285.                 "olb",          /* rsx object library */
  286.                 "pmd",          /* rsx post mortem dump */
  287.                 "stb",          /* rsx symbol table */
  288.                 "sys",          /* rsx bootable system image */
  289.                 "tsk",          /* rsx executable image */
  290.         "dir",
  291.         "upd",
  292.         "tlo",
  293.         "tlb",
  294.                 ""              /* null string terminates list */
  295.         };
  296.         register int    i;
  297.  
  298.         i = -1;
  299.         while (*type[++i])
  300.                 if (strncmp(str, type[i],3) == 0)
  301.                         return(0);      /* found a match, file to be ignored */
  302.         return(1);                      /* no match found */
  303. }
  304.  
  305. process_file(buffer)
  306. char    *buffer;
  307. {
  308.     int    i, n;
  309.     char    *p, *q;
  310.     short    dsize, nblk, lnch;
  311.  
  312.     int    c;
  313.     short    *s;
  314.  
  315.     int     procf;
  316.  
  317.     s = (short *) buffer;
  318.  
  319.     /* check the header word */
  320.     if (*s != 257) {
  321.         printf("Snark: invalid data header\n");
  322.         exit(1);
  323.     }
  324.  
  325.     c = 2;
  326.     for (i = 0; i < FANO; i++) {
  327.         file_table[i] = (struct bsa *) &buffer[c];
  328. #ifndef    SWAP
  329.         dsize = file_table[i]->bsa_dol_w_size;
  330. #else
  331.         swap(&file_table[i]->bsa_dol_w_size, &dsize, sizeof(short));
  332. #endif
  333.         c += dsize + 4;
  334.     }
  335.  
  336.     /* extract file name */
  337. #ifndef    SWAP
  338.     dsize = file_table[0]->bsa_dol_w_size;
  339. #else
  340.     swap(&file_table[0]->bsa_dol_w_size, &dsize, sizeof(short));
  341. #endif
  342.     p = file_table[0]->bsa_dol_t_text;
  343.     q = filename;
  344.     for (i = 0; i < dsize; i++)
  345.         *q++ = *p++;
  346.     *q = '\0';
  347.  
  348.     /* extract file's record attributes */
  349. #ifndef    SWAP
  350.     dsize = file_table[5]->bsa_dol_w_size;
  351. #else
  352.     swap(&file_table[5]->bsa_dol_w_size, &dsize, sizeof(short));
  353. #endif
  354.     p = file_table[5]->bsa_dol_t_text;
  355.     recfmt = p[0];
  356.     recatt = p[1];
  357. #ifndef    SWAP
  358.     bcopy(&p[2], &recsize, sizeof(short));
  359. #else
  360.     swap(&p[2], &recsize, sizeof(short));
  361. #endif
  362.     vfcsize = p[15];
  363.     if (vfcsize == 0)
  364.         vfcsize = 2;
  365. #ifdef    DEBUG
  366.     printf("recfmt = %d\n", recfmt);
  367.     printf("recatt = %d\n", recatt);
  368.     printf("reclen = %d\n", recsize);
  369.     printf("vfcsize = %d\n", vfcsize);
  370. #endif
  371. #ifndef    SWAP
  372.     bcopy(&p[10], &nblk, sizeof(short));
  373.     bcopy(&p[12], &lnch, sizeof(short));
  374. #else
  375.     swap(&p[10], &nblk, sizeof(short));
  376.     swap(&p[12], &lnch, sizeof(short));
  377. #endif
  378.     filesize = (nblk-1)*512 + lnch;
  379. #ifdef DEBUG
  380.     printf("nbk = %d, lnch = %d\n", nblk, lnch);
  381.     printf("filesize = 0x%x\n", filesize);
  382. #endif
  383.  
  384.     /* open the file */
  385.     if (f != NULL) {
  386.         fclose(f);
  387.         file_count = 0;
  388.         reclen = 0;
  389.     }
  390.     procf = 0;
  391.     if (goptind < gargc) 
  392.         for(i=goptind; i < gargc; i++) {
  393.             procf |= match(filename,gargv[i]);
  394.         }
  395.     else
  396.         procf = 1;
  397.     if (tflag && procf) 
  398.         printf( " %-35s %8d \n",filename,filesize);
  399.     if (xflag && procf) {
  400.         /* open file */
  401.         f = openfile(filename);
  402.         if(f != NULL && vflag) printf("extracting %s\n", filename);
  403.     }
  404. }
  405. /*
  406.  *
  407.  *  process a virtual block record (file record)
  408.  *
  409.  */
  410. process_vbn(buffer, rsize)
  411. char        *buffer;
  412. unsigned short    rsize;
  413. {
  414.     int    c, i;
  415.  
  416.     if (f == NULL) {
  417.         return;
  418.     }
  419.     i = 0;
  420.     while (file_count+i < filesize && i < rsize) {
  421.         switch (recfmt) {
  422.         case FAB_dol_C_FIX:
  423.             if (reclen == 0) {
  424.                 reclen = recsize;
  425.             }
  426.             fputc(buffer[i], f);
  427.             i++;
  428.             reclen--;
  429.             break;
  430.  
  431.         case FAB_dol_C_VAR:
  432.         case FAB_dol_C_VFC:
  433.             if (reclen == 0) {
  434.                 reclen = *((short *) &buffer[i]);
  435. #ifdef    SWAP
  436.                 swap(&reclen, &reclen, sizeof(short));
  437. #endif
  438. #ifdef    NEWD
  439.                 fprintf(lf, "---\n");
  440.                 fprintf(lf, "reclen = %d\n", reclen);
  441.                 fprintf(lf, "i = %d\n", i);
  442.                 fprintf(lf, "rsize = %d\n", rsize);
  443. #endif    NEWD
  444.                 fix = reclen;
  445.                 i += 2;
  446.                 if (recfmt == FAB_dol_C_VFC) {
  447.                     i += vfcsize;
  448.                     reclen -= vfcsize;
  449.                 }
  450.             } else if (reclen == fix
  451.                     && recatt == (1 << FAB_dol_V_FTN)) {
  452.                     /****
  453.                     if (buffer[i] == '0')
  454.                         fputc('\n', f);
  455.                     else if (buffer[i] == '1')
  456.                         fputc('\f', f);
  457.                     *** sow ***/
  458.                     fputc(buffer[i],f); /** sow **/
  459.                     i++;
  460.                     reclen--;
  461.             } else {
  462.                 fputc(buffer[i], f);
  463.                 i++;
  464.                 reclen--;
  465.             }
  466.             if (reclen == 0) {
  467.                 fputc('\n', f);
  468.                 if (i & 1)
  469.                     i++;
  470.             }
  471.             break;
  472.  
  473.         case FAB_dol_C_STM:
  474.         case FAB_dol_C_STMLF:
  475.             if (reclen < 0) {
  476.                 printf("SCREAM\n");
  477.             }
  478.             if (reclen == 0) {
  479.                 reclen = 512;
  480.             }
  481.             c = buffer[i++];
  482.             reclen--;
  483.             if (c == '\n') {
  484.                 reclen = 0;
  485.             }
  486.             fputc(c, f);
  487.             break;
  488.  
  489.         case FAB_dol_C_STMCR:
  490.             c = buffer[i++];
  491.             if (c == '\r')
  492.                 fputc('\n', f);
  493.             else
  494.                 fputc(c, f);
  495.             break;
  496.  
  497.         default:
  498.             fclose(f);
  499.             unlink(filename);
  500.             fprintf(stderr, "Invalid record format = %d\n", recfmt);
  501.             return;
  502.         }
  503.     }
  504.     file_count += i;
  505. }
  506. #ifdef    SWAP
  507. /*
  508.  *
  509.  *  do swapping for Motorola type architectures
  510.  *
  511.  */
  512. swap(from, to, nbytes)
  513. char    *from, *to;
  514. int    nbytes;
  515. {
  516.     int    i, j;
  517.     char    temp[100];
  518.  
  519.     for (i = 0; i < nbytes; i++)
  520.         temp[i] = from[i];
  521.     for (i = 0, j = nbytes-1; i < nbytes; i++, j--)
  522.         to[i] = temp[j];
  523. }
  524. #endif
  525. /*
  526.  *
  527.  *  process a backup block
  528.  *
  529.  */
  530. process_block(block, blocksize)
  531. char    *block;
  532. int    blocksize;
  533. {
  534.  
  535.     unsigned short    bhsize, rsize, rtype;
  536.     unsigned long    bsize, i;
  537.  
  538.     i = 0;
  539.  
  540.     /* read the backup block header */
  541.     block_header = (struct bbh *) &block[i];
  542.     i += sizeof(struct bbh);
  543.  
  544.     bhsize = block_header->bbh_dol_w_size;
  545.     bsize = block_header->bbh_dol_l_blocksize;
  546. #ifdef    SWAP
  547.     swap(&bhsize, &bhsize, sizeof(short));
  548.     swap(&bsize, &bsize, sizeof(long));
  549. #endif
  550.  
  551.     /* check the validity of the header block */
  552.     if (bhsize != sizeof(struct bbh)) {
  553.         fprintf(stderr, "Snark: Invalid header block size\n");
  554.         exit(1);
  555.     }
  556.     if (bsize != 0 && bsize != blocksize) {
  557.         fprintf(stderr, "Snark: Invalid block size\n");
  558.         exit(1);
  559.     }
  560. #ifdef    DEBUG
  561.     printf("new block: i = %d, bsize = %d\n", i, bsize);
  562. #endif
  563.  
  564.     /* read the records */
  565.     while (i < bsize) {
  566.         /* read the backup record header */
  567.         record_header = (struct brh *) &block[i];
  568.         i += sizeof(struct brh);
  569.  
  570.         rtype = record_header->brh_dol_w_rtype;
  571.         rsize = record_header->brh_dol_w_rsize;
  572. #ifdef    SWAP
  573.         swap(&rtype, &rtype, sizeof(short));
  574.         swap(&rsize, &rsize, sizeof(short));
  575. #endif
  576. #ifdef    DEBUG
  577.         printf("rtype = %d\n", rtype);
  578.         printf("rsize = %d\n", rsize);
  579.         printf("flags = 0x%x\n", record_header->brh_dol_l_flags);
  580.         printf("addr = 0x%x\n", record_header->brh_dol_l_address);
  581.         printf("i = %d\n", i);
  582. #endif
  583.  
  584.         switch (rtype) {
  585.  
  586.         case brh_dol_k_null:
  587. #ifdef    DEBUG
  588.             printf("rtype = null\n");
  589. #endif
  590.             break;
  591.  
  592.         case brh_dol_k_summary:
  593. #ifdef    DEBUG
  594.             printf("rtype = summary\n");
  595. #endif
  596.             break;
  597.  
  598.         case brh_dol_k_file:
  599. #ifdef    DEBUG
  600.             printf("rtype = file\n");
  601. #endif
  602.             process_file(&block[i]);
  603.             break;
  604.  
  605.         case brh_dol_k_vbn:
  606. #ifdef    DEBUG
  607.             printf("rtype = vbn\n");
  608. #endif
  609.             process_vbn(&block[i], rsize);
  610.             break;
  611.  
  612.         case brh_dol_k_physvol:
  613. #ifdef    DEBUG
  614.             printf("rtype = physvol\n");
  615. #endif
  616.             break;
  617.  
  618.         case brh_dol_k_lbn:
  619. #ifdef    DEBUG
  620.             printf("rtype = lbn\n");
  621. #endif
  622.             break;
  623.  
  624.         case brh_dol_k_fid:
  625. #ifdef    DEBUG
  626.             printf("rtype = fid\n");
  627. #endif
  628.             break;
  629.  
  630.         default:
  631.             fprintf(stderr, " Snark: invalid record type\n");
  632.             fprintf(stderr, " record type = %d\n", rtype);
  633.             exit(1);
  634.         }
  635. #ifdef pyr
  636.         i = i + rsize;
  637. #else
  638.         i += rsize;
  639. #endif
  640.     }
  641. }
  642.  
  643. rdhead()
  644. {
  645.     int i, nfound;
  646.     char name[80];
  647.     nfound = 1;
  648.     /* read the tape label - 4 records of 80 bytes */
  649.     while ((i = read(fd, label, LABEL_SIZE)) != 0) {
  650.         if (i != LABEL_SIZE) {
  651.             fprintf(stderr, "Snark: bad label record\n");
  652.             exit(1);
  653.         }
  654.         if (strncmp(label, "VOL1",4) == 0) {
  655.             sscanf(label+4, "%14s", name);
  656.             if(vflag || tflag) printf("Volume: %s\n",name);
  657.         }
  658.         if (strncmp(label, "HDR1",4) == 0) {
  659.             sscanf(label+4, "%14s", name);
  660.             sscanf(label+31, "%4d", &setnr);
  661.         }
  662.         /* get the block size */
  663.         if (strncmp(label, "HDR2", 4) == 0) {
  664.             nfound = 0;
  665.             sscanf(label+5, "%5d", &blocksize);
  666. #ifdef    DEBUG
  667.             printf("blocksize = %d\n", blocksize);
  668. #endif
  669.         }
  670.     }
  671.     if((vflag || tflag) && !nfound) 
  672.         printf("Saveset name: %s   number: %d\n",name,setnr);
  673.     /* get the block buffer */
  674.     block = (char *) malloc(blocksize);
  675.     if (block == (char *) 0) {
  676.         fprintf(stderr, "memory allocation for block failed\n");
  677.         exit(1);
  678.     }
  679.     return(nfound);
  680. }
  681.  
  682. rdtail()
  683. {
  684.     int i;
  685.     char name[80];
  686.     /* read the tape label - 4 records of 80 bytes */
  687.     while ((i = read(fd, label, LABEL_SIZE)) != 0) {
  688.         if (i != LABEL_SIZE) {
  689.             fprintf(stderr, "Snark: bad label record\n");
  690.             exit(1);
  691.         }
  692.         if (strncmp(label, "EOF1",4) == 0) {
  693.             sscanf(label+4, "%14s", name);
  694.             if(vflag || tflag)
  695.                 printf("End of saveset: %s\n\n\n",name);
  696.         }
  697.     }
  698. }
  699.  
  700. usage(progname)
  701. char    *progname;
  702. {
  703.     fprintf(stderr,
  704.       "Usage:  %s -{tx}[cdevw][-s setnumber][-f tapefile]\n",progname);
  705. }
  706. main(argc, argv)
  707. int    argc;
  708. char    *argv[];
  709. {
  710.     
  711.     char *progname;
  712.     int    c, i, eoffl;
  713.     int    selset;
  714.     extern int optind;
  715.     extern char *optarg;
  716.  
  717.     progname = argv[0];
  718.     if(argc < 2){
  719.         usage(progname);
  720.         exit(1);
  721.     }
  722.     gargv = argv;
  723.     gargc = argc;
  724.     tapefile = def_tapefile;
  725.     cflag=dflag=eflag=sflag=tflag=vflag=wflag=xflag=0;
  726.     while((c=getopt(argc,argv,"cdef:s:tvwx")) != EOF)
  727.         switch(c){
  728.         case 'c':
  729.             cflag++;
  730.             break;
  731.         case 'd':
  732.             dflag++;
  733.             break;
  734.         case 'e':
  735.             eflag++;
  736.             break;
  737.         case 'f':
  738.             tapefile = optarg;
  739.             break;
  740.         case 's':
  741.             sflag++;
  742.             sscanf(optarg,"%d",&selset);
  743.             break;
  744.         case 't':
  745.             tflag++;
  746.             break;
  747.         case 'v':
  748.             vflag++;
  749.             break;
  750.         case 'w':
  751.             wflag++;
  752.             break;
  753.         case 'x':
  754.             xflag++;
  755.             break;
  756.         case '?':
  757.             usage(progname);
  758.             exit(1);
  759.             break;
  760.         };
  761.     if(!tflag && !xflag) {
  762.         usage(progname);
  763.         exit(1);
  764.     }
  765.     goptind = optind;
  766.  
  767. #ifdef    NEWD
  768.     /* open debug file */
  769.     lf = fopen("log", "w");
  770.     if (lf == NULL) {
  771.         perror("log");
  772.         exit(1);
  773.     }
  774. #endif
  775.  
  776.     /* open the tape file */
  777.     fd = open(tapefile, O_RDONLY);
  778.     if (fd < 0) {
  779.         perror(tapefile);
  780.         exit(1);
  781.     }
  782.  
  783.     /* rewind the tape */
  784.     op.mt_op = MTREW;
  785.     op.mt_count = 1;
  786.     i = ioctl(fd, MTIOCTOP, &op);
  787.     if (i < 0) {
  788.         perror(tapefile);
  789.         exit(1);
  790.     }
  791.  
  792.     eoffl = rdhead();
  793.     /* read the backup tape blocks until end of tape */ 
  794.     while (!eoffl) {
  795.         if(sflag && setnr != selset) {
  796.             op.mt_op = MTFSF;
  797.             op.mt_count = 1;
  798.             i = ioctl(fd, MTIOCTOP, &op);
  799.             if (i < 0) {
  800.                 perror(tapefile);
  801.                 exit(1);
  802.             }
  803.             i = 0;
  804.         }
  805.         else
  806.             i = read(fd, block, blocksize);
  807.         if(i == 0) {
  808.             rdtail();
  809.             eoffl=rdhead();
  810.         }
  811.         else if (i != blocksize) {
  812.             fprintf(stderr, "bad block read i = %d\n", i);
  813.             exit(1);
  814.         }
  815.         else{
  816.             eoffl = 0;
  817.             process_block(block, blocksize);
  818.         }
  819.     }
  820.     if(vflag || tflag) printf("End of tape\n");
  821.  
  822.     /* close the tape */
  823.     close(fd);
  824.  
  825. #ifdef    NEWD
  826.     /* close debug file */
  827.     fclose(lf);
  828. #endif    NEWD
  829.  
  830.     /* exit cleanly */
  831.     exit(0);
  832. }
  833.