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_create.c < prev    next >
Text File  |  1993-11-20  |  19KB  |  846 lines

  1.  
  2. #pragma segment TAR
  3.  
  4. /*
  5.  * Macintosh Tar
  6.  *
  7.  * Modifed by Craig Ruff for use on the Macintosh.
  8.  */
  9. /*
  10.  * Create a tar archive.
  11.  *
  12.  * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  13.  *
  14.  * @(#)create.c 1.19 9/9/86 Public Domain - gnu
  15.  */
  16.  
  17. #include "tar.h"
  18. #include "stat.h"
  19. #include <string.h>
  20.  
  21. union record        *StartHeader();
  22. extern union record    *head;
  23. extern struct
  24.     {
  25.     long    st_size;
  26.     long    st_mtime;
  27.     } hstat;            /* Fake stat struct for compat. */
  28.  
  29. void    FinishHeader();
  30. void    ToOct();
  31. Boolean    DumpDir(), DumpFile(), FillName(), WriteEot();
  32.  
  33. /*
  34.  * Used to save pathname info while descending the directory hierarchy.
  35.  */
  36. struct PathInfo {
  37.     struct PathInfo    *next;
  38.     char        name[32];
  39. };
  40. typedef struct PathInfo PathInfo;
  41. PathInfo    pathHead;
  42.  
  43. /*
  44.  * ArCreate - manage the creation of an archive
  45.  *
  46.  *    Asks for the archive name, creates the archive and then
  47.  *    loops asking for directories to add to the archive.
  48.  */
  49.  
  50. #ifdef TCLAPPL
  51.  
  52. ArCreate()
  53.     {
  54.     Boolean        errFound = false;
  55.     Point        where;
  56.     SFReply        reply;
  57.     CInfoPBRec    pb;
  58.     CursHandle    cursor;
  59.     Str255        name;
  60.  
  61. /*TGE*/ extern WindowPtr    theFeedbackWindow;
  62. /*TGE*/ extern short        feedback_showing;
  63. /*TGE*/ extern short        in_back_ground;
  64.  
  65.     /*
  66.      * Put up a standard file dialog asking for the archive file name.
  67.      */
  68.     where.h = where.v = 75;
  69.     name[0] = 0;
  70.     MyPutFile(where, "\pTar Archive:", name, nil, &reply);
  71.     if (!reply.good)
  72.         return;
  73.  
  74.     arName = reply.fName;
  75.     WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
  76.  
  77.     if (OpenArchive(0))
  78.         {
  79.         /* Open for writing */
  80.         WPrintf("Open Archive Failed.");
  81.         return;
  82.         }
  83.  
  84. /*TGE*/    UBegYield();
  85.  
  86.     /*
  87.      * Ask for directories to add to the archive.
  88.      * Note that this is WHOLE directories.
  89.      */
  90.     while (!errFound)
  91.         {
  92. /*TGE*/    if (in_back_ground)
  93.             while (in_back_ground)
  94. /*TGE*/            pausing();
  95.         
  96.         if (! GetFolderPathName("Directory To Archive:", name, &dirVRefNum, &dirDirID))
  97.             break;
  98. #ifdef NEVER_DEFINED
  99.         if (! GetDir("\pDirectory to Archive:", false))
  100.             break;
  101. #endif
  102.         
  103. /*TGE*/    ShowFeedback();
  104.         
  105.         /*
  106.          * Get the catalog info for the selected directory.
  107.          */
  108.         pathHead.next = nil;
  109.         pathHead.name[0] = '\0';
  110.         pb.hFileInfo.ioCompletion = nil;
  111.         pb.hFileInfo.ioNamePtr = pathHead.name;
  112.         pb.hFileInfo.ioVRefNum = dirVRefNum;
  113.         pb.hFileInfo.ioDirID = dirDirID;
  114.         pb.hFileInfo.ioFDirIndex = -1;
  115.         if (PBGetCatInfo(&pb, false) != noErr)
  116.             {
  117.             OSAlert("\pArCreate", "\pPBGetCatInfo", pathHead.name,
  118.                     pb.hFileInfo.ioResult);
  119.             break;
  120.  
  121.             }
  122.         else
  123.             {
  124.             /*
  125.              * Add the directory to the archive,
  126.              * while printing the files being added.
  127.              */
  128.             if (WindInit())
  129.                 goto done;
  130.  
  131. /*TGE*/        SetPort(theFeedbackWindow);
  132.             TextFace(underline);
  133.             WPrintf(header);
  134. /*TGE*/        SetPort(theFeedbackWindow);
  135.             TextFace(0);
  136.             if ((cursor = GetCursor(watchCursor)) != nil)
  137.                 SetCursor(*cursor);
  138.  
  139.             errFound = DumpDir(&pb, &pathHead);
  140.             
  141.             SetCursor(&qd.arrow);
  142.             FlushEvents(everyEvent, 0);
  143.             }
  144.             
  145. /*TGE*/    /*HideFeedback();*/
  146.         }
  147.  
  148. /*TGE*/    ShowFeedback();
  149.     
  150.     WriteEot();
  151.  
  152. done:
  153.  
  154.     CloseArchive();
  155.  
  156.     WPrintf("--- tar creation completed.");
  157. /*TGE*/
  158.     UEndYield();
  159.     UInitCursor();
  160.     }
  161.  
  162. #endif /* TCLAPPL */
  163.  
  164.  
  165. Cmd_Archive(clientData, interp, argc, argv)
  166.     char        *clientData;
  167.     Tcl_Interp    *interp;
  168.     int            argc;
  169.     char        *argv[];
  170.     {
  171.     Boolean        errFound = false, save_cvtNl;
  172.     int            tarArgc, arg_index, myerr, result = TCL_OK;
  173.     char        **tarArgv, *name;
  174.     Tcl_DString    tildeBuf;
  175.     CInfoPBRec    pb;
  176.     CursHandle    cursor;
  177.     char        *ptr, *default_pathhead = NULL, *archive_arg;
  178.     Str255        arname, itemname;
  179.     PathInfo    subPath, *dirPathPtr, filepath;
  180.     struct stat    statbuf;
  181.  
  182. #pragma unused (clientData)
  183.  
  184.     extern WindowPtr    theFeedbackWindow;
  185.     extern short        feedback_showing;
  186.     extern short        in_back_ground;
  187.  
  188.     if (argc < 3)
  189.         {
  190.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  191.                         " ?-p prefix? archive_filename filelist\"", (char *) NULL);
  192.         result = TCL_ERROR;
  193.         }
  194.  
  195.     save_cvtNl = cvtNl;
  196.     cvtNl = false;
  197.     default_pathhead = NULL;
  198.     
  199.     for ( arg_index = 1 ; arg_index < argc && ! errFound ; ++arg_index )
  200.         {
  201.         if (argv[arg_index][0] != '-')
  202.             break;
  203.         
  204.         if (argv[arg_index][1] == '-' && argv[arg_index][2] == '\0')
  205.             break;
  206.         
  207.         if (argv[arg_index][1] == 'p' && argv[arg_index][2] == '\0')
  208.             {
  209.             default_pathhead = argv[arg_index+1];
  210.             ++arg_index;
  211.             }
  212.         else if (argv[arg_index][1] == 'a' && argv[arg_index][2] == '\0')
  213.             {
  214.             cvtNl = true;
  215.             }
  216.         else
  217.             break;
  218.         }
  219.  
  220.     if (arg_index >= argc)
  221.         {
  222.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  223.                         " ?-p prefix? archive_filename filelist\"", (char *) NULL);
  224.         result = TCL_ERROR;
  225.         }
  226.  
  227.     archive_arg = argv[arg_index++];
  228.     ptr = strrchr( archive_arg, ':' );
  229.     if ( *archive_arg == ':' || ptr == NULL )
  230.         {
  231.         arDirID = TclMac_CWDDirID();
  232.         arVRefNum = TclMac_CWDVRefNum();
  233.         strcpy(arname, (ptr == NULL ? archive_arg : ptr + 1));
  234.         }
  235.     else
  236.         {
  237.         *ptr = '\0';
  238.         myerr = stat( archive_arg, &statbuf );
  239.         if ( myerr < 0 )
  240.             {
  241.             Tcl_AppendResult(interp, "could not locate directory \"", archive_arg,
  242.                             "\" to create archive - ", Tcl_PosixError(interp), NULL);
  243.             *ptr = ':';
  244.             result = TCL_ERROR;
  245.             }
  246.         if ( ! S_ISDIR(statbuf.st_mode) )
  247.             {
  248.             Tcl_AppendResult(interp, "\"", archive_arg, "\" is not a directory", NULL);
  249.             *ptr = ':';
  250.             result = TCL_ERROR;
  251.             }
  252.         *ptr = ':';
  253.         
  254.         arDirID = statbuf.st_ino;
  255.         arVRefNum = statbuf.st_dev;
  256.         strcpy(arname, ptr + 1);
  257.         }
  258.     c2pstr(arname);
  259.     arName = arname;
  260.     
  261.     if (arg_index >= argc)
  262.         {
  263.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  264.                         " ?-p prefix? archive_filename filelist\"", (char *) NULL);
  265.         result = TCL_ERROR;
  266.         }
  267.  
  268.     if ( Tcl_SplitList(interp, argv[arg_index], &tarArgc, &tarArgv) != TCL_OK )
  269.         {
  270.         cvtNl = save_cvtNl;
  271.         return TCL_ERROR;
  272.         }
  273.  
  274.     tar_scripting = 1;
  275.     tar_interp = interp;
  276.  
  277.     if (OpenArchive(0))    /* Open for writing */
  278.         {
  279.         Tcl_AppendResult(interp, "could not open \"", argv[arg_index-1],
  280.                         "\" to write archive into", (char *) NULL);
  281.         tar_scripting = 0;
  282.         tar_interp = NULL;
  283.         cvtNl = save_cvtNl;
  284.         return TCL_ERROR;
  285.         }
  286.  
  287.     UBegYield();
  288.  
  289.     pathHead.next = NULL;
  290.     pathHead.name[0] = 1;
  291.     pathHead.name[1] = '.';
  292.     
  293.     if (default_pathhead != NULL)
  294.         {
  295.         if (*default_pathhead == '/' && *(default_pathhead + 1) == '\0')
  296.             {
  297.             pathHead.name[0] = '\0';
  298.             }
  299.         else
  300.             {
  301.             strcpy(pathHead.name, default_pathhead);
  302.             c2pstr(pathHead.name);
  303.             }
  304.         }
  305.     
  306.     /*
  307.      * Ask for directories to add to the archive.
  308.      * Note that this is WHOLE directories.
  309.      */
  310.     for ( arg_index = 0 ; arg_index < tarArgc && result == TCL_OK ; ++arg_index )
  311.         {
  312.         if (in_back_ground)
  313.             while (in_back_ground)
  314.                 pausing();
  315.         
  316.         Tcl_DStringInit (&tildeBuf);
  317.         name = Tcl_TildeSubst(interp, tarArgv[arg_index], &tildeBuf);
  318.         if (name == NULL)
  319.             {
  320.             Tcl_AppendResult(interp, "could not substitute for directory \"",
  321.                                     tarArgv[arg_index], "\" ", (char *) NULL);
  322.             continue;
  323.             }
  324.  
  325.         if (tcl_path_to_dir(name, &dirVRefNum, &dirDirID) != noErr)
  326.             {
  327.             Tcl_AppendResult(interp, "could not locate \"", name,
  328.                             "\" for archival", (char *) NULL);
  329.             continue;
  330.             }
  331.  
  332.         ShowFeedback();
  333.         
  334.         /*
  335.          * Get the catalog info for the selected directory.
  336.          */
  337.         itemname[0] = '\0';
  338.  
  339.         subPath.next = NULL;
  340.         
  341.         ptr = strrchr(name, ':');
  342.         if (ptr != NULL)
  343.             {
  344.             /* Has path elements */
  345.             if ( *(ptr + 1) == '\0' )
  346.                 {
  347.                 /* Directory only -> ":dir:dir:dir:" */
  348.                 }
  349.             else
  350.                 {
  351.                 /* Directory or File -> ":dir:dir:item" */
  352.                 strcpy(itemname, ptr + 1);
  353.                 c2pstr(itemname);
  354.                 }
  355.             }
  356.         else
  357.             {
  358.             /* Has NO path elements */
  359.             /* Directory or File -> "item" */
  360.             strcpy(itemname, name);
  361.             c2pstr(itemname);
  362.             }
  363.         
  364.         pb.hFileInfo.ioCompletion = 0;
  365.         pb.hFileInfo.ioVRefNum = dirVRefNum;
  366.         pb.hFileInfo.ioDirID = dirDirID;
  367.         pb.hFileInfo.ioNamePtr = itemname;
  368.         pb.hFileInfo.ioFDirIndex = itemname[0] ? 0 : -1;
  369.         
  370.         if (PBGetCatInfo(&pb, false) != noErr)
  371.             {
  372.             Tcl_AppendResult(interp, "could not get info for \"", name,
  373.                             "\"", (char *) NULL);
  374.             }
  375.         else
  376.             {
  377.             /*
  378.             ** Add the item to the archive,
  379.             ** while printing the files being added.
  380.             */
  381.             //if (WindInit())
  382.             //    goto done;
  383.  
  384.             SetPort(theFeedbackWindow);
  385.             TextFace(underline);
  386.             WPrintf(header);
  387.             SetPort(theFeedbackWindow);
  388.             TextFace(0);
  389.             
  390.             if ( (cursor = GetCursor(watchCursor)) != NULL )
  391.                 SetCursor(*cursor);
  392.  
  393.             if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  394.                 {
  395.                 dirPathPtr = &pathHead;
  396.                 if (itemname[0] != '\0')
  397.                     {
  398.                     BlockMove(itemname, subPath.name, itemname[0]+1);
  399.                     pathHead.next = &subPath;
  400.                     dirPathPtr = &subPath;
  401.                     }
  402.                 errFound = DumpDir(&pb, dirPathPtr);
  403.                 }
  404.             else
  405.                 {
  406.                 BlockMove(itemname, subPath.name, itemname[0]+1);
  407.                 pathHead.next = &subPath;
  408.  
  409.                 errFound = DumpFile(&pb);
  410.  
  411.                 pathHead.next = nil;
  412.                 }
  413.             
  414.             if (errFound)
  415.                 {
  416.                 Tcl_AppendResult(interp, "error could not archive ",
  417.                         ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ? "directory" : "file" ),
  418.                         " \"", name, "\"", (char *) NULL);
  419.                 result = TCL_ERROR;
  420.                 }
  421.             
  422.             SetCursor(&qd.arrow);
  423.             FlushEvents(everyEvent, 0);
  424.             }
  425.  
  426.         Tcl_DStringFree (&tildeBuf);
  427.         pathHead.next = NULL;
  428.         }
  429.  
  430.     ShowFeedback();
  431.     
  432.     if (result == TCL_OK)
  433.         WriteEot();
  434.  
  435.     CloseArchive();
  436.  
  437.     ckfree ((char *) tarArgv);
  438.     
  439.     cvtNl = save_cvtNl;
  440.     tar_scripting = 0;
  441.     tar_interp = NULL;
  442.  
  443.     WPrintf("--- tar creation completed.");
  444.  
  445.     UEndYield();
  446.     UInitCursor();
  447.     
  448.     return result;
  449.     }
  450.  
  451. /*
  452.  * DumpDir - add a directory (possibly recursively) to the archive
  453.  *
  454.  *    Exits via a longjmp on unrecoverable error
  455.  *    Returns normally otherwise
  456.  */
  457. Boolean
  458. DumpDir(dir, path)
  459.     CInfoPBRec    *dir;
  460.     PathInfo    *path;
  461.     {
  462.     union record    *header;
  463.     int        i;
  464.     Boolean        errFound = false;
  465.     CInfoPBRec    pb;
  466.     PathInfo    file;
  467.     char        *routine = "\pDumpDir";
  468.     extern int cancel_current_op;
  469.     extern short pause_op;
  470.  
  471.     /*
  472.     WPrintf("DumpDir(%d, %d, <%.*s>) ENTER",
  473.                 dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
  474.                 dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
  475.     */
  476.     
  477.     /*
  478.      * Output directory header record with permissions
  479.      * FIXME, do this AFTER files, to avoid R/O dir problems?
  480.      * If Unix Std format, don't put / on end of dir name
  481.      * If old archive format, don't write record at all.
  482.      */
  483.     if (!oldArch) {
  484.         /*
  485.          * If people could really read standard archives,
  486.          * this should be:        (FIXME)
  487.          * header = start_header(f_standard? p: namebuf, statbuf);
  488.          * but since they'd interpret LF_DIR records as
  489.          * regular files, we'd better put the / on the name.
  490.          */
  491.         if ((header = StartHeader(dir)) == nil)
  492.             return(true);
  493.  
  494.         if (standard)
  495.             header->header.linkflag = LF_DIR;
  496.             
  497.         FinishHeader(header);    /* Done with directory header */
  498.         head = header;
  499.         PrintHeader();
  500.     }
  501.  
  502.     file.next = nil;
  503.     path->next = &file;
  504.     
  505.     /*
  506.      * Check all entries in the directory.
  507.      * Add regular files, recurse on subdirectories.
  508.      */
  509.     file.name[0] = '\0';
  510.     pb.hFileInfo.ioCompletion = nil;
  511.     pb.hFileInfo.ioNamePtr = file.name;
  512.     pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
  513.     for (i = 1; !errFound; i++) {
  514. /*TGE*/    DoYield();
  515.         if (pause_op)
  516.             while (pause_op)
  517.                 pausing();
  518.         if (cancel_current_op)
  519. /*TGE*/        break;
  520.  
  521.         pb.hFileInfo.ioCompletion = nil;
  522.         pb.hFileInfo.ioDirID = dir->dirInfo.ioDrDirID;
  523.         pb.hFileInfo.ioFDirIndex = i;
  524.         pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
  525.         if (PBGetCatInfo(&pb, false) != noErr) {
  526.             if (pb.hFileInfo.ioResult == fnfErr)
  527.                 break;
  528.  
  529. /*TGE*/        WPrintf("DumpDir pPBGetCatInfo(%d, %d, #%d) result = %d.",
  530.                     dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID, i, pb.hFileInfo.ioResult);
  531.             OSAlert(routine, "\pPBGetCatInfo", "\pDirectory search",
  532.                     pb.hFileInfo.ioResult);
  533.             return(true);
  534.         }
  535.  
  536.         if ((unsigned char) file.name[0] > 32) {
  537.             /*
  538.              * Sanity check, we have overwritten our stack!
  539.              */
  540.             PgmAlert(routine, "\pName too long", file.name);
  541.             return(true);
  542.         }
  543.  
  544.         if (DIRECTORY(pb)) {
  545.             errFound = DumpDir(&pb, &file);
  546.  
  547.         } else {
  548.             if (pb.hFileInfo.ioFRefNum == archive) {
  549.                 /*
  550.                  * DO NOT add the archive to itself!
  551.                  */
  552.                 ArSkipAlert();
  553.                 continue;
  554.             }
  555.  
  556.             errFound = DumpFile(&pb);
  557.         }
  558.  
  559.     }
  560.  
  561.     /*
  562.     WPrintf("DumpDir(%d, %d, <%.*s>) EXIT",
  563.                 dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
  564.                 dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
  565.     */
  566.     
  567.     /*
  568.      * Done with this directory, make sure we don't run out
  569.      * of working directories.
  570.      */
  571.     path->next = nil;
  572.     return(errFound);
  573. }
  574.  
  575. /*
  576.  * DumpFile - Dump a single file.
  577.  *
  578.  *    Exits via longjmp on unrecoverable error.
  579.  *    Result is 1 for success, 0 for failure.
  580.  */
  581. Boolean
  582. DumpFile(file)
  583.     CInfoPBRec    *file;
  584.     {
  585.     union record    *header;
  586.     register char    *p;
  587.     char        *buf;
  588.     HParamBlockRec    fpb;
  589.     long        bufsize, count, i;
  590.     register long    sizeleft;
  591.     register union record     *start;
  592.     char        *routine = "\pDumpFile";
  593.     
  594.     if ((header = StartHeader(file)) == nil)
  595.         return(true);
  596.     FinishHeader(header);
  597.     
  598.     /*
  599.      * Get the size of the file.
  600.      * Don't bother opening it if it is zero length.
  601.      */
  602.     head = header;
  603.     hstat.st_size = file->hFileInfo.ioFlLgLen;
  604.     PrintHeader();
  605.     if ((sizeleft = file->hFileInfo.ioFlLgLen) == 0)
  606.         return(false);
  607.  
  608.     fpb.fileParam.ioCompletion = nil;
  609.     fpb.fileParam.ioNamePtr = file->hFileInfo.ioNamePtr;
  610.     fpb.fileParam.ioVRefNum = file->hFileInfo.ioVRefNum;
  611.     fpb.fileParam.ioFVersNum = 0;
  612.     fpb.fileParam.ioDirID = file->hFileInfo.ioFlParID;
  613.     fpb.ioParam.ioPermssn = fsRdPerm;
  614.     fpb.ioParam.ioMisc = nil;
  615.     if (PBHOpen(&fpb, false) != noErr) {
  616.         OSAlert(routine, "\pPBHOpen", file->hFileInfo.ioNamePtr,
  617.                 fpb.fileParam.ioResult);
  618.         return(true);
  619.     }
  620.  
  621.     /*
  622.      * Dump the file to the archive.
  623.      * Note: this only dumps the data fork!
  624.      */
  625.     while (sizeleft > 0) {
  626.         if ((start = FindRec()) == nil)
  627.             return(true);
  628.             
  629.         bufsize = EndOfRecs()->charptr - start->charptr;
  630.         buf = start->charptr;
  631.     again:
  632.         count = (sizeleft < bufsize) ? sizeleft : bufsize;
  633.         fpb.ioParam.ioBuffer = buf;
  634.         fpb.ioParam.ioReqCount = count;
  635.         fpb.ioParam.ioPosMode = fsAtMark;
  636.         fpb.ioParam.ioPosOffset = 0;
  637.         if (PBRead((ParmBlkPtr) &fpb, false) != noErr) {
  638.             OSAlert(routine, "\pPBRead", file->hFileInfo.ioNamePtr,
  639.                     fpb.ioParam.ioResult);
  640.             return(true);
  641.         }
  642.  
  643.         count = fpb.ioParam.ioActCount;
  644.         if (cvtNl) {
  645.             /*
  646.              * Convert returns to newlines for Unix compat.
  647.              */
  648.             for (i = count, p = buf; --i >= 0; p++)
  649.                 if (*p == RETURN)
  650.                     *p = LF;
  651.         }
  652.  
  653.         sizeleft -= count;
  654.         UseRec(start + (count - 1) / RECORDSIZE);
  655.     }
  656.     
  657.     PBClose((ParmBlkPtr) &fpb, false);
  658.  
  659.     /* Clear last block garbage to zeros, FIXME */
  660.     return(false);
  661. }
  662.  
  663.  
  664. /*
  665.  * Make a header block for the file  name  whose stat info is  st .
  666.  * Return header pointer for success, NULL if the name is too long.
  667.  */
  668. union record *
  669. StartHeader(pb)
  670. CInfoPBRec    *pb;
  671. {
  672.     register union record *header;
  673.     Boolean    directory = DIRECTORY(*pb);
  674.  
  675.     if ((header = (union record *) FindRec()) == nil)
  676.         return(nil);
  677.         
  678.     bzero(header->charptr, sizeof(union record)); /* XXX speed up */
  679.     /*
  680.      * Generate the pathname, make sure we don't overflow
  681.      * the field in the tar header.
  682.      */
  683.     if (FillName(header, directory)) {
  684.         char    buf[NAMSIZ + 1];
  685.  
  686.         buf[0] = NAMSIZ;
  687.         memcpy(&buf[1], header->header.name, NAMSIZ);
  688.         PgmAlert("\pStartHeader", "\pName too long", buf);
  689.         return(nil);
  690.     }
  691.  
  692.     /*
  693.      * Fake the file mode, uid, gid.
  694.      * Convert from Mac based time to Unix based time.
  695.      */
  696.     ToOct((directory) ? 0755L : 0644L, 8,  header->header.mode);
  697.     ToOct(0L, 8,  header->header.uid);
  698.     ToOct(0L, 8,  header->header.gid);
  699.     ToOct((directory) ? 0L : pb->hFileInfo.ioFlLgLen, 1+12,
  700.             header->header.size);
  701.     ToOct((directory) ? pb->dirInfo.ioDrMdDat - TIMEDIFF :
  702.                 pb->hFileInfo.ioFlMdDat - TIMEDIFF,
  703.             1+12, header->header.mtime);
  704.     /* header->header.linkflag is left as null */
  705.     return(header);
  706. }
  707.  
  708. /* 
  709.  * Finish off a filled-in header block and write it out.
  710.  */
  711. void
  712. FinishHeader(header)
  713. register union record *header;
  714. {
  715.     register int    i;
  716.     register long    sum;
  717.     register char    *p;
  718.  
  719.     memcpy(header->header.chksum, CHKBLANKS, sizeof(header->header.chksum));
  720.  
  721.     sum = 0;
  722.     p = header->charptr;
  723.     for (i = sizeof(union record); --i >= 0; ) {
  724.         /*
  725.          * We can't use unsigned char here because of old compilers,
  726.          * e.g. V7.
  727.          */
  728.         sum += 0xFF & *p++;
  729.     }
  730.  
  731.     /*
  732.      * Fill in the checksum field.  It's formatted differently
  733.      * from the other fields:  it has [6] digits, a null, then a
  734.      * space -- rather than digits, a space, then a null.
  735.      * We use to_oct then write the null in over to_oct's space.
  736.      * The final space is already there, from checksumming, and
  737.      * to_oct doesn't modify it.
  738.      *
  739.      * This is a fast way to do:
  740.      * (void) sprintf(header->header.chksum, "%6o", sum);
  741.      */
  742.     ToOct((long) sum, 8, header->header.chksum);
  743.     header->header.chksum[6] = '\0';    /* Zap the space */
  744.     UseRec(header);
  745.     return;
  746. }
  747.  
  748.  
  749. /*
  750.  * Quick and dirty octal conversion.
  751.  * Converts long "value" into a "digs"-digit field at "where",
  752.  * including a trailing space and room for a null.  "digs"==3 means
  753.  * 1 digit, a space, and room for a null.
  754.  *
  755.  * We assume the trailing null is already there and don't fill it in.
  756.  * This fact is used by start_header and finish_header, so don't change it!
  757.  *
  758.  * This should be equivalent to:
  759.  *    (void) sprintf(where, "%*lo ", digs-2, value);
  760.  * except that sprintf fills in the trailing null and we don't.
  761.  */
  762. void
  763. ToOct(value, digs, where)
  764. register long    value;
  765. register int    digs;
  766. register char    *where;
  767. {
  768.     --digs;                /* Trailing null slot is left alone */
  769.     where[--digs] = ' ';        /* Put in the space, though */
  770.  
  771.     /* Produce the digits -- at least one */
  772.     do {
  773.         where[--digs] = '0' + (value & 7);    /* one octal digit */
  774.         value >>= 3;
  775.     } while (digs > 0 && value != 0);
  776.  
  777.     /* Leading spaces, if necessary */
  778.     while (digs > 0)
  779.         where[--digs] = ' ';
  780.  
  781. }
  782.  
  783. /*
  784.  * Write the EOT block(s).
  785.  */
  786. Boolean
  787. WriteEot()
  788. {
  789.     union record *p;
  790.  
  791.     if ((p = FindRec()) == nil)
  792.         return(true);
  793.         
  794.     bzero(p->charptr, RECORDSIZE);
  795.     UseRec(p);
  796.     /* FIXME, only one EOT block should be needed. */
  797.     if ((p = FindRec()) == nil)
  798.         return(true);
  799.         
  800.     bzero(p->charptr, RECORDSIZE);
  801.     UseRec(p);
  802.     return(false);
  803. }
  804.  
  805. /*
  806.  * FillName - generate the file or directory pathname
  807.  *
  808.  *    Converts to Unix style pathnames.
  809.  *    Appends a '/' for a directory.
  810.  */
  811. Boolean
  812. FillName(header, directory)
  813. register union record *header;
  814. Boolean    directory;
  815. {
  816.     register PathInfo    *p;
  817.     register char        *d, *s;
  818.     char            c;
  819.     int            i;
  820.  
  821.     d = header->header.name;
  822.     for (p = &pathHead; p != nil; p = p->next) {
  823.         s = &p->name[1];
  824.         for (i = p->name[0]; i > 0; i--) {
  825.             c = *s++;
  826.             if (c == '/')
  827.                 *d++ = ':';
  828.  
  829.             else if ((c < ' ') || (c > '~'))
  830.                 *d++ = '_';
  831.  
  832.             else
  833.                 *d++ = c;
  834.         }
  835.  
  836.         *d++ = (p->next == nil) ? '\0' : '/';
  837.     }
  838.  
  839.     if (directory) {
  840.         *(d - 1) = '/';
  841.         *d = '\0';
  842.     }
  843.  
  844.     return((d - header->header.name) > NAMSIZ);
  845. }
  846.