|
|
/*++
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"
////////////////////////////////////////////////////////////////////////////
//
// 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.
//
if (*lpstr < CHAR_SPACE) { 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.
//
// Returns TRUE upon succes, otherwise FALSE (not enough buffer room),
//
////////////////////////////////////////////////////////////////////////////
BOOL AppendExt( LPTSTR lpszPath, DWORD cchPath, LPCTSTR lpExtension, BOOL bWildcard) { WORD wOffset; SHORT i; TCHAR szExt[MAX_PATH + 1]; BOOL bRet = TRUE;
if (lpExtension && *lpExtension) { wOffset = (WORD)lstrlen(lpszPath); if (bWildcard) { if (wOffset < (cchPath - 1)) { *(lpszPath + wOffset) = CHAR_STAR; wOffset++; } else { bRet = FALSE; } }
if (bRet) { //
// Add a period.
//
if (wOffset < (cchPath - 1)) { *(lpszPath + wOffset) = CHAR_DOT; wOffset++; } else { bRet = FALSE; }
if (bRet) { 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.
//
bRet = SUCCEEDED(StringCchCopy(lpszPath + wOffset, (cchPath - wOffset), szExt)); } } }
return bRet; }
////////////////////////////////////////////////////////////////////////////
//
// 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]; DWORD dwRet;
EVAL(SUCCEEDED(StringCchCopy(szFile, ARRAYSIZE(szFile), lpszFile))); dwRet = ParseFile(szFile, TRUE, FALSE, TRUE); nFileOffset = (SHORT)LOWORD(dwRet);
ASSERT(nFileOffset > 0); 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) { TCHAR szCaption[128]; TCHAR szWarning[128 + MAX_FULLPATHNAME]; BOOL bRet = FALSE;
if (CDLoadString( g_hinst, iszOverwriteQuestion, szCaption, ARRAYSIZE(szCaption))) {
//
// 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; }
if (SUCCEEDED(StringCchPrintf(szWarning, ARRAYSIZE(szWarning), szCaption, szFileName))) { GetWindowText(hDlg, szCaption, ARRAYSIZE(szCaption)); bRet = (MessageBox( hDlg, szWarning, szCaption, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION ) == IDYES); } } return bRet; }
////////////////////////////////////////////////////////////////////////////
//
// CreateFileDlg
//
////////////////////////////////////////////////////////////////////////////
int CreateFileDlg( HWND hDlg, LPTSTR szPath) { TCHAR szCaption[128]; TCHAR szWarning[128 + MAX_FULLPATHNAME]; int nRet = IDNO; //
// 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, ARRAYSIZE(szCaption))) { if (lstrlen(szPath) > TOOLONGLIMIT) { *(szPath + TOOLONGLIMIT) = CHAR_NULL; }
if (SUCCEEDED(StringCchPrintf(szWarning, ARRAYSIZE(szWarning), szCaption, szPath))) { GetWindowText(hDlg, szCaption, ARRAYSIZE(szCaption));
nRet = (MessageBox( hDlg, szWarning, szCaption, MB_YESNO | MB_ICONQUESTION )); } }
return nRet; }
|