home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Information / Programming / Stuart's Tech Notes / StUU / Sources / DSUserProcs.c < prev    next >
Encoding:
Text File  |  1995-12-02  |  37.0 KB  |  1,160 lines  |  [TEXT/R*ch]

  1. /******************************************************************************
  2. **
  3. **  Project Name:    DropShell
  4. **     File Name:    DSUserProcs.c
  5. **
  6. **   Description:    Specific AppleEvent handlers used by the DropBox
  7. **
  8. *******************************************************************************
  9. **                       A U T H O R   I D E N T I T Y
  10. *******************************************************************************
  11. **
  12. **    Initials    Name
  13. **    --------    -----------------------------------------------
  14. **    LDR            Leonard Rosenthol
  15. **    MTC            Marshall Clow
  16. **    SCS            Stephan Somogyi
  17. **
  18. *******************************************************************************
  19. **                      R E V I S I O N   H I S T O R Y
  20. *******************************************************************************
  21. **
  22. **      Date        Time    Author    Description
  23. **    --------    -----    ------    ---------------------------------------------
  24. **    06/23/94            LDR        Added support for ProcessItem and ProcessFolder handling
  25. **    02/20/94            LDR        Modified Preflight & Postflight to take item count
  26. **    01/25/92            LDR        Removed the use of const on the userDataHandle
  27. **    12/09/91            LDR        Added the new SelectFile userProc
  28. **                                Added the new Install & DisposeUserGlobals procs
  29. **                                Modified PostFlight to only autoquit on odoc, not pdoc
  30. **    11/24/91            LDR        Added the userProcs for pdoc handler
  31. **                                Cleaned up the placement of braces
  32. **                                Added the passing of a userDataHandle
  33. **    10/29/91            SCS        Changes for THINK C 5
  34. **    10/28/91            LDR        Officially renamed DropShell (from QuickShell)
  35. **                                Added a bunch of comments for clarification
  36. **    10/06/91    00:02    MTC        Converted to MPW C
  37. **    04/09/91    00:02    LDR        Added to Projector
  38. **
  39. ******************************************************************************/
  40.  
  41. //#include <stdio.h>
  42.  
  43. #include <StandardFile.h>
  44. #include <Timer.h>            // For timing
  45.  
  46. #include <ICTypes.h>
  47. #include <ICKeys.h>
  48. #include <ICAPI.h>
  49.  
  50. #include <StuTypes.h>
  51.  
  52. #include "DSGlobals.h"
  53. #include "DSUserProcs.h"
  54.  
  55. #ifdef __MC68K__
  56. #define WideAdd(A,B)
  57. #define WideSubtract(A,B)
  58. #endif
  59.  
  60. // Static Prototypes
  61. static OSErr ProcessItem(FSSpecPtr myFSSPtr);
  62. static OSErr ProcessFolder(FSSpecPtr myFSSPtr);
  63.  
  64.  
  65. /*
  66.     Uncomment this line if you want each item of a dropped folder processed
  67.     as an individual item
  68. */
  69. #define qWalkFolders
  70.  
  71.  
  72. /*
  73.     This routine is called during init time.
  74.     
  75.     It allows you to install more AEVT Handlers beyond the standard four
  76. */
  77. #pragma segment Main
  78. pascal void InstallOtherEvents(void) {
  79. }
  80.  
  81.  
  82. /*    
  83.     This routine is called when an OAPP event is received.
  84.     
  85.     Currently, all it does is set the gOApped flag, so you know that
  86.     you were called initally with no docs, and therefore you shouldn't 
  87.     quit when done processing any following odocs.
  88. */
  89. #pragma segment Main
  90. pascal void OpenApp(void) {
  91.     gOApped = true;
  92. }
  93.  
  94.  
  95. /*    
  96.     This routine is called when an QUIT event is received.
  97.     
  98.     We simply set the global done flag so that the main event loop can
  99.     gracefully exit.  We DO NOT call ExitToShell for two reasons:
  100.     1) It is a pretty ugly thing to do, but more importantly
  101.     2) The Apple event manager will get REAL upset!
  102. */
  103. #pragma segment Main
  104. pascal void QuitApp(void) {
  105.     gDone = true;    /*    All Done! */
  106. }
  107.  
  108.  
  109. /*    
  110.     This routine is the first one called when an ODOC or PDOC event is received.
  111.     
  112.     In this routine you would place code used to setup structures, etc. 
  113.     which would be used in a 'for all docs' situation (like "Archive all
  114.     dropped files")
  115.  
  116.     Obviously, the opening boolean tells you whether you should be opening
  117.     or printing these files based on the type of event recieved.
  118.     
  119.     NEW IN 2.0!
  120.     The itemCount parameter is simply the number of items that were dropped on
  121.     the application and that you will be processing.  This gives you the ability
  122.     to do a single preflight for memory allocation needs, rather than doing it
  123.     once for each item as in previous versions.
  124.     
  125.     userDataHandle is a handle that you can create & use to store your own
  126.     data structs.  This dataHandle will be passed around to the other 
  127.     odoc/pdoc routines so that you can get at your data without using
  128.     globals - just like the new StandardFile.  
  129.     
  130.     We also return a boolean to tell the caller if you support this type
  131.     of event.  By default, our dropboxes don't support the pdoc, so when
  132.     opening is FALSE, we return FALSE to let the caller send back the
  133.     proper error code to the AEManager.
  134.  
  135.     You will probably want to remove the #pragma unused (currently there to fool the compiler!)
  136. */
  137.  
  138. #define DO_TIMING 0
  139.  
  140. #define IsError(X) ((X) !=1 && (X) != 0)
  141. #define Alert_FileDeleted 129
  142. #define Alert_NoIC        130
  143.  
  144. #if DO_TIMING
  145. static UnsignedWide total_start_time, total_end_time;
  146. static UnsignedWide create_start_time, create_end_time, create_time;
  147. static UnsignedWide write_start_time, write_end_time, write_time;
  148. static UnsignedWide close_start_time, close_end_time, close_time;
  149. static UnsignedWide read_start_time, read_end_time, read_time;
  150. #endif
  151.  
  152. typedef struct { HParamBlockRec io; void *GlobalsReg; } ex_HParamBlockRec;
  153.  
  154. static Handle InBuffer;                    // Handle to input buffer
  155. static Handle OutBuffer1, OutBuffer2;    // Handles to output output buffers
  156. static Handle OutBuffer;                // Handle to current output buffer
  157. static long InHandleSize;                // Size of input buffer
  158. static long OutHandleSize;                // Size of output buffer
  159. static short ReadRefNum;                // The file being read
  160. static long BytesRemaining;                // Bytes of file still unread
  161. static Ptr InPtr = NULL;                // Next character to read from buffer
  162. static Ptr InEnd = NULL;                // End of data in buffer
  163. static char *CurrentLine;                // Pointer to current line in the inbuffer
  164. static int  LineLength;                    // Length of current line
  165. static char TempLineBuffer[256];        // Temp holding buffer for lines spanning buffers
  166. static Ptr OutPtr;                        // Next byte to written to buffer
  167. static Ptr OutEnd;                        // End output buffer
  168. static WindowPtr StatusWindow;
  169. static ICInstance IC_instance = NULL;
  170. static Handle     IC_mappings = NULL;
  171. static ex_HParamBlockRec Async_Create_PB;
  172. static HParamBlockRec Async_Info_PB, Async_Open_PB;
  173. static ParamBlockRec Async_Write_PB, Async_Close_PB;
  174. static volatile Boolean FileBeingCreated;
  175. static FSSpec outputSpec, writeSpec, closeSpec;
  176. static unsigned char output_namesuffix;
  177. static unsigned char output_namesuffix_length;
  178. static OSType output_type, output_creator;
  179. static const Point zeroPoint;
  180.  
  181. static void MakeStatusWindow(void)
  182.     {
  183.     Rect bounds;
  184.     short screencentre = (qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2;
  185.     bounds.top    = 100;
  186.     bounds.bottom = 290;
  187.     bounds.left   = screencentre - 200;
  188.     bounds.right  = screencentre + 200;
  189.     StatusWindow = NewWindow(NULL, &bounds, NULL, TRUE, dBoxProc, (WindowPtr)-1, FALSE, 0);
  190.     SetPort(StatusWindow);
  191.     TextSize(12);
  192.     TextFace(bold);
  193.     MoveTo(20, 30); DrawString("\pFast, Simple, Stupid UUDecoder");
  194.     MoveTo(20, 50); DrawString("\pby Stuart Cheshire <cheshire@cs.stanford.edu>");
  195.     MoveTo(20, 70); DrawString("\pThis software is free.");
  196.     MoveTo(20, 90); DrawString("\pYou can do whatever you like with it.");
  197.     TextFace(0);
  198.     MoveTo(20,110); DrawString("\pBuilt using DropShell 2.0");
  199.     MoveTo(20,130); DrawString("\pby Leonard Rosenthol, Marshall Clow, and Stephan Somogyi");
  200.     }
  201.  
  202. static void UpdateStatusWindowInput(unsigned char *inputname)
  203.     {
  204.     Rect r = StatusWindow->portRect;
  205.     r.top    = 135;
  206.     r.bottom = 155;
  207.     r.left   = 20;
  208.     EraseRect(&r);
  209.     TextFace(bold);
  210.     MoveTo(20,150); DrawString("\pReading File: "); DrawString(inputname);
  211.     }
  212.  
  213. static void UpdateStatusWindowOutput(unsigned char *outputname)
  214.     {
  215.     Rect r = StatusWindow->portRect;
  216.     r.top    = 155;
  217.     r.left   = 20;
  218.     EraseRect(&r);
  219.     TextFace(bold);
  220.     MoveTo(20,170); DrawString("\pWriting File: "); DrawString(outputname);
  221.     }
  222.  
  223. static void UpdateStatusWindow(Boolean reading)
  224.     {
  225.     Rect r = StatusWindow->portRect;
  226.     r.top   = 135;
  227.     r.right =  20;
  228.     EraseRect(&r);
  229.     if (reading) MoveTo(8,150); else MoveTo(8,170);
  230.     TextFace(0);
  231.     DrawString("\p•");
  232.     }
  233.  
  234. static void StUU_Exit(void)
  235.     {
  236.     if (InBuffer )    { DisposHandle(InBuffer );     InBuffer      = NULL; }
  237.     if (OutBuffer1)   { DisposHandle(OutBuffer1);    OutBuffer1    = NULL; }
  238.     if (OutBuffer2)   { DisposHandle(OutBuffer2);    OutBuffer2    = NULL; }
  239.     if (StatusWindow) { DisposeWindow(StatusWindow); StatusWindow  = NULL; }
  240.     if (IC_instance)  { ICStop(IC_instance);         IC_instance   = NULL; }
  241.     }
  242.  
  243. #pragma segment Main
  244. pascal Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle) {
  245. #pragma unused ( itemCount )
  246. #pragma unused ( userDataHandle )
  247.     OSErr resultCode1 = noErr, resultCode2 = noErr;
  248.     
  249.     ICError ICerr;
  250.     ICAttr attr;
  251.     
  252.     Size growTM = 0, growHM = 0;
  253.     Size maxTM = TempMaxMem(&growTM) & ~0xFFF;
  254.     Size maxHM = MaxMem(&growHM)     & ~0xFFF;
  255.     
  256.     if (!opening) return(FALSE);    // we support opening, but not printing - see above
  257.     
  258.     if (maxTM > maxHM)
  259.         {
  260.         InHandleSize = maxTM >> 1;
  261.         OutHandleSize = InHandleSize >> 1;
  262.         while (InHandleSize > 0x1000)
  263.             {
  264.             InBuffer  = TempNewHandle(InHandleSize, &resultCode1);
  265.             if (resultCode1) { InHandleSize >>= 1; continue; }
  266.             break;
  267.             }
  268.         while (OutHandleSize > 0x1000)
  269.             {
  270.             OutBuffer1 = TempNewHandle(OutHandleSize, &resultCode2);
  271.             if (resultCode2) { OutHandleSize >>= 1; continue; }
  272.             OutBuffer2 = TempNewHandle(OutHandleSize, &resultCode2);
  273.             if (resultCode2) { DisposeHandle(OutBuffer1); OutHandleSize >>= 1; continue; }
  274.             break;
  275.             }
  276.         }
  277.     else
  278.         {
  279.         InHandleSize = maxHM >> 1;
  280.         OutHandleSize = InHandleSize >> 1;
  281.         while (InHandleSize > 0x1000)
  282.             {
  283.             InBuffer  = NewHandle(InHandleSize);
  284.             if (!InBuffer) { InHandleSize >>= 1; continue; }
  285.             break;
  286.             }
  287.         while (OutHandleSize > 0x1000)
  288.             {
  289.             OutBuffer1 = NewHandle(OutHandleSize);
  290.             if (!OutBuffer1) { InHandleSize >>= 1; continue; }
  291.             OutBuffer2 = NewHandle(OutHandleSize);
  292.             if (!OutBuffer2) { DisposeHandle(OutBuffer1); InHandleSize >>= 1; continue; }
  293.             break;
  294.             }
  295.         resultCode1 = (InBuffer   == NULL);
  296.         resultCode2 = (OutBuffer2 == NULL);
  297.         }
  298.     if (resultCode1 || resultCode2) { DebugStr("\pNo Mem"); StUU_Exit(); return(FALSE); }
  299.     ICerr = ICStart(&IC_instance, 'StUU');
  300.     if (!ICerr) ICerr = ICFindConfigFile(IC_instance, 0, nil);
  301.     if (!ICerr) ICerr = ICGetPrefHandle(IC_instance, kICMapping, &attr, &IC_mappings);
  302.     if (ICerr) { CenterAlert(Alert_NoIC); (void)Alert(Alert_NoIC, NULL); IC_mappings = NULL; }
  303.     
  304.     MakeStatusWindow();
  305.     HLock(InBuffer);
  306.     HLock(OutBuffer1);
  307.     HLock(OutBuffer2);
  308.     OutBuffer = OutBuffer1;
  309. #if DO_TIMING
  310.     Microseconds(&total_start_time);
  311.     create_time.lo = create_time.hi = 0;
  312.     write_time .lo = write_time .hi = 0;
  313.     close_time .lo = close_time .hi = 0;
  314.     read_time  .lo = read_time  .hi = 0;
  315. #endif
  316.     return(TRUE);
  317. }
  318.  
  319.  
  320. /*    
  321.     This routine is called for each file passed in the ODOC event.
  322.     
  323.     In this routine you would place code for processing each file/folder/disk that
  324.     was dropped on top of you.
  325.     
  326.     You will probably want to remove the #pragma unused (currently there to fool the compiler!)
  327. */
  328. #pragma segment Main
  329. pascal void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle ) {
  330. #pragma unused ( myFSSPtr )
  331. #pragma unused ( opening )
  332. #pragma unused ( userDataHandle )
  333.     OSErr    err = noErr;
  334.     
  335.     
  336.     #ifdef qWalkFolders
  337.     /*
  338.         For this case we need to determine if the FSSpec is a file or folder.
  339.         If it's a folder, we then need to process each item in that folder,
  340.         otherwise just process the item.
  341.     */
  342.     if (FSpIsFolder(myFSSPtr))
  343.         err = ProcessFolder(myFSSPtr);
  344.     else
  345.         err = ProcessItem(myFSSPtr);
  346.     #else
  347.     /*
  348.         For this case we just call ProcessItem on the FSSpec above.
  349.     */
  350.     err = ProcessItem(myFSSPtr);
  351.     #endif
  352.     
  353.     // you should probably do something if you get back an error ;)
  354. }
  355.  
  356. // CopyData Benchmarks
  357. //  82 182 number of bytes (copied 100,000 times)
  358. // --- ---
  359. // 145 212 BlockMove
  360. // 145 212 BlockMoveData
  361. // 134 283 CopyData
  362. // 113 237 CopyData in assembler
  363. // Conclusion: For small blocks, a subroutine is quicker than the BlockMove trap
  364.  
  365. static void CopyData(register Ptr src, register Ptr dest, register unsigned short len)
  366.     {
  367. #if THINK_C
  368.     asm    {
  369.         sub.w    #1, len
  370.     @0    move.b    (src)+, (dest)+
  371.         dbra    len, @0
  372.         }
  373. #else
  374.     while(len) { *dest++ = *src++; len--; }
  375. #endif
  376.     }
  377.  
  378. static void ShowMsg(unsigned char *msg, unsigned long time)
  379.     {
  380.     unsigned char buffer[128], number[16];
  381.     NumToString(time, number);
  382.     CopyData((Ptr)msg    + 1, (Ptr)buffer + 1, msg[0]);
  383.     CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
  384.     buffer[0] = msg[0] + number[0];
  385.     DebugStr(buffer);
  386.     }
  387.  
  388. static void ShowWindowMsg(short y, unsigned char *msg, unsigned long time)
  389.     {
  390.     unsigned char buffer[128], number[16];
  391.     NumToString(time, number);
  392.     CopyData((Ptr)msg    + 1, (Ptr)buffer + 1, msg[0]);
  393.     CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
  394.     buffer[0] = msg[0] + number[0];
  395.     MoveTo(20, y); DrawString(buffer);
  396.     }
  397.  
  398.  
  399. /*    
  400.     This routine is the last routine called as part of an ODOC event.
  401.     
  402.     In this routine you would place code to process any structures, etc. 
  403.     that you setup in the PreflightDocs routine.
  404.  
  405.     NEW IN 2.0!
  406.     The itemCount parameter was the number of items that you processed.
  407.     It is passed here just in case you need it ;)  
  408.     
  409.     If you created a userDataHandle in the PreFlightDocs routines, this is
  410.     the place to dispose of it since the Shell will NOT do it for you!
  411.     
  412.     You will probably want to remove the #pragma unusued (currently there to fool the compiler!)
  413. */
  414.  
  415. #pragma segment Main
  416. pascal void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle ) {
  417. #pragma unused ( opening )
  418. #pragma unused ( itemCount )
  419. #pragma unused ( userDataHandle )
  420.  
  421.     if ( (opening) && (!gOApped) )
  422.         gDone = true;    //    close everything up!
  423.  
  424.     HUnlock(InBuffer);
  425.     HUnlock(OutBuffer1);
  426.     HUnlock(OutBuffer2);
  427.  
  428. #if DO_TIMING
  429.     Microseconds(&total_end_time);
  430.     WideSubtract((wide*)&total_end_time, (wide*)&total_start_time);
  431.     EraseRect(&StatusWindow->portRect);
  432.     ShowWindowMsg( 30, "\pRead: ",   read_time.hi     *1000 + read_time.lo/1000);
  433.     ShowWindowMsg( 50, "\pCreate: ", create_time.hi   *1000 + create_time.lo/1000);
  434.     ShowWindowMsg( 70, "\pWrite: ",  write_time.hi    *1000 + write_time.lo/1000);
  435.     ShowWindowMsg( 90, "\pClose: ",  close_time.hi    *1000 + close_time.lo/1000);
  436.     ShowWindowMsg(110, "\pTotal: ",  total_end_time.hi*1000 + total_end_time.lo/1000);
  437.     while (!Button()) continue;
  438. #endif
  439.  
  440.     StUU_Exit();
  441.     
  442.     /*
  443.         The reason we do not auto quit is based on a recommendation in the
  444.         Apple event Registry which specifically states that you should NOT
  445.         quit on a 'pdoc' as the Finder will send you a 'quit' when it is 
  446.         ready for you to do so.
  447.     */
  448. }
  449.  
  450. // *********************************************************************************
  451. // Main StUU Code body starts here
  452.  
  453. // I treat any non-printing ascii character as a line break (non-printing ascii
  454. // characters except for CR and/or NL have no business being in UUencoded data anyway)
  455. #define IS_LINE_BREAK(C) ((unsigned char)(C) < (unsigned char)32)
  456.  
  457. static Ptr ReadNextLine(void)
  458.     {
  459.     register Ptr theData;
  460.     register Ptr InPtrCopy  = InPtr;
  461.     register Ptr InPtrLimit = InPtrCopy + 255;
  462.     
  463.     // If data is all contiguous in memory use fast optimistic case
  464.     if (InPtrLimit < InEnd)
  465.         {
  466.         if (IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
  467.         theData = InPtrCopy;
  468.         while (InPtrCopy < InPtrLimit && !IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
  469.         if (InPtrCopy > theData)                    // If we read a non-blank line
  470.             {
  471.             LineLength = InPtrCopy - theData;        // Set the length
  472.             *InPtrCopy++ = 0;                        // Terminate the line
  473.             InPtr = InPtrCopy;                        // Update the pointer
  474.             return(theData);                        // and return
  475.             }
  476.         }
  477.  
  478.     // If the optimistic case fails, try the slow mechanism
  479.     InPtrLimit = InEnd;
  480.     theData = TempLineBuffer;
  481.  
  482.     while (1)
  483.         {
  484.         while (InPtrCopy < InPtrLimit && theData < &TempLineBuffer[255] &&
  485.             !IS_LINE_BREAK(*InPtrCopy)) *theData++ = *InPtrCopy++;
  486.         
  487.         if (InPtrCopy < InPtrLimit)        // We haven't run out of data yet
  488.             {
  489.             InPtrCopy++;    // Skip over the newline
  490.             // Test to see if the line is has some contents (empty lines are ignored)
  491.             if (theData > TempLineBuffer) break;
  492.             }
  493.         else    // We broke out of loop because this chunk is over, so read some more
  494.             {
  495.             OSErr err;
  496.             long inOutCount = BytesRemaining;
  497.             if (inOutCount > InHandleSize) inOutCount = InHandleSize;
  498.             if (inOutCount == 0) break;        // If no more to read, stop
  499. #if DO_TIMING
  500.             Microseconds(&read_start_time);
  501. #endif
  502.             UpdateStatusWindow(TRUE);
  503.             err = FSRead(ReadRefNum, &inOutCount, *InBuffer);
  504.             UpdateStatusWindow(FALSE);
  505. #if DO_TIMING
  506.             Microseconds(&read_end_time);
  507.             WideSubtract((wide*)&read_end_time, (wide*)&read_start_time);
  508.             WideAdd((wide*)&read_time, (wide*)&read_end_time);
  509. #endif
  510.             if (err) break;                    // If error reading, stop
  511.             InPtrCopy = *InBuffer;
  512.             InPtrLimit = InPtrCopy + inOutCount;
  513.             BytesRemaining -= inOutCount;
  514.             if (inOutCount == 0) { DebugStr("\pError: FSRead read nothing"); break; }
  515.             }
  516.         }
  517.  
  518.     LineLength = theData - TempLineBuffer;    // Set the length
  519.     *theData = 0;                            // Terminate the line
  520.     InPtr = InPtrCopy;                        // Update the pointers
  521.     InEnd = InPtrLimit;
  522.     return(TempLineBuffer);
  523.     }
  524.  
  525. static void GetFileType(const unsigned char *name, OSType *t, OSType *c)
  526.     {
  527.     ICMapEntry entry;
  528.     ICError ICerr = -1;
  529.     *t = *c = '????';
  530.     if (IC_mappings) ICerr = ICMapEntriesFilename(IC_instance, IC_mappings, name, &entry);
  531.     if (ICerr == noErr) { *t = entry.file_type; *c = entry.file_creator; }
  532.     }
  533.  
  534. static void SetNextName(void)
  535.     {
  536.     outputSpec.name[output_namesuffix_length  ] = output_namesuffix++;
  537.     outputSpec.name[0] = output_namesuffix_length;
  538.     outputSpec.name[output_namesuffix_length-1] = '.';
  539.     if (output_namesuffix == '9'+1) output_namesuffix = 'A';
  540.     }
  541.  
  542. static pascal void CreateComplete(void)
  543.     {
  544. #ifdef __MC68K__
  545.     ex_HParamBlockRec *pb = GetRegisterA0();
  546.     void *oldGlobals = GetGlobalsRegister();
  547.     SetGlobalsRegister(pb->GlobalsReg);
  548. #endif
  549.     if (Async_Create_PB.io.ioParam.ioResult == dupFNErr)            // Duplicate name; try again
  550.         { SetNextName(); PBHCreateAsync(&Async_Create_PB.io); }
  551.     else FileBeingCreated = FALSE;
  552. #ifdef __MC68K__
  553.     SetGlobalsRegister(oldGlobals);
  554. #endif
  555.     }
  556.  
  557. #if GENERATINGCFM
  558. static RoutineDescriptor CreateCompleteRD =  BUILD_ROUTINE_DESCRIPTOR(uppIOCompletionProcInfo, CreateComplete);
  559. #else
  560. #define CreateCompleteRD CreateComplete
  561. #endif
  562.  
  563. static OSErr StUU_Create(FSSpecPtr sourceFSSPtr, unsigned char *fileName)
  564.     {
  565.     OSErr err = noErr;
  566.     unsigned long the_time;
  567.  
  568. #if DO_TIMING
  569.     Microseconds(&create_start_time);
  570. #endif
  571.  
  572.     // Set up new FSSpec
  573.     outputSpec.vRefNum = sourceFSSPtr->vRefNum;
  574.     outputSpec.parID   = sourceFSSPtr->parID;
  575.     CopyData((Ptr)fileName, (Ptr)outputSpec.name, 1+fileName[0]);
  576.  
  577.     // Prepare the name suffix, in case we need it because of duplicate file names
  578.     output_namesuffix = '1';
  579.     output_namesuffix_length = fileName[0]+2;
  580.     if (output_namesuffix_length > 31) output_namesuffix_length = 31;
  581.  
  582.     // Fire off the asynchronous file creation
  583.     FileBeingCreated = TRUE;
  584.     Async_Create_PB.io.ioParam.ioCompletion = &CreateCompleteRD;
  585.     Async_Create_PB.io.ioParam.ioResult     = noErr;
  586.     Async_Create_PB.io.ioParam.ioNamePtr    = outputSpec.name;
  587.     Async_Create_PB.io.ioParam.ioVRefNum    = outputSpec.vRefNum;
  588.     Async_Create_PB.io.fileParam.ioDirID    = outputSpec.parID;
  589. #ifdef __MC68K__
  590.     Async_Create_PB.GlobalsReg = GetGlobalsRegister();
  591. #endif
  592.     err = PBHCreateAsync(&Async_Create_PB.io);
  593.     if (err) FileBeingCreated = FALSE;
  594.  
  595.     // Ask Internet Config to tell us the type and creator codes for this file        
  596.     GetFileType(fileName, &output_type, &output_creator);
  597.  
  598.     // Wait for file creation to complete
  599.     while (FileBeingCreated) continue;
  600.     if (Async_Create_PB.io.ioParam.ioResult) err = Async_Create_PB.io.ioParam.ioResult;
  601.  
  602.     if (!err)
  603.         {
  604.         // Set the creator and type
  605.         GetDateTime(&the_time);
  606.         Async_Info_PB.fileParam.ioCompletion = NULL;
  607.         Async_Info_PB.fileParam.ioNamePtr    = outputSpec.name;
  608.         Async_Info_PB.fileParam.ioVRefNum    = outputSpec.vRefNum;
  609.         Async_Info_PB.fileParam.ioDirID      = outputSpec.parID;
  610.         Async_Info_PB.fileParam.ioFlCrDat    = the_time;
  611.         Async_Info_PB.fileParam.ioFlMdDat    = the_time;
  612.         Async_Info_PB.fileParam.ioFlFndrInfo.fdType     = output_type;
  613.         Async_Info_PB.fileParam.ioFlFndrInfo.fdCreator  = output_creator;
  614.         Async_Info_PB.fileParam.ioFlFndrInfo.fdFlags    = 0;
  615.         Async_Info_PB.fileParam.ioFlFndrInfo.fdLocation = zeroPoint;
  616.         Async_Info_PB.fileParam.ioFlFndrInfo.fdFldr     = 0;
  617.         err = PBHSetFInfoAsync(&Async_Info_PB);
  618.         }
  619.     
  620.     if (!err)
  621.         {
  622.         // Open the file
  623.         Async_Open_PB.fileParam.ioCompletion = NULL;
  624.         Async_Open_PB.fileParam.ioNamePtr    = outputSpec.name;
  625.         Async_Open_PB.fileParam.ioVRefNum    = outputSpec.vRefNum;
  626.         Async_Open_PB.fileParam.ioDirID      = outputSpec.parID;
  627.         Async_Open_PB.ioParam.ioPermssn      = fsWrPerm;
  628.         err = PBHOpenDFAsync(&Async_Open_PB);
  629.         }
  630.  
  631. #if DO_TIMING
  632.     Microseconds(&create_end_time);
  633.     WideSubtract((wide*)&create_end_time, (wide*)&create_start_time);
  634.     WideAdd((wide*)&create_time, (wide*)&create_end_time);
  635. #endif
  636.  
  637.     if (err) { FSpDelete(&outputSpec); ErrorAlert(kErrStringID, kCantCreateErr, err); }
  638.     else UpdateStatusWindowOutput(outputSpec.name);
  639.  
  640.     return(err);
  641.     }
  642.  
  643. static void ReportErrorAndDelete(void)
  644.     {
  645.     GrafPtr savePort;
  646.     GetPort(&savePort);
  647.     FSpDelete(&closeSpec);
  648.     ParamText(closeSpec.name, NULL, NULL, NULL);
  649.     CenterAlert(Alert_FileDeleted);
  650.     (void)Alert(Alert_FileDeleted, NULL);
  651.     SetPort(savePort);
  652.     }
  653.  
  654. static OSErr WriteChunk(void)
  655.     {
  656.     OSErr err;
  657. #if DO_TIMING
  658.     Microseconds(&write_start_time);
  659. #endif
  660.     while (Async_Open_PB.ioParam.ioResult == 1) continue; // Make sure open is complete
  661.     while (Async_Write_PB.ioParam.ioResult == 1) continue; // Make sure previous write is complete
  662.     
  663.     // If the previous write failed, report it now
  664.     if (Async_Write_PB.ioParam.ioResult) return(Async_Write_PB.ioParam.ioResult);
  665.     
  666.     // If our file opened correctly, write to it
  667.     if (Async_Write_PB.ioParam.ioResult == noErr && Async_Open_PB.ioParam.ioResult == noErr)
  668.         {
  669.         writeSpec = outputSpec;
  670.         Async_Write_PB.ioParam.ioCompletion = NULL;
  671.         Async_Write_PB.ioParam.ioResult     = noErr;
  672.         Async_Write_PB.ioParam.ioRefNum     = Async_Open_PB.ioParam.ioRefNum;
  673.         Async_Write_PB.ioParam.ioBuffer     = *OutBuffer;
  674.         Async_Write_PB.ioParam.ioReqCount   = OutPtr - *OutBuffer;
  675.         Async_Write_PB.ioParam.ioPosMode    = fsAtMark;
  676.         Async_Write_PB.ioParam.ioPosOffset  = 0;
  677.         err = PBWriteAsync(&Async_Write_PB);
  678.         if (err) { Async_Write_PB.ioParam.ioResult = err; err = noErr; }
  679.         if (OutBuffer == OutBuffer1) OutBuffer = OutBuffer2; else OutBuffer = OutBuffer1;
  680.         }
  681.     OutPtr = *OutBuffer;
  682.     OutEnd = *OutBuffer + OutHandleSize;
  683. #if DO_TIMING
  684.         Microseconds(&write_end_time);
  685.         WideSubtract((wide*)&write_end_time, (wide*)&write_start_time);
  686.         WideAdd((wide*)&write_time, (wide*)&write_end_time);
  687. #endif
  688.     return(err);
  689.     }
  690.  
  691. // Note, made the decode table unsigned instead of signed to prevent the C compiler
  692. // from trying to sign-extend the bytes every time it ORs them into a long
  693. static unsigned char zero_char;
  694. static unsigned char decode_table[256];
  695. #define    DEC(c)     (decode_table[(unsigned char)(c)])    /* single character decode */
  696.  
  697. static void reset_default_table(void)
  698.     {
  699.     int i;
  700.     for (i= 0; i< 32; i++) decode_table[i] = 0;
  701.     for (i=32; i< 96; i++) decode_table[i] = i - 32;
  702.     for (i=96; i<256; i++) decode_table[i] = 0;
  703.     decode_table['`'] = decode_table[' '];
  704.     decode_table['~'] = decode_table['^'];
  705.     zero_char = ' ';
  706.     }
  707.  
  708. static void read_translation_table(void)
  709.     {
  710.     unsigned char val = 0;
  711.     int i;
  712.     for (i=0; i<256; i++) decode_table[i] = 0;
  713.     while (CurrentLine[0])
  714.         {
  715.         unsigned char *p;
  716.         CurrentLine = ReadNextLine();
  717.         p = (unsigned char *)CurrentLine;
  718.         while (p[LineLength - 1] == ' ') p[--LineLength] = 0;
  719.         if (val == 0) zero_char = *p;
  720.         while (*p) decode_table[*p++] = val++;
  721.         if (val >= 64) return;
  722.         }
  723.     }
  724.  
  725. /* Old Think C inline assembly chunk
  726. #if THINK_C
  727.         asm {
  728.             clr.l    d0
  729.             move.b    (p)+, d0            ; read first byte
  730.             move.b    (decoder,d0), val    ; move translated value into val
  731.             move.b    (p)+, d0            ; read second byte
  732.             lsl.l    #6, val                ; shift val left six bits
  733.             or.b    (decoder,d0), val    ; OR translated value into val
  734.             move.b    (p)+, d0            ; read third byte
  735.             lsl.l    #6, val                ; shift val left six bits
  736.             or.b    (decoder,d0), val    ; OR translated value into val
  737.             move.b    (p)+, d0            ; read final byte
  738.             lsl.l    #6, val                ; shift val left six bits
  739.             or.b    (decoder,d0), val    ; OR translated value into val
  740.             }
  741. */
  742.  
  743. static OSErr DecodeLine(register Ptr p)
  744.     {
  745.     register Ptr out;
  746.     register unsigned char *decoder = decode_table;
  747.     register unsigned long val;
  748.     register int linelen = decoder[(unsigned char)(*p++)];
  749.     if (OutEnd - OutPtr < 256) { OSErr err = WriteChunk(); if (err) return(err); }
  750.     out = OutPtr;        // Careful! Don't set this till *after* WriteChunk
  751.  
  752.     while (linelen > 0)
  753.         {
  754.         val  = decoder[(unsigned char)(p[0])]; val <<= 6;
  755.         val |= decoder[(unsigned char)(p[1])]; val <<= 6;
  756.         val |= decoder[(unsigned char)(p[2])]; val <<= 6;
  757.         val |= decoder[(unsigned char)(p[3])];
  758.         out[2] = val; val >>= 8;
  759.         out[1] = val; val >>= 8;
  760.         out[0] = val;
  761.         p += 4;
  762.         out += 3;
  763.         linelen -= 3;
  764.         }
  765.     // Because we output blocks of three, we may overshoot the correct end of line
  766.     // In this case linelen will be negative and we backtrack OutPtr by that amount.
  767.     OutPtr = out + linelen;
  768.     return(noErr);
  769.     }
  770.  
  771. #define MAX_STASHED_LINES 8
  772. #define MAX_STASHED_LINE_LENGTH 88
  773. typedef struct { char c[MAX_STASHED_LINE_LENGTH]; } TextLine;
  774.  
  775. #define PERFECT_LINE (CurrentLine[0] == 'M' && LineLength >= 61 && LineLength <= 63)
  776.  
  777. // sourceFSSPtr is the uuencoded file being decoded
  778. // hp points to the "begin 644 filename.foo" header line
  779. static OSErr DecodeFile(FSSpecPtr sourceFSSPtr, Ptr hp)
  780.     {
  781.     OSErr err, closeErr;
  782.     TextLine LastPerfectLine;
  783.     TextLine LineStash[MAX_STASHED_LINES];
  784.     int num_stashed_lines = 0;
  785.     Boolean GotPerfectLine = FALSE;
  786.     int i;
  787.     Ptr filemode;
  788.     unsigned char filename[32];
  789.     filename[0] = 0;
  790.     hp += 6;                            // Skip the "begin "
  791.     while (*hp == ' ') hp++;            // Skip the spaces
  792.     filemode = hp;                        // The Unix file permissions
  793.     while (*hp && *hp != ' ') hp++;        // Read the permission portion
  794.     if (*hp) *hp++ = 0;                    // Terminate the permission string
  795.     while (*hp == ' ') hp++;            // Skip the spaces
  796.     while (*hp && *hp != ' ' && filename[0] < sizeof(filename) - 1)
  797.         filename[++filename[0]] = (unsigned char) *hp++;
  798.  
  799.     CurrentLine = ReadNextLine();        // Read the first line of encoded data
  800.     if (!filename[0]) { ErrorAlert(kErrStringID, kNoFileNameErr, 0); return(noErr); }
  801.  
  802.     // Create output file in the same directory
  803.     err = StUU_Create(sourceFSSPtr, filename);
  804.     if (err) return(err);
  805.     // The file is opened asynchronously, so the open proceeds while we start decoding
  806.     
  807.     OutPtr = *OutBuffer;                // Set up output pointers
  808.     OutEnd = *OutBuffer + OutHandleSize;
  809.     
  810.     while (CurrentLine[0] && !err)
  811.         {
  812.         // "Good Loop": Read as many good lines as possible
  813.         while (PERFECT_LINE && !err)
  814.             { err = DecodeLine(CurrentLine); CurrentLine = ReadNextLine(); }
  815.         
  816.         GotPerfectLine = FALSE;
  817.         // "Bad Loop": Skip past the bad or questionable lines
  818.         while (CurrentLine[0] && !err)
  819.             {
  820.             int origlen = DEC(CurrentLine[0]);                // Original length
  821.             int codedlength = 1 + (origlen * 4 + 2) / 3;    // UUencoded length
  822.             
  823.             if (CurrentLine[0] == 'e' &&        // check for "end" marker
  824.                 CurrentLine[1] == 'n' &&
  825.                 CurrentLine[2] == 'd') goto end_of_file;
  826.             
  827.             if (CurrentLine[0] == 'b' &&        // check for another "begin" marker
  828.                 CurrentLine[1] == 'e' &&
  829.                 CurrentLine[2] == 'g' &&
  830.                 CurrentLine[3] == 'i' &&
  831.                 CurrentLine[4] == 'n' &&
  832.                 CurrentLine[5] == ' ') goto end_of_file;
  833.             
  834.             if (CurrentLine[0] == 't' &&        // check for another "table" marker
  835.                 CurrentLine[1] == 'a' &&
  836.                 CurrentLine[2] == 'b' &&
  837.                 CurrentLine[3] == 'l' &&
  838.                 CurrentLine[4] == 'e') goto end_of_file;
  839.  
  840.             // If line too short, restore truncated trailing spaces
  841.             if (LineLength < codedlength)
  842.                 {
  843.                 // Mustn't try to expand line in place!
  844.                 if (CurrentLine != TempLineBuffer)
  845.                     {
  846.                     CopyData(CurrentLine, TempLineBuffer, LineLength+1);
  847.                     CurrentLine = TempLineBuffer;
  848.                     }
  849.                 while (LineLength < codedlength) CurrentLine[LineLength++] = zero_char;
  850.                 CurrentLine[LineLength] = 0;        // Terminate the line
  851.                 }
  852.             
  853.             // If we get one perfect line stash it, and set the flag.
  854.             // If we get a second perfect line, assume we are back in body text,
  855.             // and go back to the good loop
  856.             if (PERFECT_LINE)
  857.                 {
  858.                 if (GotPerfectLine) { err = DecodeLine(LastPerfectLine.c); break; }
  859.                 num_stashed_lines = 0;
  860.                 GotPerfectLine = TRUE;
  861.                 CopyData(CurrentLine, LastPerfectLine.c, LineLength);
  862.                 }
  863.             else
  864.                 {
  865.                 // Stash runt lines in case they turn out to be
  866.                 // the last few lines before the "end" marker
  867.                 GotPerfectLine = FALSE;
  868.                 if (LineLength <= MAX_STASHED_LINE_LENGTH &&
  869.                     num_stashed_lines < MAX_STASHED_LINES &&
  870.                     origlen > 0 && origlen <= 45 && LineLength <= codedlength + 3)
  871.                         CopyData(CurrentLine, LineStash[num_stashed_lines++].c, LineLength);
  872.                 }
  873.             CurrentLine = ReadNextLine();                        // Read the next line
  874.             }
  875.         }
  876. end_of_file:
  877.     // Decode any stashed lines
  878.     if (!err && GotPerfectLine) err = DecodeLine(LastPerfectLine.c);
  879.     for (i=0; !err && i<num_stashed_lines; i++) err = DecodeLine(LineStash[i].c);
  880.  
  881.     // Make sure open is complete
  882.     while (Async_Open_PB.ioParam.ioResult == 1) continue;
  883.     
  884.     // If file failed to be opened, give the error message
  885.     if (Async_Open_PB.ioParam.ioResult)
  886.         {
  887.         FSpDelete(&outputSpec);
  888.         ErrorAlert(kErrStringID, kCantCreateErr, Async_Open_PB.ioParam.ioResult);
  889.         return(Async_Open_PB.ioParam.ioResult);
  890.         }
  891.  
  892.     // Write the data out to disk
  893.     if (!err && OutPtr > *OutBuffer) err = WriteChunk();
  894.     // The file is written asynchronously, so the write proceeds while we continue
  895.     // decoding the next file
  896.  
  897.     // And close the file
  898. #if DO_TIMING
  899.     Microseconds(&close_start_time);
  900. #endif
  901.     while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure previous close is complete
  902.     if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
  903.     // If the previous close failed, report it now
  904.  
  905.     closeSpec = outputSpec;
  906.     Async_Close_PB.ioParam.ioCompletion = NULL;
  907.     Async_Close_PB.ioParam.ioResult     = noErr;
  908.     Async_Close_PB.ioParam.ioRefNum     = Async_Open_PB.ioParam.ioRefNum;
  909.     closeErr = PBCloseAsync(&Async_Close_PB);
  910.     if (closeErr) Async_Close_PB.ioParam.ioResult = closeErr;
  911.     // The file is closed asynchronously, so the close proceeds while we continue
  912.     // decoding the next file
  913.  
  914. #if DO_TIMING
  915.     Microseconds(&close_end_time);
  916.     WideSubtract((wide*)&close_end_time, (wide*)&close_start_time);
  917.     WideAdd((wide*)&close_time, (wide*)&close_end_time);
  918. #endif
  919.     
  920.     if (err) ReportErrorAndDelete();
  921.  
  922.     return(err);
  923.     }
  924.  
  925. // *********************************************************************************
  926.  
  927. /*
  928.     This routine gets called for each item (which could be either a file or a folder)
  929.     that the caller wants dropped.  The determining factor is the definition of the 
  930.     qWalkFolder compiler directive.   Either way, the item in question should be
  931.     processed as a single item and not "dissected" into component units (like subfiles
  932.     of a folder!)
  933. */
  934. static OSErr ProcessItem(FSSpecPtr myFSSPtr)
  935. {
  936.     OSErr    err = noErr;
  937.     
  938.     err = FSpOpenDF(myFSSPtr, fsRdPerm, &ReadRefNum);
  939.     if (err) return(err);
  940.     
  941.     err = GetEOF(ReadRefNum, &BytesRemaining);
  942.     if (err) { FSClose(ReadRefNum); return(err); }
  943.     InPtr = InEnd = NULL;
  944.     
  945.     UpdateStatusWindowInput(myFSSPtr->name);
  946.     
  947.     reset_default_table();
  948.     CurrentLine = ReadNextLine();        // Load first line of file into CurrentLine
  949.     while (err == noErr && CurrentLine[0])
  950.         {
  951.         if (CurrentLine[0] == 'e' &&        // check for "end" marker
  952.             CurrentLine[1] == 'n' &&
  953.             CurrentLine[2] == 'd') reset_default_table();
  954.         
  955.         if (CurrentLine[0] == 't' &&
  956.             CurrentLine[1] == 'a' &&
  957.             CurrentLine[2] == 'b' &&
  958.             CurrentLine[3] == 'l' &&
  959.             CurrentLine[4] == 'e') read_translation_table();
  960.             
  961.         if (CurrentLine[0] == 'b' &&
  962.             CurrentLine[1] == 'e' &&
  963.             CurrentLine[2] == 'g' &&
  964.             CurrentLine[3] == 'i' &&
  965.             CurrentLine[4] == 'n' &&
  966.             CurrentLine[5] == ' ')
  967.                 {
  968.                 err = DecodeFile(myFSSPtr, CurrentLine);
  969.                 if (IsError(Async_Write_PB.ioParam.ioResult)) break;
  970.                 reset_default_table();
  971.                 }
  972.         else CurrentLine = ReadNextLine();    // Read next line
  973.         }
  974.     FSClose(ReadRefNum);
  975.     while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure final output close is complete
  976.  
  977.     // If we exited because of a write error, report it and delete the file
  978.     if (Async_Write_PB.ioParam.ioResult)
  979.         {
  980.         ErrorAlert(kErrStringID, kDiskFullErr, Async_Write_PB.ioParam.ioResult);
  981.         FSpDelete(&writeSpec);
  982.         ParamText(writeSpec.name, NULL, NULL, NULL);
  983.         CenterAlert(Alert_FileDeleted);
  984.         (void)Alert(Alert_FileDeleted, NULL);
  985.         }
  986.     if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
  987.     return(err);
  988. }
  989.  
  990. /*
  991.     This routine gets called for any folder (or disk) that the caller wants 
  992.     processed as a set of component items, instead of as a single entity.
  993.     The determining factor is the definition of the qWalkFolder compiler directive.
  994. */
  995. static OSErr ProcessFolder(FSSpecPtr myFSSPtr)
  996. {
  997.     OSErr        err = noErr;
  998.     short        index, oldIndex, localIndex;
  999.     FSSpec        localFSSpec, curFSSpec;
  1000.     CInfoPBRec    cipb;
  1001.     Str255        fName, vFName;
  1002.     long        dirID, origDirID;
  1003.     Boolean        foundPosition;
  1004.  
  1005.      // copy the source locally to avoid recursion problems
  1006.      BlockMoveData(myFSSPtr, &localFSSpec, sizeof(FSSpec));
  1007.      
  1008.     //    get the dirID for THIS folder, not it's parent!
  1009.     BlockMoveData(localFSSpec.name, fName, 32);
  1010.     
  1011.     cipb.hFileInfo.ioCompletion    = 0L;
  1012.     cipb.hFileInfo.ioNamePtr    = fName;
  1013.     cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1014.     cipb.hFileInfo.ioFDirIndex    = 0;    // use the dir & vRefNum;
  1015.     cipb.hFileInfo.ioDirID        = localFSSpec.parID;
  1016.     err = PBGetCatInfoSync(&cipb);
  1017.     
  1018.     if (!err) {        
  1019.         origDirID = cipb.dirInfo.ioDrDirID; // copy the sucker
  1020.         index = 1;
  1021.                 
  1022.         // index through all contents of this folder
  1023.         while (err == noErr) {
  1024.             dirID = origDirID;
  1025.             localIndex = index;
  1026.             fName [0] = 0;
  1027.             cipb.hFileInfo.ioCompletion    = 0L;
  1028.             cipb.hFileInfo.ioNamePtr    = fName;
  1029.             cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1030.             cipb.hFileInfo.ioFDirIndex    = localIndex;    // use a real index
  1031.             cipb.hFileInfo.ioDirID        = dirID;
  1032.             err = PBGetCatInfoSync(&cipb);
  1033.  
  1034.             if (!err) {
  1035.                 BlockMoveData(fName, curFSSpec.name, 32);
  1036.                 curFSSpec.vRefNum    = cipb.hFileInfo.ioVRefNum;
  1037.                 curFSSpec.parID        = dirID;
  1038.             
  1039.                 /*    
  1040.                     Check to see if this entry is a folder.
  1041.                 */
  1042.                 if (cipb.hFileInfo.ioFlAttrib & ioDirMask) {
  1043.                     err = ProcessFolder(&curFSSpec);
  1044.                  } else
  1045.                     err = ProcessItem(&curFSSpec);
  1046.             
  1047.                 /*    If we've had an error, get out! */
  1048.                 if (err)    break;
  1049.  
  1050.                 // dirID = origDirID;    
  1051.                 localIndex = index;    
  1052.  
  1053.                 /*    
  1054.                     Now take into account new files being created
  1055.                     in the current directory & messing up our index.
  1056.                     See Dev.CD Vol. XI:Tools & Apps (Moof!):Misc Utilities:
  1057.                     Disinfectant & Source 2.5.1:Sample:Notes:Scan Alg    
  1058.                 */
  1059.                 vFName [0] = 0;
  1060.                 cipb.hFileInfo.ioCompletion    = 0L;
  1061.                 cipb.hFileInfo.ioNamePtr    = vFName;
  1062.                 cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1063.                 cipb.hFileInfo.ioFDirIndex    = localIndex;    // use a real index
  1064.                 cipb.hFileInfo.ioDirID        = dirID;
  1065.                 err = PBGetCatInfoSync(&cipb);
  1066.                 oldIndex = index;
  1067.                 if (!err) {
  1068.                     /*    If they're equal - same place, go to next */
  1069.                     if (EqualString (vFName, fName, false, false))
  1070.                         index++;
  1071.                 }
  1072.                 
  1073.                 /*    If we didn't advance, then perhaps a file was created or deleted */
  1074.                 if (oldIndex == index) {
  1075.                     oldIndex        = index;    /* save off the old */
  1076.                     index            = 0;        /* and start at the beginning */
  1077.                     err                = noErr;
  1078.                     vFName [0]        = 0;
  1079.                     foundPosition    = false;
  1080.                     
  1081.                     while (!foundPosition) {
  1082.                         index++;
  1083.                         vFName [0] = 0;
  1084.                         cipb.hFileInfo.ioCompletion    = 0L;
  1085.                         cipb.hFileInfo.ioNamePtr    = vFName;
  1086.                         cipb.hFileInfo.ioVRefNum    = localFSSpec.vRefNum;
  1087.                         cipb.hFileInfo.ioFDirIndex    = index;    /* now use a real index */
  1088.                         cipb.hFileInfo.ioDirID        = dirID;
  1089.                         err = PBGetCatInfoSync(&cipb);
  1090.                         
  1091.                         if (err == fnfErr) {  // we've just been deleted
  1092.                             index = oldIndex;
  1093.                             foundPosition = true;
  1094.                             err = noErr;    // have to remember to reset this!
  1095.                         }
  1096.                         
  1097.                     /*    found same file & same index position */
  1098.                     /*    so try the next item */
  1099.                         if ((!foundPosition) && EqualString(fName, vFName, false, false)) {
  1100.                             index++;
  1101.                             foundPosition = true;
  1102.                         }
  1103.                     }
  1104.                 }
  1105.             }
  1106.         }
  1107.     }
  1108.     
  1109.     return(err);
  1110. }
  1111.  
  1112. /*
  1113.     This routine is called when the user chooses "Select File…" from the
  1114.     File Menu.
  1115.     
  1116.     Currently it simply calls the new StandardGetFile routine to have the
  1117.     user select a single file (any type, numTypes = -1) and then calls the
  1118.     SendODOCToSelf routine in order to process it.  
  1119.             
  1120.     The reason we send an odoc to ourselves is two fold: 1) it keeps the code
  1121.     cleaner as all file openings go through the same process, and 2) if events
  1122.     are ever recordable, the right things happen (this is called Factoring!)
  1123.  
  1124.     Modification of this routine to only select certain types of files, selection
  1125.     of multiple files, and/or handling of folder & disk selection is left 
  1126.     as an exercise to the reader.
  1127. */
  1128. pascal void SelectFile(void)
  1129. {
  1130.     StandardFileReply    stdReply;
  1131.     SFTypeList            theTypeList;
  1132.  
  1133.     StandardGetFile(NULL, -1, theTypeList, &stdReply);
  1134.     if (stdReply.sfGood)    // user did not cancel
  1135.         SendODOCToSelf(&stdReply.sfFile);    // so send me an event!
  1136. }
  1137.  
  1138. /*
  1139.     This routine is called during the program's initialization and gives you
  1140.     a chance to allocate or initialize any of your own globals that your
  1141.     dropbox needs.
  1142.     
  1143.     You return a boolean value which determines if you were successful.
  1144.     Returning false will cause DropShell to exit immediately.
  1145. */
  1146. pascal Boolean InitUserGlobals(void)
  1147. {
  1148.     return(true);    // nothing to do, it we must be successful!
  1149. }
  1150.  
  1151. /*
  1152.     This routine is called during the program's cleanup and gives you
  1153.     a chance to deallocate any of your own globals that you allocated 
  1154.     in the above routine.
  1155. */
  1156. pascal void DisposeUserGlobals(void)
  1157. {
  1158.     // nothing to do for our sample dropbox
  1159. }
  1160.