home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / online / source / c / compilers / Tickle-4.0.sit.hqx / Tickle-4.0 / src / tar_extract.c < prev    next >
Text File  |  1993-11-18  |  13KB  |  599 lines

  1.  
  2. #pragma segment TAR
  3.  
  4. /*
  5.  * Macintosh Tar
  6.  *
  7.  * Modified by Craig Ruff for use on the Macintosh.
  8.  */
  9. /*
  10.  * Extract files from a tar archive.
  11.  *
  12.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  13.  *
  14.  * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
  15.  */
  16.  
  17. #include "tar.h"
  18. #include "stat.h"
  19. #include <string.h>
  20.  
  21. extern union record    *head;        /* Points to current tape header */
  22. extern struct
  23.     {
  24.     long    st_size;
  25.     long    st_mtime;
  26.     } hstat;                /* Fake stat struct for compat. */
  27.  
  28. Boolean ExtractArchive();
  29. extern void PrintHeader();
  30. extern Boolean SkipFile();
  31.  
  32. int MakeDirs();            /* Makes required directories */
  33.  
  34. #ifdef TCLAPPL
  35.  
  36. /*
  37.  * Extract - extract the entire archive
  38.  */
  39. Extract() {
  40.     Point        where;
  41.     SFReply        reply;
  42.     char        name[256];
  43.     extern WindowPtr    theFeedbackWindow;
  44.  
  45.     /*
  46.      * Use the standard file dialog to select the archive.
  47.      */
  48.     where.h = where.v = 75;
  49.     MyGetFile(where, "\pTar Archive:", nil, -1, nil, nil, &reply);
  50.     if (!reply.good)
  51.         return;
  52.  
  53.     /*
  54.      * Remember the VRefNum and Name for OpenArchive.
  55.      * Find out where to put the extracted files.
  56.      */
  57.     arName = reply.fName;
  58.     WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
  59.     if (! GetFolderPathName("Extraction Directory:", name, &dirVRefNum, &dirDirID))
  60.         return;
  61.  
  62.     /*
  63.      * Extract and print the files as found in the archive.
  64.      */
  65.     if (WindInit())
  66.         return;
  67. /*TGE*/    UBegYield();
  68.  
  69. /*TGE*/    ShowFeedback();
  70.  
  71. /*TGE*/    SetPort(theFeedbackWindow);
  72.     TextFace(underline);
  73.     WPrintf(header);
  74. /*TGE*/    SetPort(theFeedbackWindow);
  75.     TextFace(0);
  76.     
  77.     ReadAnd(ExtractArchive);
  78.     CloseArchive();
  79.  
  80.     WPrintf("--- tar extraction completed.");
  81.  
  82.     WindEnd(autoPage);
  83. /*TGE*/    UEndYield();
  84. }
  85.  
  86. #endif
  87.  
  88. AEExtract(myFSS)
  89.     FSSpec        *myFSS;
  90.     {
  91.     int            myerr;
  92.     char        name[256];
  93.     extern WindowPtr    theFeedbackWindow;
  94.  
  95. #ifdef TCLAPPL
  96.     if (! GetFolderPathName("Extraction Directory:", name, &dirVRefNum, &dirDirID))
  97.         {
  98.         Feedback("Tar extract canceled.");
  99.         return noErr;
  100.         }
  101. #else
  102.     dirDirID = myFSS->parID;
  103.     dirVRefNum = myFSS->vRefNum;
  104. #endif
  105.  
  106.     /*
  107.      * Extract and print the files as found in the archive.
  108.      */
  109.     if (WindInit())
  110.         return;
  111.  
  112.     /*
  113.     ** Remember the VRefNum and Name for OpenArchive.
  114.     ** Find out where to put the extracted files.
  115.     */
  116.     
  117.     arName = myFSS->name;
  118.     arDirID = myFSS->parID;
  119.     arVRefNum = myFSS->vRefNum;
  120.  
  121.     UBegYield();
  122.  
  123.     ShowFeedback();
  124.  
  125.     SetPort(theFeedbackWindow);
  126.     TextFace(underline);
  127.     WPrintf(header);
  128.     SetPort(theFeedbackWindow);
  129.     TextFace(0);
  130.     
  131.     ReadAnd(ExtractArchive);
  132.     CloseArchive();
  133.  
  134.     WPrintf("--- tar extraction completed.");
  135.  
  136.     WindEnd(autoPage);
  137.     UEndYield();
  138.     
  139.     return noErr;
  140.     }
  141.  
  142. Cmd_Extract(clientData, interp, argc, argv)
  143.     char        *clientData;
  144.     Tcl_Interp    *interp;
  145.     int            argc;
  146.     char        *argv[];
  147.     {
  148.     int            arg_index;
  149.     short        vref;
  150.     long        dirid;
  151.     char        pascal_name[256], *archive_name, *dir_name, *ptr;
  152.     struct stat    arstat, dirstat;
  153.     char        saveType[8], saveCreator[8], buf[8];
  154.     Boolean        save_cvtNl = cvtNl;
  155. #pragma unused (clientData)
  156.  
  157.     extern WindowPtr    theFeedbackWindow;
  158.     
  159.     if (argc < 3)
  160.         {
  161.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  162.                         " ?options...? archive_filename extract_dirname\"", (char *) NULL);
  163.         return TCL_ERROR;
  164.         }
  165.  
  166.     memcpy(saveType, fdType, 4);
  167.     memcpy(saveCreator, fdCreator, 4);
  168.     cvtNl = false;
  169.     
  170.     for ( arg_index = 1 ; arg_index < argc ; ++arg_index )
  171.         {
  172.         if (argv[arg_index][0] != '-')
  173.             break;
  174.         
  175.         if (argv[arg_index][1] == '-' && argv[arg_index][2] == '\0')
  176.             break;
  177.         
  178.         if (argv[arg_index][1] == 't' && argv[arg_index][2] == '\0')
  179.             {
  180.             sprintf(buf, "%-4.4s", argv[arg_index+1]);
  181.             memcpy(fdType, buf, 4);
  182.             ++arg_index;
  183.             }
  184.         else if (argv[arg_index][1] == 'c' && argv[arg_index][2] == '\0')
  185.             {
  186.             sprintf(buf, "%-4.4s", argv[arg_index+1]);
  187.             memcpy(fdCreator, buf, 4);
  188.             ++arg_index;
  189.             }
  190.         else if (argv[arg_index][1] == 'a' && argv[arg_index][2] == '\0')
  191.             {
  192.             cvtNl = true;
  193.             }
  194.         else
  195.             {
  196.             Tcl_AppendResult(interp, "invalid option \"", argv[arg_index],
  197.                         "\"", (char *) NULL);
  198.             memcpy(fdType, saveType, 4);
  199.             memcpy(fdCreator, saveCreator, 4);
  200.             cvtNl = save_cvtNl;
  201.             return TCL_ERROR;
  202.             }
  203.         }
  204.  
  205.     if (arg_index >= argc)
  206.         {
  207.         Tcl_AppendResult(interp, "missing archive name argument", (char *) NULL);
  208.         memcpy(fdType, saveType, 4);
  209.         memcpy(fdCreator, saveCreator, 4);
  210.         cvtNl = save_cvtNl;
  211.         return TCL_ERROR;
  212.         }
  213.     
  214.     archive_name = argv[arg_index++];
  215.     
  216.     if (arg_index >= argc)
  217.         {
  218.         Tcl_AppendResult(interp, "missing extract directory argument", (char *) NULL);
  219.         memcpy(fdType, saveType, 4);
  220.         memcpy(fdCreator, saveCreator, 4);
  221.         cvtNl = save_cvtNl;
  222.         return TCL_ERROR;
  223.         }
  224.     
  225.     if ( stat( archive_name, &arstat ) < 0)
  226.         {
  227.         Tcl_AppendResult(interp, "could not locate archive file \"", archive_name,
  228.                         "\"", (char *) NULL);
  229.         memcpy(fdType, saveType, 4);
  230.         memcpy(fdCreator, saveCreator, 4);
  231.         cvtNl = save_cvtNl;
  232.         return TCL_ERROR;
  233.         }
  234.  
  235.     dir_name = argv[arg_index++];
  236.  
  237.     if ( stat( dir_name, &dirstat ) < 0)
  238.         {
  239.         Tcl_AppendResult(interp, "could not locate extract directory \"", dir_name,
  240.                         "\"", (char *) NULL);
  241.         memcpy(fdType, saveType, 4);
  242.         memcpy(fdCreator, saveCreator, 4);
  243.         cvtNl = save_cvtNl;
  244.         return TCL_ERROR;
  245.         }
  246.  
  247.     if ( ! S_ISDIR(dirstat.st_mode) )
  248.         {
  249.         Tcl_AppendResult(interp, "extract directory \"", dir_name,
  250.                             "\" not a directory ", (char *) NULL);
  251.         memcpy(fdType, saveType, 4);
  252.         memcpy(fdCreator, saveCreator, 4);
  253.         cvtNl = save_cvtNl;
  254.         return TCL_ERROR;
  255.         }
  256.  
  257.     dirVRefNum = dirstat.st_dev;
  258.     dirDirID = dirstat.st_ino;
  259.     
  260.     /*
  261.      * Extract and print the files as found in the archive.
  262.      */
  263.     WindInit();
  264.  
  265.     arDirID = arstat.st_parid;
  266.     arVRefNum = arstat.st_dev;
  267.     ptr = strrchr(archive_name, ':');
  268.     if (ptr == NULL)
  269.         strcpy(pascal_name, archive_name);
  270.     else
  271.         strcpy(pascal_name, ptr + 1);
  272.     c2pstr(pascal_name);
  273.     arName = pascal_name;
  274.     
  275.     tar_scripting = 1;
  276.     tar_interp = interp;
  277.  
  278.     UBegYield();
  279.  
  280.     SetPort(theFeedbackWindow);
  281.     TextFace(underline);
  282.     WPrintf(header);
  283.     SetPort(theFeedbackWindow);
  284.     TextFace(0);
  285.     
  286.     ReadAnd(ExtractArchive);
  287.     CloseArchive();
  288.  
  289.     WPrintf("--- tar extraction completed.");
  290.  
  291.     WindEnd(autoPage);
  292.     UEndYield();
  293.     
  294.     tar_scripting = 0;
  295.     tar_interp = NULL;
  296.  
  297.     memcpy(fdType, saveType, 4);
  298.     memcpy(fdCreator, saveCreator, 4);
  299.     cvtNl = save_cvtNl;
  300.  
  301.     return TCL_OK;
  302.     }
  303.  
  304. /*
  305.  * Extract a file from the archive.
  306.  */
  307. Boolean
  308. ExtractArchive()
  309.     {
  310.     register char    *data;
  311.     register char    *p;
  312.     union record    *rec;
  313.     int        i, namelen;
  314.     Boolean        errFound = false;
  315.     long        check, written;
  316.     long        size;
  317.     OSErr        err;
  318.     char        macName[256];
  319.     HParamBlockRec    dpb;
  320.     HParamBlockRec    fpb;
  321.     char        *routine = "\pExtractArchive";
  322.  
  323.     SaveRec(&head);            /* Make sure it sticks around */
  324.     UseRec(head);            /* And go past it in the archive */
  325.     DecodeHeader(head, &hstat, 1);    /* Snarf fields */
  326.  
  327.     /* Print the record from 'head' and 'hstat' */
  328.     PrintHeader();
  329.  
  330.     switch (head->header.linkflag) {
  331.     default:
  332.         WPrintf("Unknown file type %d for %s",
  333.             head->header.linkflag, head->header.name);
  334.         break;
  335.  
  336.     case LF_OLDNORMAL:
  337.     case LF_NORMAL:
  338.         /*
  339.          * Appears to be a file.
  340.          * See if it's really a directory.
  341.          */
  342.         namelen = strlen(head->header.name) - 1;
  343.         if (head->header.name[namelen] == '/')
  344.             goto really_dir;
  345.  
  346.         FixName(head->header.name, macName);
  347.     again_file:
  348.         fpb.fileParam.ioCompletion = nil;
  349.         fpb.fileParam.ioNamePtr = macName;
  350.         fpb.fileParam.ioVRefNum = dirVRefNum;
  351.         fpb.fileParam.ioFVersNum = 0;
  352.         fpb.fileParam.ioDirID = dirDirID;
  353.         err = PBHCreate(&fpb, false);
  354.         if (err == noErr) {
  355.             fpb.fileParam.ioCompletion = nil;
  356.             fpb.fileParam.ioNamePtr = macName;
  357.             fpb.fileParam.ioVRefNum = dirVRefNum;
  358.             fpb.fileParam.ioDirID = dirDirID;
  359.             fpb.fileParam.ioFVersNum = 0;
  360.             fpb.fileParam.ioFDirIndex = 0;
  361.             if (PBHGetFInfo(&fpb, false)) {
  362.                 OSAlert(routine, "\pPBHGetFInfo", macName,
  363.                         fpb.fileParam.ioResult);
  364.                 goto doNext;
  365.             }
  366.  
  367.             strncpy((char *)&fpb.fileParam.ioFlFndrInfo.fdCreator, fdCreator, 4);
  368.             strncpy((char *)&fpb.fileParam.ioFlFndrInfo.fdType, fdType, 4);
  369.             fpb.fileParam.ioCompletion = nil;
  370.             fpb.fileParam.ioNamePtr = macName;
  371.             fpb.fileParam.ioVRefNum = dirVRefNum;
  372.             fpb.fileParam.ioDirID = dirDirID;
  373.             fpb.fileParam.ioFVersNum = 0;
  374.             if (PBHSetFInfo(&fpb, false)) {
  375.                 OSAlert(routine, "\pPBHSetFInfo", macName,
  376.                         fpb.fileParam.ioResult);
  377.                 goto doNext;
  378.             }
  379.  
  380.             fpb.ioParam.ioPermssn = fsWrPerm;
  381.             fpb.ioParam.ioMisc = nil;
  382.             err = PBHOpen(&fpb, false);
  383.         }
  384.  
  385.         if (err != noErr) {
  386.             if (MakeDirs(macName))
  387.                 goto again_file;
  388.  
  389.             PgmAlert(routine, "\pCould not make file", macName);
  390.             errFound = SkipFile((long)hstat.st_size);
  391.             break;
  392.         }
  393.  
  394.         /*
  395.          * Note that this only extracts data forks!
  396.          */
  397.         for (size = hstat.st_size;
  398.              size > 0;
  399.              size -= written) {
  400.             /*
  401.              * Locate data, determine max length
  402.              * writeable, write it, record that
  403.              * we have used the data, then check
  404.              * if the write worked.
  405.              */
  406.             if ((rec = FindRec()) == nil)
  407.                 return(true);
  408.                 
  409.             data = rec->charptr;
  410.             written = EndOfRecs()->charptr - data;
  411.             if (written > size)
  412.                 written = size;
  413.  
  414.             /*
  415.              * Convert newlines to return for Mac compatability.
  416.              */
  417.             if (cvtNl) {
  418.                 for (i = written, p = data; --i >= 0; p++)
  419.                     if (*p == LF)
  420.                         *p = RETURN;
  421.             }
  422.  
  423.             check = written;
  424.             fpb.ioParam.ioBuffer = data;
  425.             fpb.ioParam.ioReqCount = check;
  426.             fpb.ioParam.ioPosMode = fsAtMark;
  427.             fpb.ioParam.ioPosOffset = 0;
  428.             err = PBWrite((ParmBlkPtr) &fpb, false);
  429.             if (err != noErr) {
  430.                 OSAlert(routine, "\pPBWrite", macName, err);
  431.                 goto doNext;
  432.             }
  433.  
  434.             check = fpb.ioParam.ioActCount;
  435.             /*
  436.              * The following is in violation of strict
  437.              * typing, since the arg to userec
  438.              * should be a struct rec *.  FIXME.
  439.              */
  440.             UseRec(data + written - 1);
  441.             if (check == written)
  442.                 continue;
  443.                 
  444.             /*
  445.              * Error in writing to file.
  446.              * Print it, skip to next file in archive.
  447.              */
  448.             PgmAlert(routine, "\pWrite short", macName);
  449.         doNext:
  450.             PBClose((ParmBlkPtr) &fpb, false);
  451.             errFound = SkipFile((long)(size - written));
  452.             goto quit;
  453.         }
  454.  
  455.         PBClose((ParmBlkPtr) &fpb, false);
  456.     quit:
  457.         break;
  458.  
  459.     case LF_DIR:
  460.         /* Check for trailing / */
  461.         namelen = strlen(head->header.name)-1;
  462.     really_dir:
  463.         while (namelen && head->header.name[namelen] == '/')
  464.             head->header.name[namelen--] = '\0';    /* Zap / */
  465.         
  466.         FixName(head->header.name, macName);
  467.         /* FALL THROUGH */
  468.     again_dir:
  469.         dpb.fileParam.ioCompletion = nil;
  470.         dpb.fileParam.ioNamePtr = macName;
  471.         dpb.fileParam.ioVRefNum = dirVRefNum;
  472.         dpb.fileParam.ioFVersNum = 0;
  473.         dpb.fileParam.ioDirID = dirDirID;
  474.         err = PBDirCreate(&dpb, false);
  475.         if ((err != noErr) && (err != dupFNErr)) {
  476.             if (MakeDirs(macName))
  477.                 goto again_dir;
  478.  
  479.             PgmAlert(routine, "\pCould not make directory", macName);
  480.         }
  481.         
  482.         break;
  483.     }
  484.  
  485.     /* We don't need to save it any longer. */
  486.     SaveRec((union record **) 0);
  487.     return(errFound);
  488. }
  489.  
  490. /*
  491.  * After a file/link/symlink/dir creation has failed, see if
  492.  * it's because some required directory was not present, and if
  493.  * so, create all required dirs.
  494.  */
  495. int
  496. MakeDirs(pathname)
  497. char *pathname;
  498. {
  499.     int        madeone = 0;    /* Did we do anything yet? */
  500.     int        i, savedLen;
  501.     OSErr        err;
  502.     HParamBlockRec    pb;
  503.  
  504.     savedLen = pathname[0] & 0xff;
  505.     /*
  506.      * skip initial ':'
  507.      *
  508.      * Note that the directory name has already been converted to Mac style.
  509.      */
  510.     for (i = 2; i < savedLen; i++) {
  511.         while ((i < savedLen) && (pathname[i] != ':'))
  512.             i++;
  513.  
  514.         if (i == savedLen)
  515.             break;
  516.  
  517.         pathname[0] = i++ - 1;
  518.         pb.fileParam.ioCompletion = nil;
  519.         pb.fileParam.ioNamePtr = pathname;
  520.         pb.fileParam.ioVRefNum = dirVRefNum;
  521.         pb.fileParam.ioFVersNum = 0;
  522.         pb.fileParam.ioDirID = dirDirID;
  523.         err = PBDirCreate(&pb, false);
  524.         if (err == dupFNErr) {
  525.             continue;
  526.  
  527.         } else if (err != noErr) {
  528.             OSAlert("\pMakeDirs", "\pPBDirCreate", pathname,
  529.                     pb.fileParam.ioResult);
  530.             return(0);
  531.  
  532.         } else {
  533.             madeone++;        /* Remember if we made one */
  534.             continue;
  535.         }
  536.     }
  537.  
  538.     pathname[0] = savedLen;
  539.     return(madeone);        /* Tell them to retry if we made one */
  540. }
  541.  
  542. /*
  543.  * FixName - convert to a Mac style pathname
  544.  *
  545.  *    Conversions:
  546.  *        .    ->        (Stay at this directory level)
  547.  *        ..    -> ::        (Up a directory level)
  548.  *        .xxxx    -> _xxxx    (Don't get in trouble with device names)
  549.  *        xx:xx    -> xx/xx    (Don't get in trouble with directory delims)
  550.  */
  551. FixName(tar, mac)
  552. register char    *tar;
  553. char    *mac;
  554. {
  555.     char        *end = tar + strlen(tar);
  556.     register char    *p = mac + 1;
  557.     register char    *next;
  558.  
  559.     for (next = tar; tar < end; next++) {
  560.         /*
  561.          * Swallow up all contiguous '/' characters.
  562.          */
  563.         while (*next && (*next == '/'))
  564.             next++;
  565.  
  566.         /*
  567.          * Find the entire name up until the next '/'.
  568.          */
  569.         tar = next;
  570.         while (*next && (*next != '/'))
  571.             next++;
  572.  
  573.         *next = 0;
  574.         *p++ = ':';
  575.         if (*tar == '.')
  576.             switch (*(tar + 1)) {
  577.             case '\0':
  578.                 p--;
  579.                 continue;
  580.  
  581.             case '.':
  582.                 if (*(tar + 2) == 0)
  583.                     continue;
  584.                 /* else FALL THROUGH */
  585.  
  586.             default:
  587.                 *tar = '_';
  588.             }
  589.  
  590.         while (tar < next) {
  591.             if (*tar == ':')
  592.                 *tar = '/';
  593.             *p++ = *tar++;
  594.         }
  595.     }
  596.  
  597.     *mac = p - mac - 1;
  598. }
  599.