home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / GUSI 1.4.1 / GUSI / GUSIFSp.cp < prev    next >
Encoding:
Text File  |  1994-05-01  |  17.1 KB  |  863 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIFSp.cp        -    Dealing with paths
  4. Author    :    Matthias Neeracher <neeri@iis.ethz.ch>
  5. Language    :    MPW C++
  6.  
  7. $Log: GUSIFSp.cp,v $
  8. Revision 1.2  1994/05/01  23:38:45  neeri
  9. Another rename() fix.
  10.  
  11. Revision 1.1  1994/03/08  22:06:44  neeri
  12. Initial revision
  13.  
  14. Revision 0.12  1994/02/04  00:00:00  neeri
  15. Make TFileSpec constructors preserve case to allow above
  16.  
  17. Revision 0.11  1993/10/24  00:00:00  neeri
  18. Allow changing case in a rename
  19.  
  20. Revision 0.10  1993/09/27  00:00:00  neeri
  21. FSpSmartMove
  22.  
  23. Revision 0.9  1993/07/17  00:00:00  neeri
  24. LastInfo
  25.  
  26. Revision 0.8  1993/06/21  00:00:00  neeri
  27. Throw out the inline
  28.  
  29. Revision 0.7  1993/03/01  00:00:00  neeri
  30. Bless
  31.  
  32. Revision 0.6  1993/02/06  00:00:00  neeri
  33. Use FSMakeFSSpec if possible
  34.  
  35. Revision 0.5  1993/01/15  00:00:00  neeri
  36. IsParentOf
  37.  
  38. Revision 0.4  1992/11/15  00:00:00  neeri
  39. Rename GUSIFSp_P.h to TFileSpec.h (there we go again)
  40.  
  41. Revision 0.3  1992/11/15  00:00:00  neeri
  42. Forgot a few consts
  43.  
  44. Revision 0.2  1992/09/12  00:00:00  neeri
  45. Renamed Paths.h to GUSIFSp_P.h
  46.  
  47. Revision 0.1  1992/09/06  00:00:00  neeri
  48. Clear ioACUser
  49.  
  50. *********************************************************************/
  51.  
  52. #include "GUSI_P.h"
  53. #include "TFileSpec.h"
  54.  
  55. #include <Errors.h>
  56. #include <Memory.h>
  57. #include <Aliases.h>
  58. #include <string.h>
  59. #include <ioctl.h>
  60. #include <fcntl.h>
  61. #include <errno.h>
  62. #include <TextUtils.h>
  63.  
  64. OSErr         TFileSpec::error;
  65. short         TFileSpec::curVol;
  66. long            TFileSpec::curDir    =    -1;
  67. CInfoPBRec    TFileSpec::lastInfo;
  68.  
  69. OSErr TFileSpec::ChDir(const TFileSpec & spec)
  70. {
  71.     TFileSpec nudir(spec);
  72.     
  73.     nudir += "\p";
  74.     
  75.     if (error)
  76.         return error;
  77.     
  78.     curVol    =    nudir.vRefNum;
  79.     curDir    =    nudir.parID;
  80.  
  81.     return noErr;
  82. }
  83.  
  84. static OSErr CurrentDir(short & vRefNum, long & parID)
  85. {
  86.     OSErr        error;
  87.     WDPBRec    vol;
  88.     Str63        name;
  89.     
  90.     vol.ioNamePtr    =    name;
  91.     
  92.     if (error = PBHGetVolSync(&vol))
  93.         return error;
  94.         
  95.     vRefNum    =    vol.ioWDVRefNum;
  96.     parID        =    vol.ioWDDirID;
  97.     
  98.     return noErr;
  99. }
  100.  
  101. OSErr TFileSpec::DefaultDir()
  102. {
  103.     if (curDir != -1)    {
  104.         vRefNum    =    curVol;
  105.         parID        =    curDir;
  106.         
  107.         return noErr;
  108.     } else
  109.         return error = CurrentDir(vRefNum, parID);
  110. }
  111.  
  112. OSErr TFileSpec::Default()
  113. {
  114.     if (!DefaultDir())
  115.         --*this;
  116.     
  117.     return error;
  118. }
  119.  
  120. TFileSpec::TFileSpec(const TFileSpec & spec)
  121.     : FSSpec(spec)
  122. {
  123. }
  124.  
  125. TFileSpec::TFileSpec(const FSSpec & spec, Boolean useAlias)                            
  126.     : FSSpec(spec) 
  127. {
  128.     if (!useAlias && hasAlias)
  129.         Resolve();
  130. }
  131.  
  132. TFileSpec::TFileSpec(short vRefNum, long parID, ConstStr31Param name, Boolean useAlias)
  133. {
  134.     if (!hasMakeFSSpec || FSMakeFSSpec(vRefNum, parID, name, this)) {
  135.         this->vRefNum    =    vRefNum;
  136.         this->parID        =    parID;
  137.         memcpy(this->name, name, *name+1);
  138.     }
  139.     
  140.     if (!useAlias && hasAlias)
  141.         Resolve();    
  142.         
  143.     if (EqualString(this->name, name, false, true))
  144.         memcpy(this->name, name, *name+1);        
  145. }
  146.  
  147. TFileSpec::TFileSpec(short wd, ConstStr31Param name, Boolean useAlias)
  148. {
  149.     if (!hasMakeFSSpec || FSMakeFSSpec(wd, 0, name, this)) {
  150.         WDPBRec     wdPB;
  151.         
  152.         wdPB.ioNamePtr     = nil;
  153.         wdPB.ioVRefNum     = wd;
  154.         wdPB.ioWDIndex     = 0;
  155.         wdPB.ioWDProcID     = 0;
  156.         
  157.         /* Change the Working Directory number in vRefnum into a real vRefnum */
  158.         /* and DirID. The real vRefnum is returned in ioVRefnum, and the real */
  159.         /* DirID is returned in ioWDDirID. */
  160.         
  161.         if (error = PBGetWDInfoSync(&wdPB))
  162.             return;
  163.         
  164.         vRefNum    = wdPB.ioWDVRefNum;
  165.         parID        = wdPB.ioWDDirID;
  166.         memcpy(this->name, name, *name+1);
  167.     }
  168.     
  169.     if (!useAlias && hasAlias)
  170.         Resolve();
  171.         
  172.     if (EqualString(this->name, name, false, true))
  173.         memcpy(this->name, name, *name+1);        
  174. }
  175.  
  176. TFileSpec::TFileSpec(OSType object, short vol, long dir)
  177. {
  178.     if (object == kTempFileType && dir) {
  179.         vRefNum    =    vol;
  180.         parID        =    dir;
  181.     } else if (
  182.         error = 
  183.             FindFolder(
  184.                 vol, (object == kTempFileType) ? kTemporaryFolderType : object,
  185.                 true, &vRefNum, &parID)
  186.     ) 
  187.         return;
  188.         
  189.     if (object == kTempFileType) {
  190.         static long nr = -1;
  191.         
  192.         strcpy((char *) name, (char *) "\ptmp00000");
  193.         
  194.         do {
  195.             nr = (nr + 1) % 100000;
  196.             
  197.             sprintf((char *) name+4, "%05ld", nr);
  198.         } while (Exists());
  199.     } else {
  200.         *this -= 1;
  201.     }
  202. }
  203.  
  204. void TFileSpec::Bless()
  205. {
  206.     if (hasMakeFSSpec) {
  207.         FSSpec    spec    =    *this;    // Dont know what happens to an aliased name
  208.     
  209.         error = FSMakeFSSpec(spec.vRefNum, spec.parID, spec.name, this);
  210.         
  211.         if (EqualString(spec.name, name, false, true))
  212.             memcpy(name, spec.name, *spec.name+1);        
  213.     } else
  214.         error = noErr;
  215. }
  216.  
  217. #define    maxPathLen 512
  218.  
  219. static char fullPath[maxPathLen];
  220.  
  221. /* Convert an FSSpec into a full pathname. The pathname is accumulated on the
  222.    high end of path to avoid excessive copying.
  223. */
  224.  
  225. char * TFileSpec::FullPath() const
  226. {
  227.     char *            curPath;
  228.     
  229.     /* Special case: a volume was specified */
  230.     if (parID == fsRtParID)    {
  231.         memcpy(fullPath, name+1, *name);
  232.         curPath        =    fullPath+*name;
  233.         curPath[0]    =    ':';
  234.         curPath[1]    =    0;
  235.         error            =    noErr;
  236.         
  237.         return fullPath;
  238.     }
  239.         
  240.     fullPath[maxPathLen-1]    =    0;
  241.     curPath                         =    fullPath+maxPathLen-*name-1;
  242.     
  243.     memcpy(curPath, name+1, *name);
  244.     
  245.    lastInfo.dirInfo.ioNamePtr = (StringPtr) fullPath;
  246.    lastInfo.dirInfo.ioDrParID = parID;
  247.  
  248.    do {
  249.       lastInfo.dirInfo.ioVRefNum     = vRefNum;
  250.       lastInfo.dirInfo.ioFDirIndex     = -1;
  251.       lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  252.  
  253.       if (error = PBGetCatInfoSync(&lastInfo))
  254.              return "";
  255.       *--curPath    =    ':';
  256.         curPath    -=    *fullPath;
  257.         memmove(curPath, fullPath+1, *fullPath);
  258.    } while (lastInfo.dirInfo.ioDrDirID != fsRtDirID);
  259.  
  260.     return curPath;
  261. }
  262.  
  263. char * TFileSpec::RelPath() const
  264. {
  265.     short            curVRef;
  266.     long            curDirID;
  267.     char *        curPath;
  268.     
  269.     if (CurrentDir(curVRef, curDirID))
  270.         return FullPath();
  271.         
  272.     /* Special case: a volume was specified */
  273.     if (parID == fsRtParID)    {
  274.         if (vRefNum == curVRef && curDirID == fsRtDirID)
  275.             curPath = fullPath;
  276.         else {
  277.             memcpy(fullPath, name+1, *name);
  278.             curPath        =    fullPath+*name;
  279.         }
  280.         
  281.         curPath[0]    =    ':';
  282.         curPath[1]    =    0;
  283.         
  284.         return fullPath;
  285.     }
  286.  
  287.     lastInfo.dirInfo.ioNamePtr        =    (StringPtr)fullPath;
  288.     lastInfo.dirInfo.ioVRefNum     =     curVRef;
  289.     lastInfo.dirInfo.ioFDirIndex     =     -1;
  290.     lastInfo.dirInfo.ioDrDirID     =     curDirID;
  291.     
  292.     if (error = PBGetCatInfoSync(&lastInfo))
  293.         return "";
  294.     
  295.     if (curVRef == vRefNum 
  296.         && lastInfo.dirInfo.ioDrParID == parID
  297.         && !memcmp (fullPath, name, *fullPath+1)
  298.     )    {
  299.         fullPath[0]    =    ':';
  300.         fullPath[1] =     0;
  301.         
  302.         return fullPath;
  303.     }
  304.         
  305.     fullPath[maxPathLen-1]    =    0;
  306.     curPath                         =    fullPath+maxPathLen-*name-1;
  307.     
  308.     memcpy(curPath, name+1, *name);
  309.     
  310.    lastInfo.dirInfo.ioNamePtr = (StringPtr) fullPath;
  311.    lastInfo.dirInfo.ioDrParID = parID;
  312.  
  313.    do {
  314.       *--curPath    =    ':';
  315.          
  316.         /* Test fur current directory */
  317.         if (curVRef == vRefNum && curDirID == lastInfo.dirInfo.ioDrParID)
  318.             return strchr(curPath+1, ':') ? curPath : curPath+1;
  319.             
  320.         lastInfo.dirInfo.ioVRefNum     = vRefNum;
  321.         lastInfo.dirInfo.ioFDirIndex = -1;
  322.         lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  323.         
  324.         if (error = PBGetCatInfoSync(&lastInfo))
  325.             return "";
  326.         curPath    -=    *fullPath;
  327.         memmove(curPath, fullPath+1, *fullPath);
  328.    } while (lastInfo.dirInfo.ioDrDirID != fsRtDirID);
  329.  
  330.     return curPath;
  331. }    
  332.  
  333. OSErr    TFileSpec::CatInfo(CInfoPBRec & info, Boolean dirInfo) const
  334. {
  335.    info.dirInfo.ioVRefNum         = vRefNum;
  336.    info.dirInfo.ioDrDirID         = parID;
  337.    info.dirInfo.ioNamePtr         = name;
  338.    info.dirInfo.ioFDirIndex     = dirInfo ? -1 : 0;
  339. #ifdef OnceThisFieldIsDefined
  340.     info.dirInfo.ioACUser         = 0;
  341. #else
  342.     info.dirInfo.filler2         = 0;
  343. #endif
  344.         
  345.    return error = PBGetCatInfoSync(&info);
  346. }
  347.  
  348. TFileSpec TFileSpec::operator--()
  349. {
  350.       CatInfo(lastInfo, true);
  351.     
  352.     if (!error)
  353.         parID    = lastInfo.dirInfo.ioDrParID;
  354.     
  355.     return *this;
  356. }
  357.  
  358. TFileSpec TFileSpec::operator-=(int levels)
  359. {
  360.     while (levels-- > 0)    {
  361.         --*this;
  362.         if (this->Error())
  363.             break;
  364.     }
  365.     
  366.     return *this;
  367. }
  368.  
  369. TFileSpec TFileSpec::operator-(int levels) const
  370. {
  371.     TFileSpec    spec    =    *this;
  372.     
  373.     return spec -= levels;
  374. }
  375.  
  376. OSErr TFileSpec::Resolve(const CInfoPBRec & info)
  377. {
  378.     Boolean        isFolder;
  379.     Boolean        wasAlias;
  380.     
  381.     return error = 
  382.         (hasAlias && IsAlias(info)) ? 
  383.             ResolveAliasFile(this, true, &isFolder, &wasAlias) :
  384.             noErr;
  385. }
  386.  
  387. OSErr TFileSpec::Resolve(Boolean gently)
  388. {
  389.     CatInfo(lastInfo);
  390.     
  391.     if (error)
  392.         if (gently)
  393.             return error = noErr;
  394.         else
  395.             return error;
  396.     else
  397.         return Resolve(lastInfo);
  398. }
  399.  
  400. Boolean TFileSpec::Exists() const
  401. {
  402.     Boolean        res;
  403.     
  404.     res     = !CatInfo(lastInfo);
  405.     error    =    noErr;
  406.     
  407.     return res;
  408. }
  409.  
  410. Boolean TFileSpec::operator==(const TFileSpec & other) const
  411. {
  412.     return     vRefNum == other.vRefNum 
  413.         &&        parID == other.parID 
  414.         &&     EqualString(name, other.name, false, true);
  415. }
  416.  
  417. Boolean TFileSpec::operator!=(const TFileSpec & other) const
  418. {
  419.     return     vRefNum != other.vRefNum 
  420.         ||        parID != other.parID 
  421.         ||     !EqualString(name, other.name, false, true);
  422. }
  423.  
  424. Boolean TFileSpec::IsParentOf(const TFileSpec & other) const
  425. {
  426.     for (TFileSpec oth = other - 1; !oth.Error() && *this != oth; --oth);
  427.     
  428.     return !oth.Error();
  429. }
  430.  
  431. TFileSpec TFileSpec::operator+=(ConstStr31Param name)
  432. {
  433.     if (*name > 63)
  434.         return bdNamErr;
  435.         
  436.    if (CatInfo(lastInfo))
  437.         goto punt;
  438.     
  439.     // Resolve if an alias
  440.     
  441.     if (IsAlias(lastInfo))
  442.         if (Resolve(lastInfo) || CatInfo(lastInfo))
  443.             goto punt;
  444.     
  445.     if (IsFile(lastInfo)) {
  446.         error = bdNamErr;
  447.         
  448.         goto punt;
  449.     }
  450.     
  451.     parID    = lastInfo.dirInfo.ioDrDirID;
  452.  
  453.     memcpy(this->name, name, *name+1);
  454.  
  455. punt:    
  456.     return *this;
  457. }
  458.  
  459. TFileSpec TFileSpec::operator+(ConstStr31Param name) const
  460. {
  461.     TFileSpec    spec    =    *this;
  462.     
  463.     return spec += name;
  464. }
  465.  
  466. TFileSpec TFileSpec::operator+=(const char * name)
  467. {
  468.     int    len = strlen(name);
  469.     
  470.     if (len > 63)
  471.         return bdNamErr;
  472.         
  473.    if (CatInfo(lastInfo))
  474.         goto punt;
  475.     
  476.     // Resolve if an alias
  477.     
  478.     if (IsAlias(lastInfo))
  479.         if (Resolve(lastInfo) || CatInfo(lastInfo))
  480.             goto punt;
  481.     
  482.     if (IsFile(lastInfo)) {
  483.         error = bdNamErr;
  484.         
  485.         goto punt;
  486.     }
  487.     
  488.     parID    = lastInfo.dirInfo.ioDrDirID;
  489.  
  490.     memcpy(this->name+1, name, *this->name = len);
  491.  
  492. punt:    
  493.     return *this;
  494. }
  495.  
  496. TFileSpec TFileSpec::operator+(const char * name) const
  497. {
  498.     TFileSpec    spec    =    *this;
  499.     
  500.     return spec += name;
  501. }
  502.  
  503. TFileSpec TFileSpec::operator[](short index) const
  504. {
  505.     TFileSpec    spec     = *this;
  506.     
  507.    lastInfo.dirInfo.ioVRefNum     = spec.vRefNum;
  508.    lastInfo.dirInfo.ioDrDirID         = spec.parID;
  509.    lastInfo.dirInfo.ioNamePtr     = spec.name;
  510.    lastInfo.dirInfo.ioFDirIndex     = index;
  511.  
  512.    error = PBGetCatInfoSync(&lastInfo);
  513.     
  514.     return spec;
  515. }
  516.  
  517. TFileSpec::TFileSpec(const char * path, Boolean useAlias)
  518. {
  519.     int            pathLen    =     strlen(path);
  520.     StringPtr    name        =    (StringPtr) fullPath;
  521.     char *        nextPath;
  522.     
  523.     if (hasMakeFSSpec) {
  524.         DefaultDir();
  525.         
  526.         nextPath = (char *) memccpy((char *) name + 1, path, 0, 254);
  527.     
  528.         if (nextPath)
  529.             name[0] = nextPath - (char *) name - 2;
  530.         else
  531.             name[0] = 254;
  532.         
  533.         switch (error = FSMakeFSSpec(vRefNum, parID, name, this)) {
  534.         case fnfErr:
  535.             error = noErr;
  536.             
  537.             return;
  538.         case noErr:
  539.             if (!useAlias && hasAlias)
  540.                 Resolve();
  541.  
  542.             if (nextPath = strrchr(path, ':'))
  543.                 path = nextPath+1;
  544.  
  545.             nextPath = (char *) memccpy((char *) name + 1, path, 0, 254);
  546.     
  547.             if (nextPath)
  548.                 name[0] = nextPath - (char *) name - 2;
  549.             else
  550.                 name[0] = 254;
  551.         
  552.             if (EqualString(this->name, name, false, true))
  553.                 memcpy(this->name, name, *name+1);        
  554.             
  555.             return;
  556.         default:
  557.             break;
  558.         }
  559.     }
  560.     
  561.     if (path[0] == ':' || !(nextPath = strchr(path, ':'))) {
  562.         Default();
  563.         
  564.         if (*path == ':')
  565.             ++path;
  566.     } else {
  567.         ParamBlockRec    vol;
  568.         
  569.         if (nextPath - (char *) path > 62) {
  570.             error = bdNamErr;
  571.             
  572.             return;
  573.         }
  574.             
  575.         memcpy(name+1, (char *) path, *name = nextPath - (char *)  path + 1);
  576.         
  577.         vol.volumeParam.ioNamePtr    =    name;
  578.         vol.volumeParam.ioVolIndex    =    -1;
  579.         
  580.         if (error = PBGetVInfoSync(&vol))
  581.             return;
  582.         
  583.         vRefNum    =    vol.volumeParam.ioVRefNum;
  584.         parID        =    fsRtDirID;
  585.         
  586.         path = nextPath + 1;
  587.  
  588.         --*this;
  589.     }
  590.         
  591.     if (error)
  592.         return;
  593.     
  594.     while (*path) {
  595.         if (*path == ':')    {
  596.             --*this;
  597.             ++path;
  598.             
  599.             if (error)
  600.                 return;
  601.             else
  602.                 continue;
  603.         }
  604.         
  605.         if (nextPath = strchr(path, ':'))
  606.             *nextPath = 0;
  607.         
  608.         *this += path;
  609.  
  610.         if (error)
  611.             return;
  612.             
  613.         if (nextPath) {
  614.             *nextPath = ':';
  615.             path = nextPath + 1;
  616.         } else
  617.             break;
  618.     }
  619.  
  620.     if (!useAlias && hasAlias)
  621.         Resolve();
  622. }
  623.  
  624. /* Convert a FSSpec into a full pathname. */
  625. char * FSp2FullPath(const FSSpec * desc)
  626. {
  627.     TFileSpec    spec(*desc);
  628.     
  629.     return spec.FullPath();
  630. }
  631.  
  632. /* Convert a FSSpec into a relative pathname. */
  633. char * FSp2RelPath(const FSSpec * desc)
  634. {
  635.     TFileSpec    spec(*desc);
  636.     
  637.     return spec.RelPath();
  638. }
  639.  
  640. /* Convert a working directory & file name into a FSSpec. */
  641. OSErr WD2FSSpec(short wd, ConstStr31Param name, FSSpec * desc)
  642. {
  643.     TFileSpec    spec(wd, name);
  644.     
  645.     *desc = spec;
  646.     
  647.     return spec.Error();
  648. }
  649.  
  650. /* Convert a pathname into a file spec. */
  651. OSErr Path2FSSpec(const char * path, FSSpec * desc)
  652. {
  653.     TFileSpec    spec(path);
  654.     
  655.     *desc = spec;
  656.     
  657.     return spec.Error();
  658. }
  659.  
  660. /* Convert a working directory & file name into a FSSpec. */
  661. OSErr Special2FSSpec(OSType object, short vol, long dirID, FSSpec * desc)
  662. {
  663.     TFileSpec    spec(object, vol, dirID);
  664.     
  665.     *desc = spec;
  666.     
  667.     return spec.Error();
  668. }
  669.  
  670. /* Return FSSpec of (vRefNum, parID) */
  671. OSErr FSpUp(FSSpec * desc)
  672. {
  673.     TFileSpec    spec(*desc);
  674.  
  675.     *desc = --spec;
  676.     
  677.     return spec.Error();
  678. }
  679.  
  680. /* Return FSSpec of file in directory denoted by desc */
  681. OSErr FSpDown(FSSpec * desc, ConstStr31Param name)
  682. {
  683.     TFileSpec    spec(*desc);
  684.  
  685.     *desc    = spec + name;
  686.     
  687.     return spec.Error();
  688. }
  689.  
  690. /* Call GetCatInfo for file system object. */
  691. OSErr    FSpCatInfo(const FSSpec * desc, CInfoPBRec * info)
  692. {
  693.     TFileSpec    spec(*desc);
  694.     
  695.     return spec.CatInfo(*info);
  696. }
  697.  
  698. /* Return FSSpec of nth file in directory denoted by (vRefNum, parID) */
  699. OSErr FSpIndex(FSSpec * desc, short n)
  700. {
  701.     TFileSpec    spec(*desc);
  702.     
  703.     *desc = spec[n];
  704.     
  705.     return spec.Error();
  706. }
  707.  
  708. static OSErr DefaultVRef(short & vRef)
  709. {
  710.     OSErr                err;
  711.     ParamBlockRec    vol;
  712.     
  713.     vol.volumeParam.ioNamePtr = nil;
  714.     
  715.     if (err = PBGetVolSync(&vol))
  716.         return err;
  717.     
  718.     vRef = vol.volumeParam.ioVRefNum;
  719.     
  720.     return noErr;
  721. }
  722.  
  723. OSErr FSpSmartMove(const FSSpec * from, const FSSpec * to)
  724. {
  725.     OSErr            err;
  726.     TFileSpec    fromspec(*from, true);
  727.     TFileSpec    tospec(*to, true);
  728.     TFileSpec    toparent = tospec - 1;
  729.     TFileSpec    corner;
  730.     CInfoPBRec    fromInfo;
  731.     CInfoPBRec    toInfo;
  732.     
  733.     if (!fromspec.vRefNum)
  734.         if (err = DefaultVRef(fromspec.vRefNum))
  735.             return err;
  736.             
  737.     if (!tospec.vRefNum)
  738.         if (err = DefaultVRef(tospec.vRefNum))
  739.             return err;
  740.             
  741.     if (fromspec.vRefNum != tospec.vRefNum)
  742.         return badMovErr;
  743.         
  744.     Boolean        diffname    =    !EqualString(fromspec.name, tospec.name, false, true);
  745.     Boolean        diffdir    =    fromspec.parID != tospec.parID;
  746.     Boolean        toexists = !tospec.CatInfo(toInfo);
  747.     Boolean        lockfrom;
  748.     Boolean        lockto;
  749.     TFileSpec    tmpto;
  750.     
  751.     if (err = fromspec.CatInfo(fromInfo))
  752.         return err;
  753.     
  754.     if (!IsFile(fromInfo) && fromspec.IsParentOf(tospec))
  755.         return badMovErr;
  756.                 
  757.     if (lockfrom = IsFile(fromInfo) && fromInfo.hFileInfo.ioFlAttrib & 0x01)
  758.         HRstFLock(fromspec.vRefNum, fromspec.parID, fromspec.name);
  759.  
  760.     if (!diffname && !diffdir)    { /* Files are identical, except possibly for case */
  761.         err = noErr;
  762.         
  763.         goto cleanupcase;
  764.     }
  765.     
  766.     if (toexists)
  767.         if (!IsFile(toInfo) && toInfo.dirInfo.ioDrNmFls)
  768.             return fBsyErr;
  769.         else {
  770.             tmpto = TFileSpec(kTempFileType, tospec.vRefNum, tospec.parID);
  771.             
  772.             if (lockto = IsFile(toInfo) && toInfo.hFileInfo.ioFlAttrib & 0x01)
  773.                 HRstFLock(tospec.vRefNum, tospec.parID, tospec.name);
  774.                 
  775.             if (err = HRename(tospec.vRefNum, tospec.parID, tospec.name, tmpto.name))
  776.                 return err;
  777.         }
  778.         
  779.     if (!diffdir) {
  780.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  781.         
  782.         goto cleanuptmp;
  783.     } else if (!diffname) {
  784.         err = 
  785.             CatMove(
  786.                 fromspec.vRefNum, fromspec.parID, fromspec.name, 
  787.                 toparent.parID, toparent.name);
  788.         
  789.         goto cleanuptmp;
  790.     }
  791.     
  792.     corner = TFileSpec(fromspec.vRefNum, fromspec.parID, tospec.name);
  793.     
  794.     if (!corner.Exists()) {
  795.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  796.         
  797.         if (!err)
  798.             if (err = 
  799.                 CatMove(
  800.                     fromspec.vRefNum, fromspec.parID, tospec.name,
  801.                     toparent.parID, toparent.name)
  802.             )
  803.                 HRename(fromspec.vRefNum, fromspec.parID, tospec.name, fromspec.name);
  804.         
  805.         goto cleanuptmp;
  806.     }
  807.         
  808.     {
  809.         TFileSpec    secondcorner(kTempFileType, tospec.vRefNum, tospec.parID);
  810.         
  811.         memcpy(corner.name, secondcorner.name, secondcorner.name[0]+1);
  812.         
  813.         while (corner.Exists() || secondcorner.Exists()) {
  814.             TFileSpec newcorner(kTempFileType, secondcorner.vRefNum, secondcorner.parID);
  815.             
  816.             memcpy(corner.name, newcorner.name, newcorner.name[0]+1);
  817.             memcpy(secondcorner.name, newcorner.name, newcorner.name[0]+1);
  818.         }
  819.     }
  820.     
  821.     err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, corner.name);
  822.     
  823.     if (!err)
  824.         if (err = 
  825.             CatMove(
  826.                 fromspec.vRefNum, fromspec.parID, corner.name,
  827.                 toparent.parID, toparent.name)
  828.         ) 
  829.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);
  830.         else if (err =
  831.             HRename(tospec.vRefNum, tospec.parID, corner.name, tospec.name)
  832.         ) {
  833.             TFileSpec fromparent = fromspec - 1;
  834.             
  835.             CatMove(
  836.                 tospec.vRefNum, tospec.parID, corner.name,
  837.                 fromparent.parID, fromparent.name);
  838.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);            
  839.         }
  840.     
  841. cleanuptmp:
  842.     if (toexists)
  843.         if (err) {
  844.             HRename(tmpto.vRefNum, tmpto.parID, tmpto.name, tospec.name);
  845.             
  846.             if (lockto)
  847.                 HSetFLock(tospec.vRefNum, tospec.parID, tospec.name);
  848.         } else
  849.             HDelete(tmpto.vRefNum, tmpto.parID, tmpto.name);
  850.  
  851. cleanupcase:
  852.     if (!err && !diffname)
  853.         err = HRename(tospec.vRefNum, tospec.parID, tospec.name, tospec.name);
  854.     if (lockfrom)
  855.         if (err)
  856.             HSetFLock(fromspec.vRefNum, fromspec.parID, fromspec.name);
  857.         else
  858.             HSetFLock(tospec.vRefNum, tospec.parID, tospec.name);
  859.         
  860.     return err;
  861. }
  862.  
  863.