|
|
/*****************************************************************************
* * * FM.c * * * * Copyright (C) Microsoft Corporation 1990 - 1994. * * All Rights reserved. * * * ****************************************************************************** * * * Module Intent * * * * Routines for manipulating FMs (File Monikers, equivalent to file names). * * WINDOWS LAYER * * ****************************************************************************** * * * Current Owner: davej * * * *****************************************************************************/
/*****************************************************************************
* * Revision History: * -- Mar 92 adapted from WinHelps FM.C * 26-Jun-1992 RussPJ #293 - Now using OpenFile(OF_EXIST) instead of * access(). * 29-Jun-1992 RussPJ #723 - Using OF_SHARE_DENY_NONE for OpenFile() call. * 18/10/94 a-kevct Fixed FmNewTemp to use "higher temperature" filenames * so that problems with calling it twice in rapid succession * are avoided. * 7/28/95 FM changed to just an LPSTR !!! - davej * 3/05/97 erinfox Change errors to HRESULTS * *****************************************************************************/
#include <mvopsys.h>
#include <stdlib.h> /* for _MAX_ constants & min and max macros*/
#include <dos.h> /* for FP_OFF macros and file attribute constants */
#include <io.h> /* for tell() and eof() */
#include <direct.h> /* for getcwd */
#include <string.h>
#include <misc.h>
#include <iterror.h>
#include <wrapstor.h>
#include <orkin.h>
#ifndef _NT
#include <errno.h>
#endif
#include <_mvutil.h>
static char s_aszModule[] = __FILE__; /* For error report */
/*****************************************************************************
* * * Defines * * * *****************************************************************************/ #define cbPathName _MAX_PATH
#define cbMessage 50
/*****************************************************************************
* * * Variables * * * *****************************************************************************/
void InitRandomPrefix(char *sz, int cb);
PRIVATE BOOL PASCAL NEAR fIsTrailByte(SZ psz,SZ pch);
// InitRandomPrefix
//
// Given a buffer at least cb + 1 chars long,
// fills sz[0] through sz[ch-1] with random alphabetic chars
// and null terminates at sz[cb].
//
//
#ifdef _IT_FULL_CRT
void InitRandomPrefix(char *sz, int cb) { while (--cb >= 0) *sz++ = 'A' + rand() % 26; *sz = '\0'; } #endif
/***************************************************************************
* - Name: SnoopPath() - * Purpose: * Looks through a string for the various components of a file name and * returns the offsets into the string where each part starts. * * Arguments: * sz - string to snoop * *iDrive - offset for the drive specification if present * *iDir - offset for the directory path if present * *iBase - offset to the filename if present * *iExt - offset to the extension (including dot) if present * * Returns: * sets the index parameters for each respective part. the index is set * to point to the end of the string if the part is not present (thus * making it point to a null string). * * * +++ * * Notes: * ***************************************************************************/ void FAR PASCAL SnoopPath (LPSTR sz, int far * iDrive, int far *iDir, int far *iBase, int far *iExt) {
short i = 0; short cb = (short) STRLEN(sz); BOOL fDir = FALSE;
*iDrive = *iExt = cb; *iDir = *iBase = 0;
while (*(sz + i)) { switch (*(sz + i)) { case ':': *iDrive = 0; *iDir = i + 1; *iBase = i + 1; break;
case '/': case '\\': // make sure we are not looking at a trail byte
if(fIsTrailByte(sz,sz+i)) break;
fDir = TRUE; *iBase = i + 1; *iExt = cb; break;
case '.': *iExt = i; break;
default: break;
} i++; }
if (!fDir) *iDir = i; else if (*iBase == '.') *iExt = cb; }
/***************************************************************************\
* - Function: fIsTrailByte(psz, pch) - * Purpose: Determine if pch is the trail byte of a DBC. * * ASSUMES * * args IN: psz - the beginning of the string * pch - the character to test * * Notes: This function is necessary because the Shift-JIS trail byte * ranges include the value 0x5C (backslash) and we need to * know the difference between a real backslash and a DBC that * has this trail byte value. * \***************************************************************************/
PRIVATE BOOL PASCAL NEAR fIsTrailByte(SZ psz,SZ pch) { #if JRUSH
return (IsDBCSLeadByte (*(pch - 1))); #else
SZ pchTemp = psz; if(pch==psz || pch<psz) return FALSE;
while(1) { pchTemp = CharNext(pchTemp);
if(pchTemp==pch) return FALSE; // it's a lead byte
if(pchTemp>pch) return TRUE; // we stepped past it - must have been a trail byte
} #endif
}
/***************************************************************************
* - Name: SzGetDir - * Purpose: returns the rooted path of a DIR * * Arguments: dir - DIR (must be one field only, and must be an actual dir - * not dirPath) * sz - buffer for storage (should be at least cchMaxPath) * * Returns: sz - fine * NULL - OS Error (check rcIOError) * * Globals Used: rcIOError * * +++ * * ***************************************************************************/
// Hey! what about dirTemp? This wasn't handled before so I'm not going
// to add it. But someday the case should be handled.
PRIVATE SZ PASCAL NEAR SzGetDir(DIR dir, SZ sz, PHRESULT phr) { int i=0; QCH qch; char LocalBuffer1[_MAX_PATH];
if (sz == NULL) { SetErrCode (phr, E_INVALIDARG); return(NULL); };
switch (dir) { case dirCurrent: if (GETCWD(LocalBuffer1, cchMaxPath) == NULL) { SetErrCode (phr, RcGetDOSError()); sz = NULL; } else { STRCPY (sz, LocalBuffer1); } break;
case dirHelp: #ifdef _WIN64
GetModuleFileName((HINSTANCE)GetWindowLongPtr(GetActiveWindow(), GWLP_HINSTANCE), #elif _32BIT
GetModuleFileName((HINSTANCE)GetWindowLong(GetActiveWindow(), GWL_HINSTANCE), #else
GetModuleFileName((HINSTANCE)GetWindowWord(GetActiveWindow(), GWW_HINSTANCE), #endif
sz, cchMaxPath); qch = sz + STRLEN(sz); while ((*qch != '\\' && !fIsTrailByte(sz,qch)) && *qch != '/') --qch; *qch = '\0'; break; /* dirHelp */
case dirSystem: i = GetWindowsDirectory((LPSTR)sz, cchMaxPath); if (i > cchMaxPath || i == 0) { SetErrCode (phr, E_FAIL); sz = NULL; } break; /* dirSystem */
default: SetErrCode (phr, E_INVALIDARG); sz = NULL; break; }
if (sz != NULL) { qch = sz+STRLEN(sz);
/*------------------------------------------------------------*
| Make sure that the string ends with a slash. *------------------------------------------------------------*/ if ((*(qch-1) != '\\' && *(qch-1) != '/') || fIsTrailByte(sz,qch-1)) { *qch++='\\'; *qch='\0'; } assert(qch < sz+_MAX_PATH && *qch=='\0'); }
return sz; }
/***************************************************************************
* - Name: FmNew - * Purpose: Allocate and initialize a new FM * * Arguments: sz - filename string * * Returns: FM (handle to fully canonicalized filename) * * Globals Used: rcIOError * * +++ * * Notes: * ***************************************************************************/
FM EXPORT_API PASCAL FAR FmNew(LPSTR sz, PHRESULT phr) { QAFM qafm; FM fm = fmNil; char LocalBuffer1[_MAX_PATH]; char LocalBuffer2[_MAX_PATH]; #ifndef _IT_FULL_CRT
LPSTR lpAddr; #endif
STRCPY(LocalBuffer2, sz); #ifdef _IT_FULL_CRT
if (_fullpath(LocalBuffer1, LocalBuffer2, _MAX_PATH) == NULL) #else
if (0 == GetFullPathName(LocalBuffer2, _MAX_PATH, LocalBuffer1, &lpAddr)) #endif
{ SetErrCode (phr, E_FAIL); return(NULL); } else { fm = (FM) NewMemory( (WORD)(lstrlen(LocalBuffer1)+1) ); //fm = (FM) _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE|GMEM_MOVEABLE,
// (LONG)lstrlen(LocalBuffer1)+1);
if (fm == fmNil) { SetErrCode (phr, E_OUTOFMEMORY); return fm; } qafm = (QAFM)fm; //qafm = (QAFM) _GLOBALLOCK(fm);
STRCPY(qafm->rgch, LocalBuffer1); // Convert to upper case to make it less likely that two
// FMs will contain different strings yet refer to the
// same file.
AnsiUpper(qafm->rgch); //_GLOBALUNLOCK((HANDLE)fm);
}
return fm; }
/***************************************************************************
* - Name: FmNewSzDir - * Purpose: Create an FM describing the file "sz" in the directory "dir" * If sz is a simple filename the FM locates the file in the * directory specified. If there is a drive or a rooted path * in the filename the directory parameter is ignored. * Relative paths are allowed and will reference off the dir * parameter or the default (current) directory as appropriate. * * This does not create a file or expect one to exist at the * final destination (that's what FmNewExistSzDir is for), all * wind up with is a cookie to a fully qualified path name. * * Arguments: sz - filename ("File.ext"), * or partial pathname ("Dir\File.ext"), * or current directory ("c:File.ext"), * or full pathname ("C:\Dir\Dir\File.ext") * dir - dirCurrent et al. * * Returns: the new FM, or fmNil if error * sz is unchanged * * Globals Used: * * +++ * * Notes: * ***************************************************************************/ PUBLIC FM PASCAL FAR FmNewSzDir(LPSTR sz, DIR dir, PHRESULT phr) { char nsz[_MAX_PATH]; int iDrive, iDir, iBase, iExt; int cb;
if (sz == NULL || *sz == '\0') { SetErrCode (phr, E_INVALIDARG); return NULL; }
cb = (int) STRLEN(sz); SnoopPath(sz, &iDrive, &iDir, &iBase, &iExt);
if (*(sz + iBase) == '\0') /* no name */ { *nsz = '\0'; /* force error */ } else if (*(sz + iDrive) || (*(sz + iDir) == '\\' && !fIsTrailByte(sz,sz+iDir)) || *(sz + iDir) == '/') { /* there's a drive or root slash so we have an implicit directory spec */ /* and we can ignore the directory parameter and use what was passed. */ STRCPY(nsz, sz); }
else {
/* dir & (dir-1) checks to make sure there is only one bit set which is */ /* followed by a check to make sure that it is also a permitted bit */ assert(((dir & (dir-1)) == (WORD)0) && (dir & (dirCurrent|dirTemp|dirHelp|dirSystem|dirPath)));
if (SzGetDir(dir, nsz, phr) == NULL) return NULL;
SzNzCat(nsz, sz + iDir, (WORD)max(1, iBase - iDir)); SzNzCat(nsz, sz + iBase, (WORD)max(1, iExt - iBase)); STRCAT(nsz, sz + iExt); }
/* We've got all the parameters, now make the FM */ return FmNew(nsz, phr); }
FM PASCAL FmNewExistSzDir(LPSTR sz, DIR dir, PHRESULT phr) { char nsz[_MAX_PATH]; FM fm = fmNil; OFSTRUCT of; char szANSI[_MAX_PATH]; int iDrive, iDir, iBase, iExt; int cb; DIR idir, xdir;
if (sz == NULL || *sz == '\0') { SetErrCode (phr, E_INVALIDARG); return NULL; }
cb = (int) STRLEN(sz); SnoopPath(sz, &iDrive, &iDir, &iBase, &iExt);
if (*(sz + iBase) == '\0') /* no name */ { SetErrCode (phr, E_INVALIDARG); return NULL; } if (*(sz + iDrive) || (*(sz + iDir) == '\\' && !fIsTrailByte(sz,sz+iDir)) || *(sz + iDir) == '/') { /* was given a drive or rooted path, so ignore dir parameter */ fm = FmNew(sz, phr); if (!FExistFm(fm)) { DisposeFm(fm); SetErrCode (phr, E_NOTEXIST); fm = fmNil; } return fm; }
for (idir = dirFirst, fm = fmNil; idir <= dirLast && fm==fmNil; idir <<= 1) { xdir = dir & idir; if (xdir == dirPath) { /* search $PATH using the full string which will catch
the case of a relative path and also do the right thing searching $PATH */ if (OpenFile(sz, (LPOFSTRUCT)&of, OF_EXIST | OF_SHARE_DENY_NONE) != (HFILE)-1) { OemToAnsi(of.szPathName, szANSI); fm = FmNew(szANSI, phr); } } else if (xdir) { if (SzGetDir(xdir, nsz, phr) != NULL) { SzNzCat(nsz, sz + iDir, (WORD)max(1, iBase - iDir)); SzNzCat(nsz, sz + iBase, (WORD)max(1, iExt - iBase)); STRCAT(nsz, sz + iExt); fm = FmNew(nsz, phr); if (!FValidFm(fm)) { SetErrCode (phr, E_FAIL); } else if (!FExistFm(fm)) { DisposeFm(fm); fm=fmNil; } } } } /* for */ if ((!FValidFm(fm))) SetErrCode (phr, E_NOTEXIST);
return fm; }
/***************************************************************************
* - Name: FmNewTemp - * * Purpose: Create a unique FM for a temporary file * * Arguments: LPSTR Filename: filename's template * * Returns: the new FM, or fmNil if failure * * Globals Used: rcIOError * * +++ * * Notes: * ***************************************************************************/ FM PASCAL FmNewTemp(LPSTR filename, PHRESULT phr) { char nsz[_MAX_PATH]; FM fm = fmNil; char template[5]; int i;
if (filename == NULL || *filename == 0) { // WARNING: we MUST generate our own random
// prefix here since GetTempFileName does NOT
// always return a unique name when called twice
// in very rapid succession.
#ifndef _IT_FULL_CRT
/* InitRandomPrefix calls rand, which pulls in the c run-time
* startup code. Since this is never called we remove the * functionality */ if(phr) { ITASSERT(FALSE); SetErrCode(phr, E_NOTSUPPORTED); } return fmNil; #else
char szPrefix[4]; InitRandomPrefix(szPrefix, 3); GETTEMPFILENAME(0, szPrefix, 0, nsz); #endif
} else { for (i = 0; *filename && i < 5; filename++) { if ((*filename | 0x20) >= 'a' && (*filename | 0x20) <= 'z') { template[i] = *filename; i++; } } template[i] = 0; GETTEMPFILENAME(0, template, 0, nsz); } fm = FmNew(nsz, phr);
if (fm && RcUnlinkFm(fm) != S_OK) { DisposeFm(fm); SetErrCode (phr, E_FAIL); return fmNil; }
return fm; }
/***************************************************************************
* - Name: FmNewSameDirFmSz - * Purpose: Makes a new FM to a file called sz in the same directory * as the file described by fm. * * Arguments: fm - original fm * sz - new file name (including extention, if desired) * * Returns: new FM or fmNil and sets the rc global on failure * * Globals Used: * rcIOError * * +++ * * Notes: * This will ignore the passed FM if the filename is fully qualified. * This is in keeping consistent with the other functions above that * ignore the directory parameter in such a case. It will fail if it * is given a drive with anything but a rooted path. * ***************************************************************************/ FM PASCAL FmNewSameDirFmSz(FM fm, LPSTR szName, PHRESULT phr) { char nszNew[_MAX_PATH]; QAFM qafm; FM fmNew = fmNil; int iDrive, iDir, iBase, iExt;
if (!FValidFm(fm) || szName == NULL || *szName == '\0') { SetErrCode (phr, E_INVALIDARG); return fmNil; }
qafm = (QAFM)_GLOBALLOCK((HANDLE)fm);
/* check for a drive or rooted file name and just return it if it is so */ SnoopPath(szName, &iDrive, &iDir, &iBase, &iExt);
if (*(szName + iDrive) || (*(szName + iDir) == '\\' && !fIsTrailByte(szName,szName+iDir)) || *(szName +iDir) == '/') STRCPY(nszNew, szName); else { if (*(szName + iDrive) != '\0') { fmNew = fmNil; goto bail_out; } else { SnoopPath(qafm->rgch, &iDrive, &iDir, &iBase, &iExt); STRNCPY(nszNew, qafm->rgch, iBase); *(nszNew + iBase) = '\0'; STRCAT(nszNew, szName); } }
fmNew = FmNew((SZ)nszNew, phr);
bail_out:
_GLOBALUNLOCK((HANDLE)fm);
return fmNew; }
/***************************************************************************
* - DisposeFm - * Purpose * You must call this routine to free the memory used by an FM, which * was created by one of the FmNew* routines * * Arguments * fm - original FM * * Returns * nothing * * Globals Used: * * +++ * * Notes: * ***************************************************************************/ VOID PASCAL DisposeFm (FM fm) { if (FValidFm(fm)) DisposeMemory(fm); //_GLOBALFREE ((HANDLE)fm);
}
/***************************************************************************
* - Name: FmCopyFm - * Purpose: return an FM to the same file as the passed one * * Arguments: fm * * Returns: FM - for now, it's a real copy. Later, we may implement caching * and counts. * If fmNil, either it's an error (check WGetIOError()) or the * original fm was nil too * * Globals Used: rcIOError (indirectly) * * +++ * * Notes: * ***************************************************************************/ FM PASCAL FmCopyFm(FM fmSrc, PHRESULT phr) { FM fmDest = fmNil; QAFM qafmSrc, qafmDest;
if (!FValidFm(fmSrc)) { SetErrCode (phr, E_INVALIDARG); return fmNil; }
qafmSrc = (QAFM)fmSrc; //qafmSrc = (QAFM)_GLOBALLOCK((HANDLE)fmSrc);
fmDest = (FM) NewMemory((WORD)(lstrlen(qafmSrc->rgch) + 1)); //fmDest = (FM) _GLOBALALLOC(GMEM_ZEROINIT|GMEM_SHARE|GMEM_MOVEABLE,
// (size_t)lstrlen(qafmSrc->rgch) + 1);
if (fmDest == fmNil) { SetErrCode(phr, E_OUTOFMEMORY); //_GLOBALUNLOCK((HANDLE)fmSrc);
return fmNil; }
qafmDest = (QAFM)fmDest; //qafmDest = (QAFM)_GLOBALLOCK((HANDLE)fmDest);
STRCPY(qafmDest->rgch, qafmSrc->rgch);
//_GLOBALUNLOCK((HANDLE)fmSrc);
//_GLOBALUNLOCK((HANDLE)fmDest);
return fmDest; }
/***************************************************************************
* - Name: FExistFm - * Purpose: Does the file exist? * * Arguments: FM * * Returns: TRUE if it does * FALSE if it doesn't, or if there's an error * (call _ to find out what error it was) * * Globals Used: rcIOError * * +++ * * Notes: * ***************************************************************************/ BOOL PASCAL FExistFm(FM fm) { QAFM qafm; BOOL fExist; OFSTRUCT ofs; HRESULT rc; char LocalBuffer1[_MAX_PATH]; HRESULT errb;
if (!FValidFm(fm)) { SetErrCode(&errb, E_INVALIDARG); return FALSE; }
qafm = (QAFM)fm; //qafm = _GLOBALLOCK((HANDLE)fm);
STRCPY(LocalBuffer1, qafm->rgch); /* bring the filename into near space */ //_GLOBALUNLOCK((HANDLE)fm);
// try in both modes!
fExist = (OpenFile(LocalBuffer1, &ofs, OF_EXIST | OF_SHARE_DENY_NONE) != (HFILE)-1) || (OpenFile(LocalBuffer1, &ofs, OF_EXIST) != (HFILE)-1);
rc = S_OK;
if (!fExist) { #ifdef _NT
if( GetLastError() != ERROR_FILE_NOT_FOUND ) #else
if( errno != ENOENT ) #endif // _NT
rc = RcGetDOSError(); }
SetErrCode(&errb, rc);
return fExist; }
/***************************************************************************
* - CbPartsFm - * Purpose: * Before calling szPartsFm, call this routine to find out how much * space to allocate for the string. * * Arguments: * FM - the File Moniker you'll be extracting the string from * INT iPart - the parts of the full pathname you want * * Returns: * The length in bytes, INCLUDING the terminating null, of the string * specified by iPart of the filename of FM, or -1 if error * * Globals Used: * * +++ * * Notes: * ***************************************************************************/ int PASCAL FAR EXPORT_API CbPartsFm(FM fm, int grfPart) { char rgch[_MAX_PATH];
if (!FValidFm(fm)) return -1;
(void)SzPartsFm(fm, rgch, _MAX_PATH, grfPart);
return ((int) STRLEN(rgch) + 1); /* add space for the null */ }
/***************************************************************************
* - SzPartsFm - * Purpose: * Extract a string from an FM * * Arguments: * FM - the File Moniker you'll be extracting the string from * SZ szDest - destination string * INT cbDest - bytes allocated for the string * INT iPart - the parts of the full pathname you want * * Returns: * szDest, or NULL if error (?) * * Globals Used: * * +++ * * Notes: * ***************************************************************************/ LPSTR PASCAL SzPartsFm(FM fm, LPSTR szDest, int cbDest, int iPart) { QAFM qafm; int iDrive, iDir, iBase, iExt; int cb; HRESULT errb;
if (!FValidFm(fm) || szDest == NULL || cbDest < 1) { SetErrCode (&errb, E_INVALIDARG); return NULL; }
qafm = (QAFM) fm; //qafm = (QAFM) _GLOBALLOCK(fm);
/* special case so we don't waste effort */ if (iPart == partAll) { STRNCPY(szDest, qafm->rgch, cbDest); *(szDest + cbDest - 1) = '\0'; //_GLOBALUNLOCK((HANDLE)fm);
return szDest; }
SnoopPath(qafm->rgch, &iDrive, &iDir, &iBase, &iExt);
*szDest = '\0';
if (iPart & partDrive) { cb = max(0, iDir - iDrive); SzNzCat(szDest, qafm->rgch + iDrive, (WORD)(min(cb + 1, cbDest) - 1)); cbDest -= cb; }
if (iPart & partDir) { cb = max(0, iBase - iDir); SzNzCat(szDest, qafm->rgch + iDir, (WORD)(min(cb + 1, cbDest) - 1)); cbDest -= cb; }
if (iPart & partBase) { cb = max(0, iExt - iBase); SzNzCat(szDest, qafm->rgch + iBase, (WORD)(min(cb + 1, cbDest) - 1)); cbDest -= cb; }
if (iPart & partExt) { SzNzCat(szDest, qafm->rgch + iExt, (WORD)(cbDest - 1)); }
//_GLOBALUNLOCK((HANDLE)fm);
return szDest; }
/***************************************************************************
* - Name: FSameFmFm - * Purpose: Compare two FM's * * Arguments: fm1, fm2 * * Returns: TRUE or FALSE * * Globals Used: * * +++ * * Notes: case insensitive compare is used because strings are * upper cased at FM creation time * ***************************************************************************/ BOOL PASCAL FSameFmFm(FM fm1, FM fm2)
{ QAFM qafm1; QAFM qafm2; BOOL fSame;
if (fm1 == fm2) return TRUE;
if (!FValidFm(fm1) || !FValidFm(fm2)) return FALSE;
qafm1 = (QAFM)fm1; qafm2 = (QAFM)fm2; //qafm1 = _GLOBALLOCK(fm1);
//qafm2 = _GLOBALLOCK(fm2);
fSame = STRCMP(qafm1->rgch, qafm2->rgch) == 0;
//_GLOBALUNLOCK(fm1);
//_GLOBALUNLOCK(fm2);
return fSame; }
|