home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / tapeutils.zip / tuvrta.c < prev    next >
C/C++ Source or Header  |  1988-08-16  |  20KB  |  775 lines

  1. #include <stdio.h>
  2. #include <ctype.h>
  3.  
  4. #define NOKNET
  5. #define    import_spp
  6. #define    import_knames
  7. #include <iraf.h>
  8.  
  9. /*
  10.  * RTAR -- Read a UNIX tar format tape containing files with legal IRAF
  11.  * virtual filenames.  Map tape filenames to host system filenames using
  12.  * IRAF filename mapping if the tape does not contain legal host system
  13.  * filenames.
  14.  *
  15.  * Switches:
  16.  *        a    advance to first file in filelist before doing
  17.  *              anything.  useful for restarting an aborted
  18.  *              operation.  first file is not otherwise used.
  19.  *        b    generate only C style binary byte stream output
  20.  *              files (default is to write a text file when
  21.  *              the input stream is text).
  22.  *        d    print debug messages
  23.  *        e    exclude, rather than include, listed files
  24.  *        f    read from named file rather than stdin
  25.  *        l    do not try to resolve links by a file copy
  26.  *        n    do not strip tailing blank lines from text files
  27.  *        o    omit binary files (e.g. when foreign host has
  28.  *              incompatible binary file format)
  29.  *        p    omit the given pathname prefix when creating files
  30.  *        r    replace existing file at extraction
  31.  *        t    print name of each file matched
  32.  *        v    verbose; print full description of each file
  33.  *        x    extract files (extract everything if no files
  34.  *              listed or if -e is set)
  35.  *
  36.  * Switches must be given in a group, in any order, e.g.:
  37.  *
  38.  *    rtar -xetvf tarfile sys/osb sys/os lib/config.h$
  39.  *
  40.  * would extract all files from tarfile with names not beginning with sys/os
  41.  * or sys/osb or with names not equal to lib/config.h, printing a verbose
  42.  * description of each file extracted.  If an exclude filename does not end
  43.  * with a $ all files with the given string as a prefix are excluded.
  44.  */
  45.  
  46. #define TBLOCK        512
  47. #define NBLOCK        20
  48. #define NAMSIZ        100
  49. #define    MAXERR        20
  50. #define    MAXTRYS        100
  51. #define    SZ_TAPEBUFFER    (TBLOCK * NBLOCK)
  52. #define    EOS        '\0'
  53. #define    ERR        (-1)
  54. #define    OK        0
  55. #define    RWXR_XR_X    0755
  56. #define    SZ_PADBUF    8196
  57.  
  58. /* File header structure.  One of these precedes each file on the tape.
  59.  * Each file occupies an integral number of TBLOCK size logical blocks
  60.  * on the tape.  The number of logical blocks per physical block is variable,
  61.  * with at most NBLOCK logical blocks per physical tape block.  Two zero
  62.  * blocks mark the end of the tar file.
  63.  */
  64. union hblock {
  65.     char dummy[TBLOCK];
  66.     struct header {
  67.         char name[NAMSIZ];    /* NULL delimited        */
  68.         char mode[8];        /* octal, ascii            */
  69.         char uid[8];
  70.         char gid[8];
  71.         char size[12];
  72.         char mtime[12];
  73.         char chksum[8];
  74.         char linkflag;
  75.         char linkname[NAMSIZ];
  76.     } dbuf;
  77. };
  78.  
  79.  
  80. /* Decoded file header.
  81.  */
  82. struct fheader {
  83.     char    name[NAMSIZ];
  84.     int    mode;
  85.     int    uid;
  86.     int    gid;
  87.     int    isdir;
  88.     long    size;
  89.     long    mtime;
  90.     long    chksum;
  91.     int    linkflag;
  92.     char    linkname[NAMSIZ];
  93. };
  94.  
  95.  
  96. int    advance;        /* Advance to named file        */
  97. int    stripblanks;        /* strip blank padding at end of file    */
  98. int    debug;            /* Print debugging messages        */
  99. int    binaryout;        /* make only binary byte stream files    */
  100. int    omitbinary;        /* omit binary files (do not write)    */
  101. int    extract;        /* Extract files from the tape        */
  102. int    replace;        /* Replace existing files        */
  103. int    exclude;        /* Excluded named files            */
  104. int    printfnames;        /* Print file names            */
  105. int    verbose;        /* Print everything            */
  106. int    links;            /* Defeat copy to resolve link        */
  107.  
  108. char    *pathprefix = NULL;
  109. int    len_pathprefix = 0;
  110. struct    fheader *curfil;
  111. int    eof;
  112. int    nerrs;
  113. char    *first_file;
  114. char    tapeblock[SZ_TAPEBUFFER];
  115. char    *nextblock;
  116. int    nblocks;
  117.  
  118. char    *getblock();
  119.  
  120.  
  121. /* MAIN -- "rtar [xtvlef] [names]".  The default operation is to extract all
  122.  * files from the tar format standard input in quiet mode.
  123.  */
  124. main (argc, argv)
  125. int    argc;
  126. char    *argv[];
  127. {
  128.     struct    fheader fh;
  129.     char    **argp;
  130.     char    *ip;
  131.     int    in = 0, out;
  132.     int    ftype;
  133.     int    ch;
  134.  
  135.     ZZSTRT();        /* initialize the IRAF kernel    */
  136.  
  137.     advance        = 0;
  138.     debug        = 0;
  139.     binaryout    = 0;
  140.     omitbinary    = 0;
  141.     extract        = 0;
  142.     replace        = 0;
  143.     exclude        = 0;
  144.     printfnames    = 0;
  145.     verbose        = 0;
  146.     links        = 0;
  147.     stripblanks    = 1;    /* strip blanks at end of file by default */
  148.  
  149.     /* Get parameters.  Argp is left pointing at the list of files to be
  150.      * extracted (default all if no files named).
  151.      */
  152.     argp = &argv[1];
  153.     if (argc <= 1)
  154.         extract++;
  155.     else {
  156.         while (*argp && **argp == '-') {
  157.         ip = *argp++ + 1;
  158.         while ((ch = *ip++) != EOS) {
  159.             switch (ch) {
  160.             case 'a':
  161.             advance++;
  162.             break;
  163.             case 'n':
  164.             stripblanks = 0;
  165.             break;
  166.             case 'x':
  167.             extract++;
  168.             break;
  169.             case 'b':
  170.             binaryout++;
  171.             break;
  172.             case 'd':
  173.             debug++;
  174.             break;
  175.             case 'e':
  176.             exclude++;
  177.             break;
  178.             case 'r':
  179.             replace++;
  180.             break;
  181.             case 't':
  182.             printfnames++;
  183.             break;
  184.             case 'v':
  185.             verbose++;
  186.             break;
  187.             case 'l':
  188.             links++;
  189.             break;
  190.             case 'o':
  191.             omitbinary++;
  192.             break;
  193.             case 'p':
  194.             if (*argp != NULL) {
  195.                 pathprefix = *argp++;
  196.                 len_pathprefix = strlen (pathprefix);
  197.             }
  198.             break;
  199.             case 'f':
  200.             if (*argp == NULL) {
  201.                 fprintf (stderr, "missing filename argument\n");
  202.                 exit (OSOK+1);
  203.             }
  204.             in = tape_open (*argp, 0);
  205.             if (in == ERR) {
  206.                 fprintf (stderr, "cannot open `%s'\n", *argp);
  207.                 ZZSTOP();
  208.                 exit (OSOK+1);
  209.             }
  210.             argp++;
  211.             break;
  212.             default:
  213.             fprintf (stderr, "Warning: unknown switch `%c'\n", ch);
  214.             fflush (stderr);
  215.             break;
  216.             }
  217.         }
  218.         }
  219.     }
  220.  
  221.     /* If advancing to a file get the name of the file.  This file name
  222.      * occurs at the beginning of the file list but is not part of the list.
  223.      * Only full filenames are permitted here.
  224.      */
  225.     if (advance)
  226.         first_file = *argp++;
  227.  
  228.     /* Step along through the tar format file.  Read file header and if
  229.      * file is in list and extraction is enabled, extract file.
  230.      */
  231.     while (getheader (in, &fh) != EOF) {
  232.         curfil = &fh;
  233.         if (advance)
  234.         if (strcmp (fh.name, first_file) == 0) {
  235.             if (debug)
  236.             fprintf (stderr, "match\n");
  237.             advance = 0;
  238.         } else {
  239.             if (debug)
  240.             printheader (stderr, &fh, verbose);
  241.             skipfile (in, &fh);
  242.             continue;
  243.         }
  244.  
  245.         if (matchfile (fh.name, argp) == exclude) {
  246.         if (debug)
  247.             fprintf (stderr, "skip file `%s'\n", fh.name);
  248.         skipfile (in, &fh);
  249.         continue;
  250.         }
  251.  
  252.         if (printfnames) {
  253.         printheader (stdout, &fh, verbose);
  254.         fflush (stdout);
  255.         }
  256.  
  257.         if (fh.linkflag) {
  258.         /* No file follows header if file is a link.  Try to resolve
  259.          * the link by copying the original file, assuming it has been
  260.          * read from the tape.
  261.          */
  262.         if (extract) {
  263.             if (!links) {
  264.             if (replace)
  265.                 os_delete (fh.name);
  266.             if (os_fcopy (fh.linkname, fh.name) == ERR) {
  267.                 fprintf (stderr, "Copy `%s' to `%s' fails\n",
  268.                 fh.linkname, fh.name);
  269.             } else {
  270.                 os_setfmode (fh.name, fh.mode);
  271.                 os_setowner (fh.name, fh.uid, fh.gid);
  272.                 os_setmtime (fh.name, fh.mtime);
  273.             }
  274.             } else {
  275.             fprintf (stderr,
  276.                 "Warning: cannot make link `%s' to `%s'\n",
  277.                 fh.name, fh.linkname);
  278.             }
  279.         }
  280.         continue;
  281.         }
  282.  
  283.         if (extract) {
  284.         ftype = filetype (in, &fh);
  285.         if (fh.size > 0 && ftype == BINARY_FILE && omitbinary) {
  286.             if (printfnames)
  287.             fprintf (stderr, "omit binary file `%s'\n", fh.name);
  288.             skipfile (in, &fh);
  289.             continue;
  290.         }
  291.         out = newfile (fh.name, fh.mode, fh.uid, fh.gid, ftype);
  292.         if (out == ERR) {
  293.             fprintf (stderr, "cannot create file `%s'\n", fh.name);
  294.             skipfile (in, &fh);
  295.             continue;
  296.         }
  297.         if (!fh.isdir) {
  298.             copyfile (in, out, &fh, ftype);
  299.             os_close (out);
  300.         }
  301.         os_setfmode (fh.name, fh.mode);
  302.         os_setowner (fh.name, fh.uid, fh.gid);
  303.         os_setmtime (fh.name, fh.mtime);
  304.         } else
  305.         skipfile (in, &fh);
  306.     }
  307.  
  308.     /* End of TAR file normally occurs when a zero tape block is read;
  309.      * this is not the same as the physical end of file, leading to
  310.      * problems when reading from sequential devices (e.g. pipes and
  311.      * magtape).  Advance to the physical end of file before exiting.
  312.      */
  313.     if (!eof)
  314.         while (tape_read (in, tapeblock, SZ_TAPEBUFFER) > 0)
  315.         ;
  316.     if (in)
  317.         tape_close (in);
  318.  
  319.     ZZSTOP();
  320.     exit (OSOK);
  321. }
  322.  
  323.  
  324. /* MATCHFILE -- Search the filelist for the named file.  If the file list
  325.  * is empty anything is a match.  If the list element ends with a $ an
  326.  * exact match is required (excluding the $), otherwise we have a match if
  327.  * the list element is a prefix of the filename.
  328.  */
  329. matchfile (fname, files)
  330. char    *fname;            /* filename to be compared to list    */
  331. register char    **files;    /* pointer to array of fname pointers    */
  332. {
  333.     register char *fn, *ln;
  334.     register int firstchar;
  335.  
  336.     if (*files == NULL)
  337.         return (1);
  338.  
  339.     firstchar = *fname;
  340.     do {
  341.         if (**files++ == firstchar) {
  342.         for (fn=fname, ln = *(files-1);  *ln && *ln == *fn++;  )
  343.             ln++;
  344.         if (*ln == EOS)
  345.             return (1);
  346.         else if (*ln == '$' && *(fn-1) == EOS)
  347.             return (1);
  348.         }
  349.     } while (*files);
  350.  
  351.     return (0);
  352. }
  353.  
  354.  
  355. /* GETHEADER -- Read the next file block and attempt to interpret it as a
  356.  * file header.  A checksum error on the file header is fatal and usually
  357.  * indicates that the tape is not positioned to the beginning of a file.
  358.  * If we have a legal header, decode the character valued fields into binary.
  359.  */
  360. getheader (in, fh)
  361. int    in;            /* input file            */
  362. register struct    fheader *fh;    /* decoded file header (output)    */
  363. {
  364.     register char *ip, *op;
  365.     register int n;
  366.     union    hblock *hb;
  367.     int    tape_checksum, ntrys;
  368.  
  369.     for (ntrys=0;  ;  ntrys++) {
  370.         if ((hb = (union hblock *)getblock (in)) == NULL)
  371.         return (EOF);
  372.  
  373.         /* Decode the checksum value saved in the file header and then
  374.          * overwrite the field with blanks, as the field was blank when
  375.          * the checksum was originally computed.  Compute the actual
  376.          * checksum as the sum of all bytes in the header block.  If the
  377.          * sum is zero this indicates the end of the tar file, otherwise
  378.          * the checksums must match.
  379.          */
  380.         if (*hb->dbuf.chksum == '\0' && cchksum ((char *)hb, TBLOCK) == 0)
  381.         return (EOF);
  382.         else
  383.         sscanf (hb->dbuf.chksum, "%o", &tape_checksum);
  384.  
  385.         for (ip=hb->dbuf.chksum, n=8;  --n >= 0;  )
  386.         *ip++ = ' ';
  387.         if (cchksum ((char *)hb, TBLOCK) != tape_checksum) {
  388.         /* If a checksum error occurs try to advance to the next
  389.          * header block.
  390.          */
  391.         if (ntrys == 0) {
  392.             fprintf (stderr,
  393.             "rtar: file header checksum error %o != %o\n",
  394.                 cchksum ((char *)hb, TBLOCK), tape_checksum);
  395.         } else if (ntrys >= MAXTRYS) {
  396.             fprintf (stderr, "cannot recover from checksum error\n");
  397.             exit (OSOK+1);
  398.         }
  399.         } else
  400.         break;
  401.     }
  402.  
  403.     if (ntrys > 1)
  404.         fprintf (stderr, "found next file following checksum error\n");
  405.  
  406.     /* Decode the ascii header fields into the output file header
  407.      * structure.
  408.      */
  409.     for (ip=hb->dbuf.name, op=fh->name;  (*op++ = *ip++);  )
  410.         ;
  411.     fh->isdir = (*(op-2) == '/');
  412.  
  413.     sscanf (hb->dbuf.mode,     "%o",  &fh->mode);
  414.     sscanf (hb->dbuf.uid,      "%o",  &fh->uid);
  415.     sscanf (hb->dbuf.gid,      "%o",  &fh->gid);
  416.     sscanf (hb->dbuf.size,     "%lo", &fh->size);
  417.     sscanf (hb->dbuf.mtime,    "%lo", &fh->mtime);
  418.  
  419.     n = hb->dbuf.linkflag;
  420.     if (n >= '0' && n <= '9')
  421.         fh->linkflag = n - '0';
  422.     else
  423.         fh->linkflag = 0;
  424.  
  425.     if (fh->linkflag)
  426.         strcpy (fh->linkname, hb->dbuf.linkname);
  427.     
  428.     return (TBLOCK);
  429. }
  430.  
  431.  
  432. /* CCHKSUM -- Compute the checksum of a byte array.
  433.  */
  434. cchksum (p, nbytes)
  435. register char    *p;
  436. register int    nbytes;
  437. {
  438.     register int    sum;
  439.  
  440.     for (sum=0;  --nbytes >= 0;  )
  441.         sum += *p++;
  442.  
  443.     return (sum);
  444. }
  445.  
  446.  
  447. struct _modebits {
  448.     int    code;
  449.     char    ch;
  450. } modebits[] = {
  451.     040000,    'd',
  452.     0400,    'r',
  453.     0200,    'w',
  454.     0100,    'x',
  455.     040,    'r',
  456.     020,    'w',
  457.     010,    'x',
  458.     04,    'r',
  459.     02,    'w',
  460.     01,    'x',
  461.     0,    0
  462. };
  463.  
  464.  
  465. /* PRINTHEADER -- Print the file header in either short or long (verbose)
  466.  * format, e.g.:
  467.  *        drwxr-xr-x  9 tody         1024 Nov  3 17:53 .
  468.  */
  469. printheader (out, fh, verbose)
  470. FILE    *out;            /* output file            */
  471. register struct fheader *fh;    /* file header struct        */
  472. int    verbose;        /* long format output        */
  473. {
  474.     register struct    _modebits *mp;
  475.     char    *tp, *ctime();
  476.  
  477.     if (!verbose) {
  478.         fprintf (out, "%s\n", fh->name);
  479.         return;
  480.     }
  481.  
  482.     for (mp=modebits;  mp->code;  mp++)
  483.         fprintf (out, "%c", mp->code & fh->mode ? mp->ch : '-');
  484.  
  485.     tp = ctime (&fh->mtime);
  486.     fprintf (out, "%3d %4d %2d %8d %-12.12s %-4.4s %s",
  487.         fh->linkflag,
  488.         fh->uid,
  489.         fh->gid,
  490.         fh->size,
  491.         tp + 4, tp + 20,
  492.         fh->name);
  493.  
  494.     if (fh->linkflag)
  495.         fprintf (out, " -> %s\n", fh->linkname);
  496.     else
  497.         fprintf (out, "\n");
  498. }
  499.  
  500.  
  501. /* FILETYPE -- Determine the file type (text, binary, or directory) of the
  502.  * next file on the input stream.  Directory files are easy; the tar format
  503.  * identifies directories unambiguously.  Discriminating between text and
  504.  * binary files is not possible in general because UNIX does not make such
  505.  * a distinction, but in practice we can apply a heuristic which will work
  506.  * in nearly all cases.  This can be overriden, producing only binary byte
  507.  * stream files as output, by a command line switch.
  508.  */
  509. filetype (in, fh)
  510. int    in;            /* input file            */
  511. struct    fheader *fh;        /* decoded file header        */
  512. {
  513.     register char    *cp;
  514.     register int    n;
  515.  
  516.     /* Easy cases first.
  517.      */
  518.     if (fh->isdir)
  519.         return (DIRECTORY_FILE);
  520.     else if (fh->size == 0 || binaryout)
  521.         return (BINARY_FILE);
  522.  
  523.     /* Get a pointer to the first block of the input file and set the
  524.      * input pointers back so that the block is returned by the next
  525.      * call to getblock.
  526.      */
  527.     if ((cp = getblock (in)) == NULL)
  528.         return (BINARY_FILE);
  529.     nextblock -= TBLOCK;
  530.     nblocks++;
  531.  
  532.     /* Examine the data to see if it is text.  The simple heuristic
  533.      * used requires that all characters be either printable ascii
  534.      * or common control codes.
  535.      */
  536.     n = (fh->size < TBLOCK) ? fh->size : TBLOCK;
  537.     while (--n >= 0 && (isprint(*cp) || isspace(*cp)))
  538.         cp++;
  539.         
  540.     return (n < 0 ? TEXT_FILE : BINARY_FILE);
  541. }
  542.  
  543.  
  544. /* NEWFILE -- Try to open a new file for writing, creating the new file
  545.  * with the mode bits given.  Create all directories leading to the file if
  546.  * necessary (and possible).
  547.  */
  548. newfile (fname, mode, uid, gid, type)
  549. char    *fname;            /* pathname of file        */
  550. int    mode;            /* file mode bits        */
  551. int    uid, gid;        /* file owner, group codes    */
  552. int    type;            /* text, binary, directory    */
  553. {
  554.     int    fd;
  555.     char    *cp;
  556.     char    *rindex();
  557.  
  558.     if (len_pathprefix && strncmp(fname,pathprefix,len_pathprefix) == 0)
  559.         fname += len_pathprefix;
  560.  
  561.     if (debug)
  562.         fprintf (stderr, "newfile `%s':\n", fname);
  563.  
  564.     if (checkdir (fname, mode, uid, gid) == ERR)
  565.         return (ERR);
  566.  
  567.     if (type == DIRECTORY_FILE) {
  568.         cp = rindex (fname, '/');
  569.         if (cp && *(cp+1) == EOS)
  570.         *cp = EOS;
  571.         fd = os_createdir (fname, mode);
  572.  
  573.         /* Ignore any error creating directory, as this may just mean
  574.          * that the directory already exists.  If the directory does
  575.          * not exist and cannot be created, there will be plenty of
  576.          * other errors when we try to write files into it.
  577.          */
  578.         fd = OK;
  579.  
  580.     } else {
  581.         if (replace)
  582.         os_delete (fname);
  583.         fd = os_createfile (fname, mode, type);
  584.     }
  585.  
  586.     return (fd);
  587. }
  588.  
  589.  
  590. /* CHECKDIR -- Verify that all the directories in the pathname of a file
  591.  * exist.  If they do not exist, try to create them.
  592.  */
  593. checkdir (path, mode, uid, gid)
  594. register char *path;
  595. int    mode;
  596. int    uid, gid;
  597. {
  598.     register char    *cp;
  599.     char    *rindex();
  600.  
  601.     /* Quick check to see if the directory exists.
  602.      */
  603.     if ((cp = rindex (path, '/')) == NULL)
  604.         return (OK);
  605.  
  606.     *cp = EOS;
  607.     if (os_access (path, 0, DIRECTORY_FILE) == YES) {
  608.         *cp = '/';
  609.         return (OK);
  610.     }
  611.     *cp = '/';
  612.  
  613.     /* The directory cannot be accessed.  Try to make all directories
  614.      * in the pathname.  If the file is itself a directory leave its
  615.      * creation until later.
  616.      */
  617.     for (cp=path;  *cp;  cp++) {
  618.         if (*cp != '/')
  619.         continue;
  620.         if (*(cp+1) == EOS)
  621.         return (OK);
  622.  
  623.         *cp = EOS;
  624.         if (os_access (path, 0, DIRECTORY_FILE) == NO) {
  625.         if (os_createdir (path, RWXR_XR_X) == ERR) {
  626.             fprintf (stderr, "cannot create directory `%s'\n", path);
  627.             *cp = '/';
  628.             return (ERR);
  629.         } else
  630.             os_setowner (path, uid, gid);
  631.         }
  632.         *cp = '/';
  633.     }
  634.  
  635.     return (OK);
  636. }
  637.  
  638.  
  639. /* COPYFILE -- Copy bytes from the input (tar) file to the output file.
  640.  * Each file consists of a integral number of TBLOCK size blocks on the
  641.  * input file.
  642.  */
  643. copyfile (in, out, fh, ftype)
  644. int    in;            /* input file            */
  645. int    out;            /* output file            */
  646. struct    fheader *fh;        /* file header structure    */
  647. int    ftype;            /* text or binary file        */
  648. {
  649.     long    nbytes = fh->size;
  650.     int    nblocks = 0, maxpad;
  651.     char    *bp;
  652.  
  653.     /* Link files are zero length on the tape. */
  654.     if (fh->linkflag)
  655.         return;
  656.  
  657.     if (ftype == BINARY_FILE || !stripblanks)
  658.         maxpad = 0;
  659.     else
  660.         maxpad = SZ_PADBUF;
  661.  
  662.     /* Copy all but the last MAXPAD characters if the file is a text file
  663.      * and stripping is enabled.
  664.      */
  665.     while (nbytes > maxpad && (bp = getblock (in)) != NULL)
  666.         if (os_write (out, bp, nbytes<TBLOCK ? (int)nbytes:TBLOCK) == ERR) {
  667.         fprintf (stderr, "Warning: file write error on `%s'\n",
  668.             curfil->name);
  669.         if (nerrs++ > MAXERR) {
  670.             fprintf (stderr, "Too many errors\n");
  671.             exit (OSOK+1);
  672.         }
  673.         } else {
  674.         nbytes -= TBLOCK;
  675.         nblocks++;
  676.         }
  677.  
  678.     /* Strip whitespace at end of file added by WTAR when the archive was
  679.      * created.
  680.      */
  681.     if (nbytes > 0)
  682.         strip_blanks (in, out, nbytes);
  683.  
  684.     if (debug)
  685.         fprintf (stderr, "%d blocks written\n", nblocks);
  686. }
  687.  
  688.  
  689. /* STRIP_BLANKS -- Read the remaining file data into the pad buffer.
  690.  * Write out the remaining data, minus any extra blanks or empty blank lines
  691.  * at the end of the file.  Some versions of WTAR (e.g., VMS) do not know
  692.  * the actual size of a text file and have to pad with blanks at the end to
  693.  * make the file the size noted in the file header.
  694.  */
  695. strip_blanks (in, out, nbytes)
  696. int    in, out;
  697. long    nbytes;
  698. {
  699.     register char    *ip, *op;
  700.     char    padbuf[SZ_PADBUF+10];
  701.     char    *lastnl;
  702.     int    n;
  703.  
  704.     /* Fill buffer.
  705.      */
  706.     op = padbuf;
  707.     while (nbytes > 0 && (ip = getblock (in)) != NULL) {
  708.         n = nbytes < TBLOCK ? (int)nbytes : TBLOCK;
  709.         os_amovb (ip, op, n + sizeof(XCHAR)-1);
  710.         nbytes -= n;
  711.         op += n;
  712.     }
  713.  
  714.     /* Backspace from the end of the buffer until the last nonblank line
  715.      * is found.
  716.      */
  717.     lastnl = op - 1;
  718.     for (ip=lastnl;  ip > padbuf;  --ip)
  719.         if (*ip == '\n')
  720.         lastnl = ip;
  721.         else if (*ip != ' ')
  722.         break;
  723.  
  724.     /* Write out everything up to and including the newline at the end of
  725.      * the last line containing anything but blanks.
  726.      */
  727.     os_write (out, padbuf, lastnl - padbuf + 1);
  728. }
  729.  
  730.  
  731. /* SKIPFILE -- Skip the indicated number of bytes on the input (tar) file.
  732.  */
  733. skipfile (in, fh)
  734. int    in;            /* input file            */
  735. struct    fheader *fh;        /* file header            */
  736. {
  737.     register long nbytes = fh->size;
  738.  
  739.     /* Link files are zero length on the tape. */
  740.     if (fh->linkflag)
  741.         return;
  742.  
  743.     while (nbytes > 0 && getblock (in) != NULL)
  744.         nbytes -= TBLOCK;
  745. }
  746.  
  747.  
  748. /* GETBLOCK -- Return a pointer to the next file block of size TBLOCK bytes
  749.  * in the input file.
  750.  */
  751. char *
  752. getblock (in)
  753. int    in;            /* input file            */
  754. {
  755.     char    *bp;
  756.     int    nbytes;
  757.  
  758.     for (;;) {
  759.         if (eof)
  760.         return (NULL);
  761.         else if (--nblocks >= 0) {
  762.         bp = nextblock;
  763.         nextblock += TBLOCK;
  764.         return (bp);
  765.         }
  766.  
  767.         if ((nbytes = tape_read (in, tapeblock, SZ_TAPEBUFFER)) < TBLOCK)
  768.         eof++;
  769.         else {
  770.         nblocks = (nbytes + TBLOCK-1) / TBLOCK;
  771.         nextblock = tapeblock;
  772.         }
  773.     }
  774. }
  775.