Microsoft-3D-Movie-Maker/kauai/SRC/FNIMAC.CPP

843 lines
20 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
File name management.
***************************************************************************/
#include "util.h"
#include <folders.h>
ASSERTNAME
// This is the FTG to use for temp files - clients may set this to whatever
// they want.
FTG vftgTemp = kftgTemp;
/***************************************************************************
Implementation notes: The _fss is a standard FSSpec record and is
always filled in via a call to FSMakeFSSpec. If the fni is a directory,
_ftg will be kftgDir. For files, _lwDir is the same as _fss.parID.
For directories, _lwDir is the directory id for the directory described
by the fni (while _fss.parID is the id of its parent).
***************************************************************************/
typedef StandardFileReply SFR;
priv bool _FFssDir(FSS *pfss, long *plwDir);
RTCLASS(FNI)
RTCLASS(FNE)
/***************************************************************************
Sets the fni to nil values.
***************************************************************************/
void FNI::SetNil(void)
{
_ftg = ftgNil;
_lwDir = 0;
ClearPb(&_fss, size(_fss));
AssertThis(ffniEmpty);
}
/***************************************************************************
Constructor for fni class.
***************************************************************************/
FNI::FNI(void)
{
SetNil();
}
/***************************************************************************
Get an fni (for opening) from the user.
***************************************************************************/
bool FNI::FGetOpen(FTG *prgftg, short cftg)
{
AssertThis(0);
AssertNilOrVarMem(prgftg);
SFR sfr;
StandardGetFile(pvNil, cftg <= 0 ? -1 : cftg, (ulong *)prgftg, &sfr);
if (sfr.sfGood)
{
_fss = sfr.sfFile;
_ftg = sfr.sfType;
_lwDir = _fss.parID;
AssertThis(ffniFile);
}
else
SetNil();
return sfr.sfGood;
}
/***************************************************************************
Get an fni (for saving) from the user.
***************************************************************************/
bool FNI::FGetSave(FTG ftg, PST pstPrompt, PST pstDefault)
{
AssertThis(0);
AssertNilOrSt(pstPrompt);
AssertNilOrSt(pstDefault);
SFR sfr;
StandardPutFile((byte *)pstPrompt, (byte *)pstDefault, &sfr);
if (sfr.sfGood)
{
_fss = sfr.sfFile;
_ftg = ftg;
_lwDir = _fss.parID;
AssertThis(ffniFile);
}
else
SetNil();
return sfr.sfGood;
}
/***************************************************************************
Get a unique filename in the given directory.
***************************************************************************/
bool FNI::FGetUnique(FTG ftg)
{
AssertThis(ffniFile | ffniDir);
static long _dlw = 0;
long lw;
short cact;
short err;
STN stn;
long lwDir = _lwDir;
short swVol = _fss.vRefNum;
Assert(ftg != kftgDir && ftg != ftgNil, "bad ftg");
lw = TsSystemCurrent() + ++_dlw;
for (cact = 0; cact < 20; cact++, lw += ++_dlw)
{
AssertDo(stn.FFormatSz(PszLit("~TempFile#%08x"), lw), 0);
err = FSMakeFSSpec(swVol, lwDir, &stn, &_fss);
if (err == fnfErr)
{
_ftg = ftg;
_lwDir = _fss.parID;
AssertThis(ffniFile);
return fTrue;
}
}
SetNil();
PushErc(ercFniGeneral);
return fFalse;
}
/***************************************************************************
Get a temporary fni.
***************************************************************************/
bool FNI::FGetTemp(void)
{
AssertThis(0);
static short _swVolTemp = 0;
static long _lwDirTemp = -1;
// This is so we only call FindFolder once.
if (_swVolTemp == 0 && _lwDirTemp == -1 &&
FindFolder(0, kTemporaryFolderType, kCreateFolder,
&_swVolTemp, &_lwDirTemp) != noErr)
{
_swVolTemp = 0;
_lwDirTemp = 0;
}
if (FSMakeFSSpec(_swVolTemp, _lwDirTemp, pvNil, &_fss) != noErr)
{
SetNil();
PushErc(ercFniGeneral);
return fFalse;
}
_ftg = kftgDir;
_lwDir = _lwDirTemp;
return FGetUnique(vftgTemp);
}
/***************************************************************************
Return the file type of the fni.
***************************************************************************/
FTG FNI::Ftg(void)
{
AssertThis(0);
return _ftg;
}
/***************************************************************************
Return the volume kind for the given fni.
***************************************************************************/
ulong FNI::Grfvk(void)
{
AssertThis(0);
ulong grfvk = fvkNil;
//REVIEW shonk: Mac Grfvk: implement
RawRtn();
return grfvk;
}
/***************************************************************************
Set the leaf for the fni.
***************************************************************************/
bool FNI::FSetLeaf(PSTZ pstz, FTG ftg)
{
AssertThis(ffniFile | ffniDir);
return FBuild(_fss.vRefNum, _lwDir, pstz, ftg);
}
/***************************************************************************
Set the leaf for the fni.
***************************************************************************/
bool FNI::FBuild(long lwVol, long lwDir, PSTZ pstz, FTG ftg)
{
AssertNilOrStz(pstz);
short err;
FSS fss;
Assert(FPure(ftg == kftgDir) == FPure(pstz == pvNil || CchStz(pstz) == 0),
"pstz doesn't match ftg");
if (ftg == kftgDir)
pstz = pvNil;
err = FSMakeFSSpec((short)lwVol, lwDir, (byte *)pstz, &fss);
if (ftg == kftgDir)
{
//a directory (it had better exist)
if (noErr != err)
goto LFail;
_lwDir = lwDir;
}
else
{
//not supposed to be a directory - so make sure it isn't
if (fnfErr != err)
{
if (noErr != err)
goto LFail;
if (_FFssDir(&fss, pvNil))
goto LFail;
}
_lwDir = fss.parID;
}
_fss = fss;
_ftg = ftg;
AssertThis(ffniFile | ffniDir);
return fTrue;
LFail:
SetNil();
PushErc(ercFniGeneral);
return fFalse;
}
/***************************************************************************
Set the FTG for the fni.
***************************************************************************/
bool FNI::FChangeFtg(FTG ftg)
{
AssertThis(ffniFile);
Assert(ftg != ftgNil && ftg != kftgDir, "Bad FTG");
_ftg = ftg;
return fTrue;
}
/***************************************************************************
Get the leaf name for the fni.
***************************************************************************/
void FNI::GetLeaf(PSTZ pstz)
{
AssertThis(0);
AssertMaxStz(pstz);
if (_ftg == kftgDir)
SetStzNil(pstz);
else
CopyStStz((achar *)_fss.name, pstz);
}
/***************************************************************************
Get a string representing the path of the fni.
***************************************************************************/
void FNI::GetStzPath(PSTZ pstz)
{
AssertThis(0);
AssertMaxStz(pstz);
RawRtn(); //REVIEW shonk: Mac GetStzPath: implement for real
CopyStStz((achar *)_fss.name, pstz);
}
/***************************************************************************
Determines if the file/directory exists. Returns tMaybe on error or
if the fni type (file or dir) doesn't match the disk object of the
same name or if the file/dir is invisible or is an alias. Pushes an
erc if it returns tMaybe.
***************************************************************************/
bool FNI::TExists(void)
{
AssertThis(ffniFile | ffniDir);
CInfoPBRec iob;
short err;
achar st[kcbMaxSt];
ClearPb(&iob, size(iob));
CopySt((achar *)_fss.name, st);
iob.hFileInfo.ioNamePtr = (byte *)st;
iob.hFileInfo.ioVRefNum = _fss.vRefNum;
iob.hFileInfo.ioDirID = _fss.parID;
if ((err = PBGetCatInfo(&iob, fFalse)) != noErr)
{
if (err != fnfErr)
{
PushErc(ercFniGeneral);
return tMaybe;
}
return tNo;
}
if ((_ftg == kftgDir) != FPure(iob.hFileInfo.ioFlAttrib & 0x0010))
{
PushErc(ercFniMismatch);
return tMaybe;
}
if (iob.hFileInfo.ioFlFndrInfo.fdFlags & (kIsInvisible | kIsAlias))
{
PushErc(ercFniHidden);
return tMaybe;
}
return tYes;
}
/***************************************************************************
Delete the file.
***************************************************************************/
bool FNI::FDelete(void)
{
AssertThis(ffniFile);
if (FSpDelete(&_fss) == noErr)
return fTrue;
PushErc(ercFniDelete);
return fFalse;
}
/***************************************************************************
Rename the file as indicated by *pfni. The directories must match.
***************************************************************************/
bool FNI::FRename(FNI *pfni)
{
AssertThis(ffniFile);
AssertPo(pfni, ffniFile);
Assert(_fss.vRefNum == pfni->_fss.vRefNum &&
_fss.parID == pfni->_fss.parID, "directory change");
Assert(_ftg == pfni->_ftg, "ftg's don't match");
if (FSpRename(&_fss, pfni->_fss.name) == noErr)
return fTrue;
PushErc(ercFniRename);
return fFalse;
}
/***************************************************************************
Compare two fni's for equality. Doesn't consider the ftg's.
***************************************************************************/
bool FNI::FEqual(FNI *pfni)
{
//NOTE: see IM:Text, pg 5-17. It's not documented whether the comparison
//should be case sensitive and/or diacritical sensitive. Experimenting
//with the US finder indicates that we should use case insensitive,
//diacritical sensitive comparison.
AssertThis(ffniFile | ffniDir);
AssertPo(pfni, ffniFile | ffniDir);
return pfni->_fss.vRefNum == _fss.vRefNum && pfni->_fss.parID == _fss.parID &&
EqualString(pfni->_fss.name, _fss.name, fFalse, fTrue);
}
/***************************************************************************
Return whether the fni refers to a directory.
***************************************************************************/
bool FNI::FDir(void)
{
AssertThis(0);
return _ftg == kftgDir;
}
/***************************************************************************
Return whether the directory portions of the fni's are the same.
***************************************************************************/
bool FNI::FSameDir(FNI *pfni)
{
AssertThis(ffniDir | ffniFile);
AssertPo(pfni, ffniDir | ffniFile);
return pfni->_fss.vRefNum == _fss.vRefNum &&
pfni->_lwDir == _lwDir;
}
/***************************************************************************
Determine if the directory pstz in fni exists, optionally creating it
and/or moving into it. Specify ffniCreateDir to create it if it
doesn't exist. Specify ffniMoveTo to make the fni refer to it.
If this fails, the fni is untouched.
***************************************************************************/
bool FNI::FDownDir(PSTZ pstz, ulong grffni)
{
AssertThis(ffniDir);
AssertStz(pstz);
FSS fss;
long lwDir;
short err;
//make the fss
err = FSMakeFSSpec(_fss.vRefNum, _lwDir, (byte *)pstz, &fss);
if (noErr == err)
{
//exists, make sure it is a directory and get the directory id
if (!_FFssDir(&fss, &lwDir))
{
PushErc(ercFniMismatch);
return fFalse;
}
}
else
{
if (fnfErr != err)
{
PushErc(ercFniGeneral);
return fFalse;
}
//doesn't exist
if (!(grffni & ffniCreateDir))
return fFalse;
// create it
err = FSpDirCreate(&fss, smSystemScript, &lwDir);
if (err != noErr)
{
PushErc(ercFniDirCreate);
return fFalse;
}
}
if (grffni & ffniMoveToDir)
{
_fss = fss;
_lwDir = lwDir;
_ftg = kftgDir;
}
return fTrue;
}
/***************************************************************************
Gets the lowest directory name (if pstz is not nil) and optionally
moves the fni up a level (if ffniMoveToDir is specified). If this
fails, the fni is untouched.
***************************************************************************/
bool FNI::FUpDir(PSTZ pstz, ulong grffni)
{
AssertThis(ffniDir);
AssertNilOrMaxStz(pstz);
CInfoPBRec iob;
short err;
FSS fss;
if (_lwDir == fsRtDirID)
return fFalse;
ClearPb(&iob, size(iob));
iob.dirInfo.ioFDirIndex = -1; //ignore name, look at vol/dir
iob.dirInfo.ioNamePtr = (byte *)pstz;
iob.dirInfo.ioVRefNum = _fss.vRefNum;
iob.dirInfo.ioDrDirID = _lwDir;
if (PBGetCatInfo(&iob, fFalse) != noErr)
{
PushErc(ercFniGeneral);
return fFalse;
}
if (pstz != pvNil)
SetStzCch(pstz, CchSt(pstz));
Assert(iob.dirInfo.ioFlAttrib & 0x0010, "not a directory?!");
if (grffni & ffniMoveToDir)
{
err = FSMakeFSSpec(_fss.vRefNum, iob.dirInfo.ioDrParID, pvNil, &fss);
if (noErr != err)
{
PushErc(ercFniGeneral);
return fFalse;
}
_fss = fss;
_lwDir = iob.dirInfo.ioDrParID;
_ftg = kftgDir;
AssertThis(ffniDir);
}
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Assert validity of the FNI.
***************************************************************************/
void FNI::AssertValid(ulong grffni)
{
FNI_PAR::AssertValid(0);
if (grffni == 0)
grffni = ffniEmpty | ffniFile | ffniDir;
if (_ftg == ftgNil)
{
Assert(grffni & ffniEmpty, "unexpected nil fni");
Assert(_fss.vRefNum == 0 && _fss.parID == 0 && _lwDir == 0 &&
CchSt((achar *)_fss.name) == 0, "bad nil");
}
else if (_ftg == kftgDir)
{
Assert(grffni & ffniDir, "unexpected dir");
Assert(_lwDir != _fss.parID, "dir is its own parent?");
}
else
{
Assert(grffni & ffniFile, "unexpected file");
Assert(CchSt((achar *)_fss.name) > 0, "no name for file");
Assert(_lwDir == _fss.parID, "parent not consistent?");
}
}
#endif //DEBUG
/***************************************************************************
Low level routine to determine if the given fss points to an existing
directory. If so and plwDir is not nil, sets *plwDir to the id of
the directory.
***************************************************************************/
priv bool _FFssDir(FSS *pfss, long *plwDir)
{
CInfoPBRec iob;
//find the entry
ClearPb(&iob, size(iob));
iob.hFileInfo.ioNamePtr = (StringPtr)pfss->name;
iob.hFileInfo.ioVRefNum = pfss->vRefNum;
iob.hFileInfo.ioDirID = pfss->parID;
if (PBGetCatInfo(&iob, fFalse) != noErr)
{
PushErc(ercFniGeneral);
goto LFail;
}
//entry exists so see if it's a directory
if (!(iob.hFileInfo.ioFlAttrib & 0x0010))
{
LFail:
TrashVar(plwDir);
return fFalse;
}
if (plwDir != pvNil)
*plwDir = iob.dirInfo.ioDrDirID;
return fTrue;
}
/***************************************************************************
Constructor for a File Name Enumerator.
***************************************************************************/
FNE::FNE(void)
{
AssertBaseThis(0);
_prgftg = _rgftg;
_pglfes = pvNil;
_fInited = fFalse;
AssertThis(0);
}
/***************************************************************************
Destructor for an FNE.
***************************************************************************/
FNE::~FNE(void)
{
AssertBaseThis(0);
_Free();
}
/***************************************************************************
Free all the memory associated with the FNE.
***************************************************************************/
void FNE::_Free(void)
{
if (_prgftg != _rgftg)
{
FreePpv((void **)&_prgftg);
_prgftg = _rgftg;
}
ReleasePpo(&_pglfes);
_fInited = fFalse;
AssertThis(0);
}
/***************************************************************************
Initialize the fne to do an enumeration.
***************************************************************************/
bool FNE::FInit(FNI *pfniDir, FTG *prgftg, long cftg, ulong grffne)
{
AssertThis(0);
AssertNilOrVarMem(pfniDir);
AssertIn(cftg, 0, kcbMax);
AssertPvCb(prgftg, LwMul(cftg, size(FTG)));
//free the old stuff
_Free();
if (0 >= cftg)
_cftg = 0;
else
{
long cb = LwMul(cftg, size(FTG));
if (cftg > kcftgFneBase &&
!FAllocPv((void **)&_prgftg, cb, fmemNil, mprNormal))
{
_prgftg = _rgftg;
PushErc(ercFneGeneral);
AssertThis(0);
return fFalse;
}
CopyPb(prgftg, _prgftg, cb);
_cftg = cftg;
}
if (pfniDir == pvNil)
_fesCur.lwVol = 0;
else
{
_fesCur.lwVol = (long)pfniDir->_fss.vRefNum;
_fesCur.lwDir = pfniDir->_lwDir;
}
_fesCur.iv = 0;
_fRecurse = FPure(grffne & ffneRecurse);
_fInited = fTrue;
AssertThis(0);
return fTrue;
}
/***************************************************************************
Get the next FNI in the enumeration.
***************************************************************************/
bool FNE::FNextFni(FNI *pfni, ulong *pgrffneOut, ulong grffneIn)
{
AssertThis(0);
AssertVarMem(pfni);
AssertNilOrVarMem(pgrffneOut);
short err;
if (!_fInited)
{
Bug("must initialize the FNE before using it!");
return fFalse;
}
if (grffneIn & ffneSkipDir)
{
//skip the rest of the stuff in this dir
if (pvNil == _pglfes || !_pglfes->FPop(&_fesCur))
goto LDone;
}
if (_fesCur.lwVol == 0)
{
//volume
ParamBlockRec iob;
do
{
ClearPb(&iob, size(iob));
iob.volumeParam.ioVolIndex = (short)++_fesCur.iv;
if ((err = PBGetVInfoSync(&iob)) != noErr)
{
if (err != nsvErr)
PushErc(ercFneGeneral);
goto LDone;
}
}
while (!pfni->FBuild(iob.volumeParam.ioVRefNum, 0, pvNil, kftgDir));
//we've got one
goto LGotOne;
}
// directory or file
for (;;)
{
int ich;
bool fT;
FTG *pftg;
PSZ pszExt;
CInfoPBRec iob;
achar stz[kcbMaxStz];
ClearPb(&iob, size(iob));
iob.hFileInfo.ioNamePtr = (byte *)stz;
iob.hFileInfo.ioVRefNum = (short)_fesCur.lwVol;
iob.hFileInfo.ioDirID = _fesCur.lwDir;
iob.hFileInfo.ioFDirIndex = (short)++_fesCur.iv;
if ((err = PBGetCatInfoSync(&iob)) != noErr)
{
if (err != fnfErr)
PushErc(ercFneGeneral);
goto LPop;
}
if (iob.hFileInfo.ioFlFndrInfo.fdFlags &
(kIsInvisible | kNameLocked | kIsAlias))
{
continue;
}
if (iob.hFileInfo.ioFlAttrib & 0x0010)
fT = pfni->FBuild(_fesCur.lwVol, iob.hFileInfo.ioDirID, pvNil, kftgDir);
else
{
StToStz(stz);
fT = pfni->FBuild(_fesCur.lwVol, _fesCur.lwDir, stz,
iob.hFileInfo.ioFlFndrInfo.fdType);
}
if (!fT)
continue;
if (_cftg == 0)
goto LGotOne;
for (pftg = _prgftg + _cftg; pftg-- > _prgftg; )
{
if (*pftg == pfni->_ftg)
goto LGotOne;
// see if the file has a dos-like extension matching the ftg
if (pfni->_ftg != kftgDir && (pszExt = (achar *) pftg)[3] == 0 &&
(ich = CchStz(stz) - CchSz(pszExt)) > 0 &&
PszStz(stz)[ich - 1] == '.' &&
fcmpEq == FcmpCompareInsSz(PszStz(stz) + ich, pszExt))
{
goto LGotOne;
}
}
}
Bug("How did we fall through to here?");
LPop:
if (_pglfes == pvNil || _pglfes->IvMac() == 0)
goto LDone;
//we're about to pop a directory, so send the current directory back
//with ffnePost
if (pvNil != pgrffneOut)
*pgrffneOut = ffnePost;
if (!pfni->FBuild(_fesCur.lwVol, _fesCur.lwDir, pvNil, kftgDir))
{
PushErc(ercFneGeneral);
LDone:
_Free();
AssertThis(0);
return fFalse;
}
Assert(_pglfes->FPop(&_fesCur), 0);
AssertPo(pfni, ffniDir);
AssertThis(0);
return fTrue;
LGotOne:
AssertPo(pfni, ffniFile | ffniDir);
if (pvNil != pgrffneOut)
*pgrffneOut = ffnePre | ffnePost;
if (_fRecurse && pfni->_ftg == kftgDir)
{
if ((pvNil != _pglfes || pvNil != (_pglfes = GL::PglNew(size(FES), 5))) &&
_pglfes->FPush(&_fesCur))
{
//set up the new fes
_fesCur.lwVol = pfni->_fss.vRefNum;
_fesCur.lwDir = pfni->_lwDir;
_fesCur.iv = 0;
if (pvNil != pgrffneOut)
*pgrffneOut = ffnePre;
}
else
PushErc(ercFneGeneral);
}
AssertThis(0);
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a FNE.
***************************************************************************/
void FNE::AssertValid(ulong grf)
{
FNE_PAR::AssertValid(0);
if (_fInited)
{
AssertNilOrPo(_pglfes, 0);
AssertIn(_cftg, 0, kcbMax);
AssertPvCb(_prgftg, LwMul(size(FTG), _cftg));
Assert((_cftg > kcftgFneBase) == (_prgftg == _rgftg),
"wrong _prgftg");
}
else
Assert(_pglfes == pvNil, 0);
}
/***************************************************************************
Mark memory used by the FNE.
***************************************************************************/
void FNE::MarkMem(void)
{
AssertValid(0);
FNE_PAR::MarkMem();
if (_prgftg != _rgftg)
MarkPv(_prgftg);
MarkMemObj(_pglfes);
}
#endif //DEBUG