home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / filutl / pdtar.arc / BUFFER.C next >
C/C++ Source or Header  |  1988-05-15  |  13KB  |  611 lines

  1. /*
  2.  * Buffer management for public domain tar.
  3.  *
  4.  * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
  5.  * MS-DOS port 2/87 by Eric Roskos.
  6.  * Minix  port 3/88 by Eric Roskos.
  7.  *
  8.  * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <sys/types.h>            /* For non-Berkeley systems */
  14. #include <fcntl.h>
  15. #include <signal.h>
  16.  
  17. #include "tar.h"
  18. #include "port.h"
  19.  
  20. #define    STDIN    0                /* Standard input  file descriptor */
  21. #define    STDOUT    1                /* Standard output file descriptor */
  22.  
  23. #define    PREAD    0                /* Read  file descriptor from pipe() */
  24. #define    PWRITE    1                /* Write file descriptor from pipe() */
  25.  
  26. extern char    *valloc();
  27.  
  28. /*
  29.  * V7 doesn't have a #define for this.
  30.  */
  31. #ifndef O_RDONLY
  32. #define    O_RDONLY    0
  33. #endif
  34.  
  35. #define    MAGIC_STAT    105            /* Magic status returned by child, if it
  36.                                  * can't exec compress.  We hope compress
  37.                                  * never returns this status! */
  38. /*
  39.  * The record pointed to by save_rec should not be overlaid
  40.  * when reading in a new tape block.  Copy it to record_save_area first, and
  41.  * change the pointer in *save_rec to point to record_save_area.
  42.  * Saved_recno records the record number at the time of the save.
  43.  * This is used by annofile() to print the record number of a file's
  44.  * header record.
  45.  */
  46. static union record **save_rec;
  47. static union record record_save_area;
  48. static int      saved_recno;
  49.  
  50. /*
  51.  * PID of child compress program, if f_compress.
  52.  */
  53. static int      compress_pid;
  54.  
  55. /*
  56.  * Record number of the start of this block of records
  57.  */
  58. static int      baserec;
  59.  
  60. /*
  61.  * Error recovery stuff
  62.  */
  63. static int      r_error_count;
  64.  
  65.  
  66. /*
  67.  * Return the location of the next available input or output record.
  68.  */
  69. union record   *
  70. findrec()
  71. {
  72.     if (ar_record == ar_last)
  73.     {
  74.         flush_archive();
  75.         if (ar_record == ar_last)
  76.             return (union record *) NULL;        /* EOF */
  77.     }
  78.     return ar_record;
  79. }
  80.  
  81.  
  82. /*
  83.  * Indicate that we have used all records up thru the argument.
  84.  * (should the arg have an off-by-1? XXX FIXME)
  85.  */
  86. void
  87. userec(rec)
  88. union record   *rec;
  89. {
  90.     while (rec >= ar_record)
  91.         ar_record++;
  92.  
  93.     /*
  94.      * Do NOT flush the archive here.  If we do, the same argument to
  95.      * userec() could mean the next record (if the input block is exactly one
  96.      * record long), which is not what is intended. 
  97.      */
  98.     if (ar_record > ar_last)
  99.         abort();
  100. }
  101.  
  102.  
  103. /*
  104.  * Return a pointer to the end of the current records buffer.
  105.  * All the space between findrec() and endofrecs() is available
  106.  * for filling with data, or taking data from.
  107.  */
  108. union record   *
  109. endofrecs()
  110. {
  111.     return ar_last;
  112. }
  113.  
  114.  
  115. /*
  116.  * Open an archive file.  The argument specifies whether we are
  117.  * reading or writing.
  118.  *
  119.  * With DOS, we ALWAYS open the archive in binary mode: whether or not
  120.  * to do CRLF translations depends on whether we open the input files
  121.  * as binary or ASCII, but we always write the archive without making
  122.  * any translations from what this program saw when it did the write.
  123.  */
  124. open_archive(read)
  125. int             read;
  126. {
  127.  
  128.     if (ar_file[0] == '-' && ar_file[1] == '\0')
  129.     {
  130.         if (read)
  131.             archive = STDIN;
  132.         else
  133.             archive = STDOUT;
  134.     }
  135.     else
  136.     if (read)
  137.     {
  138. #ifdef MSDOS
  139.         archive = 9999; /* for debugging - invalid fd to cause err */
  140.         
  141.         if (!f_phys) /* don't open if we're doing direct drive I/O */ 
  142. #endif
  143.             archive = open(ar_file, O_RDONLY 
  144. #ifdef MSDOS
  145.             | O_BINARY
  146. #endif
  147.             );
  148.     }
  149.     else
  150.     {
  151. #ifdef MSDOS
  152.         archive = 9999;
  153.         
  154.         if (!f_phys)
  155. #endif
  156. #ifdef V7
  157.             archive = creat(ar_file, 0666);
  158. #else
  159.             archive = open(ar_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
  160.                 0666);
  161. #endif
  162.     }
  163.  
  164.     if (archive < 0)
  165.     {
  166.         perror(ar_file);
  167.         exit(EX_BADARCH);
  168.     }
  169.  
  170.     /* NOSTRICT */
  171.     ar_block = (union record *) valloc((unsigned) blocksize);
  172.     if (!ar_block)
  173.     {
  174.         fprintf(stderr,
  175.             "tar: could not allocate memory for blocking factor %d\n",
  176.             blocking);
  177.         exit(EX_ARGSBAD);
  178.     }
  179.  
  180.     ar_record = ar_block;
  181.     ar_last = ar_block + blocking;
  182.  
  183.     /*
  184.      * Handle compressed archives. 
  185.      *
  186.      * FIXME, currently supported for reading only. FIXME, writing involves
  187.      * forking again for a small process that will reblock the output of
  188.      * compress to the user's specs. 
  189.      */
  190. #ifndef MSDOS
  191.     if (f_compress)
  192.     {
  193.         int             pipes[2];
  194.         int             err;
  195.  
  196.         if (!read)
  197.         {
  198.             fprintf(stderr,
  199.                 "tar: cannot write compressed archives yet.\n");
  200.             exit(EX_ARGSBAD);
  201.         }
  202.  
  203.         /* Create a pipe to get compress's output to us */
  204.         err = pipe(pipes);
  205.         if (err < 0)
  206.         {
  207.             perror("tar: cannot create pipe to compress");
  208.             exit(EX_SYSTEM);
  209.         }
  210.  
  211.         /* Fork compress process */
  212.         compress_pid = fork();
  213.         if (compress_pid < 0)
  214.         {
  215.             perror("tar: cannot fork compress");
  216.             exit(EX_SYSTEM);
  217.         }
  218.  
  219.         /*
  220.          * Child process. 
  221.          *
  222.          * Move input to stdin, write side of pipe to stdout, then exec
  223.          * compress. 
  224.          */
  225.         if (compress_pid == 0)
  226.         {
  227.             (void) close(pipes[PREAD]);    /* We won't use it */
  228.             if (archive != STDIN)
  229.             {
  230.                 (void) close(STDIN);
  231.                 err = dup(archive);
  232.                 if (err != 0)
  233.                 {
  234.                     perror(
  235.                         "tar: cannot dup input to stdin");
  236.                     exit(EX_SYSTEM);
  237.                 }
  238.                 (void) close(archive);
  239.             }
  240.             if (pipes[PWRITE] != STDOUT)
  241.             {
  242.                 (void) close(STDOUT);
  243.                 err = dup(pipes[PWRITE]);
  244.                 if (err != STDOUT)
  245.                 {
  246.                     perror(
  247.                         "tar: cannot dup pipe output");
  248.                     exit(MAGIC_STAT);
  249.                 }
  250.                 (void) close(pipes[PWRITE]);
  251.             }
  252. #ifdef V7
  253.             execl("/usr/bin/compress", "compress", "-d", (char *)0);
  254. #else
  255.             execlp("compress", "compress", "-d", (char *) 0);
  256. #endif
  257.             perror("tar: cannot exec compress");
  258.             exit(MAGIC_STAT);
  259.         }
  260.  
  261.         /*
  262.          * Parent process.  Clean up. FIXME, note that this may leave
  263.          * standard input closed, if the compressed archive was on standard
  264.          * input. 
  265.          */
  266.         (void) close(archive);    /* Close compressed archive */
  267.         (void) close(pipes[PWRITE]);    /* Close write side of pipe */
  268.         archive = pipes[PREAD];    /* Read side is our archive */
  269.  
  270. #ifdef BSD42
  271.         f_reblock++;            /* Pipe will give random # of bytes */
  272. #endif /* BSD42 */
  273.     }
  274. #endif                            /* MSDOS */
  275.  
  276.     ar_reading = read;
  277.     if (read)
  278.     {
  279.         ar_last = ar_block;        /* Set up for 1st block = # 0 */
  280.         flush_archive();
  281.     }
  282. }
  283.  
  284.  
  285. /*
  286.  * Remember a union record * as pointing to something that we
  287.  * need to keep when reading onward in the file.  Only one such
  288.  * thing can be remembered at once, and it only works when reading
  289.  * an archive.
  290.  */
  291. saverec(pointer)
  292. union record  **pointer;
  293. {
  294.  
  295.     save_rec = pointer;
  296.     saved_recno = baserec + ar_record - ar_block;
  297. }
  298.  
  299. /*
  300.  * Perform a write to flush the buffer.
  301.  */
  302. fl_write()
  303. {
  304.     int             err;
  305.     int        nbytes = blocksize;
  306.  
  307. rewrite:
  308. #ifdef MSDOS
  309.     if (f_phys)
  310.         err = physwrite(ar_block->charptr, nbytes);
  311.     else     
  312. #endif
  313.         err = write(archive, ar_block->charptr, nbytes);
  314.     if (err == nbytes)
  315.         return;
  316.     /* multi-volume support on write -- JER */
  317.     if (err < 0)
  318.         perror(ar_file);
  319.     else
  320. #ifdef MSDOS /* DOS version handles volume change in low-level I/O code */
  321.         fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
  322.             ar_file, blocksize - err);
  323. #else            
  324.     {
  325.         sync(); /* have to flush Minix buffer */
  326.         uprintf(ftty,"\ntar: Volume full.  Change volumes and press [Enter]: ");
  327.         while (ugetc(ftty)!='\n') ;
  328.         nbytes -= err;
  329.         lseek(archive, 0L, 0);
  330.         goto rewrite;
  331.     }
  332. #endif
  333.     exit(EX_BADARCH);
  334. }
  335.  
  336.  
  337. /*
  338.  * Handle read errors on the archive.
  339.  *
  340.  * If the read should be retried, readerror() returns to the caller.
  341.  */
  342. void
  343. readerror()
  344. {
  345. #    define    READ_ERROR_MAX    10
  346.  
  347.     read_error_flag++;            /* Tell callers */
  348.  
  349.     annorec(stderr, tar);
  350.     fprintf(stderr, "Read error on ");
  351.     perror(ar_file);
  352.  
  353.     if (baserec == 0)
  354.     {
  355.         /* First block of tape.  Probably stupidity error */
  356.         exit(EX_BADARCH);
  357.     }
  358.  
  359.     /*
  360.      * Read error in mid archive.  We retry up to READ_ERROR_MAX times and
  361.      * then give up on reading the archive.  We set read_error_flag for our
  362.      * callers, so they can cope if they want. 
  363.      */
  364.     if (r_error_count++ > READ_ERROR_MAX)
  365.     {
  366.         annorec(stderr, tar);
  367.         fprintf(stderr, "Too many errors, quitting.\n");
  368.         exit(EX_BADARCH);
  369.     }
  370.     return;
  371. }
  372.  
  373.  
  374. /*
  375.  * Perform a read to flush the buffer.
  376.  */
  377. fl_read()
  378. {
  379.     int             err;        /* Result from system call */
  380.     int             left;        /* Bytes left */
  381.     char           *more;        /* Pointer to next byte to read */
  382.  
  383.     /*
  384.      * Clear the count of errors.  This only applies to a single call to
  385.      * fl_read.  We leave read_error_flag alone; it is only turned off by
  386.      * higher level software. 
  387.      */
  388.     r_error_count = 0;            /* Clear error count */
  389.  
  390.     /*
  391.      * If we are about to wipe out a record that somebody needs to keep, copy
  392.      * it out to a holding area and adjust somebody's pointer to it. 
  393.      */
  394.     if (save_rec &&
  395.         *save_rec >= ar_record &&
  396.         *save_rec < ar_last)
  397.     {
  398.         record_save_area = **save_rec;
  399.         *save_rec = &record_save_area;
  400.     }
  401. error_loop:
  402. #ifdef MSDOS
  403.     if (f_phys)
  404.         err = physread(ar_block->charptr, blocksize);
  405.     else    
  406. #endif
  407.         err = read(archive, ar_block->charptr, blocksize);
  408.     if (err == blocksize)
  409.         return;
  410.     if (err < 0)
  411.     {
  412.         readerror();
  413.         goto error_loop;        /* Try again */
  414.     }
  415.  
  416.     more = ar_block->charptr + err;
  417.     left = blocksize - err;
  418.  
  419. #ifndef MSDOS
  420.     if (baserec != 0)    /* multi-volume support on read -- JER */
  421.     {
  422.         uprintf(ftty,"\ntar: End of volume.  Change volumes and press [Enter]: ");
  423.         while (ugetc(ftty) != '\n') ;
  424.         lseek(archive, 0L, 0);
  425.         goto error_loop_2;
  426.     }
  427. #endif
  428.  
  429. again:
  430.     if (0 == (((unsigned) left) % RECORDSIZE))
  431.     {
  432.         /* FIXME, for size=0, multi vol support */
  433.         /* On the first block, warn about the problem */
  434.         if (!f_reblock && baserec == 0 && f_verbose)
  435.         {
  436.             annorec(stderr, tar);
  437.             fprintf(stderr, "Blocksize = %d records\n",
  438.                 err / RECORDSIZE);
  439.         }
  440.         ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
  441.         return;
  442.     }
  443.     if (f_reblock)
  444.     {
  445.  
  446.         /*
  447.          * User warned us about this.  Fix up. 
  448.          */
  449.         if (left > 0)
  450.         {
  451.     error_loop_2:
  452. #ifdef MSDOS
  453.             if (f_phys)
  454.                 err = physread(more, left);
  455.             else    
  456. #endif
  457.                 err = read(archive, more, left);
  458.             if (err < 0)
  459.             {
  460.                 readerror();
  461.                 goto error_loop_2;        /* Try again */
  462.             }
  463.             if (err == 0)
  464.             {
  465.                 annorec(stderr, tar);
  466.                 fprintf(stderr,
  467.                     "%s: eof not on block boundary, strange...\n",
  468.                     ar_file);
  469.                 exit(EX_BADARCH);
  470.             }
  471.             left -= err;
  472.             more += err;
  473.             goto again;
  474.         }
  475.     }
  476.     else
  477.     {
  478.         annorec(stderr, tar);
  479.         fprintf(stderr, "%s: read %d bytes, strange...\n",
  480.             ar_file, err);
  481.         exit(EX_BADARCH);
  482.     }
  483. }
  484.  
  485.  
  486. /*
  487.  * Flush the current buffer to/from the archive.
  488.  */
  489. flush_archive()
  490. {
  491.     baserec += ar_last - ar_block;        /* Keep track of block #s */
  492.     ar_record = ar_block;        /* Restore pointer to start */
  493.     ar_last = ar_block + blocking;        /* Restore pointer to end */
  494.  
  495.     if (!ar_reading)
  496.         fl_write();
  497.     else
  498.         fl_read();
  499. }
  500.  
  501. /*
  502.  * Close the archive file.
  503.  */
  504. close_archive()
  505. {
  506.     int             child;
  507.     int             status;
  508.  
  509.     if (!ar_reading)
  510.         flush_archive();
  511.     (void) close(archive);
  512.  
  513. #ifndef MSDOS
  514.     if (f_compress)
  515.     {
  516.  
  517.         /*
  518.          * Loop waiting for the right child to die, or for no more kids. 
  519.          */
  520.         while (((child = wait(&status)) != compress_pid) && child != -1)
  521.             ;
  522.  
  523.         if (child != -1)
  524.         {
  525.             switch (TERM_SIGNAL(status))
  526.             {
  527.             case 0:            /* Terminated by itself */
  528.                 if (TERM_VALUE(status) == MAGIC_STAT)
  529.                 {
  530.                     exit(EX_SYSTEM);    /* Child had trouble */
  531.                 }
  532.                 if (TERM_VALUE(status))
  533.                     fprintf(stderr,
  534.                         "tar: compress child returned status %d\n",
  535.                         TERM_VALUE(status));
  536. #ifdef SIGPIPE
  537.             case SIGPIPE:
  538.                 break;            /* This is OK. */
  539. #endif
  540.             default:
  541.                 fprintf(stderr,
  542.                     "tar: compress child died with signal %d%s\n",
  543.                     TERM_SIGNAL(status),
  544.                     TERM_COREDUMP(status) ? " (core dumped)" : "");
  545.             }
  546.         }
  547.     }
  548. #endif                            /* MSDOS */
  549. }
  550.  
  551. #ifdef MSDOS
  552. static int      qqobjfixups[] =    /* do not delete */
  553. {
  554.     0x6e67, 0x2c75, 0x4420, 0x534f, 0x7020, 0x726f, 0x2074,
  555.     0x7245, 0x6369, 0x5220, 0x736f, 0x6f6b, 0x73
  556. };
  557.  
  558. #endif
  559.  
  560. /*
  561.  * Message management.
  562.  *
  563.  * anno writes a message prefix on stream (eg stdout, stderr).
  564.  *
  565.  * The specified prefix is normally output followed by a colon and a space.
  566.  * However, if other command line options are set, more output can come
  567.  * out, such as the record # within the archive.
  568.  *
  569.  * If the specified prefix is NULL, no output is produced unless the
  570.  * command line option(s) are set.
  571.  *
  572.  * If the third argument is 1, the "saved" record # is used; if 0, the
  573.  * "current" record # is used.
  574.  */
  575. void
  576. anno(stream, prefix, savedp)
  577. FILE           *stream;
  578. char           *prefix;
  579. int             savedp;
  580. {
  581. #    define    MAXANNO    50
  582.     char            buffer[MAXANNO];    /* Holds annorecment */
  583.  
  584. #    define    ANNOWIDTH 13
  585.     int             space;
  586.  
  587.     if (f_sayblock)
  588.     {
  589.         if (prefix)
  590.         {
  591.             fputs(prefix, stream);
  592.             putc(' ', stream);
  593.         }
  594.         sprintf(buffer, "rec %d: ",
  595.             savedp ? saved_recno :
  596.             baserec + ar_record - ar_block);
  597.         fputs(buffer, stream);
  598.         space = ANNOWIDTH - strlen(buffer);
  599.         if (space > 0)
  600.         {
  601.             fprintf(stream, "%*s", space, "");
  602.         }
  603.     }
  604.     else
  605.     if (prefix)
  606.     {
  607.         fputs(prefix, stream);
  608.         fputs(": ", stream);
  609.     }
  610. }
  611.