Source code of Windows XP (NT5)
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.

1039 lines
27 KiB

  1. /*
  2. * UTILITY.C
  3. *
  4. * Utility routines for functions inside OLE2UI.DLL
  5. *
  6. * General:
  7. * ----------------------
  8. * HourGlassOn Displays the hourglass
  9. * HourGlassOff Hides the hourglass
  10. *
  11. * Misc Tools:
  12. * ----------------------
  13. * Browse Displays the "File..." or "Browse..." dialog.
  14. * ReplaceCharWithNull Used to form filter strings for Browse.
  15. * ErrorWithFile Creates an error message with embedded filename
  16. * OpenFileError Give error message for OpenFile error return
  17. * ChopText Chop a file path to fit within a specified width
  18. * DoesFileExist Checks if file is valid
  19. *
  20. * Registration Database:
  21. * ----------------------
  22. * HIconFromClass Extracts the first icon in a class's server path
  23. * FServerFromClass Retrieves the server path for a class name (fast)
  24. * UClassFromDescription Finds the classname given a description (slow)
  25. * UDescriptionFromClass Retrieves the description for a class name (fast)
  26. * FGetVerb Retrieves a specific verb for a class (fast)
  27. *
  28. *
  29. * Copyright (c)1992 Microsoft Corporation, All Right Reserved
  30. */
  31. #define STRICT 1
  32. #include "ole2ui.h"
  33. #include <stdlib.h>
  34. #include <commdlg.h>
  35. #include <memory.h>
  36. #include <cderr.h>
  37. #include "common.h"
  38. #include "utility.h"
  39. #include "geticon.h"
  40. OLEDBGDATA
  41. /*
  42. * HourGlassOn
  43. *
  44. * Purpose:
  45. * Shows the hourglass cursor returning the last cursor in use.
  46. *
  47. * Parameters:
  48. * None
  49. *
  50. * Return Value:
  51. * HCURSOR Cursor in use prior to showing the hourglass.
  52. */
  53. HCURSOR WINAPI HourGlassOn(void)
  54. {
  55. HCURSOR hCur;
  56. hCur=SetCursor(LoadCursor(NULL, IDC_WAIT));
  57. ShowCursor(TRUE);
  58. return hCur;
  59. }
  60. /*
  61. * HourGlassOff
  62. *
  63. * Purpose:
  64. * Turns off the hourglass restoring it to a previous cursor.
  65. *
  66. * Parameters:
  67. * hCur HCURSOR as returned from HourGlassOn
  68. *
  69. * Return Value:
  70. * None
  71. */
  72. void WINAPI HourGlassOff(HCURSOR hCur)
  73. {
  74. ShowCursor(FALSE);
  75. SetCursor(hCur);
  76. return;
  77. }
  78. /*
  79. * Browse
  80. *
  81. * Purpose:
  82. * Displays the standard GetOpenFileName dialog with the title of
  83. * "Browse." The types listed in this dialog are controlled through
  84. * iFilterString. If it's zero, then the types are filled with "*.*"
  85. * Otherwise that string is loaded from resources and used.
  86. *
  87. * Parameters:
  88. * hWndOwner HWND owning the dialog
  89. * lpszFile LPSTR specifying the initial file and the buffer in
  90. * which to return the selected file. If there is no
  91. * initial file the first character of this string should
  92. * be NULL.
  93. * lpszInitialDir LPSTR specifying the initial directory. If none is to
  94. * set (ie, the cwd should be used), then this parameter
  95. * should be NULL.
  96. * cchFile UINT length of pszFile
  97. * iFilterString UINT index into the stringtable for the filter string.
  98. * dwOfnFlags DWORD flags to OR with OFN_HIDEREADONLY
  99. *
  100. * Return Value:
  101. * BOOL TRUE if the user selected a file and pressed OK.
  102. * FALSE otherwise, such as on pressing Cancel.
  103. */
  104. BOOL WINAPI Browse(HWND hWndOwner, LPTSTR lpszFile, LPTSTR lpszInitialDir, UINT cchFile, UINT iFilterString, DWORD dwOfnFlags)
  105. {
  106. UINT cch;
  107. TCHAR szFilters[256];
  108. OPENFILENAME ofn;
  109. BOOL fStatus;
  110. DWORD dwError;
  111. TCHAR szDlgTitle[128]; // that should be big enough
  112. if (NULL==lpszFile || 0==cchFile)
  113. return FALSE;
  114. /*
  115. * REVIEW: Exact contents of the filter combobox is TBD. One idea
  116. * is to take all the extensions in the RegDB and place them in here
  117. * with the descriptive class name associate with them. This has the
  118. * extra step of finding all extensions of the same class handler and
  119. * building one extension string for all of them. Can get messy quick.
  120. * UI demo has only *.* which we do for now.
  121. */
  122. if (0!=iFilterString)
  123. cch=LoadString(ghInst, iFilterString, (LPTSTR)szFilters, sizeof(szFilters)/sizeof(TCHAR));
  124. else
  125. {
  126. szFilters[0]=0;
  127. cch=1;
  128. }
  129. if (0==cch)
  130. return FALSE;
  131. ReplaceCharWithNull(szFilters, szFilters[cch-1]);
  132. //Prior string must also be initialized, if there is one.
  133. _fmemset((LPOPENFILENAME)&ofn, 0, sizeof(ofn));
  134. ofn.lStructSize =sizeof(ofn);
  135. ofn.hwndOwner =hWndOwner;
  136. ofn.lpstrFile =lpszFile;
  137. ofn.nMaxFile =cchFile;
  138. ofn.lpstrFilter =(LPTSTR)szFilters;
  139. ofn.nFilterIndex=1;
  140. if (LoadString(ghInst, IDS_BROWSE, (LPTSTR)szDlgTitle, sizeof(szDlgTitle)/sizeof(TCHAR)))
  141. ofn.lpstrTitle =(LPTSTR)szDlgTitle;
  142. ofn.hInstance = ghInst;
  143. ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEOPEN);
  144. if (NULL != lpszInitialDir)
  145. ofn.lpstrInitialDir = lpszInitialDir;
  146. ofn.Flags= OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | (dwOfnFlags) ;
  147. //On success, copy the chosen filename to the static display
  148. fStatus = GetOpenFileName((LPOPENFILENAME)&ofn);
  149. dwError = CommDlgExtendedError();
  150. return fStatus;
  151. }
  152. /*
  153. * ReplaceCharWithNull
  154. *
  155. * Purpose:
  156. * Walks a null-terminated string and replaces a given character
  157. * with a zero. Used to turn a single string for file open/save
  158. * filters into the appropriate filter string as required by the
  159. * common dialog API.
  160. *
  161. * Parameters:
  162. * psz LPTSTR to the string to process.
  163. * ch int character to replace.
  164. *
  165. * Return Value:
  166. * int Number of characters replaced. -1 if psz is NULL.
  167. */
  168. int WINAPI ReplaceCharWithNull(LPTSTR psz, int ch)
  169. {
  170. int cChanged=-1;
  171. if (NULL!=psz)
  172. {
  173. while (0!=*psz)
  174. {
  175. if (ch==*psz)
  176. {
  177. *psz=TEXT('\0');
  178. cChanged++;
  179. }
  180. psz++;
  181. }
  182. }
  183. return cChanged;
  184. }
  185. /*
  186. * ErrorWithFile
  187. *
  188. * Purpose:
  189. * Displays a message box built from a stringtable string containing
  190. * one %s as a placeholder for a filename and from a string of the
  191. * filename to place there.
  192. *
  193. * Parameters:
  194. * hWnd HWND owning the message box. The caption of this
  195. * window is the caption of the message box.
  196. * hInst HINSTANCE from which to draw the idsErr string.
  197. * idsErr UINT identifier of a stringtable string containing
  198. * the error message with a %s.
  199. * lpszFile LPSTR to the filename to include in the message.
  200. * uFlags UINT flags to pass to MessageBox, like MB_OK.
  201. *
  202. * Return Value:
  203. * int Return value from MessageBox.
  204. */
  205. int WINAPI ErrorWithFile(HWND hWnd, HINSTANCE hInst, UINT idsErr
  206. , LPTSTR pszFile, UINT uFlags)
  207. {
  208. int iRet=0;
  209. HANDLE hMem;
  210. const UINT cb=(2*OLEUI_CCHPATHMAX_SIZE);
  211. LPTSTR psz1, psz2, psz3;
  212. if (NULL==hInst || NULL==pszFile)
  213. return iRet;
  214. //Allocate three 2*OLEUI_CCHPATHMAX byte work buffers
  215. hMem=GlobalAlloc(GHND, (DWORD)(3*cb));
  216. if (NULL==hMem)
  217. return iRet;
  218. psz1=GlobalLock(hMem);
  219. psz2=psz1+cb;
  220. psz3=psz2+cb;
  221. if (0!=LoadString(hInst, idsErr, psz1, cb))
  222. {
  223. wsprintf(psz2, psz1, pszFile);
  224. //Steal the caption of the dialog
  225. GetWindowText(hWnd, psz3, cb);
  226. iRet=MessageBox(hWnd, psz2, psz3, uFlags);
  227. }
  228. GlobalUnlock(hMem);
  229. GlobalFree(hMem);
  230. return iRet;
  231. }
  232. /*
  233. * HIconFromClass
  234. *
  235. * Purpose:
  236. * Given an object class name, finds an associated executable in the
  237. * registration database and extracts the first icon from that
  238. * executable. If none is available or the class has no associated
  239. * executable, this function returns NULL.
  240. *
  241. * Parameters:
  242. * pszClass LPSTR giving the object class to look up.
  243. *
  244. * Return Value:
  245. * HICON Handle to the extracted icon if there is a module
  246. * associated to pszClass. NULL on failure to either
  247. * find the executable or extract and icon.
  248. */
  249. HICON WINAPI HIconFromClass(LPTSTR pszClass)
  250. {
  251. HICON hIcon;
  252. TCHAR szEXE[OLEUI_CCHPATHMAX];
  253. UINT Index;
  254. CLSID clsid;
  255. if (NULL==pszClass)
  256. return NULL;
  257. CLSIDFromStringA(pszClass, &clsid);
  258. if (!FIconFileFromClass((REFCLSID)&clsid, szEXE, OLEUI_CCHPATHMAX_SIZE, &Index))
  259. return NULL;
  260. hIcon=ExtractIcon(ghInst, szEXE, Index);
  261. if ((HICON)32 > hIcon)
  262. hIcon=NULL;
  263. return hIcon;
  264. }
  265. /*
  266. * FServerFromClass
  267. *
  268. * Purpose:
  269. * Looks up the classname in the registration database and retrieves
  270. * the name undet protocol\StdFileEditing\server.
  271. *
  272. * Parameters:
  273. * pszClass LPSTR to the classname to look up.
  274. * pszEXE LPSTR at which to store the server name
  275. * cch UINT size of pszEXE
  276. *
  277. * Return Value:
  278. * BOOL TRUE if one or more characters were loaded into pszEXE.
  279. * FALSE otherwise.
  280. */
  281. BOOL WINAPI FServerFromClass(LPTSTR pszClass, LPTSTR pszEXE, UINT cch)
  282. {
  283. DWORD dw;
  284. LONG lRet;
  285. HKEY hKey;
  286. if (NULL==pszClass || NULL==pszEXE || 0==cch)
  287. return FALSE;
  288. /*
  289. * We have to go walking in the registration database under the
  290. * classname, so we first open the classname key and then check
  291. * under "\\LocalServer" to get the .EXE.
  292. */
  293. //Open up the class key
  294. lRet=RegOpenKey(HKEY_CLASSES_ROOT, pszClass, &hKey);
  295. if ((LONG)ERROR_SUCCESS!=lRet)
  296. return FALSE;
  297. //Get the executable path.
  298. dw=(DWORD)cch;
  299. lRet=RegQueryValue(hKey, TEXT("LocalServer"), pszEXE, &dw);
  300. RegCloseKey(hKey);
  301. return ((ERROR_SUCCESS == lRet) && (dw > 0));
  302. }
  303. /*
  304. * UClassFromDescription
  305. *
  306. * Purpose:
  307. * Looks up the actual OLE class name in the registration database
  308. * for the given descriptive name chosen from a listbox.
  309. *
  310. * Parameters:
  311. * psz LPSTR to the descriptive name.
  312. * pszClass LPSTR in which to store the class name.
  313. * cb UINT maximum length of pszClass.
  314. *
  315. * Return Value:
  316. * UINT Number of characters copied to pszClass. 0 on failure.
  317. */
  318. UINT WINAPI UClassFromDescription(LPTSTR psz, LPTSTR pszClass, UINT cb)
  319. {
  320. DWORD dw;
  321. HKEY hKey;
  322. TCHAR szClass[OLEUI_CCHKEYMAX];
  323. LONG lRet;
  324. UINT i;
  325. //Open up the root key.
  326. lRet=RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);
  327. if ((LONG)ERROR_SUCCESS!=lRet)
  328. return 0;
  329. i=0;
  330. lRet=RegEnumKey(hKey, i++, szClass, OLEUI_CCHKEYMAX_SIZE);
  331. //Walk the available keys
  332. while ((LONG)ERROR_SUCCESS==lRet)
  333. {
  334. dw=(DWORD)cb;
  335. lRet=RegQueryValue(hKey, szClass, pszClass, &dw);
  336. //Check if the description matches the one just enumerated
  337. if ((LONG)ERROR_SUCCESS==lRet)
  338. {
  339. if (!lstrcmp(pszClass, psz))
  340. break;
  341. }
  342. //Continue with the next key.
  343. lRet=RegEnumKey(hKey, i++, szClass, OLEUI_CCHKEYMAX_SIZE);
  344. }
  345. //If we found it, copy to the return buffer
  346. if ((LONG)ERROR_SUCCESS==lRet)
  347. lstrcpy(pszClass, szClass);
  348. else
  349. dw=0L;
  350. RegCloseKey(hKey);
  351. return (UINT)dw;
  352. }
  353. /*
  354. * UDescriptionFromClass
  355. *
  356. * Purpose:
  357. * Looks up the actual OLE descriptive name name in the registration
  358. * database for the given class name.
  359. *
  360. * Parameters:
  361. * pszClass LPSTR to the class name.
  362. * psz LPSTR in which to store the descriptive name.
  363. * cb UINT maximum length of psz.
  364. *
  365. * Return Value:
  366. * UINT Number of characters copied to pszClass. 0 on failure.
  367. */
  368. UINT WINAPI UDescriptionFromClass(LPTSTR pszClass, LPTSTR psz, UINT cb)
  369. {
  370. DWORD dw;
  371. HKEY hKey;
  372. LONG lRet;
  373. if (NULL==pszClass || NULL==psz)
  374. return 0;
  375. //Open up the root key.
  376. lRet=RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);
  377. if ((LONG)ERROR_SUCCESS!=lRet)
  378. return 0;
  379. //Get the descriptive name using the class name.
  380. dw=(DWORD)cb;
  381. lRet=RegQueryValue(hKey, pszClass, psz, &dw);
  382. RegCloseKey(hKey);
  383. psz+=lstrlen(psz)+1;
  384. *psz=0;
  385. if ((LONG)ERROR_SUCCESS!=lRet)
  386. return 0;
  387. return (UINT)dw;
  388. }
  389. // returns width of line of text. this is a support routine for ChopText
  390. static LONG GetTextWSize(HDC hDC, LPTSTR lpsz)
  391. {
  392. SIZE size;
  393. if (GetTextExtentPoint(hDC, lpsz, lstrlen(lpsz), (LPSIZE)&size))
  394. return size.cx;
  395. else {
  396. return 0;
  397. }
  398. }
  399. /*
  400. * ChopText
  401. *
  402. * Purpose:
  403. * Parse a string (pathname) and convert it to be within a specified
  404. * length by chopping the least significant part
  405. *
  406. * Parameters:
  407. * hWnd window handle in which the string resides
  408. * nWidth max width of string in pixels
  409. * use width of hWnd if zero
  410. * lpch pointer to beginning of the string
  411. *
  412. * Return Value:
  413. * pointer to the modified string
  414. */
  415. LPTSTR WINAPI ChopText(HWND hWnd, int nWidth, LPTSTR lpch)
  416. {
  417. #define PREFIX_SIZE 7 + 1
  418. #define PREFIX_FORMAT TEXT("%c%c%c...\\")
  419. TCHAR szPrefix[PREFIX_SIZE];
  420. BOOL fDone = FALSE;
  421. int i;
  422. RECT rc;
  423. HDC hdc;
  424. HFONT hfont;
  425. HFONT hfontOld = NULL;
  426. if (!hWnd || !lpch)
  427. return NULL;
  428. /* Get length of static field. */
  429. if (!nWidth) {
  430. GetClientRect(hWnd, (LPRECT)&rc);
  431. nWidth = rc.right - rc.left;
  432. }
  433. /* Set up DC appropriately for the static control */
  434. hdc = GetDC(hWnd);
  435. hfont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0L);
  436. if (NULL != hfont) // WM_GETFONT returns NULL if window uses system font
  437. hfontOld = SelectObject(hdc, hfont);
  438. /* check horizontal extent of string */
  439. if (GetTextWSize(hdc, lpch) > nWidth) {
  440. /* string is too long to fit in static control; chop it */
  441. /* set up new prefix & determine remaining space in control */
  442. wsprintf((LPTSTR) szPrefix, PREFIX_FORMAT, lpch[0], lpch[1], lpch[2]);
  443. nWidth -= (int)GetTextWSize(hdc, (LPTSTR) szPrefix);
  444. /*
  445. ** advance a directory at a time until the remainder of the
  446. ** string fits into the static control after the "x:\...\" prefix
  447. */
  448. while (!fDone) {
  449. #ifdef DBCS
  450. while (*lpch && (*lpch != TEXT('\\')))
  451. #ifdef WIN32
  452. lpch = CharNext(lpch);
  453. #else
  454. lpch = AnsiNext(lpch);
  455. #endif
  456. if (*lpch)
  457. #ifdef WIN32
  458. lpch = CharNext(lpch);
  459. #else
  460. lpch = AnsiNext(lpch);
  461. #endif
  462. #else
  463. while (*lpch && (*lpch++ != TEXT('\\')));
  464. #endif
  465. if (!*lpch || GetTextWSize(hdc, lpch) <= nWidth) {
  466. if (!*lpch)
  467. /*
  468. ** Nothing could fit after the prefix; remove the
  469. ** final "\" from the prefix
  470. */
  471. szPrefix[lstrlen((LPTSTR) szPrefix) - 1] = 0;
  472. /* rest or string fits -- stick prefix on front */
  473. for (i = lstrlen((LPTSTR) szPrefix) - 1; i >= 0; --i)
  474. *--lpch = szPrefix[i];
  475. fDone = TRUE;
  476. }
  477. }
  478. }
  479. if (NULL != hfont)
  480. SelectObject(hdc, hfontOld);
  481. ReleaseDC(hWnd, hdc);
  482. return(lpch);
  483. #undef PREFIX_SIZE
  484. #undef PREFIX_FORMAT
  485. }
  486. /*
  487. * OpenFileError
  488. *
  489. * Purpose:
  490. * display message for error returned from OpenFile
  491. *
  492. * Parameters:
  493. * hDlg HWND of the dialog.
  494. * nErrCode UINT error code returned in OFSTRUCT passed to OpenFile
  495. * lpszFile LPSTR file name passed to OpenFile
  496. *
  497. * Return Value:
  498. * None
  499. */
  500. void WINAPI OpenFileError(HWND hDlg, UINT nErrCode, LPTSTR lpszFile)
  501. {
  502. switch (nErrCode) {
  503. case 0x0005: // Access denied
  504. ErrorWithFile(hDlg, ghInst, IDS_CIFILEACCESS, lpszFile, MB_OK);
  505. break;
  506. case 0x0020: // Sharing violation
  507. ErrorWithFile(hDlg, ghInst, IDS_CIFILESHARE, lpszFile, MB_OK);
  508. break;
  509. case 0x0002: // File not found
  510. case 0x0003: // Path not found
  511. ErrorWithFile(hDlg, ghInst, IDS_CIINVALIDFILE, lpszFile, MB_OK);
  512. break;
  513. default:
  514. ErrorWithFile(hDlg, ghInst, IDS_CIFILEOPENFAIL, lpszFile, MB_OK);
  515. break;
  516. }
  517. }
  518. #define chSpace TEXT(' ')
  519. #define chPeriod TEXT('.')
  520. #define PARSE_EMPTYSTRING -1
  521. #define PARSE_INVALIDDRIVE -2
  522. #define PARSE_INVALIDPERIOD -3
  523. #define PARSE_INVALIDDIRCHAR -4
  524. #define PARSE_INVALIDCHAR -5
  525. #define PARSE_WILDCARDINDIR -6
  526. #define PARSE_INVALIDNETPATH -7
  527. #define PARSE_INVALIDSPACE -8
  528. #define PARSE_EXTENTIONTOOLONG -9
  529. #define PARSE_DIRECTORYNAME -10
  530. #define PARSE_FILETOOLONG -11
  531. /*---------------------------------------------------------------------------
  532. * ParseFile
  533. * Purpose: Determine if the filename is a legal DOS name
  534. * Input: Long pointer to a SINGLE file name
  535. * Circumstance checked:
  536. * 1) Valid as directory name, but not as file name
  537. * 2) Empty String
  538. * 3) Illegal Drive label
  539. * 4) Period in invalid location (in extention, 1st in file name)
  540. * 5) Missing directory character
  541. * 6) Illegal character
  542. * 7) Wildcard in directory name
  543. * 8) Double slash beyond 1st 2 characters
  544. * 9) Space character in the middle of the name (trailing spaces OK)
  545. * 10) Filename greater than 8 characters
  546. * 11) Extention greater than 3 characters
  547. * Notes:
  548. * Filename length is NOT checked.
  549. * Valid filenames will have leading spaces, trailing spaces and
  550. * terminating period stripped in place.
  551. *
  552. * Returns: If valid, LOWORD is byte offset to filename
  553. * HIWORD is byte offset to extention
  554. * if string ends with period, 0
  555. * if no extention is given, string length
  556. * If invalid, LOWORD is error code suggesting problem (< 0)
  557. * HIWORD is approximate offset where problem found
  558. * Note that this may be beyond the offending character
  559. *--------------------------------------------------------------------------*/
  560. static long ParseFile(LPTSTR lpstrFileName)
  561. {
  562. short nFile, nExt, nFileOffset, nExtOffset;
  563. BOOL bExt;
  564. BOOL bWildcard;
  565. short nNetwork = 0;
  566. BOOL bUNCPath = FALSE;
  567. LPTSTR lpstr = lpstrFileName;
  568. /* Strip off initial white space. Note that TAB is not checked */
  569. /* because it cannot be received out of a standard edit control */
  570. /* 30 January 1991 clarkc */
  571. while (*lpstr == chSpace)
  572. lpstr++;
  573. if (!*lpstr)
  574. {
  575. nFileOffset = PARSE_EMPTYSTRING;
  576. goto FAILURE;
  577. }
  578. if (lpstr != lpstrFileName)
  579. {
  580. lstrcpy(lpstrFileName, lpstr);
  581. lpstr = lpstrFileName;
  582. }
  583. if (
  584. #ifdef WIN32
  585. *CharNext(lpstr)
  586. #else
  587. *AnsiNext(lpstr)
  588. #endif
  589. == TEXT(':')
  590. )
  591. {
  592. TCHAR cDrive = (*lpstr | (BYTE) 0x20); /* make lowercase */
  593. /* This does not test if the drive exists, only if it's legal */
  594. if ((cDrive < TEXT('a')) || (cDrive > TEXT('z')))
  595. {
  596. nFileOffset = PARSE_INVALIDDRIVE;
  597. goto FAILURE;
  598. }
  599. #ifdef WIN32
  600. lpstr = CharNext(CharNext(lpstr));
  601. #else
  602. lpstr = AnsiNext(AnsiNext(lpstr));
  603. #endif
  604. }
  605. if ((*lpstr == TEXT('\\')) || (*lpstr == TEXT('/')))
  606. {
  607. if (*++lpstr == chPeriod) /* cannot have c:\. */
  608. {
  609. if ((*++lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
  610. {
  611. if (!*lpstr) /* it's the root directory */
  612. goto MustBeDir;
  613. nFileOffset = PARSE_INVALIDPERIOD;
  614. goto FAILURE;
  615. }
  616. else
  617. ++lpstr; /* it's saying top directory (again), thus allowed */
  618. }
  619. else if ((*lpstr == TEXT('\\')) && (*(lpstr-1) == TEXT('\\')))
  620. {
  621. /* It seems that for a full network path, whether a drive is declared or
  622. * not is insignificant, though if a drive is given, it must be valid
  623. * (hence the code above should remain there).
  624. * 13 February 1991 clarkc
  625. */
  626. ++lpstr; /* ...since it's the first slash, 2 are allowed */
  627. nNetwork = -1; /* Must receive server and share to be real */
  628. bUNCPath = TRUE; /* No wildcards allowed if UNC name */
  629. }
  630. else if (*lpstr == TEXT('/'))
  631. {
  632. nFileOffset = PARSE_INVALIDDIRCHAR;
  633. goto FAILURE;
  634. }
  635. }
  636. else if (*lpstr == chPeriod)
  637. {
  638. if (*++lpstr == chPeriod) /* Is this up one directory? */
  639. ++lpstr;
  640. if (!*lpstr)
  641. goto MustBeDir;
  642. if ((*lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
  643. {
  644. nFileOffset = PARSE_INVALIDPERIOD;
  645. goto FAILURE;
  646. }
  647. else
  648. ++lpstr; /* it's saying directory, thus allowed */
  649. }
  650. if (!*lpstr)
  651. {
  652. goto MustBeDir;
  653. }
  654. /* Should point to first char in 8.3 filename by now */
  655. nFileOffset = nExtOffset = nFile = nExt = 0;
  656. bWildcard = bExt = FALSE;
  657. while (*lpstr)
  658. {
  659. /*
  660. * The next comparison MUST be unsigned to allow for extended characters!
  661. * 21 Feb 1991 clarkc
  662. */
  663. if (*lpstr < chSpace)
  664. {
  665. nFileOffset = PARSE_INVALIDCHAR;
  666. goto FAILURE;
  667. }
  668. switch (*lpstr)
  669. {
  670. case TEXT('"'): /* All invalid */
  671. case TEXT('+'):
  672. case TEXT(','):
  673. case TEXT(':'):
  674. case TEXT(';'):
  675. case TEXT('<'):
  676. case TEXT('='):
  677. case TEXT('>'):
  678. case TEXT('['):
  679. case TEXT(']'):
  680. case TEXT('|'):
  681. {
  682. nFileOffset = PARSE_INVALIDCHAR;
  683. goto FAILURE;
  684. }
  685. case TEXT('\\'): /* Subdirectory indicators */
  686. case TEXT('/'):
  687. nNetwork++;
  688. if (bWildcard)
  689. {
  690. nFileOffset = PARSE_WILDCARDINDIR;
  691. goto FAILURE;
  692. }
  693. else if (nFile == 0) /* can't have 2 in a row */
  694. {
  695. nFileOffset = PARSE_INVALIDDIRCHAR;
  696. goto FAILURE;
  697. }
  698. else
  699. { /* reset flags */
  700. ++lpstr;
  701. if (!nNetwork && !*lpstr)
  702. {
  703. nFileOffset = PARSE_INVALIDNETPATH;
  704. goto FAILURE;
  705. }
  706. nFile = nExt = 0;
  707. bExt = FALSE;
  708. }
  709. break;
  710. case chSpace:
  711. {
  712. LPTSTR lpSpace = lpstr;
  713. *lpSpace = TEXT('\0');
  714. while (*++lpSpace)
  715. {
  716. if (*lpSpace != chSpace)
  717. {
  718. *lpstr = chSpace; /* Reset string, abandon ship */
  719. nFileOffset = PARSE_INVALIDSPACE;
  720. goto FAILURE;
  721. }
  722. }
  723. }
  724. break;
  725. case chPeriod:
  726. if (nFile == 0)
  727. {
  728. if (*++lpstr == chPeriod)
  729. ++lpstr;
  730. if (!*lpstr)
  731. goto MustBeDir;
  732. if ((*lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
  733. {
  734. nFileOffset = PARSE_INVALIDPERIOD;
  735. goto FAILURE;
  736. }
  737. ++lpstr; /* Flags are already set */
  738. }
  739. else if (bExt)
  740. {
  741. nFileOffset = PARSE_INVALIDPERIOD; /* can't have one in ext */
  742. goto FAILURE;
  743. }
  744. else
  745. {
  746. nExtOffset = 0;
  747. ++lpstr;
  748. bExt = TRUE;
  749. }
  750. break;
  751. case TEXT('*'):
  752. case TEXT('?'):
  753. if (bUNCPath)
  754. {
  755. nFileOffset = PARSE_INVALIDNETPATH;
  756. goto FAILURE;
  757. }
  758. bWildcard = TRUE;
  759. /* Fall through to normal character processing */
  760. default:
  761. if (bExt)
  762. {
  763. if (++nExt == 1)
  764. nExtOffset = lpstr - lpstrFileName;
  765. else if (nExt > 3)
  766. {
  767. nFileOffset = PARSE_EXTENTIONTOOLONG;
  768. goto FAILURE;
  769. }
  770. if ((nNetwork == -1) && (nFile + nExt > 11))
  771. {
  772. nFileOffset = PARSE_INVALIDNETPATH;
  773. goto FAILURE;
  774. }
  775. }
  776. else if (++nFile == 1)
  777. nFileOffset = lpstr - lpstrFileName;
  778. else if (nFile > 8)
  779. {
  780. /* If it's a server name, it can have 11 characters */
  781. if (nNetwork != -1)
  782. {
  783. nFileOffset = PARSE_FILETOOLONG;
  784. goto FAILURE;
  785. }
  786. else if (nFile > 11)
  787. {
  788. nFileOffset = PARSE_INVALIDNETPATH;
  789. goto FAILURE;
  790. }
  791. }
  792. #ifdef WIN32
  793. lpstr = CharNext(lpstr);
  794. #else
  795. lpstr = AnsiNext(lpstr);
  796. #endif
  797. break;
  798. }
  799. }
  800. /* Did we start with a double backslash but not have any more slashes? */
  801. if (nNetwork == -1)
  802. {
  803. nFileOffset = PARSE_INVALIDNETPATH;
  804. goto FAILURE;
  805. }
  806. if (!nFile)
  807. {
  808. MustBeDir:
  809. nFileOffset = PARSE_DIRECTORYNAME;
  810. goto FAILURE;
  811. }
  812. if ((*(lpstr - 1) == chPeriod) && /* if true, no extention wanted */
  813. (
  814. #ifdef WIN32
  815. *CharNext(lpstr-2)
  816. #else
  817. *AnsiNext(lpstr-2)
  818. #endif
  819. == chPeriod
  820. ))
  821. *(lpstr - 1) = TEXT('\0'); /* Remove terminating period */
  822. else if (!nExt)
  823. FAILURE:
  824. nExtOffset = lpstr - lpstrFileName;
  825. return(MAKELONG(nFileOffset, nExtOffset));
  826. }
  827. /*
  828. * DoesFileExist
  829. *
  830. * Purpose:
  831. * Determines if a file path exists
  832. *
  833. * Parameters:
  834. * lpszFile LPTSTR - file name
  835. * lpOpenBuf OFSTRUCT FAR* - points to the OFSTRUCT structure that
  836. * will receive information about the file when the
  837. * file is first opened. this field is filled by the
  838. * Windows OpenFile API.
  839. *
  840. * Return Value:
  841. * HFILE HFILE_ERROR - file does NOT exist
  842. * file handle (as returned from OpenFile) - file exists
  843. */
  844. HFILE WINAPI DoesFileExist(LPTSTR lpszFile, OFSTRUCT FAR* lpOpenBuf)
  845. {
  846. long nRet;
  847. int i;
  848. static TCHAR *arrIllegalNames[] = {
  849. TEXT("LPT1"),
  850. TEXT("LPT2"),
  851. TEXT("LPT3"),
  852. TEXT("COM1"),
  853. TEXT("COM2"),
  854. TEXT("COM3"),
  855. TEXT("COM4"),
  856. TEXT("CON"),
  857. TEXT("AUX"),
  858. TEXT("PRN")
  859. };
  860. // Check if file name is syntactically correct.
  861. // (OpenFile sometimes crashes if path is not syntactically correct)
  862. nRet = ParseFile(lpszFile);
  863. if (LOWORD(nRet) < 0)
  864. goto error;
  865. // Check is the name is an illegal name (eg. the name of a device)
  866. for (i=0; i < (sizeof(arrIllegalNames)/sizeof(arrIllegalNames[0])); i++) {
  867. if (lstrcmpi(lpszFile, arrIllegalNames[i])==0)
  868. goto error; // illegal name FOUND
  869. }
  870. return OpenFile(lpszFile, lpOpenBuf, OF_EXIST);
  871. error:
  872. _fmemset(lpOpenBuf, 0, sizeof(OFSTRUCT));
  873. lpOpenBuf->nErrCode = 0x0002; // File not found
  874. return HFILE_ERROR;
  875. }
  876.