mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1243 lines
28 KiB
1243 lines
28 KiB
/*****************************************************************************
|
|
* *
|
|
* FM.c *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990-1995 *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* Routines for manipulating FMs (File Monikers, equivalent to file names). *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
#include "help.h"
|
|
|
|
#define MAX_MESSAGE 512
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Prototypes *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
static FM STDCALL FFindFileFromIni(PCSTR szFileName, BOOL fAsk);
|
|
static void STDCALL SnoopPath(LPCSTR sz, int * iDrive, int * iDir, int * iBase, int * iExt);
|
|
|
|
static PSTR STDCALL SzNzCat(PSTR pszDest, PCSTR pszSrc, int cch);
|
|
static LPSTR STDCALL SzGetDir(DIR dir, LPSTR sz);
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 - DIR_CURRENT et al.
|
|
*
|
|
* Returns: the new FM, or NULL if error
|
|
* sz is unchanged
|
|
*
|
|
* Globals Used:
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
FM STDCALL FmNewSzDir(LPCSTR sz, DIR dir)
|
|
{
|
|
char szBuf[MAX_PATH];
|
|
int iDrive, iDir, iBase, iExt;
|
|
int cb;
|
|
|
|
if (sz == NULL || *sz == '\0') {
|
|
rcIOError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
// REVIEW: do we need to set this here?
|
|
|
|
rcIOError = rcSuccess; // Clear error flag
|
|
|
|
cb = lstrlen(sz);
|
|
SnoopPath(sz, &iDrive, &iDir, &iBase, &iExt);
|
|
|
|
if (!sz[iBase]) // no name
|
|
*szBuf = '\0'; // force error
|
|
|
|
else if (sz[iDrive] || 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.
|
|
*/
|
|
|
|
lstrcpy(szBuf, 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)) == 0) &&
|
|
(dir &
|
|
(DIR_CURRENT | DIR_BOOKMARK | DIR_ANNOTATE | DIR_TEMP)));
|
|
|
|
if (SzGetDir(dir, szBuf) == NULL)
|
|
return NULL;
|
|
|
|
SzNzCat(szBuf, sz + iDir, max(1, iBase - iDir));
|
|
lstrcat(szBuf, sz + iBase);
|
|
}
|
|
|
|
// We've got all the parameters, now make the FM
|
|
|
|
return FmNew(szBuf);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: FmNewExistSzDir
|
|
-
|
|
* Purpose: Returns an FM describing a file that exists
|
|
*
|
|
* Arguments: sz - see FmNewSzDir
|
|
dir - DIR
|
|
*
|
|
* Returns: the new FM
|
|
*
|
|
* Globals Used: rcIOError
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
* If sz is a rooted pathname, dir is ignored. Otherwise, all directories
|
|
* specified by dir are searched in the order of the dir* enum type.
|
|
*
|
|
***************************************************************************/
|
|
|
|
// REVIEW: does this include registry location?
|
|
|
|
FM STDCALL FmNewExistSzDir(PCSTR pszFileName, DIR dir)
|
|
{
|
|
char szBuf[MAX_PATH], szCopy[MAX_PATH];
|
|
FM fm = NULL;
|
|
int iDrive, iDir, iBase, iExt;
|
|
int cb;
|
|
|
|
rcIOError = rcSuccess; // Clear error flag
|
|
|
|
if (IsEmptyString(pszFileName)) {
|
|
rcIOError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
if (!StrChrDBCS(pszFileName, '.')) {
|
|
lstrcpy(szCopy, pszFileName);
|
|
lstrcat(szCopy, txtHlpExtension);
|
|
pszFileName = (PCSTR) szCopy;
|
|
}
|
|
|
|
cb = lstrlen(pszFileName);
|
|
SnoopPath(pszFileName, &iDrive, &iDir, &iBase, &iExt);
|
|
|
|
if (pszFileName[iBase] == '\0') { // no name
|
|
rcIOError = rcBadArg;
|
|
return fm;
|
|
}
|
|
|
|
if (pszFileName[iDrive] || pszFileName[iDir] == '\\' || pszFileName[iDir] == '/' ) {
|
|
|
|
// was given a drive or rooted path, so ignore dir parameter
|
|
|
|
fm = FmNew(pszFileName);
|
|
if (!FExistFm(fm)) {
|
|
DisposeFm(fm);
|
|
|
|
// If we can't find it in the path specified, then strip off the path
|
|
// and try the normal directories.
|
|
|
|
lstrcpy(szBuf, pszFileName + iBase);
|
|
fm = FmNewExistSzDir(szBuf, dir);
|
|
if (!FExistFm(fm))
|
|
rcIOError = rcNoExists;
|
|
}
|
|
return fm;
|
|
}
|
|
|
|
else {
|
|
static DIR idir;
|
|
DIR xdir;
|
|
|
|
if (dir & DIR_ENUMERATE)
|
|
goto enumerate;
|
|
|
|
for (idir = DIR_FIRST, fm = NULL; idir <= DIR_LAST && fm == NULL;
|
|
idir <<= 1) {
|
|
xdir = dir & idir;
|
|
|
|
if (xdir == DIR_CURRENT && dir & DIR_CUR_HELP) {
|
|
HDE hde = HdeGetEnv();
|
|
if (hde || fmCreating) {
|
|
PSTR pszTmp = (PSTR) LhAlloc(LMEM_FIXED, MAX_PATH);
|
|
|
|
GetFmParts(fmCreating ? fmCreating : QDE_FM(QdeFromGh(hde)), pszTmp,
|
|
PARTDRIVE | PARTDIR);
|
|
lstrcat(pszTmp, pszFileName + iBase);
|
|
fm = FmNew(pszTmp);
|
|
FreeLh(pszTmp);
|
|
if (FExistFm(fm))
|
|
return fm;
|
|
else {
|
|
|
|
// Can't use RemoveFM because SS may not equal DS
|
|
|
|
RemoveFM(&fm);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (xdir == DIR_INI) {
|
|
fm = FFindFileFromIni(pszFileName, (fHelp != POPUP_HELP));
|
|
}
|
|
else if (xdir == DIR_PATH) {
|
|
PSTR pszFilePart;
|
|
|
|
/*
|
|
* First search the windows\help directory. If that fails,
|
|
* then search the PATH environment.
|
|
*/
|
|
|
|
ConvertToWindowsHelp(pszFileName, szBuf);
|
|
if (GetFileAttributes(szBuf) != (DWORD) -1)
|
|
fm = FmNew(szBuf);
|
|
|
|
else if (SearchPath(NULL, pszFileName, NULL, sizeof(szBuf),
|
|
szBuf, &pszFilePart)) {
|
|
fm = FmNew(szBuf);
|
|
}
|
|
}
|
|
else if (xdir == DIR_SILENT_REG) {
|
|
fm = FindThisFile(pszFileName + iBase, FALSE);
|
|
}
|
|
else if (xdir == DIR_SILENT_INI) {
|
|
fm = FFindFileFromIni(pszFileName, FALSE);
|
|
}
|
|
|
|
else if (xdir) {
|
|
if (SzGetDir(xdir, szBuf) != NULL) {
|
|
lstrcat(szBuf, pszFileName + iBase);
|
|
fm = FmNew(szBuf);
|
|
if (!fm) {
|
|
rcIOError = rcFailure;
|
|
}
|
|
else if (!FExistFm(fm)) {
|
|
RemoveFM(&fm);
|
|
}
|
|
}
|
|
}
|
|
enumerate: ;
|
|
} // for
|
|
if ((rcIOError == rcSuccess) && !fm)
|
|
rcIOError = rcNoExists;
|
|
}
|
|
|
|
return fm;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 DIR_PATH)
|
|
* sz - buffer for storage (should be at least MAX_PATH)
|
|
*
|
|
* Returns: sz - fine
|
|
* NULL - OS Error (check rcIOError)
|
|
*
|
|
* Globals Used: rcIOError
|
|
*
|
|
***************************************************************************/
|
|
|
|
static LPSTR STDCALL SzGetDir(DIR dir, LPSTR sz)
|
|
{
|
|
int i=0;
|
|
LPSTR psz;
|
|
|
|
ASSERT(sz);
|
|
|
|
switch (dir) {
|
|
case DIR_CURRENT:
|
|
GetCurrentDirectory(MAX_PATH, sz);
|
|
break;
|
|
|
|
case DIR_BOOKMARK:
|
|
GetWindowsDirectory(sz, MAX_PATH);
|
|
break;
|
|
|
|
case DIR_ANNOTATE:
|
|
GetRegWindowsDirectory(sz);
|
|
AddTrailingBackslash(sz);
|
|
psz = sz + lstrlen(sz);
|
|
strcpy(psz, txtHlpDir);
|
|
if (GetFileAttributes(sz) != (DWORD) -1)
|
|
lstrcat(sz, "\\");
|
|
else {
|
|
GetWindowsDirectory(sz, MAX_PATH);
|
|
AddTrailingBackslash(sz);
|
|
psz = sz + lstrlen(sz);
|
|
strcpy(psz, txtHlpDir);
|
|
if (GetFileAttributes(sz) != (DWORD) -1) {
|
|
lstrcat(sz, "\\");
|
|
}
|
|
else {
|
|
*psz = '\0';
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if 0
|
|
// Removed for WinHelp 4.0
|
|
|
|
case DIR_HELP:
|
|
GetModuleFileName(hInsNow, sz, MAX_PATH);
|
|
psz = sz + lstrlen(sz);
|
|
|
|
// psz should point to the last non-null character in the string.
|
|
|
|
if (psz > sz)
|
|
psz--;
|
|
|
|
// Be careful of plain old file names, as ROM Windows supports
|
|
|
|
while (*psz != '\\' && *psz != '/' && *psz != '\0')
|
|
--psz;
|
|
if (*psz == '\0') {
|
|
|
|
// For some reason, there is no path name (ROM Windows?)
|
|
|
|
rcIOError = rcFailure;
|
|
sz = NULL;
|
|
}
|
|
else
|
|
*psz = '\0';
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
rcIOError = rcBadArg;
|
|
sz = NULL;
|
|
break;
|
|
}
|
|
|
|
if (sz != NULL) {
|
|
ASSERT(*sz);
|
|
psz = sz;
|
|
|
|
// Make sure that the string ends with a slash.
|
|
|
|
AddTrailingBackslash(psz);
|
|
psz = SzEnd(sz);
|
|
ASSERT(psz < sz + MAX_PATH && *psz == '\0');
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: FmNewTemp
|
|
-
|
|
* Purpose: Create a unique FM for a temporary file
|
|
*
|
|
* Arguments: none
|
|
*
|
|
* Returns: the new FM, or NULL if failure
|
|
*
|
|
* Globals Used: rcIOError
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
// REVIEW: might be a good candidate for the CTmpFile class used by hcrtf
|
|
// This gets called whenever we write the .GID file, which means every time
|
|
// we shut down.
|
|
|
|
extern const char txtTmpPrefix[]; // "~wh";
|
|
|
|
FM STDCALL FmNewTemp(void)
|
|
{
|
|
char szName[MAX_PATH];
|
|
char szPath[MAX_PATH];
|
|
FM fm = NULL;
|
|
DWORD attributes;
|
|
|
|
rcIOError = rcSuccess;
|
|
|
|
GetTempPath(sizeof(szPath), szPath);
|
|
attributes = GetFileAttributes(szPath);
|
|
if (attributes == (DWORD) -1 || !(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
GetWindowsDirectory(szPath, sizeof(szPath));
|
|
AddTrailingBackslash(szPath);
|
|
}
|
|
GetTempFileName(szPath, txtTmpPrefix, 0, szName);
|
|
fm = FmNew(szName);
|
|
|
|
if (fm && (RcUnlinkFm(fm) != rcSuccess)) {
|
|
DisposeFm(fm);
|
|
rcIOError = rcFailure;
|
|
return NULL;
|
|
}
|
|
|
|
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 NULL 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 STDCALL FmNewSameDirFmSz(FM fm, LPCSTR szName)
|
|
{
|
|
char szNew[MAX_PATH];
|
|
int iDrive, iDir, iBase, iExt;
|
|
|
|
if (!fm || szName == NULL || *szName == '\0') {
|
|
rcIOError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
// 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) == '\\' || *(szName + iDir) == '/')
|
|
lstrcpy(szNew, szName);
|
|
else {
|
|
if (*(szName + iDrive) != '\0') {
|
|
return NULL;
|
|
}
|
|
else {
|
|
SnoopPath(PszFromGh(fm), &iDrive, &iDir, &iBase, &iExt);
|
|
lstrcpyn(szNew, PszFromGh(fm), iBase + 1);
|
|
ASSERT(strlen(szNew) == 0 || szNew[strlen(szNew) - 1] == '\\' ||
|
|
szNew[strlen(szNew) - 1] == ':');
|
|
lstrcat(szNew, szName);
|
|
}
|
|
}
|
|
|
|
return FmNew(szNew);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: FmNewSystemFm
|
|
-
|
|
* Purpose:
|
|
* creates an FM which is the name of the requested system file. this
|
|
* means the generic help code can be completely ignorant of how these
|
|
* filenames are arrived at.
|
|
*
|
|
* Arguments:
|
|
* fm - the current file, if we need it, or NULL
|
|
* fWhich - one of: FM_UHLP - using help
|
|
* FM_ANNO - the annotation file for the passed fm
|
|
* FM_BKMK - the bookmark file
|
|
*
|
|
* Returns:
|
|
* an fm to the requested file, NULL if there's a problem
|
|
*
|
|
* Globals Used:
|
|
* rcIOError
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
* We clearly cannot condone the #define and the extern below. When
|
|
* Rob finally relents, we will fix this and do it right.
|
|
*
|
|
* Review: The help on help file (winhelp.hlp) is never created. Is
|
|
* this a problem?
|
|
*
|
|
***************************************************************************/
|
|
|
|
/*------------------------------------------------------------*\
|
|
| hack alert!!! Review!!!
|
|
\*------------------------------------------------------------*/
|
|
|
|
extern const char txtAnnoExt[];
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
static const char txtBmkExt[] = ".BMK";
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
FM STDCALL FmNewSystemFm(FM fm, WORD fWhich)
|
|
{
|
|
char szPath[MAX_PATH];
|
|
FM fmNew;
|
|
|
|
switch (fWhich) {
|
|
case FM_ANNO:
|
|
if (!fm) {
|
|
rcIOError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
// First try to open it in all the usual help file locations
|
|
|
|
GetFmParts(fm, szPath, PARTBASE);
|
|
lstrcat(szPath, txtAnnoExt);
|
|
fmNew = FmNewExistSzDir(szPath,
|
|
DIR_PATH | DIR_CURRENT | DIR_CUR_HELP);
|
|
if (!fmNew) {
|
|
|
|
// Couldn't find it, so create it in the windows\help directory
|
|
|
|
if (!SzGetDir(DIR_ANNOTATE, szPath))
|
|
fmNew = NULL;
|
|
else
|
|
GetFmParts(fm, szPath + lstrlen(szPath), PARTBASE);
|
|
lstrcat(szPath, txtAnnoExt);
|
|
fmNew = FmNew(szPath);
|
|
}
|
|
break;
|
|
|
|
case FM_BKMK:
|
|
strcpy(szPath, txtWinHlp32);
|
|
ChangeExtension(szPath, txtBmkExt);
|
|
fmNew = FmNewSzDir(szPath, DIR_BOOKMARK);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
}
|
|
|
|
rcIOError = rcSuccess;
|
|
return fmNew;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 STDCALL FmNew(LPCSTR psz)
|
|
{
|
|
FM fm;
|
|
char szFullPath[MAX_PATH];
|
|
|
|
if (IsEmptyString(psz))
|
|
return NULL;
|
|
|
|
if (!_fullpath(szFullPath, psz, sizeof(szFullPath))) {
|
|
rcIOError = rcInvalid;
|
|
return NULL;
|
|
}
|
|
else {
|
|
if (!(fm = (FM) lcStrDup(szFullPath))) {
|
|
rcIOError = rcOutOfMemory;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Convert to upper case to make it less likely that two FMs will
|
|
* contain different strings yet refer to the same file.
|
|
*/
|
|
|
|
CharUpper(szFullPath);
|
|
rcIOError = rcSuccess;
|
|
}
|
|
return fm;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 STDCALL DisposeFm(FM fm)
|
|
{
|
|
if (fm)
|
|
FreeLh((HGLOBAL) fm);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: RemoveFM
|
|
|
|
PURPOSE: Same as DisposeFm, but this one also zero's the handle.
|
|
|
|
PARAMETERS:
|
|
pfm
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
25-Oct-1993 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
void STDCALL RemoveFM(FM* pfm)
|
|
{
|
|
if (*pfm) {
|
|
FreeLh((HLOCAL) *pfm);
|
|
*pfm = NULL;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 NULL, either it's an error (check WGetIOError()) or the
|
|
* original fm was nil too
|
|
*
|
|
* Globals Used: rcIOError (indirectly)
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
FM STDCALL FmCopyFm(FM fmSrc)
|
|
{
|
|
FM fmDest;
|
|
|
|
if (!fmSrc) {
|
|
rcIOError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
fmDest = (FM) LhAlloc(LMEM_FIXED, lstrlen(PszFromGh(fmSrc)) + 1);
|
|
if (fmDest == NULL) {
|
|
rcIOError = rcOutOfMemory;
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpy(PszFromGh(fmDest), PszFromGh(fmSrc));
|
|
|
|
rcIOError = rcSuccess;
|
|
|
|
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
|
|
*
|
|
* +++
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL STDCALL FExistFm(FM fm)
|
|
{
|
|
if (!fm || GetFileAttributes(fm) == (DWORD) -1) {
|
|
#ifdef _DEBUG
|
|
GetLastError();
|
|
#endif
|
|
rcIOError = rcNoExists;
|
|
return FALSE;
|
|
}
|
|
rcIOError = rcSuccess;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: GetFmParts
|
|
|
|
PURPOSE: Get the parts of a file moniker
|
|
|
|
PARAMETERS:
|
|
fm
|
|
pszDest
|
|
iPart
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
WARNING!!! Do not change fm -- it's assumed to be PCSTR elsewhere
|
|
|
|
MODIFICATION DATES:
|
|
02-Apr-1995 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
void STDCALL GetFmParts(FM fm, PSTR pszDest, int iPart)
|
|
{
|
|
int iDrive, iDir, iBase, iExt;
|
|
|
|
ASSERT(fm && pszDest);
|
|
|
|
if (!fm || pszDest == NULL) {
|
|
*pszDest = '\0';
|
|
rcIOError = rcBadArg;
|
|
return;
|
|
}
|
|
|
|
ASSERT(iPart != PARTALL);
|
|
|
|
SnoopPath(PszFromGh(fm), &iDrive, &iDir, &iBase, &iExt);
|
|
|
|
if (iPart & PARTBASE) {
|
|
lstrcpy(pszDest, PszFromGh(fm) + iBase);
|
|
if (iPart & PARTEXT)
|
|
return;
|
|
|
|
// remove extension if not specifically requested.
|
|
|
|
else {
|
|
PSTR psz = StrChrDBCS(pszDest, '.');
|
|
if (psz && !StrChrDBCS(psz, '\\'))
|
|
*psz = '\0';
|
|
}
|
|
return;
|
|
}
|
|
else if (iPart & (PARTDRIVE | PARTDIR)) {
|
|
lstrcpy(pszDest, PszFromGh(fm));
|
|
pszDest[iBase] = '\0';
|
|
return;
|
|
}
|
|
|
|
ASSERT(iPart & PARTBASE || iPart & (PARTDRIVE | PARTDIR));
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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 STDCALL FSameFmFm(FM fm1, FM fm2)
|
|
{
|
|
if (fm1 == fm2)
|
|
return TRUE;
|
|
|
|
if (!fm1 || !fm2)
|
|
return FALSE;
|
|
|
|
return (lstrcmpi(PszFromGh(fm1), PszFromGh(fm2)) == 0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: FFindFileFromIni
|
|
-
|
|
* Purpose: Looks for a string in winhelp.ini telling what directory
|
|
* to look in for the given file.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Returns:
|
|
*
|
|
* Globals Used:
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
const char txtFiles[] = "files";
|
|
const char txtIni[] = ".INI";
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
static FM STDCALL FFindFileFromIni(PCSTR pszFileName, BOOL fAsk)
|
|
{
|
|
PSTR psz;
|
|
PSTR pszMsg = NULL;
|
|
char szDummy[3];
|
|
FM fm;
|
|
int cchFileName;
|
|
char szWinHelpIni[MAX_PATH];
|
|
char szProfileString[MAX_PATH + MAX_MESSAGE];
|
|
|
|
strcpy(szWinHelpIni, fIsThisChicago ? txtWinHelp : txtWinHlp32);
|
|
strcat(szWinHelpIni, txtIni);
|
|
|
|
// A quick test to reject no-shows.
|
|
|
|
if (GetPrivateProfileString(txtFiles, pszFileName, txtZeroLength, szDummy,
|
|
sizeof(szDummy), szWinHelpIni) > 1) {
|
|
|
|
/*--------------------------------------------------------------------*\
|
|
| The original profile string looks something like this
|
|
| a:\setup\helpfiles,Please place fred's disk in drive A:
|
|
| ^
|
|
| We transform this to look like:
|
|
| a:\setup\helpfiles\foobar.hlp Please place fred's disk in drive A:
|
|
| \_________________/\________/^\__________________________________/^
|
|
| MAX_PATH cchFileName 1 MAX_MESSAGE 1
|
|
|
|
|
\*--------------------------------------------------------------------*/
|
|
|
|
GetPrivateProfileString(txtFiles, pszFileName, txtZeroLength,
|
|
szProfileString, sizeof(szProfileString), szWinHelpIni);
|
|
|
|
cchFileName = strlen(pszFileName);
|
|
|
|
for (psz = szProfileString; *psz; psz = CharNext(psz)) {
|
|
if (*psz == ',') {
|
|
*psz = '\0';
|
|
pszMsg = psz + 1;
|
|
ASSERT(pszMsg - szProfileString <= MAX_PATH);
|
|
MoveMemory(pszMsg + cchFileName + 1, pszMsg, MAX_MESSAGE + 1);
|
|
pszMsg += cchFileName + 1;
|
|
|
|
// null-terminate that message
|
|
|
|
pszMsg[MAX_MESSAGE] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(!*psz);
|
|
AddTrailingBackslash(szProfileString);
|
|
strcat(szProfileString, pszFileName);
|
|
|
|
if (!fAsk)
|
|
return FmNewExistSzDir(szProfileString, DIR_CURRENT);
|
|
|
|
while (!(fm = FmNewExistSzDir(szProfileString, DIR_CURRENT))) {
|
|
if (MessageBox((hwndAnimate ? hwndAnimate : ahwnd[iCurWindow].hwndParent), pszMsg ? pszMsg : "", pszCaption,
|
|
MB_OKCANCEL | MB_TASKMODAL | MB_ICONHAND ) != IDOK)
|
|
break;
|
|
}
|
|
|
|
return fm;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- 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:
|
|
*
|
|
***************************************************************************/
|
|
|
|
static void STDCALL SnoopPath(LPCSTR sz, int *iDrive, int *iDir,
|
|
int *iBase, int *iExt)
|
|
{
|
|
int i;
|
|
int cb = lstrlen(sz);
|
|
BOOL fDir = FALSE;
|
|
|
|
*iDrive = *iExt = cb;
|
|
*iDir = *iBase = 0;
|
|
|
|
for (i = 0; sz[i]; i++) {
|
|
switch (sz[i]) {
|
|
case ':':
|
|
*iDrive = 0;
|
|
*iDir = i + 1;
|
|
*iBase = i + 1;
|
|
break;
|
|
|
|
case '/':
|
|
case '\\':
|
|
fDir = TRUE;
|
|
*iBase = i + 1;
|
|
*iExt = cb;
|
|
break;
|
|
|
|
case '.':
|
|
*iExt = i;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef DBCS
|
|
if (IsDBCSLeadByte(sz[i]))
|
|
i++;
|
|
#endif
|
|
}
|
|
|
|
if (!fDir)
|
|
*iDir = i;
|
|
else if (*iBase == '.')
|
|
*iExt = cb;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: SzNzCat( szDest, szSrc, cch )
|
|
-
|
|
* Purpose:
|
|
* concatenation of szSrc to szDest up to cch characters. make sure
|
|
* the destination is still \000 terminated. will copy up to cch-1
|
|
* characters. this means that cch should account for the \000 when
|
|
* passed in.
|
|
*
|
|
* Arguments:
|
|
* szDest - the LPSTR to append onto
|
|
* szSrc - the LPSTR which will be appended to szDest
|
|
* cch - the max count of characters to copy and space for the \000
|
|
*
|
|
* Returns:
|
|
* szDest
|
|
*
|
|
* Globals Used:
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
static PSTR STDCALL SzNzCat(PSTR pszDest, PCSTR pszSrc, int cch)
|
|
{
|
|
PSTR psz = SzEnd(pszDest);
|
|
|
|
strncpy(psz, pszSrc, cch);
|
|
*(psz + cch) = '\000';
|
|
|
|
return pszDest;
|
|
}
|
|
|
|
void STDCALL AddTrailingBackslash(PSTR psz)
|
|
{
|
|
if (psz != NULL && *psz != '\0') {
|
|
PSTR pszEnd = SzEnd(psz);
|
|
if (*(CharPrev(psz, pszEnd)) != '\\' && *(CharPrev(psz, pszEnd)) != '/') {
|
|
*pszEnd++ = '\\';
|
|
*pszEnd++ = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: ConverToWindowsHelp
|
|
|
|
PURPOSE: Given a filename, convert it to a path that we can write
|
|
to -- preferably, windows\help
|
|
|
|
PARAMETERS:
|
|
pszFile
|
|
pszDstPath
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
WARNING: pszDstPath had better be at least MAX_PATH in size
|
|
|
|
MODIFICATION DATES:
|
|
03-Dec-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
void STDCALL ConvertToWindowsHelp(PCSTR pszFile, PSTR pszDstPath)
|
|
{
|
|
PSTR pszHelpDir;
|
|
|
|
GetWindowsDirectory(pszDstPath, MAX_PATH);
|
|
|
|
AddTrailingBackslash(pszDstPath);
|
|
pszHelpDir = pszDstPath + strlen(pszDstPath);
|
|
lstrcat(pszDstPath, txtHlpDir);
|
|
|
|
// REVIEW: will this tell us if we have a read-only directory?
|
|
|
|
if (GetFileAttributes(pszDstPath) == (DWORD) -1) {
|
|
strcpy(pszHelpDir, "system32");
|
|
if (GetFileAttributes(pszDstPath) == (DWORD) -1)
|
|
*pszHelpDir = '\0';
|
|
else
|
|
AddTrailingBackslash(pszDstPath);
|
|
}
|
|
else
|
|
AddTrailingBackslash(pszDstPath);
|
|
|
|
GetFmParts((FM) pszFile, pszDstPath + strlen(pszDstPath), PARTBASE | PARTEXT);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: GetRegWindowsDirectory
|
|
|
|
PURPOSE: Equivalent to GetWindowsDirectory() only it checks the
|
|
registration first for the proper location
|
|
|
|
PARAMETERS:
|
|
pszDst
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
04-Dec-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
static const char txtSetupKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Setup";
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
// Can't use const since RegQueryValueEx thinks it can change this
|
|
|
|
static char txtSharedDir[] = "SharedDir";
|
|
|
|
void STDCALL GetRegWindowsDirectory(PSTR pszDstPath)
|
|
{
|
|
HKEY hkey;
|
|
DWORD type;
|
|
int cbPath = MAX_PATH;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtSetupKey, 0, KEY_READ, &hkey) ==
|
|
ERROR_SUCCESS) {
|
|
RegQueryValueEx(hkey, txtSharedDir, 0, &type, pszDstPath, &cbPath);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
if (cbPath == MAX_PATH) // means couldn't read registry key
|
|
GetWindowsDirectory(pszDstPath, MAX_PATH);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: FindThisFile
|
|
|
|
PURPOSE:
|
|
|
|
PARAMETERS:
|
|
pszFile
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
04-Dec-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg(".text", "CODE")
|
|
#endif
|
|
static const char txtHelpDirKey[] = "Software\\Microsoft\\Windows\\Help";
|
|
#ifndef NO_PRAGMAS
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
// Can't be const since RegCreateKeyEx() thinks it can modify this
|
|
|
|
static char txtDirectoryClass[] = "Folder";
|
|
|
|
FM STDCALL FindThisFile(PCSTR pszFile, BOOL fAskUser)
|
|
{
|
|
char szFile[MAX_PATH];
|
|
char szFullPath[MAX_PATH + 100];
|
|
LONG result = -1;
|
|
FM fm;
|
|
HKEY hkey;
|
|
DWORD type;
|
|
int cbPath = MAX_PATH;
|
|
|
|
GetFmParts((FM) pszFile, szFile, PARTBASE | PARTEXT);
|
|
if (!StrRChrDBCS(szFile, '.'))
|
|
strcat(szFile, txtHlpExtension);
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0, KEY_READ, &hkey) ==
|
|
ERROR_SUCCESS) {
|
|
result = RegQueryValueEx(hkey, szFile, 0, &type, szFullPath, &cbPath);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
if (result == ERROR_SUCCESS) {
|
|
AddTrailingBackslash(szFullPath);
|
|
strcat(szFullPath, szFile);
|
|
if (GetFileAttributes(szFullPath) != (DWORD) -1)
|
|
return FmCopyFm(szFullPath);
|
|
}
|
|
else {
|
|
GetRegWindowsDirectory(szFullPath);
|
|
AddTrailingBackslash(szFullPath);
|
|
strcat(szFullPath, txtHlpDir);
|
|
AddTrailingBackslash(szFullPath);
|
|
strcat(szFullPath, szFile);
|
|
if (GetFileAttributes(szFullPath) != (DWORD) -1)
|
|
return FmCopyFm(szFullPath);
|
|
}
|
|
|
|
if (!fAskUser)
|
|
return NULL;
|
|
|
|
/*
|
|
* At this point, we don't know where the heck this file is, so let's
|
|
* get the user to find it for us.
|
|
*/
|
|
|
|
wsprintf(szFullPath, GetStringResource(wERRS_FIND_YOURSELF), pszFile);
|
|
if (MessageBox((hwndAnimate ? hwndAnimate : ahwnd[iCurWindow].hwndParent),
|
|
szFullPath, pszCaption, MB_YESNO | MB_ICONQUESTION) != IDYES)
|
|
return NULL;
|
|
|
|
fm = DlgOpenFile(ahwnd[iCurWindow].hwndParent, pszFile, NULL);
|
|
|
|
if (fm) {
|
|
DWORD disposition;
|
|
PSTR pszFilePart;
|
|
|
|
if (SearchPath(NULL, fm, NULL, MAX_PATH, szFullPath, &pszFilePart) > 0) {
|
|
pszFilePart[-1] = '\0';
|
|
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, txtHelpDirKey, 0,
|
|
txtDirectoryClass, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
|
|
NULL, &hkey, &disposition) == ERROR_SUCCESS) {
|
|
RegSetValueEx(hkey, pszFilePart, 0, REG_SZ, szFullPath,
|
|
strlen(szFullPath) + 1);
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
return fm;
|
|
}
|