home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / tarsrc30.sit / extract.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-05  |  7.9 KB  |  369 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modified by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * Extract files from a tar archive.
  8.  *
  9.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  10.  *
  11.  * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
  12.  */
  13. #include "tar.h"
  14.  
  15. extern union record    *head;        /* Points to current tape header */
  16. extern struct stat {
  17.     long    st_size;
  18.     long    st_mtime;
  19. } hstat;                /* Fake stat struct for compat. */
  20.  
  21. Boolean ExtractArchive();
  22. extern void PrintHeader();
  23. extern Boolean SkipFile();
  24.  
  25. int MakeDirs();            /* Makes required directories */
  26.  
  27. /*
  28.  * Extract - extract the entire archive
  29.  */
  30. Extract() {
  31.     if (OpenArchive("\pExtract Archive:", true))
  32.         return;
  33.         
  34.     if (GetDir("\pExtraction Directory:", true) == false)
  35.         return;
  36.  
  37.     /*
  38.      * Extract and print the files as found in the archive.
  39.      */
  40.     if (!WindInit()) {
  41.         TextFace(underline);
  42.         WPrintf(header);
  43.         TextFace(0);
  44.         ReadAnd(ExtractArchive);
  45.         CloseArchive();
  46.         WindEnd(pref.autoPage);
  47.     }
  48.     
  49.     RlsDir();
  50. }
  51.  
  52. /*
  53.  * Extract a file from the archive.
  54.  */
  55. Boolean
  56. ExtractArchive()
  57. {
  58.     register char    *data;
  59.     register char    *s, *d;
  60.     union record    *rec;
  61.     int        i, namelen;
  62.     Boolean        errFound = false;
  63.     Boolean        crSeen;
  64.     long        bytes, check, written;
  65.     long        size;
  66.     OSErr        err;
  67.     char        macName[256];
  68.     HParamBlockRec    dpb;
  69.     HParamBlockRec    fpb;
  70.     char        *routine = "\pExtractArchive";
  71.  
  72.     SaveRec(&head);            /* Make sure it sticks around */
  73.     UseRec(head);            /* And go past it in the archive */
  74.     DecodeHeader(head, &hstat, 1);    /* Snarf fields */
  75.  
  76.     /* Print the record from 'head' and 'hstat' */
  77.     PrintHeader();
  78.  
  79.     switch (head->header.linkflag) {
  80.     default:
  81.         WPrintf("Unknown file type %d for %s",
  82.             head->header.linkflag, head->header.name);
  83.         break;
  84.  
  85.     case LF_OLDNORMAL:
  86.     case LF_NORMAL:
  87.         /*
  88.          * Appears to be a file.
  89.          * See if it's really a directory.
  90.          */
  91.         namelen = strlen(head->header.name) - 1;
  92.         if (head->header.name[namelen] == '/')
  93.             goto really_dir;
  94.  
  95.         FixName(head->header.name, macName);
  96.     again_file:
  97.         memset(&fpb, 0, sizeof(fpb));
  98.         fpb.fileParam.ioCompletion = nil;
  99.         fpb.fileParam.ioNamePtr = macName;
  100.         fpb.fileParam.ioVRefNum = dirVRefNum;
  101.         fpb.fileParam.ioFVersNum = 0;
  102.         fpb.fileParam.ioDirID = 0;
  103.         err = PBHCreate(&fpb, false);
  104.         if (err == noErr) {
  105.             fpb.fileParam.ioCompletion = nil;
  106.             fpb.fileParam.ioNamePtr = macName;
  107.             fpb.fileParam.ioVRefNum = dirVRefNum;
  108.             fpb.fileParam.ioDirID = 0;
  109.             fpb.fileParam.ioFVersNum = 0;
  110.             fpb.fileParam.ioFDirIndex = 0;
  111.             if (PBHGetFInfo(&fpb, false)) {
  112.                 OSAlert(routine, "\pPBHGetFInfo", macName,
  113.                         fpb.fileParam.ioResult);
  114.                 goto doNext;
  115.             }
  116.  
  117.             memcpy(&fpb.fileParam.ioFlFndrInfo.fdCreator,
  118.                     pref.creator, 4);
  119.             memcpy(&fpb.fileParam.ioFlFndrInfo.fdType, pref.type, 4);
  120.             fpb.fileParam.ioCompletion = nil;
  121.             fpb.fileParam.ioNamePtr = macName;
  122.             fpb.fileParam.ioVRefNum = dirVRefNum;
  123.             fpb.fileParam.ioDirID = 0;
  124.             fpb.fileParam.ioFVersNum = 0;
  125.             if (PBHSetFInfo(&fpb, false)) {
  126.                 OSAlert(routine, "\pPBHSetFInfo", macName,
  127.                         fpb.fileParam.ioResult);
  128.                 goto doNext;
  129.             }
  130.  
  131.             fpb.ioParam.ioPermssn = fsWrPerm;
  132.             fpb.ioParam.ioMisc = nil;
  133.             err = PBHOpen(&fpb, false);
  134.         }
  135.  
  136.         if (err != noErr) {
  137.             if (MakeDirs(macName))
  138.                 goto again_file;
  139.  
  140.             PgmAlert(routine, "\pCould not make file", macName);
  141.             errFound = SkipFile((long)hstat.st_size);
  142.             break;
  143.         }
  144.  
  145.         /*
  146.          * Note that this only extracts data forks!
  147.          */
  148.         if (pref.dosCvt)
  149.             crSeen = false;
  150.             
  151.         for (size = hstat.st_size;
  152.              size > 0;
  153.              size -= written) {
  154.             /*
  155.              * Locate data, determine max length
  156.              * writeable, write it, record that
  157.              * we have used the data, then check
  158.              * if the write worked.
  159.              */
  160.             if ((rec = FindRec()) == nil)
  161.                 return(true);
  162.                 
  163.             data = rec->charptr;
  164.             written = EndOfRecs()->charptr - data;
  165.             if (written > size)
  166.                 written = size;
  167.  
  168.             bytes = written;
  169.             if (pref.cvtNl) {
  170.                 /*
  171.                  * Convert newlines to return for Mac compatability.
  172.                  */
  173.                 for (i = bytes, d = data; --i >= 0; d++)
  174.                     if (*d == LF)
  175.                         *d = RETURN;
  176.                         
  177.             } else if (pref.dosCvt) {    
  178.                 /*
  179.                  * Convert DOS Style CR/LF to CR only.
  180.                  */
  181.                 for (s = d = data, i = bytes; i > 0; i--) {
  182.                      if ((*s != LF) || !crSeen)
  183.                         *d++ = *s;
  184.                         
  185.                     crSeen = *s++ == RETURN;
  186.                 }
  187.                 
  188.                 bytes -= s - d;
  189.             }
  190.  
  191.             check = bytes;
  192.             fpb.ioParam.ioBuffer = data;
  193.             fpb.ioParam.ioReqCount = check;
  194.             fpb.ioParam.ioPosMode = fsAtMark;
  195.             fpb.ioParam.ioPosOffset = 0;
  196.             err = PBWrite((ParmBlkPtr) &fpb, false);
  197.             if (err != noErr) {
  198.                 OSAlert(routine, "\pPBWrite", macName, err);
  199.                 goto doNext;
  200.             }
  201.  
  202.             check = fpb.ioParam.ioActCount;
  203.             /*
  204.              * The following is in violation of strict
  205.              * typing, since the arg to userec
  206.              * should be a struct rec *.  FIXME.
  207.              */
  208.             UseRec(data + written - 1);
  209.             if (check == bytes)
  210.                 continue;
  211.                 
  212.             /*
  213.              * Error in writing to file.
  214.              * Print it, skip to next file in archive.
  215.              */
  216.             PgmAlert(routine, "\pWrite short", macName);
  217.         doNext:
  218.             PBClose((ParmBlkPtr) &fpb, false);
  219.             errFound = SkipFile((long)(size - written));
  220.             goto quit;
  221.         }
  222.  
  223.         PBClose((ParmBlkPtr) &fpb, false);
  224.     quit:
  225.         break;
  226.  
  227.     case LF_DIR:
  228.         /* Check for trailing / */
  229.         namelen = strlen(head->header.name)-1;
  230.     really_dir:
  231.         while (namelen && head->header.name[namelen] == '/')
  232.             head->header.name[namelen--] = '\0';    /* Zap / */
  233.         
  234.         FixName(head->header.name, macName);
  235.         /* FALL THROUGH */
  236.     again_dir:
  237.         memset(&dpb, 0, sizeof(dpb));
  238.         dpb.fileParam.ioCompletion = nil;
  239.         dpb.fileParam.ioNamePtr = macName;
  240.         dpb.fileParam.ioVRefNum = dirVRefNum;
  241.         dpb.fileParam.ioFVersNum = 0;
  242.         dpb.fileParam.ioDirID = 0;
  243.         err = PBDirCreate(&dpb, false);
  244.         if ((err != noErr) && (err != dupFNErr)) {
  245.             if (MakeDirs(macName))
  246.                 goto again_dir;
  247.  
  248.             PgmAlert(routine, "\pCould not make directory", macName);
  249.         }
  250.         
  251.         break;
  252.     }
  253.  
  254.     /* We don't need to save it any longer. */
  255.     SaveRec((union record **) 0);
  256.     return(errFound);
  257. }
  258.  
  259. /*
  260.  * After a file/link/symlink/dir creation has failed, see if
  261.  * it's because some required directory was not present, and if
  262.  * so, create all required dirs.
  263.  */
  264. int
  265. MakeDirs(pathname)
  266. char *pathname;
  267. {
  268.     int        madeone = 0;    /* Did we do anything yet? */
  269.     int        i, savedLen;
  270.     OSErr        err;
  271.     HParamBlockRec    pb;
  272.  
  273.     savedLen = pathname[0] & 0xff;
  274.     /*
  275.      * skip initial ':'
  276.      *
  277.      * Note that the directory name has already been converted to Mac style.
  278.      */
  279.     for (i = 2; i < savedLen; i++) {
  280.         while ((i < savedLen) && (pathname[i] != ':'))
  281.             i++;
  282.  
  283.         if (i == savedLen)
  284.             break;
  285.  
  286.         pathname[0] = i++ - 1;
  287.         memset(&pb, 0, sizeof(pb));
  288.         pb.fileParam.ioCompletion = nil;
  289.         pb.fileParam.ioNamePtr = pathname;
  290.         pb.fileParam.ioVRefNum = dirVRefNum;
  291.         pb.fileParam.ioFVersNum = 0;
  292.         pb.fileParam.ioDirID = 0;
  293.         err = PBDirCreate(&pb, false);
  294.         if (err == dupFNErr) {
  295.             continue;
  296.  
  297.         } else if (err != noErr) {
  298.             OSAlert("\pMakeDirs", "\pPBDirCreate", pathname,
  299.                     pb.fileParam.ioResult);
  300.             return(0);
  301.  
  302.         } else {
  303.             madeone++;        /* Remember if we made one */
  304.             continue;
  305.         }
  306.     }
  307.  
  308.     pathname[0] = savedLen;
  309.     return(madeone);        /* Tell them to retry if we made one */
  310. }
  311.  
  312. /*
  313.  * FixName - convert to a Mac style pathname
  314.  *
  315.  *    Conversions:
  316.  *        .    ->        (Stay at this directory level)
  317.  *        ..    -> ::        (Up a directory level)
  318.  *        .xxxx    -> _xxxx    (Don't get in trouble with device names)
  319.  *        xx:xx    -> xx/xx    (Don't get in trouble with directory delims)
  320.  */
  321. FixName(tar, mac)
  322. register char    *tar;
  323. char    *mac;
  324. {
  325.     char        *end = tar + strlen(tar);
  326.     register char    *p = mac + 1;
  327.     register char    *next;
  328.  
  329.     for (next = tar; tar < end; next++) {
  330.         /*
  331.          * Swallow up all contiguous '/' characters.
  332.          */
  333.         while (*next && (*next == '/'))
  334.             next++;
  335.  
  336.         /*
  337.          * Find the entire name up until the next '/'.
  338.          */
  339.         tar = next;
  340.         while (*next && (*next != '/'))
  341.             next++;
  342.  
  343.         *next = 0;
  344.         *p++ = ':';
  345.         if (*tar == '.')
  346.             switch (*(tar + 1)) {
  347.             case '\0':
  348.                 p--;
  349.                 continue;
  350.  
  351.             case '.':
  352.                 if (*(tar + 2) == 0)
  353.                     continue;
  354.                 /* else FALL THROUGH */
  355.  
  356.             default:
  357.                 *tar = '_';
  358.             }
  359.  
  360.         while (tar < next) {
  361.             if (*tar == ':')
  362.                 *tar = '/';
  363.             *p++ = *tar++;
  364.         }
  365.     }
  366.  
  367.     *mac = p - mac - 1;
  368. }
  369.