windows-nt/Source/XPSP1/NT/shell/comdlg32/parse.c
2020-09-26 16:20:57 +08:00

917 lines
23 KiB
C

/*++
Copyright (c) 1990-1998, Microsoft Corporation All rights reserved.
Module Name:
parse.c
Abstract:
This module contains the parse routines for the Win32 common dialogs.
Revision History:
--*/
// precompiled headers
#include "precomp.h"
#pragma hdrstop
#include "fileopen.h"
//
// Global Variables.
//
extern TCHAR szCaption[];
extern TCHAR szWarning[];
////////////////////////////////////////////////////////////////////////////
//
// ParseFileNew
//
// On the return, pnExtOffset is the offset to the dot.
//
////////////////////////////////////////////////////////////////////////////
int ParseFileNew(
LPTSTR pszPath,
int *pnExtOffset,
BOOL bWowApp,
BOOL bNewStyle)
{
int lRet = ParseFile(pszPath, TRUE, bWowApp, bNewStyle);
if (pnExtOffset)
{
int nExt;
nExt = (int)(SHORT)HIWORD(lRet);
*pnExtOffset = ((nExt) && *(pszPath + nExt)) ? nExt : 0;
}
return ((int)(SHORT)LOWORD(lRet));
}
////////////////////////////////////////////////////////////////////////////
//
// ParseFileOld
//
// On return, pnExtOffset is the offset to the the dot and
// pnOldExt is the offset to the character following the dot.
//
////////////////////////////////////////////////////////////////////////////
int ParseFileOld(
LPTSTR pszPath,
int *pnExtOffset,
int *pnOldExt,
BOOL bWowApp,
BOOL bNewStyle)
{
int lRet = ParseFile(pszPath, TRUE, bWowApp, bNewStyle);
int nExt = (int)(SHORT)HIWORD(lRet);
*pnExtOffset = nExt;
*pnOldExt = ((nExt) && *(pszPath + nExt)) ? nExt + 1 : 0;
return ((int)(SHORT)LOWORD(lRet));
}
////////////////////////////////////////////////////////////////////////////
//
// ParseFile
//
// Determines if the filename is a legal dos name.
//
// Circumstances checked:
// 1) Valid as directory name, but not as file name
// 2) Empty String
// 3) Illegal Drive label
// 4) Period in invalid location (in extension, 1st in file name)
// 5) Missing directory character
// 6) Illegal character
// 7) Wildcard in directory name
// 8) Double slash beyond 1st 2 characters
// 9) Space character in the middle of the name (trailing spaces OK)
// -->> no longer applies : spaces are allowed in LFN
// 10) Filename greater than 8 characters : NOT APPLICABLE TO LONG FILE NAMES
// 11) Extension greater than 3 characters: NOT APPLICABLE TO LONG FILE NAMES
//
// lpstrFileName - ptr to a single file name
//
// Returns:
// LONG - LOWORD = char offset to filename,
// HIWORD = char offset to extension (dot),
// LONG - LOWORD is error code (<0), HIWORD is approx. place of problem
//
////////////////////////////////////////////////////////////////////////////
DWORD ParseFile(
LPTSTR lpstrFileName,
BOOL bLFNFileSystem,
BOOL bWowApp,
BOOL bNewStyle)
{
SHORT nFile, nExt, nFileOffset, nExtOffset = 0;
BOOL bExt;
BOOL bWildcard;
SHORT nNetwork = 0;
BOOL bUNCPath = FALSE;
LPTSTR lpstr = lpstrFileName;
//Check if the string is empty
if (!*lpstr)
{
nFileOffset = PARSE_EMPTYSTRING;
goto ParseFile_Failure;
}
//Check if the string is of form c:\foo1\foo2
if (*(lpstr + 1) == CHAR_COLON)
{
//Yes. Get the drive letter
TCHAR cDrive = CharLowerChar(*lpstr);
//
// Test to see if the drive is legal.
//
// Note: Does not test that drive exists.
//
if ((cDrive < CHAR_A) || (cDrive > CHAR_Z))
{
nFileOffset = PARSE_INVALIDDRIVE;
goto ParseFile_Failure;
}
//Move string past drive letter and ':'
lpstr = CharNext(CharNext(lpstr));
}
if ((*lpstr == CHAR_BSLASH) || (*lpstr == CHAR_SLASH && !bNewStyle))
{
//
// Cannot have "c:\."
//
if (*++lpstr == CHAR_DOT)
{
//
// Except that "c:\.\" is allowed.
//
if ((*++lpstr != CHAR_BSLASH) && (*lpstr != CHAR_SLASH || bNewStyle))
{
//
// It's the root directory.
//
if (!*lpstr)
{
goto MustBeDir;
}
else
{
lpstr--;
}
}
else
{
//
// It's saying top dir (once again), thus allowed.
//
++lpstr;
}
}
else if ((*lpstr == CHAR_BSLASH) && (*(lpstr - 1) == CHAR_BSLASH))
{
//
// It seems that for a full network path, whether a drive is
// declared or not is insignificant, though if a drive is given,
// it must be valid (hence the code above should remain there).
//
//
// ...since it's the first slash, 2 are allowed.
//
++lpstr;
//
// Must receive server and share to be real.
//
nNetwork = -1;
//
// No wildcards allowed if UNC name.
//
bUNCPath = TRUE;
}
else if (*lpstr == CHAR_SLASH && !bNewStyle)
{
nFileOffset = PARSE_INVALIDDIRCHAR;
goto ParseFile_Failure;
}
}
else if (*lpstr == CHAR_DOT)
{
//
// Up one directory.
//
if (*++lpstr == CHAR_DOT)
{
++lpstr;
}
if (!*lpstr)
{
goto MustBeDir;
}
if ((*lpstr != CHAR_BSLASH) && (*lpstr != CHAR_SLASH || bNewStyle))
{
//
// Jumping to Failure here will skip the parsing that causes
// ".xxx.txt" to return with nFileOffset = 2.
//
nFileOffset = 0;
goto ParseFile_Failure;
}
else
{
//
// Allow directory.
//
++lpstr;
}
}
if (!*lpstr)
{
goto MustBeDir;
}
//
// Should point to first char in filename by now.
//
nFileOffset = nExtOffset = nFile = nExt = 0;
bWildcard = bExt = FALSE;
while (*lpstr)
{
//
// Anything below the "Space" character is invalid.
//
#ifdef UNICODE
if (*lpstr < CHAR_SPACE)
#else
if (((UCHAR)*lpstr) < CHAR_SPACE)
#endif
{
nFileOffset = PARSE_INVALIDCHAR;
goto ParseFile_Failure;
}
switch (*lpstr)
{
case ( CHAR_COLON ) :
case ( CHAR_BAR ) :
case ( CHAR_LTHAN ) :
case ( CHAR_QUOTE ) :
{
//
// Invalid characters for all file systems.
//
nFileOffset = PARSE_INVALIDCHAR;
goto ParseFile_Failure;
}
case ( CHAR_SEMICOLON ) :
case ( CHAR_COMMA ) :
case ( CHAR_PLUS ) :
case ( CHAR_LBRACKET ) :
case ( CHAR_RBRACKET ) :
case ( CHAR_EQUAL ) :
{
if (!bLFNFileSystem)
{
nFileOffset = PARSE_INVALIDCHAR;
goto ParseFile_Failure;
}
else
{
goto RegularCharacter;
}
}
case ( CHAR_SLASH ) :
{
if (bNewStyle)
{
nFileOffset = PARSE_INVALIDCHAR;
goto ParseFile_Failure;
}
// fall thru...
}
case ( CHAR_BSLASH ) :
{
//
// Subdir indicators.
//
nNetwork++;
if (bWildcard)
{
nFileOffset = PARSE_WILDCARDINDIR;
goto ParseFile_Failure;
}
//
// if nFile==0 means that we are seeing this backslash right next to a backslash
// which is not allowed.
if (nFile == 0)
{
nFileOffset = PARSE_INVALIDDIRCHAR;
goto ParseFile_Failure;
}
else
{
//Move over the BSLASH/SLASH character.
++lpstr;
//Check if the path is valid network path name
if (!nNetwork && !*lpstr)
{
nFileOffset = PARSE_INVALIDNETPATH;
goto ParseFile_Failure;
}
//We assume that the characters we are seeing are filename characters. This BSLASH/SLASH
//character tells that characters we have seen so far specifies the name of a directory in the
//path. Reset flags so that we can start looking for filename again.
nFile = nExt = 0;
nExtOffset = 0;
bExt = FALSE;
}
break;
}
case ( CHAR_SPACE ) :
{
LPTSTR lpSpace = lpstr;
if (bLFNFileSystem)
{
// In Long file name file system space characters are O.K
goto RegularCharacter;
}
//We are not interested in the trailing spaces so null terminate it.
*lpSpace = CHAR_NULL;
// In non long file name file systems, space characters are OK at the end of file
// name. Check to see if all the characters that follows are spaces. if thats the case
// then its valid. if we have any non space character after the first space then its a
// invalid file name.
while (*++lpSpace)
{
if (*lpSpace != CHAR_SPACE)
{
*lpstr = CHAR_SPACE;
nFileOffset = PARSE_INVALIDSPACE;
goto ParseFile_Failure;
}
}
break;
}
case ( CHAR_DOT ) :
{
// In newstyle nExtOffset points to the dot and not to the first character of extension.
if (bNewStyle)
{
nExtOffset = (SHORT)(lpstr - lpstrFileName);
goto RegularCharacter;
}
if (nFile == 0)
{
nFileOffset = (SHORT)(lpstr - lpstrFileName);
if (*++lpstr == CHAR_DOT)
{
++lpstr;
}
if (!*lpstr)
{
goto MustBeDir;
}
//
// Flags already set.
//
nFile++;
++lpstr;
}
else
{
nExtOffset = 0;
++lpstr;
bExt = TRUE;
}
break;
}
case ( CHAR_STAR ) :
case ( CHAR_QMARK ) :
{
bWildcard = TRUE;
// Fall thru...
}
default :
{
RegularCharacter:
//Are we in extension part ?
if (bExt)
{
//Is this first character in extension part
if (++nExt == 1)
{
//Yes, then get the Extension offset
nExtOffset = (SHORT)(lpstr - lpstrFileName);
}
}
//We are still in file name part.
//Is this the first character in filename part ?
else if (++nFile == 1)
{
//Yes. Get the filename offset
nFileOffset = (SHORT)(lpstr - lpstrFileName);
}
//Move to the next character
lpstr = CharNext(lpstr);
break;
}
}
}
if (nNetwork == -1)
{
nFileOffset = PARSE_INVALIDNETPATH;
goto ParseFile_Failure;
}
else if (bUNCPath)
{
if (!nNetwork)
{
//
// Server and share only.(e.g \\server\foo)
//
*lpstr = CHAR_NULL;
nFileOffset = PARSE_DIRECTORYNAME;
goto ParseFile_Failure;
}
else if ((nNetwork == 1) && !nFile)
{
//
// Server and share root.(e.g \\server\foo\)
//
*lpstr = CHAR_NULL;
nFileOffset = PARSE_DIRECTORYNAME;
goto ParseFile_Failure;
}
}
if (!nFile)
{
MustBeDir:
nFileOffset = PARSE_DIRECTORYNAME;
goto ParseFile_Failure;
}
//
// If bNewStyle is true, no ext. wanted.
//
if (!bNewStyle)
{
if ((bWowApp) &&
(*(lpstr - 1) == CHAR_DOT) &&
(*CharNext(lpstr - 2) == CHAR_DOT))
{
//
// Remove terminating period.
//
*(lpstr - 1) = CHAR_NULL;
}
else if (!nExt)
{
ParseFile_Failure:
//
// Need to recheck bNewStyle since we can jump here.
//
if (!bNewStyle)
{
nExtOffset = (SHORT)(lpstr - lpstrFileName);
}
}
}
return (MAKELONG(nFileOffset, nExtOffset));
}
////////////////////////////////////////////////////////////////////////////
//
// PathRemoveBslash
//
// Removes a trailing backslash from the given path.
//
// Returns:
// Pointer to NULL that replaced the backslash OR
// Pointer to the last character if it isn't a backslash
//
////////////////////////////////////////////////////////////////////////////
LPTSTR PathRemoveBslash(
LPTSTR lpszPath)
{
int len = lstrlen(lpszPath) - 1;
#ifndef UNICODE
if (IsDBCSLeadByte(*CharPrev(lpszPath, lpszPath + len + 1)))
{
len--;
}
#endif
if (!PathIsRoot(lpszPath) && (lpszPath[len] == CHAR_BSLASH))
{
lpszPath[len] = CHAR_NULL;
}
return (lpszPath + len);
}
////////////////////////////////////////////////////////////////////////////
//
// IsWild
//
////////////////////////////////////////////////////////////////////////////
BOOL IsWild(
LPCTSTR lpsz)
{
return (StrChr(lpsz, CHAR_STAR) || StrChr(lpsz, CHAR_QMARK));
}
////////////////////////////////////////////////////////////////////////////
//
// AppendExt
//
// Appends default extension onto path name.
// It assumes the current path name doesn't already have an extension.
// lpExtension does not need to be null terminated.
//
////////////////////////////////////////////////////////////////////////////
VOID AppendExt(
LPTSTR lpszPath,
LPCTSTR lpExtension,
BOOL bWildcard)
{
WORD wOffset;
SHORT i;
TCHAR szExt[MAX_PATH + 1];
if (lpExtension && *lpExtension)
{
wOffset = (WORD)lstrlen(lpszPath);
if (bWildcard)
{
*(lpszPath + wOffset++) = CHAR_STAR;
}
//
// Add a period.
//
*(lpszPath + wOffset++) = CHAR_DOT;
for (i = 0; *(lpExtension + i) && i < MAX_PATH; i++)
{
szExt[i] = *(lpExtension + i);
}
szExt[i] = 0;
//
// Remove leading / trailing blanks in the extension.
//
PathRemoveBlanks(szExt);
//
// Add the rest.
//
lstrcpy(lpszPath + wOffset, szExt);
}
}
////////////////////////////////////////////////////////////////////////////
//
// IsUNC
//
// Determines if the given path is a UNC path.
//
// Returns:
// TRUE if path starts with "\\" or "X:\\"
// FALSE otherwise
//
////////////////////////////////////////////////////////////////////////////
BOOL IsUNC(
LPCTSTR lpszPath)
{
return ( DBL_BSLASH(lpszPath) ||
((lpszPath[1] == CHAR_COLON) && DBL_BSLASH(lpszPath + 2)) );
}
////////////////////////////////////////////////////////////////////////////
//
// PortName
//
////////////////////////////////////////////////////////////////////////////
#define PORTARRAY 14
BOOL PortName(
LPTSTR lpszFileName)
{
static TCHAR *szPorts[PORTARRAY] = { TEXT("LPT1"),
TEXT("LPT2"),
TEXT("LPT3"),
TEXT("LPT4"),
TEXT("COM1"),
TEXT("COM2"),
TEXT("COM3"),
TEXT("COM4"),
TEXT("EPT"),
TEXT("NUL"),
TEXT("PRN"),
TEXT("CLOCK$"),
TEXT("CON"),
TEXT("AUX"),
};
short i;
TCHAR cSave, cSave2;
cSave = *(lpszFileName + 4);
if (cSave == CHAR_DOT)
{
*(lpszFileName + 4) = CHAR_NULL;
}
//
// For "EPT".
//
cSave2 = *(lpszFileName + 3);
if (cSave2 == CHAR_DOT)
{
*(lpszFileName + 3) = CHAR_NULL;
}
for (i = 0; i < PORTARRAY; i++)
{
if (!lstrcmpi(szPorts[i], lpszFileName))
{
break;
}
}
*(lpszFileName + 4) = cSave;
*(lpszFileName + 3) = cSave2;
return (i != PORTARRAY);
}
////////////////////////////////////////////////////////////////////////////
//
// IsDirectory
//
////////////////////////////////////////////////////////////////////////////
BOOL IsDirectory(
LPTSTR pszPath)
{
DWORD dwAttributes;
//
// Clean up for GetFileAttributes.
//
PathRemoveBslash(pszPath);
dwAttributes = GetFileAttributes(pszPath);
return ( (dwAttributes != (DWORD)(-1)) &&
(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) );
}
////////////////////////////////////////////////////////////////////////////
//
// WriteProtectedDirCheck
//
// This function takes a full filename, strips the path, and creates
// a temp file in that directory. If it can't, the directory is probably
// write protected.
//
// Returns:
// error code if writeprotected
// 0 if successful creation of file.
//
// Assumptions:
// Full Path name on input with space for full filename appended.
//
// Note: Do NOT use this on a floppy, it's too slow!
//
////////////////////////////////////////////////////////////////////////////
int WriteProtectedDirCheck(
LPCTSTR lpszFile)
{
SHORT nFileOffset;
TCHAR szFile[MAX_PATH + 1];
TCHAR szBuf[MAX_PATH + 1];
lstrcpyn(szFile, lpszFile, MAX_PATH + 1);
nFileOffset = (SHORT)(int)LOWORD(ParseFile(szFile, TRUE, FALSE, TRUE));
szFile[nFileOffset - 1] = CHAR_NULL;
if (!GetTempFileName(szFile, TEXT("TMP"), 0, szBuf))
{
return (GetLastError());
}
else
{
DeleteFile(szBuf);
return (0); // success
}
}
////////////////////////////////////////////////////////////////////////////
//
// FOkToWriteOver
//
// Verifies that the user really does want to destroy the file,
// replacing its contents with new stuff.
//
////////////////////////////////////////////////////////////////////////////
BOOL FOkToWriteOver(
HWND hDlg,
LPTSTR szFileName)
{
if (!CDLoadString( g_hinst,
iszOverwriteQuestion,
szCaption,
WARNINGMSGLENGTH - 1 ))
{
return (FALSE);
}
//
// Since we're passed in a valid filename, if the 3rd & 4th characters
// are both slashes, weve got a dummy drive as the 1st two characters.
//
if (DBL_BSLASH(szFileName + 2))
{
szFileName = szFileName + 2;
}
wsprintf(szWarning, szCaption, szFileName);
GetWindowText(hDlg, szCaption, cbCaption);
return (MessageBox( hDlg,
szWarning,
szCaption,
MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ) == IDYES);
}
////////////////////////////////////////////////////////////////////////////
//
// CreateFileDlg
//
////////////////////////////////////////////////////////////////////////////
int CreateFileDlg(
HWND hDlg,
LPTSTR szPath)
{
//
// Since we're passed in a valid filename, if the 3rd & 4th
// characters are both slashes, we've got a dummy drive as the
// 1st two characters.
//
if (DBL_BSLASH(szPath + 2))
{
szPath = szPath + 2;
}
if (!CDLoadString(g_hinst, iszCreatePrompt, szCaption, TOOLONGLIMIT))
{
return (IDNO);
}
if (lstrlen(szPath) > TOOLONGLIMIT)
{
*(szPath + TOOLONGLIMIT) = CHAR_NULL;
}
wsprintf(szWarning, szCaption, szPath);
GetWindowText(hDlg, szCaption, TOOLONGLIMIT);
return (MessageBox( hDlg,
szWarning,
szCaption,
MB_YESNO | MB_ICONQUESTION ));
}
#ifndef UNICODE
////////////////////////////////////////////////////////////////////////////
//
// EliminateString
//
// Chops the string by the specified length. If a DBCS lead byte is
// left as the last char, then it is removed as well.
//
// NOTE: For non-Unicode strings only.
//
////////////////////////////////////////////////////////////////////////////
VOID EliminateString(
LPSTR lpStr,
int nLen)
{
LPSTR lpChar;
BOOL bFix = FALSE;
*(lpStr + nLen) = CHAR_NULL;
for (lpChar = lpStr + nLen - 1; lpChar >= lpStr; lpChar--)
{
if (!IsDBCSLeadByte(*lpChar))
{
break;
}
bFix = !bFix;
}
if (bFix)
{
*(lpStr + nLen - 1) = CHAR_NULL;
}
}
////////////////////////////////////////////////////////////////////////////
//
// IsBackSlash
//
// Decides whether a character is a '\' or a DBCS trail byte with the same
// code point value.
//
// NOTE: For non-Unicode strings only.
//
////////////////////////////////////////////////////////////////////////////
BOOL IsBackSlash(
LPSTR lpStart,
LPSTR lpChar)
{
if (*lpChar == CHAR_BSLASH)
{
BOOL bRet = TRUE;
while (--lpChar >= lpStart)
{
if (!IsDBCSLeadByte(*lpChar))
{
break;
}
bRet = !bRet;
}
return (bRet);
}
return (FALSE);
}
#endif