home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / source / tarsrc.sit / create.c < prev    next >
Text File  |  1989-09-14  |  11KB  |  494 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modifed by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * Create a tar archive.
  8.  *
  9.  * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  10.  *
  11.  * @(#)create.c 1.19 9/9/86 Public Domain - gnu
  12.  */
  13.  
  14. #include "tar.h"
  15.  
  16. union record        *StartHeader();
  17. extern union record    *head;
  18.  
  19. /*
  20.  * Fake stat struct for use with existing code
  21.  */
  22. extern struct stat {
  23.     long    st_size;
  24.     long    st_mtime;
  25. } hstat;
  26.  
  27. void    FinishHeader();
  28. void    ToOct();
  29. Boolean    DumpDir(), DumpFile(), FillName(), WriteEot();
  30.  
  31. /*
  32.  * Used to save pathname info while descending the directory hierarchy.
  33.  */
  34. struct PathInfo {
  35.     struct PathInfo    *next;
  36.     char        name[32];
  37. };
  38. typedef struct PathInfo PathInfo;
  39. PathInfo    pathHead;
  40.  
  41. /*
  42.  * ArCreate - manage the creation of an archive
  43.  *
  44.  *    Asks for the archive name, creates the archive and then
  45.  *    loops asking for directories to add to the archive.
  46.  */
  47. ArCreate() {
  48.     Boolean        errFound = false;
  49.     Point        where;
  50.     SFReply        reply;
  51.     CInfoPBRec    pb;
  52.     CursHandle    cursor;
  53.     Str255        name;
  54.  
  55.     /*
  56.      * Put up a standard file dialog asking for the archive file name.
  57.      */
  58.     where.h = where.v = 75;
  59.     name[0] = 0;
  60.     SFPutFile(where, "\pTar Archive:", name, nil, &reply);
  61.     if (!reply.good)
  62.         return;
  63.  
  64.     arVRefNum = reply.vRefNum;
  65.     arName = reply.fName;
  66.     if (OpenArchive(0))    /* Open for writing */
  67.         return;
  68.  
  69.     /*
  70.      * Ask for directories to add to the archive.
  71.      * Note that this is WHOLE directories.
  72.      */
  73.     while (!errFound && GetDir("\pDirectory to Archive:", false)) {
  74.         /*
  75.          * Get the catalog info for the selected directory.
  76.          */
  77.         pathHead.next = nil;
  78.         pb.hfileInfo.ioCompletion = nil;
  79.         pb.hfileInfo.ioNamePtr = pathHead.name;
  80.         pb.hfileInfo.ioVRefNum = dirVRefNum;
  81.         pb.hfileInfo.ioDirID = dirDirID;
  82.         pb.hfileInfo.ioFDirIndex = -1;
  83.         if (PBGetCatInfo(&pb, false) != noErr) {
  84.             OSAlert("\pArCreate", "\pPBGetCatInfo", pathHead.name,
  85.                     pb.hfileInfo.ioResult);
  86.             break;
  87.  
  88.         } else {
  89.             /*
  90.              * Add the directory to the archive,
  91.              * while printing the files being added.
  92.              */
  93.             if (WindInit())
  94.                 goto done;
  95.  
  96.             TextFace(underline);
  97.             WPrintf(header);
  98.             TextFace(0);
  99.             if ((cursor = GetCursor(watchCursor)) != nil)
  100.                 SetCursor(*cursor);
  101.  
  102.             errFound = DumpDir(&pb, &pathHead);
  103.             SetCursor(&qd.arrow);
  104.             WindEnd(autoPage);
  105.             FlushEvents(everyEvent, 0);
  106.         }
  107.     }
  108.  
  109.     WriteEot();
  110. done:
  111.     CloseArchive();
  112. }
  113.  
  114. /*
  115.  * DumpDir - add a directory (possibly recursively) to the archive
  116.  *
  117.  *    Exits via a longjmp on unrecoverable error
  118.  *    Returns normally otherwise
  119.  */
  120. Boolean
  121. DumpDir(dir, path)
  122. CInfoPBRec    *dir;
  123. PathInfo    *path;
  124. {
  125.     union record    *header;
  126.     int        i;
  127.     Boolean        errFound = false;
  128.     CInfoPBRec    pb;
  129.     PathInfo    file;
  130.     char        *routine = "\pDumpDir";
  131.     EventRecord    e;
  132.  
  133.     /*
  134.      * Output directory header record with permissions
  135.      * FIXME, do this AFTER files, to avoid R/O dir problems?
  136.      * If Unix Std format, don't put / on end of dir name
  137.      * If old archive format, don't write record at all.
  138.      */
  139.     if (!oldArch) {
  140.         /*
  141.          * If people could really read standard archives,
  142.          * this should be:        (FIXME)
  143.          * header = start_header(f_standard? p: namebuf, statbuf);
  144.          * but since they'd interpret LF_DIR records as
  145.          * regular files, we'd better put the / on the name.
  146.          */
  147.         if ((header = StartHeader(dir)) == nil)
  148.             return(true);
  149.  
  150.         if (standard)
  151.             header->header.linkflag = LF_DIR;
  152.             
  153.         FinishHeader(header);    /* Done with directory header */
  154.         head = header;
  155.         PrintHeader();
  156.     }
  157.  
  158.     file.next = nil;
  159.     path->next = &file;
  160.     
  161.     /*
  162.      * Check all entries in the directory.
  163.      * Add regular files, recurse on subdirectories.
  164.      */
  165.     pb.hfileInfo.ioCompletion = nil;
  166.     pb.hfileInfo.ioNamePtr = file.name;
  167.     pb.hfileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
  168.     for (i = 1; !errFound; i++) {
  169.         SystemTask();
  170.         if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
  171.             if (
  172.                 (e.modifiers & cmdKey) &&
  173.                 ((e.message & charCodeMask) == '.')
  174.             ) {
  175.                 GetNextEvent(keyDownMask, &e);
  176.                 break;
  177.             }
  178.         }
  179.  
  180.         pb.hfileInfo.ioDirID = dir->dirInfo.ioDrDirID;
  181.         pb.hfileInfo.ioFDirIndex = i;
  182.         if (PBGetCatInfo(&pb, false) != noErr) {
  183.             if (pb.hfileInfo.ioResult == fnfErr)
  184.                 break;
  185.  
  186.             OSAlert(routine, "\pPBGetCatInfo", "\pDirectory search",
  187.                     pb.hfileInfo.ioResult);
  188.             return(true);
  189.         }
  190.  
  191.         if ((unsigned char) file.name[0] > 32) {
  192.             /*
  193.              * Sanity check, we have overwritten our stack!
  194.              */
  195.             PgmAlert(routine, "\pName too long", file.name);
  196.             return(true);
  197.         }
  198.  
  199.         if (DIRECTORY(pb)) {
  200.             errFound = DumpDir(&pb, &file);
  201.  
  202.         } else {
  203.             if (pb.hfileInfo.ioFRefNum == archive) {
  204.                 /*
  205.                  * DO NOT add the archive to itself!
  206.                  */
  207.                 ArSkipAlert();
  208.                 continue;
  209.             }
  210.  
  211.             errFound = DumpFile(&pb);
  212.         }
  213.     }
  214.  
  215.     /*
  216.      * Done with this directory, make sure we don't run out
  217.      * of working directories.
  218.      */
  219.     path->next = nil;
  220.     return(errFound);
  221. }
  222.  
  223. /*
  224.  * DumpFile - Dump a single file.
  225.  *
  226.  *    Exits via longjmp on unrecoverable error.
  227.  *    Result is 1 for success, 0 for failure.
  228.  */
  229. Boolean
  230. DumpFile(file)
  231. CInfoPBRec    *file;
  232. {
  233.     union record    *header;
  234.     register char    *p;
  235.     char        *buf;
  236.     HParamBlockRec    fpb;
  237.     long        bufsize, count, i;
  238.     register long    sizeleft;
  239.     register union record     *start;
  240.     char        *routine = "\pDumpFile";
  241.  
  242.     if ((header = StartHeader(file)) == nil)
  243.         return(true);
  244.  
  245.     FinishHeader(header);
  246.     /*
  247.      * Get the size of the file.
  248.      * Don't bother opening it if it is zero length.
  249.      */
  250.     head = header;
  251.     hstat.st_size = file->hfileInfo.ioFlLgLen;
  252.     PrintHeader();
  253.     if ((sizeleft = file->hfileInfo.ioFlLgLen) == 0)
  254.         return(false);
  255.  
  256.     fpb.fileParam.ioCompletion = nil;
  257.     fpb.fileParam.ioNamePtr = file->hfileInfo.ioNamePtr;
  258.     fpb.fileParam.ioVRefNum = file->hfileInfo.ioVRefNum;
  259.     fpb.fileParam.ioFVersNum = 0;
  260.     fpb.fileParam.ioDirID = file->hfileInfo.ioFlParID;
  261.     fpb.ioParam.ioPermssn = fsRdPerm;
  262.     fpb.ioParam.ioMisc = nil;
  263.     if (PBHOpen(&fpb, false) != noErr) {
  264.         OSAlert(routine, "\pPBHOpen", file->hfileInfo.ioNamePtr,
  265.                 fpb.fileParam.ioResult);
  266.         return(true);
  267.     }
  268.  
  269.     /*
  270.      * Dump the file to the archive.
  271.      * Note: this only dumps the data fork!
  272.      */
  273.     while (sizeleft > 0) {
  274.         if ((start = FindRec()) == nil)
  275.             return(true);
  276.             
  277.         bufsize = EndOfRecs()->charptr - start->charptr;
  278.         buf = start->charptr;
  279.     again:
  280.         count = (sizeleft < bufsize) ? sizeleft : bufsize;
  281.         fpb.ioParam.ioBuffer = buf;
  282.         fpb.ioParam.ioReqCount = count;
  283.         fpb.ioParam.ioPosMode = fsAtMark;
  284.         fpb.ioParam.ioPosOffset = 0;
  285.         if (PBRead((ParmBlkPtr) &fpb, false) != noErr) {
  286.             OSAlert(routine, "\pPBRead", file->hfileInfo.ioNamePtr,
  287.                     fpb.ioParam.ioResult);
  288.             return(true);
  289.         }
  290.  
  291.         count = fpb.ioParam.ioActCount;
  292.         if (cvtNl) {
  293.             /*
  294.              * Convert returns to newlines for Unix compat.
  295.              */
  296.             for (i = count, p = buf; --i >= 0; p++)
  297.                 if (*p == RETURN)
  298.                     *p = LF;
  299.         }
  300.  
  301.         sizeleft -= count;
  302.         UseRec(start + (count - 1) / RECORDSIZE);
  303.     }
  304.     
  305.     PBClose((ParmBlkPtr) &fpb, false);
  306.  
  307.     /* Clear last block garbage to zeros, FIXME */
  308.     return(false);
  309. }
  310.  
  311.  
  312. /*
  313.  * Make a header block for the file  name  whose stat info is  st .
  314.  * Return header pointer for success, NULL if the name is too long.
  315.  */
  316. union record *
  317. StartHeader(pb)
  318. CInfoPBRec    *pb;
  319. {
  320.     register union record *header;
  321.     Boolean    directory = DIRECTORY(*pb);
  322.  
  323.     if ((header = (union record *) FindRec()) == nil)
  324.         return(nil);
  325.         
  326.     bzero(header->charptr, sizeof(union record)); /* XXX speed up */
  327.     /*
  328.      * Generate the pathname, make sure we don't overflow
  329.      * the field in the tar header.
  330.      */
  331.     if (FillName(header, directory)) {
  332.         char    buf[NAMSIZ + 1];
  333.  
  334.         buf[0] = NAMSIZ;
  335.         bcopy(header->header.name, &buf[1], NAMSIZ);
  336.         PgmAlert("\pStartHeader", "\pName too long", buf);
  337.         return(nil);
  338.     }
  339.  
  340.     /*
  341.      * Fake the file mode, uid, gid.
  342.      * Convert from Mac based time to Unix based time.
  343.      */
  344.     ToOct((directory) ? 0755L : 0644L, 8,  header->header.mode);
  345.     ToOct(0L, 8,  header->header.uid);
  346.     ToOct(0L, 8,  header->header.gid);
  347.     ToOct((directory) ? 0L : pb->hfileInfo.ioFlLgLen, 1+12,
  348.             header->header.size);
  349.     ToOct((directory) ? pb->dirInfo.ioDrMdDat - TIMEDIFF :
  350.                 pb->hfileInfo.ioFlMdDat - TIMEDIFF,
  351.             1+12, header->header.mtime);
  352.     /* header->header.linkflag is left as null */
  353.     return(header);
  354. }
  355.  
  356. /* 
  357.  * Finish off a filled-in header block and write it out.
  358.  */
  359. void
  360. FinishHeader(header)
  361. register union record *header;
  362. {
  363.     register int    i;
  364.     register long    sum;
  365.     register char    *p;
  366.  
  367.     bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
  368.  
  369.     sum = 0;
  370.     p = header->charptr;
  371.     for (i = sizeof(union record); --i >= 0; ) {
  372.         /*
  373.          * We can't use unsigned char here because of old compilers,
  374.          * e.g. V7.
  375.          */
  376.         sum += 0xFF & *p++;
  377.     }
  378.  
  379.     /*
  380.      * Fill in the checksum field.  It's formatted differently
  381.      * from the other fields:  it has [6] digits, a null, then a
  382.      * space -- rather than digits, a space, then a null.
  383.      * We use to_oct then write the null in over to_oct's space.
  384.      * The final space is already there, from checksumming, and
  385.      * to_oct doesn't modify it.
  386.      *
  387.      * This is a fast way to do:
  388.      * (void) sprintf(header->header.chksum, "%6o", sum);
  389.      */
  390.     ToOct((long) sum, 8, header->header.chksum);
  391.     header->header.chksum[6] = '\0';    /* Zap the space */
  392.     UseRec(header);
  393.     return;
  394. }
  395.  
  396.  
  397. /*
  398.  * Quick and dirty octal conversion.
  399.  * Converts long "value" into a "digs"-digit field at "where",
  400.  * including a trailing space and room for a null.  "digs"==3 means
  401.  * 1 digit, a space, and room for a null.
  402.  *
  403.  * We assume the trailing null is already there and don't fill it in.
  404.  * This fact is used by start_header and finish_header, so don't change it!
  405.  *
  406.  * This should be equivalent to:
  407.  *    (void) sprintf(where, "%*lo ", digs-2, value);
  408.  * except that sprintf fills in the trailing null and we don't.
  409.  */
  410. void
  411. ToOct(value, digs, where)
  412. register long    value;
  413. register int    digs;
  414. register char    *where;
  415. {
  416.     --digs;                /* Trailing null slot is left alone */
  417.     where[--digs] = ' ';        /* Put in the space, though */
  418.  
  419.     /* Produce the digits -- at least one */
  420.     do {
  421.         where[--digs] = '0' + (value & 7);    /* one octal digit */
  422.         value >>= 3;
  423.     } while (digs > 0 && value != 0);
  424.  
  425.     /* Leading spaces, if necessary */
  426.     while (digs > 0)
  427.         where[--digs] = ' ';
  428.  
  429. }
  430.  
  431. /*
  432.  * Write the EOT block(s).
  433.  */
  434. Boolean
  435. WriteEot()
  436. {
  437.     union record *p;
  438.  
  439.     if ((p = FindRec()) == nil)
  440.         return(true);
  441.         
  442.     bzero(p->charptr, RECORDSIZE);
  443.     UseRec(p);
  444.     /* FIXME, only one EOT block should be needed. */
  445.     if ((p = FindRec()) == nil)
  446.         return(true);
  447.         
  448.     bzero(p->charptr, RECORDSIZE);
  449.     UseRec(p);
  450.     return(false);
  451. }
  452.  
  453. /*
  454.  * FillName - generate the file or directory pathname
  455.  *
  456.  *    Converts to Unix style pathnames.
  457.  *    Appends a '/' for a directory.
  458.  */
  459. Boolean
  460. FillName(header, directory)
  461. register union record *header;
  462. Boolean    directory;
  463. {
  464.     register PathInfo    *p;
  465.     register char        *d, *s;
  466.     char            c;
  467.     int            i;
  468.  
  469.     d = header->header.name;
  470.     for (p = &pathHead; p != nil; p = p->next) {
  471.         s = &p->name[1];
  472.         for (i = p->name[0]; i > 0; i--) {
  473.             c = *s++;
  474.             if (c == '/')
  475.                 *d++ = ':';
  476.  
  477.             else if ((c <= ' ') || (c >= 0x7f))
  478.                 *d++ = '_';
  479.  
  480.             else
  481.                 *d++ = c;
  482.         }
  483.  
  484.         *d++ = (p->next == nil) ? '\0' : '/';
  485.     }
  486.  
  487.     if (directory) {
  488.         *(d - 1) = '/';
  489.         *d = '\0';
  490.     }
  491.  
  492.     return((d - header->header.name) > NAMSIZ);
  493. }
  494.