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