home *** CD-ROM | disk | FTP | other *** search
/ Hall of Fame / HallofFameCDROM.cdr / proglc / zoo141_c.lzh / ZOOEXT.C < prev    next >
C/C++ Source or Header  |  1987-02-07  |  21KB  |  626 lines

  1. /* zooext.c */
  2. /*
  3. Copyright (C) 1986 Rahul Dhesi -- All rights reserved
  4. */
  5. #include "options.h"
  6. /* Extract file from archive.  Extracts files specified in parameter-list 
  7.    from archive zoo_path.  If none specified, extracts all files from 
  8.    archive. */
  9.  
  10. #include "zoo.h"
  11. #include "parse.h"      /* defines struct for parse() */
  12.  
  13. #include "portable.h"   /* portable I/O definitions */
  14. #include "machine.h"    /* machine-specific declarations */
  15.  
  16. #include <stdio.h>
  17. #include "various.h"
  18.  
  19. #ifndef NOSIGNAL
  20. #include <signal.h>
  21. #endif
  22.  
  23. #include "zoofns.h"
  24.  
  25. /* for low-level I/O */
  26. #ifdef FLAT
  27. #include <types.h>
  28. #include <stat.h>
  29. #else
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #endif
  33.  
  34. #ifdef NOFCNTL
  35. #include <file.h>
  36. #else
  37. #include <fcntl.h>
  38. #endif
  39.  
  40. extern int quiet;
  41.  
  42. #include "errors.i"
  43.  
  44. /* Following two are used by ctrl_c() also, hence declared here */
  45. char extfname[LFNAMESIZE];             /* filename of extracted file */
  46. static int this_han;                   /* handle of file to extract */
  47.  
  48. static int tofile;                     /* true if not pipe or null device */
  49. extern unsigned int crccode;
  50. extern char *out_buf_adr;              /* address of output buffer */
  51.  
  52. void zooext(zoo_path, option)
  53. char *zoo_path, *option;
  54. {
  55. char *whichname;                          /* which name to extract */
  56. char matchname[PATHSIZE];                 /* for pattern matching only */
  57. #ifndef NOSIGNAL
  58. int (*oldsignal)();        /* to save previous SIGINT handler */
  59. #endif
  60. int zoo_han;                              /* handle for open archive */
  61. long next_ptr;                            /* pointer to within archive */
  62. struct zoo_header zoo_header;             /* header for archive */
  63. int status;                               /* error status */
  64. char ans[3];                              /* answer to "Overwrite?" */
  65. int error_message = 1;                    /* Whether to give error message */
  66. unsigned long disk_space;                 /* disk space left */
  67. int matched = 0;                          /* Any files matched? */
  68. int overwrite = 0;                        /* force overwrite of files? */
  69. int needdel = 0;                          /* extract deleted files too */
  70. int usepath = 0;                          /* use path for extraction */
  71. int todot = 0;                            /* extract relative to . */
  72. int zootime = 0;                          /* just set archive time */
  73. int badcrc_count = 0;                     /* how many files with bad CRC */
  74. int bad_header = 0;                       /* to avoid spurious messages later */
  75. long fiz_ofs = 0;                         /* offset where to start */
  76. int pipe = 0;                             /* are we piping output? */
  77. int fast_ext = 0;                         /* fast extract as *.?Z? */
  78. int null_device = 0;                      /* are we sending to null device? */
  79. int alloc_size;                           /* disk allocation unit size */
  80. struct direntry direntry;                 /* directory entry */
  81.  
  82. #ifdef OOZ
  83.    static char extract_ver[] = "A higher version of OOZ is needed to extract ";
  84.    static char no_space[] = "Insufficient disk space to extract ";
  85. #else
  86.    static char extract_ver[] = "Zoo %d.%d is needed to extract %s.\n";
  87.    static char no_space[] = "Insufficient disk space to extract %s.\n";
  88. #endif
  89.  
  90. #ifndef OOZ
  91. while (*option) {
  92.    switch (*option) {
  93. #ifndef PORTABLE
  94.       case 'z': fast_ext++; break;
  95. #endif
  96.       case 'x':
  97.       case 'e': break;
  98.       case 'N': null_device++; break;
  99.       case 'O': overwrite += 2; break;
  100.       case 'o': overwrite++; break;
  101.       case 'p': pipe++; break;
  102.       case 'd': needdel++; break;
  103.       case 'q': quiet++; break;
  104.       case '/': usepath++; break;
  105.       case '.': todot++; break;
  106.       case '@': 
  107.          ++option;
  108.          fiz_ofs = calc_ofs(option); 
  109.          goto no_more;
  110.       default:
  111.          prterror ('w', option_ignored, *option);
  112.          break;
  113.    }
  114.    option++;
  115. }
  116.  
  117. no_more: /* come from exit in while loop above */
  118.  
  119.  
  120. if (overwrite == 1)                 /* must be at least 2 to begin with */
  121.    overwrite--;
  122.  
  123. if (null_device && pipe) {
  124.    prterror ('w', option_ignored, 'p');
  125.    pipe = 0;
  126. }
  127.  
  128. if (overwrite && pipe)
  129.    prterror ('w', option_ignored, 'O');
  130.  
  131. if (null_device && fast_ext) {
  132.    prterror ('w', option_ignored, 'N');
  133.    null_device = 0;
  134. }
  135.  
  136. tofile = !pipe && !null_device;     /* sending to actual file */
  137. #else
  138. tofile = 1;
  139. #endif
  140.  
  141.  
  142. zoo_han = OPEN(zoo_path, F_READ);
  143.  
  144. if (zoo_han == -1)
  145. #ifdef OOZ
  146.    prterror ('f', could_not_open, zoo_path, ".\n");
  147. #else
  148.    prterror ('f', could_not_open, zoo_path);
  149. #endif
  150.  
  151. if (fiz_ofs != 0L) {                /* if offset specified, start there */
  152.    prterror('m', "Starting at %ld\n", fiz_ofs);
  153.    lseek (zoo_han, fiz_ofs, 0);
  154. } else {
  155.    /* read header */
  156.    rd_zooh (&zoo_header, zoo_han);
  157.    /* read (zoo_han, (char *) &zoo_header, sizeof(zoo_header)); */
  158.    if ((zoo_header.zoo_start + zoo_header.zoo_minus) != 0L) {
  159.       prterror ('w', failed_consistency, NULL, NULL);
  160.       bad_header++;
  161.    }
  162.    lseek (zoo_han, zoo_header.zoo_start, 0); /* seek to where data begins */
  163. }
  164.  
  165. #ifndef PORTABLE
  166. disk_space = space (0, &alloc_size);         /* remember disk space left */
  167. #else
  168. alloc_size = 0;
  169. disk_space = MAXLONG;              /* infinite disk space */
  170. #endif
  171.  
  172. #ifndef OOZ
  173. /* Ooz does no piping */
  174. /* if piping output we open the output device just once */
  175. /* NOTE:  the DOS device "NUL" was causing the system to hang up.
  176.    therefore we create our own.  All functions called must recognize
  177.    -2 as a null output handle and ignore all output to that */
  178. if (null_device) {                  /* -2 means null device */
  179.    this_han = -2;
  180. } else if (pipe)
  181.    this_han = (int) fileno(stdout);    /* standard output */
  182. #endif
  183.  
  184. while (1) {
  185.    rd_dir (&direntry, zoo_han);
  186.    /* read (zoo_han, (char *) &direntry, sizeof(direntry)); */
  187.    if (direntry.zoo_tag != ZOO_TAG) {
  188.       long currpos, zoolength;
  189.  
  190. #ifdef OOZ
  191.          prterror ('f', invalid_header, NULL, NULL);
  192. #else
  193.          prterror ('F', invalid_header);
  194.  
  195.          /* Note:  if header was bad, there's no point trying to find
  196.             how many more bytes aren't processed -- our seek position is
  197.             likely very wrong */
  198.    
  199.          if (!bad_header)
  200.             if ((currpos = tell (zoo_han)) != -1L)
  201.                if (lseek (zoo_han, 0L, 2) != -1)
  202.                   if ((zoolength = tell (zoo_han)) != -1L)
  203.                      printf (cant_process, zoolength - currpos);              
  204.          exit (1);
  205. #endif
  206.    }
  207.    if (direntry.next == 0L) {                /* END OF CHAIN */
  208.       break;                                 /* EXIT on end of chain */
  209.    }
  210.    next_ptr = direntry.next;                 /* ptr to next dir entry */
  211.  
  212.    whichname = choosefname(&direntry);       /* which filename */
  213.    fixfname(whichname);                      /* fix syntax */
  214.    combine (matchname,
  215.              direntry.dirlen > 0 ? direntry.dirname : "",
  216.             (direntry.namlen > 0) ? direntry.lfname : direntry.fname
  217.            );
  218.  
  219.    if (todot && *direntry.dirname == *PATH_CH) {
  220.       char tmpstr[PATHSIZE];
  221.       strcpy(tmpstr, direntry.dirname);
  222.       strcpy(direntry.dirname,CUR_DIR);
  223.       strcat(direntry.dirname, tmpstr);
  224.    }
  225.  
  226.    /* matchname now holds the full pathname for pattern matching */
  227.  
  228.    if ( ( (needdel && direntry.deleted) ||
  229.             (needdel < 2 && !direntry.deleted)
  230.         ) && needed(matchname)) {
  231.       matched++;           /* update count of files extracted */
  232.  
  233.       if (direntry.major_ver > MAJOR_EXT_VER ||
  234.          (direntry.major_ver == MAJOR_EXT_VER && 
  235.             direntry.minor_ver > MINOR_EXT_VER)) {
  236.  
  237. #ifdef OOZ
  238.             prterror ('e', extract_ver, whichname, ".\n");
  239. #else
  240.             prterror ('e', extract_ver, direntry.major_ver, 
  241.                            direntry.minor_ver, whichname);
  242. #endif
  243.             goto loop_again;
  244.       }
  245.  
  246.       /* 
  247.       If extracting to null device, or if user requested extraction
  248.       of entire path, include any directory name in filename.
  249.       If extraction to current directory requested, and if extfname
  250.       begins with path separator, fix it */
  251.  
  252.       strcpy (extfname, whichname);
  253.       if ((usepath || null_device) && direntry.dirlen > 0) {
  254.          combine(extfname, direntry.dirname, whichname);
  255.          if (usepath > 1 && !null_device)
  256.             makepath(direntry.dirname);         /* make dir prefix */
  257.       }
  258.  
  259.       if (tofile) {
  260.          int present = 0;
  261.  
  262. #ifndef OOZ
  263. #ifndef PORTABLE
  264.          /* 
  265.          if Z format (fast) extraction, extension is created as
  266.          follows:  for no current extension, new extension is "zzz";
  267.          for current extension "a", new extension is "azz";  for 
  268.          current extension "ab", new extension is "azb";  and for
  269.          current extension "abc", new extension is "azc".
  270.          */
  271.            
  272.          if (fast_ext) {
  273.             int length;
  274.             struct path_st path_st;
  275.             parse (&path_st, extfname);         /* split filename */
  276.             strcpy (extfname, path_st.fname);   /* just root filename */
  277.             length = strlen (path_st.ext);
  278.             strcat (extfname, ".");
  279.             if (length == 0)
  280.                strcat (extfname, "zzz");        /* no ext -> .zzz */
  281.             else if (length == 1) {
  282.                strcat (extfname, path_st.ext);
  283.                strcat (extfname, "zz");         /* *.?    -> *.?zz */
  284.             } else { /* length is 2 or 3 */
  285.                if (length == 2)                 /* allow .aa, .ab, etc. */
  286.                   path_st.ext[2] = path_st.ext[1];
  287.                path_st.ext[1] = 'z';
  288.                strcat (extfname, path_st.ext);  /* *.??   -> *.?z? */
  289.             }
  290.          }
  291. #endif   /* ifndef PORTABLE */
  292. #endif   /* ifndef OOZ */
  293.  
  294.  
  295.          if (overwrite) {
  296.             this_han = CREATE(extfname, F_RDWR);
  297.          } else {
  298.             if (exists (extfname)) {
  299.                present = 1;
  300.                this_han = -1;
  301.             } else
  302.                this_han = CREATE(extfname, F_RDWR);
  303.          }
  304.          if (this_han == -1) {
  305.             if (present == 1) {      /* if file exists already */
  306.                do {
  307.  
  308. #ifdef OOZ
  309.                      putstr ("Overwrite "); putstr (extfname);
  310.                      putstr (" (Yes/No/All)? ");
  311. #else
  312.                      printf ("Overwrite %s (Yes/No/All)? ", extfname);
  313. #endif
  314.                   fflush (stdin);
  315.                   fgets (ans, 3, stdin);
  316.                   strlwr (ans);
  317.                } while (*ans != 'y' && *ans != 'n' && *ans != 'a');
  318.    
  319.                /* NOTE:  We use CREAT even if we are trying to overwrite
  320.                   the file, because a data path utility (Search or Dpath)
  321.                   could have fooled us before. */
  322.                if (*ans == 'a')
  323.                   overwrite++;
  324.                if (*ans == 'y' || *ans == 'a') {
  325.                   this_han = CREATE(extfname, F_RDWR);
  326.                   error_message = 1; /* give error message if open fails */
  327.                } else {
  328.                   error_message = 0; /* user said 'n', so no error message */
  329.                }
  330.             } else {
  331.                error_message = 1;   /* Real error -- give error message */
  332.             }
  333.          } /* end if */
  334.       } /* end if */
  335.  
  336.       if (this_han == -1) {         /* file couldn't be opened */
  337.          if (error_message == 1) {
  338.             unlink (extfname);
  339. #ifdef OOZ
  340.                prterror ('e', could_not_open, extfname, " for output.\n");
  341. #else
  342.                prterror ('e', "Can't open %s for output.\n", extfname);
  343. #endif
  344.  
  345. #ifndef PORTABLE
  346.             /* if error was due to full disk, abort */
  347.             if (space(0, &alloc_size) < alloc_size)
  348. #endif
  349.                prterror ('f', disk_full, NULL, NULL);
  350.  
  351.          }
  352.       } else if (lseek (zoo_han, direntry.offset, 0) == -1L) {
  353.          prterror ('e', "Could not seek to file data.\n");
  354.          close_han (this_han);
  355.       } else {
  356. #ifndef PORTABLE
  357.          /* check Dos's free disk space if we seem to be running low 
  358.             (within 1 cluster of being full) */
  359.          if (tofile && disk_space < direntry.org_size + alloc_size) {
  360.             disk_space = space (0, &alloc_size);
  361.             if (disk_space < alloc_size) {
  362.                close_han (this_han);
  363.                unlink (extfname);
  364.                prterror ('f', disk_full, NULL, NULL);
  365.             }              
  366.          }
  367. #endif
  368.          if (tofile && disk_space < direntry.org_size) {
  369. #ifdef PORTABLE
  370.             ;
  371. #else
  372. #ifdef OOZ
  373.                prterror ('e', no_space, extfname, ".\n");
  374. #else
  375.                prterror ('e', no_space, extfname);
  376. #endif
  377.  
  378.             unlink (extfname);               /* delete any created file */
  379. #endif   /* portable */
  380.  
  381.          } else { 
  382.  
  383. #ifndef OOZ
  384. #ifndef PORTABLE
  385.             if (fast_ext) {            /* fast ext -> create header */
  386.                void make_tnh();
  387.                struct tiny_header tiny_header;
  388.                make_tnh(&tiny_header, &direntry);
  389.                write (this_han, (char *) &tiny_header, sizeof (tiny_header));
  390.  
  391.                if (direntry.cmt_size != 0) { /* copy comment */
  392.                   long save_pos;
  393.                   save_pos = tell (zoo_han);
  394.                   lseek (zoo_han, direntry.comment, 0);
  395.                   getfile (zoo_han, this_han, 
  396.                           (long) direntry.cmt_size, 0);
  397.                   lseek (zoo_han, save_pos, 0);
  398.                }
  399.             }
  400. #endif /* ifndef PORTABLE */
  401. #endif /* ifndef OOZ */
  402.  
  403.             crccode = 0;      /* Initialize CRC before extraction */
  404. #ifdef OOZ
  405.                putstr (extfname); 
  406.                {
  407.                   register int i;
  408.                   for (i = strlen(direntry.fname); i < 13; i++)
  409.                      putstr (" ");
  410.                   putstr ("-- ");
  411.                }
  412. #else
  413.                if (!pipe) {
  414. #ifdef PORTABLE
  415.                   prterror ('m', "%-14s -- ", extfname);
  416. #else
  417.                   if (fast_ext)
  418.                      prterror ('m', "%-12s ==> %-12s -- ", 
  419.                         direntry.fname, extfname);
  420.                   else
  421.                      prterror ('m', "%-12s -- ", extfname);
  422. #ifdef COMMENT
  423.                   prterror ('m', 
  424.                         fast_ext ? "%-12s ==> %-12s -- " : "%-12s -- ",
  425.                         direntry.fname, extfname);
  426. #endif /* COMMENT */
  427. #endif /* PORTABLE */
  428.  
  429.                } else {            /* must be pipe */
  430.                   prterror ('M', "\n\n********\n%s\n********\n", direntry.fname);
  431.                   fflush (stdout);     /* make 'm appear right here */
  432.  
  433. #ifdef COMMENT
  434. #ifdef SETMODE
  435.                   MODE_BIN(this_han);           /* make std output binary so
  436.                                                    ^Z won't cause error */
  437. #endif
  438. #endif
  439.                }
  440. #endif /* OOZ */
  441.  
  442. #ifndef NOSIGNAL
  443. #ifndef OOZ
  444.             if (tofile)
  445. #endif /* not OOZ */
  446.                {
  447.                   oldsignal = signal (SIGINT, SIG_IGN);
  448.                   if (oldsignal != SIG_IGN) 
  449.                      signal (SIGINT, ctrl_c); /* Trap ^C & erase partial file */
  450.                }
  451. #endif /* not NOSIGNAL */
  452.  
  453.             if (direntry.packing_method == 0)
  454.                /* 4th param 1 means CRC update */
  455.                status = getfile (zoo_han, this_han, direntry.size_now, 1);
  456.  
  457. #ifndef OOZ
  458.             else if (fast_ext)
  459.                /* 4th param 0 means no CRC update */
  460.                status = getfile (zoo_han, this_han, direntry.size_now, 0);
  461. #endif
  462.  
  463.  
  464.             else if (direntry.packing_method == 1)
  465.                status = lzd (zoo_han, this_han); /* uncompress */
  466.             else {
  467. #ifdef OOZ
  468.                prterror ('e', "File ", whichname,
  469.                   ":  impossible packing method.\n", "");
  470. #else
  471.                prterror ('e', "File %s:  impossible packing method.\n",
  472.                   whichname);
  473. #endif
  474.                   unlink(extfname);
  475.                   goto loop_again;
  476.             }
  477.  
  478.  
  479. #ifndef NOSIGNAL
  480. #ifndef OOZ
  481.             if (tofile)
  482. #endif /* not OOZ */
  483.                signal (SIGINT, oldsignal);
  484. #endif /* not NOSIGNAL */
  485.  
  486. #ifdef COMMENT
  487. #ifndef OOZ
  488. #ifdef SETMODE
  489.             if (pipe)
  490.                MODE_TEXT(this_han);          /* restore text mode */
  491. #endif
  492. #endif
  493. #endif
  494.    
  495. #ifndef OOZ
  496.             if (tofile) {
  497. #endif
  498.                /* set date/time of file being extracted */
  499. #ifdef NIXTIME
  500.                close_han (this_han);
  501.                setutime (direntry.fname, direntry.date, direntry.time);
  502. #else
  503.                settime (this_han, direntry.date, direntry.time);
  504.                close_han (this_han);
  505. #endif
  506. #ifndef OOZ
  507.             }
  508. #endif
  509.             if (status != 0) {
  510.                if (tofile)
  511.                   unlink (extfname);
  512.                if (status == 1) {
  513.                   memerr();
  514.                /* To avoid spurious errors due to ^Z being sent to screen,
  515.                   we don't check for I/O error if output was piped */
  516.                } else if (!pipe && (status == 2 || status == 3)) {
  517. #ifdef OOZ
  518.                      prterror ('e', no_space, direntry.fname, ".\n");
  519. #else
  520.                      prterror ('e', no_space, direntry.fname);
  521. #endif
  522.                }
  523.             } else {
  524.                /* file extracted, so update disk space.  */
  525.                /* we subtract the original size of the file, rounded
  526.                   UP to the nearest multiple of the disk allocation
  527.                   size. */
  528. #ifndef PORTABLE
  529.                {
  530.                   unsigned long temp;
  531.                   temp = (direntry.org_size + alloc_size) / alloc_size;
  532.                   disk_space -= temp * alloc_size;
  533.                }
  534. #endif
  535.  
  536. #ifdef OOZ
  537.                if (direntry.file_crc != crccode)
  538.                   putstr ("extracted  \007WARNING:  Bad CRC.\n");
  539.                else
  540.                   putstr ("extracted\n");
  541. #else
  542.                if (!fast_ext && direntry.file_crc != crccode) {
  543.                   badcrc_count++;
  544.                   if (!pipe) {
  545.                      if (!null_device)
  546.                         prterror ('M', "extracted   ");
  547.                      prterror ('w', bad_crc, direntry.fname);
  548.                   }
  549.                   else {   /* duplicate to standard error */
  550.                      static char stars[] = "\n******\n";
  551.                      putstr (stars);
  552.                      prterror ('w', bad_crc, direntry.fname);
  553.                      putstr (stars);
  554.                      fprintf (stderr, "WARNING:  ");
  555.                      fprintf (stderr, bad_crc, direntry.fname);
  556.                   }
  557.                } else
  558.                   if (!pipe)
  559.                      prterror ('M', null_device ? "OK\n" : "extracted\n");
  560. #endif
  561.  
  562.             } /* end if */
  563.          } /* end if */
  564.       } /* end if */
  565.    } /* end if */
  566.  
  567. loop_again:
  568.    lseek (zoo_han, next_ptr, 0); /* ..seek to next dir entry */
  569. } /* end while */
  570.  
  571. close (zoo_han);
  572. if (!matched)
  573.    putstr (no_match);
  574.  
  575. #ifndef OOZ
  576. if (badcrc_count) {
  577.    prterror ('w', "%d File(s) with bad CRC.\n", badcrc_count);
  578.    exit (1);
  579. } else if (null_device)
  580.    prterror ('m', "Archive seems OK.\n");
  581. #endif
  582.  
  583. } /* end zooext */
  584.  
  585. /* close_han() */
  586. /* closes a handle if and only if we aren't sending output to 
  587.    a pipe or to the null device */
  588.  
  589. void close_han (handle)
  590. int handle;
  591. {
  592.    if (tofile)
  593.       close (handle);
  594. }
  595.  
  596. /* Ctrl_c() is called if ^C is hit while a file is being extracted.
  597.    It closes the files, deletes it, and exits. */
  598. int ctrl_c()
  599. {
  600. #ifndef NOSIGNAL
  601.    signal (SIGINT, SIG_IGN);     /* ignore any more */
  602. #endif
  603.    close (this_han);
  604.    unlink (extfname);
  605.    exit (1);
  606. }
  607.  
  608. /* make_tnh copies creates a tiny_header */
  609. void make_tnh (tiny_header, direntry)
  610. struct tiny_header *tiny_header;
  611. struct direntry *direntry;
  612. {
  613.    tiny_header->tinytag = TINYTAG;
  614.    tiny_header->type = 1;
  615.    tiny_header->packing_method = direntry->packing_method;
  616.    tiny_header->date = direntry->date;
  617.    tiny_header->time = direntry->time;
  618.    tiny_header->file_crc = direntry->file_crc;
  619.    tiny_header->org_size = direntry->org_size;
  620.    tiny_header->size_now = direntry->size_now;
  621.    tiny_header->major_ver = direntry->major_ver;
  622.    tiny_header->minor_ver = direntry->minor_ver;
  623.    tiny_header->cmt_size = direntry->cmt_size;
  624.    strcpy (tiny_header->fname, direntry->fname);
  625.