Windows NT 4.0 source code leak
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.

3309 lines
78 KiB

4 years ago
  1. /***************************************************************************
  2. *
  3. * HPJ.C
  4. *
  5. * Copyright (C) Microsoft Corporation 1990 - 1994.
  6. * All Rights reserved.
  7. *
  8. ****************************************************************************
  9. *
  10. * Module Intent
  11. *
  12. * This module contains routines for parsing the .HPJ file.
  13. *
  14. ****************************************************************************/
  15. #include "stdafx.h"
  16. #include "cfontmap.h"
  17. #include "..\common\coutput.h"
  18. #include <errno.h> // ERANGE
  19. #include <io.h>
  20. #include <direct.h>
  21. #include <time.h>
  22. #include "..\common\resource.h"
  23. #ifdef _DEBUG
  24. #undef THIS_FILE
  25. static char THIS_FILE[] = __FILE__;
  26. #endif
  27. /*****************************************************************************
  28. * *
  29. * Defines *
  30. * *
  31. *****************************************************************************/
  32. // Parser states
  33. #define CCOM ((STATE) 1) // inside a C-style comment
  34. #define LEADING ((STATE) 2) // still eating leading white space
  35. #define COM_EOL ((STATE) 3) // inside a comment to EOL (either ; or //)
  36. #define SLASH ((STATE) 4) // seen a /
  37. #define CCOMSTAR ((STATE) 5) // maybe about to end a C-style comment
  38. #define MEAT ((STATE) 6) // something on this line isn't a comment
  39. const char SPACE = ' ';
  40. const char CHAR_TAB = '\t';
  41. // Constants for Window Smag stuff
  42. // three possible outcomes of trying to read something out of the buffer:
  43. // bad syntax, read ok, or value wasn't there before the comma
  44. const int WINDOW_READ_INVALID = 0;
  45. const int WINDOW_READ_OK = 1;
  46. const int WINDOW_READ_MISSING = 2;
  47. /*****************************************************************************
  48. * *
  49. * Typedefs *
  50. * *
  51. *****************************************************************************/
  52. typedef RC_TYPE (STDCALL NEAR* PFPARSE)(PSTR);
  53. /*
  54. File Entry structure (for file stack)
  55. REVIEW - currently the full path spec is stored for each file.
  56. */
  57. typedef struct {
  58. CInput* pinput;
  59. char rgchFile[_MAX_PATH];
  60. int iLine; // line number
  61. } FE, *PFE;
  62. /*
  63. File Stack
  64. This is used to store the file handles, names, and current line
  65. numbers of the open .HPJ file and files included therein.
  66. */
  67. typedef struct {
  68. int ifeTop; // index to current file (-1 for empty)
  69. int ifeComment; // file in which C-style comment started
  70. int iCommentLine; // line on which C-style comment started
  71. FE rgfe[MAX_INCLUDE];
  72. } FILESTACK;
  73. typedef FILESTACK* PFILESTACK;
  74. /*****************************************************************************
  75. * *
  76. * Static Variables *
  77. * *
  78. *****************************************************************************/
  79. static PFILESTACK pfs;
  80. const char txtSecondary[] = "secondary";
  81. static const char txtDefine[] = "#define";
  82. static const char txtIfDef[] = "#ifdef";
  83. static const char txtIfnDef[] = "#ifndef";
  84. static int curConfig = 0;
  85. // section table
  86. // The section parsing functions
  87. RC_TYPE STDCALL RcParseAliasSz(PSTR);
  88. RC_TYPE STDCALL RcParseBaggageSz(PSTR);
  89. RC_TYPE STDCALL RcParseBitmapsSz(PSTR);
  90. RC_TYPE STDCALL RcParseBogusSz(PSTR);
  91. RC_TYPE STDCALL RcParseBuildTagsSz(PSTR);
  92. RC_TYPE STDCALL RcParseCharSet(PSTR);
  93. RC_TYPE STDCALL RcParseConfigSz(PSTR);
  94. RC_TYPE STDCALL RcParseFilesSz(PSTR);
  95. RC_TYPE STDCALL RcParseFonts(PSTR);
  96. RC_TYPE STDCALL RcParseMacros(PSTR);
  97. RC_TYPE STDCALL RcParseMapSz(PSTR);
  98. RC_TYPE STDCALL RcParseOptionsSz(PSTR);
  99. RC_TYPE STDCALL RcParseSecondaryConfigSz(PSTR);
  100. RC_TYPE STDCALL RcParseWindowsSz(PSTR);
  101. // Associates a section name with a section parsing function.
  102. typedef struct {
  103. PSTR szName;
  104. PFPARSE pfparse;
  105. } SECTION;
  106. SECTION rgSection[] = {
  107. { "ALIAS", RcParseAliasSz },
  108. { "BAGGAGE", RcParseBaggageSz },
  109. { "BITMAPS", RcParseBitmapsSz },
  110. { "BUILDTAGS", RcParseBuildTagsSz },
  111. { "CONFIG", RcParseConfigSz },
  112. { "FILES", RcParseFilesSz },
  113. { "MAP", RcParseMapSz },
  114. { "OPTIONS", RcParseOptionsSz },
  115. { "WINDOWS", RcParseWindowsSz },
  116. // New to 4.0
  117. { "CHARSET", RcParseCharSet },
  118. { "MACROS", RcParseMacros },
  119. { "FONTS", RcParseFonts },
  120. { NULL, RcParseBogusSz },
  121. };
  122. // Strings used for COMPRESS option in addition to yes/no strings
  123. const char txtLow[] = "low";
  124. const char txtMedium[] = "medium";
  125. const char txtHigh[] = "high";
  126. const char txtNone[] = "none";
  127. const char txtJohn[] = "JOHN";
  128. // Language stuff
  129. char *rgszSortOrder[] = {
  130. "ENGLISH",
  131. "SCANDINAVIAN",
  132. "JAPANESE",
  133. "KOREAN",
  134. "CHINESE",
  135. "CZECH",
  136. "POLISH",
  137. "HUNGARIAN",
  138. "RUSSIAN",
  139. "EASTERN EUROPE",
  140. "NLS",
  141. };
  142. #define MAX_SORT ELEMENTS(rgszSortOrder)
  143. COutput* pLogFile;
  144. static CTable* ptblDefine;
  145. /*****************************************************************************
  146. * *
  147. * Prototypes *
  148. * *
  149. *****************************************************************************/
  150. static void STDCALL DispSignOn(void);
  151. static BOOL STDCALL FIsIconPf(FILE* pf);
  152. static BOOL STDCALL FIsRtf(PSTR);
  153. static BOOL STDCALL FPushFilePfs(PSTR szFile);
  154. static UINT STDCALL FReadInt16(PSTR *psz, WORD* pi, WORD *pgrf, UINT f);
  155. static UINT STDCALL FReadRgb(PSTR *psz, LONG *pl, UINT *pgrf, UINT f);
  156. static void STDCALL ParseMapFontSizeOption(PSTR, PCSTR);
  157. static PFPARSE STDCALL PfparseFromSz(PSTR);
  158. static RC_TYPE STDCALL RcGetLogicalLine(CStr*);
  159. static RC_TYPE STDCALL RcLoadBitmapFm(FM fmSource, FM FAR* pfmDest, UINT FAR* pwObjrg, BOOL fsCompress);
  160. static void STDCALL ReportDuplicateOption(PSTR* pszOption, PSTR pszName);
  161. static void STDCALL ReportBadOption(PSTR pszOptionValue, PSTR pszOption);
  162. static RC_TYPE STDCALL ParseMacros(CStr* pcszLine);
  163. __inline CInput* STDCALL PfTopPfs(void);
  164. __inline BOOL STDCALL FVerifyBuildTag(PSTR psz);
  165. __inline BOOL STDCALL FPopPfs(void);
  166. /***************************************************************************
  167. FUNCTION: SzGetDriveAndDir
  168. PURPOSE: Return the drive letter and directory from a file spec
  169. PARAMETERS:
  170. pszFile - a filespec that may or may not have drive and/or
  171. directory. It isn't NULL or "".
  172. pszDst - buffer to receive the info. If NULL, a buffer
  173. is allocated.
  174. RETURNS: pointer to buffer where the drive and dir are placed.
  175. This may be allocated (see pszDst above).
  176. COMMENTS:
  177. MODIFICATION DATES:
  178. 03-Jul-1993 [ralphw]
  179. ***************************************************************************/
  180. // REVIEW: allocating memory is risky if caller forgets to free it
  181. PSTR STDCALL SzGetDriveAndDir(PCSTR pszFile, PSTR pszDst)
  182. {
  183. if (!pszFile)
  184. return lcStrDup(".");
  185. ASSERT(pszFile != NULL);
  186. char szFileCopy[_MAX_FNAME];
  187. strcpy(szFileCopy, pszFile);
  188. PSTR psz;
  189. for (psz = szFileCopy + strlen(szFileCopy) - 1;
  190. psz > szFileCopy &&
  191. ((*psz != ':' && *psz != '\\' && *psz != '/') ||
  192. IsFirstByte(*(psz - 1)));
  193. --psz);
  194. int cb = psz - szFileCopy + 1;
  195. if (pszDst == NULL)
  196. pszDst = (PSTR) lcMalloc(cb + 1);
  197. if (cb > 1)
  198. memcpy(pszDst, szFileCopy, cb);
  199. else
  200. cb = 0;
  201. pszDst[cb] = '\0';
  202. return pszDst;
  203. }
  204. /***************************************************************************\
  205. *
  206. - Function: FIsRtf( szFile )
  207. -
  208. * Purpose: Determine whether szFile is an RTF file (or looks like one).
  209. *
  210. * ASSUMES
  211. *
  212. * args IN: szFile - filename
  213. *
  214. * PROMISES
  215. *
  216. * returns: TRUE if file contains a normal looking header
  217. * FALSE if there is some i/o error or it doesn't
  218. * contain rtf header info.
  219. *
  220. * Side Effects: File is opened and closed.
  221. *
  222. \***************************************************************************/
  223. static BOOL STDCALL FIsRtf(PSTR pszFile)
  224. {
  225. char szBuf[_MAX_PATH];
  226. // easy way to check for devices (aux, prn, com1)
  227. if (HceResolveFileNameSz(pszFile, NULL, szBuf) != HCE_OK)
  228. return FALSE;
  229. CRead cr(szBuf);
  230. if (cr.hf == HFILE_ERROR)
  231. return FALSE;
  232. cr.read(szBuf, 5);
  233. if (_stricmp(szBuf, "{\\rtf") == 0) // is this an RTF file?
  234. return TRUE;
  235. else
  236. return FALSE;
  237. }
  238. /***************************************************************************\
  239. *
  240. - Function: FIsIconPf( pf )
  241. -
  242. * Purpose: Determine whether pf is an icon file (or looks like one).
  243. *
  244. * ASSUMES
  245. *
  246. * args IN: pf - file pointer
  247. *
  248. * PROMISES
  249. *
  250. * returns: TRUE if file contains a normal looking header
  251. * FALSE if there is some i/o error or it doesn't
  252. * contain icon header info.
  253. *
  254. * Method: Read the first four bytes and check that they match
  255. * what an .ICO file has.
  256. *
  257. * Note: This takes a FILE * rather than a filename because
  258. * I have one handle.
  259. *
  260. \***************************************************************************/
  261. static BOOL STDCALL FIsIconPf(FILE* pf)
  262. {
  263. char szBuf[4];
  264. return (fread(szBuf, 1, 4, pf) == 4 &&
  265. szBuf[0] == 0 && szBuf[1] == 0 && szBuf[2] == 1 && szBuf[3] == 0);
  266. }
  267. /***************************************************************************\
  268. *
  269. - Function: HceResolveFileNameSz( szFile, szRoot, szBuffer, ppf )
  270. -
  271. * Purpose: Resolve a filename relative to a given root directory.
  272. * If the filename is absolute (either drive letter or
  273. * leading '\' given), the root directory is ignored.
  274. *
  275. * ASSUMES
  276. *
  277. * args IN: szFile - the file name (non-null)
  278. * szRoot - the home directory name; full path with drive
  279. * (NULL OK)
  280. * ppf - pointer to FILE* for open file (NULL to not open)
  281. *
  282. * PROMISES
  283. *
  284. * returns: HCE_OK
  285. * hceFileNameTooLong
  286. * hcePathNameTooLong
  287. * hceBadlyFormed
  288. * hceFileNotFound
  289. * hceFileIsDirectory
  290. * hceFileIsDevice
  291. *
  292. * args OUT: szBuffer - resolved file name goes here if successful
  293. * ppf - open FILE * placed here
  294. *
  295. *
  296. * Notes: Doesn't check for badly formed file names, such as
  297. * filenames containing spaces, multiple colons, etc.
  298. * Such bogus names will just come back as an error
  299. * opening the file.
  300. *
  301. * stat() doesn't tell me if the file is a device.
  302. * I use isatty().
  303. *
  304. \***************************************************************************/
  305. HCE STDCALL HceResolveFileNameSz(PSTR szFile, PSTR pszRoot,
  306. PSTR pszBuffer, FILE** ppf, CInput** ppinput)
  307. {
  308. ASSERT(pszBuffer);
  309. int cbRoot = (pszRoot == NULL) ? 0 : strlen(pszRoot);
  310. if (strlen(szFile) + cbRoot >= _MAX_PATH)
  311. return HCE_NAME_TOO_LONG;
  312. if (*szFile == '\\' || szFile[1] == ':' ||
  313. pszRoot == NULL || *pszRoot == '\0')
  314. pszBuffer[0] = '\0';
  315. else {
  316. // last char of pszRoot if any
  317. strcpy(pszBuffer, pszRoot);
  318. // [olympus 306 - chauv]
  319. // changed code to check for trailing backslash
  320. int i = strlen(pszBuffer);
  321. if ( ((i > 1) && (pszBuffer[i-1] == '\\') && (IsDBCSLeadByte(pszBuffer[i-2]))) ||
  322. ( (i > 0) && (pszBuffer[i-1] != '\\') && (pszBuffer[i-1] != ':')) )
  323. strcat(pszBuffer, "\\");
  324. }
  325. strcat(pszBuffer, szFile);
  326. /*
  327. * If we have a replacement string, and we have a path specification,
  328. * and the first part of that path matches our replace string, then
  329. * perform a substitution. Its up to the help author to specify a valid
  330. * replacement string.
  331. */
  332. if (options.pszReplace && (StrChr(pszBuffer, CH_BACKSLASH, fDBCSSystem) ||
  333. StrChr(pszBuffer, CH_COLON, fDBCSSystem))) {
  334. if (nstrisubcmp(pszBuffer, options.pszReplace)) {
  335. CStr szTmp(pszBuffer);
  336. strcpy(pszBuffer, options.pszReplaceWith);
  337. strcat(pszBuffer, (PSTR) szTmp + strlen(options.pszReplace));
  338. }
  339. }
  340. if (ppinput) {
  341. *ppinput = new CInput(pszBuffer);
  342. if (!(*ppinput)->fInitialized) {
  343. delete *ppinput;
  344. *ppinput = NULL;
  345. DWORD attribute = GetFileAttributes(pszBuffer);
  346. if (attribute == HFILE_ERROR)
  347. return HCE_FILE_NOT_FOUND;
  348. else if (attribute == FILE_ATTRIBUTE_DIRECTORY)
  349. return HCE_FILE_IS_DIRECTORY;
  350. else
  351. return HCE_CANNOT_OPEN;
  352. }
  353. return HCE_OK;
  354. }
  355. FILE *pf;
  356. if (!iflags.fTrusted || ppf) {
  357. /*
  358. * REVIEW: 12-Apr-1994 [ralphw] if ppf == NULL, the only reason
  359. * for opening this is to see if the file is a device (isatty). For
  360. * very small RTF files, this is a significant time hit (believe it
  361. * or not!) Might be worthwhile to simply remove it and simply check
  362. * for known device names.
  363. */
  364. if (!ppf) {
  365. if (GetFileAttributes(pszBuffer) != HFILE_ERROR) {
  366. return HCE_OK;
  367. }
  368. else {
  369. return HCE_FILE_NOT_FOUND;
  370. }
  371. }
  372. pf = fopen(pszBuffer, "r");
  373. if (pf == NULL) {
  374. DWORD attribute = GetFileAttributes(pszBuffer);
  375. if (attribute == HFILE_ERROR)
  376. return HCE_FILE_NOT_FOUND;
  377. else if (attribute == FILE_ATTRIBUTE_DIRECTORY)
  378. return HCE_FILE_IS_DIRECTORY;
  379. else
  380. return HCE_NO_PERMISSION; // REVIEW - can this be anything else?
  381. }
  382. if (_isatty(_fileno(pf))) {
  383. fclose(pf);
  384. return HCE_FILE_IS_DEVICE;
  385. }
  386. if (ppf == NULL)
  387. fclose(pf);
  388. else
  389. *ppf = pf;
  390. }
  391. return HCE_OK;
  392. }
  393. HCE STDCALL HceResolveTableDir(PSTR pszFile, CTable *ptblDir,
  394. PSTR pszBuffer, FILE** ppf)
  395. {
  396. HCE hce;
  397. if (!ptblDir)
  398. return HceResolveFileNameSz(pszFile, NULL, pszBuffer, ppf);
  399. if (ptblDir->CountStrings() > 0) {
  400. for (int pos = 1; pos <= ptblDir->CountStrings(); pos++) {
  401. hce = HceResolveFileNameSz(pszFile, ptblDir->GetPointer(pos),
  402. pszBuffer, ppf);
  403. if (hce != HCE_FILE_NOT_FOUND)
  404. return hce;
  405. }
  406. }
  407. return HCE_FILE_NOT_FOUND;
  408. }
  409. /***************************************************************************\
  410. *
  411. - Function: RcParseBogusSz( sz)
  412. -
  413. * Status: stub
  414. *
  415. * Purpose: Parse an invalid line. (is this function really needed?
  416. maybe, if don't want to spew endless error messages....
  417. *
  418. * ASSUMES
  419. *
  420. * args IN: sz - string
  421. *
  422. * PROMISES
  423. *
  424. * returns: success code (seems meaningless in this instance)
  425. *
  426. \***************************************************************************/
  427. RC_TYPE STDCALL RcParseBogusSz(PSTR sz)
  428. {
  429. Unreferenced(sz);
  430. return RC_Success;
  431. }
  432. /***************************************************************************\
  433. *
  434. - Function: ParseMapFontSizeOption( pszRHS, poptions, perr )
  435. -
  436. * Purpose: Parse the string into the FONTRANGE struct.
  437. *
  438. * "mapfontsize" "=" <number> "-" <number> ":" <number> |
  439. * "mapfontsize" "=" <number> ":" <number>
  440. * <number> refers to size in points.
  441. *
  442. * ASSUMES
  443. *
  444. * args IN: pszRHS -
  445. * poptions -
  446. * perr -
  447. *
  448. * PROMISES
  449. *
  450. * args OUT: poptions - iFontRangeMac, rgFontRange updated.
  451. *
  452. * Side Effects: May emit one of the following errors:
  453. *
  454. * hceInvalidFontRangeFormat
  455. * hceTooManyFontRanges
  456. * hceFontRangeOverlap
  457. *
  458. * Notes: Doesn't recognize fractional points.
  459. *
  460. \***************************************************************************/
  461. static void STDCALL ParseMapFontSizeOption(PSTR pszRHS, PCSTR pszLine)
  462. {
  463. HALFPT halfptInMin, halfptInMost, halfptOut;
  464. UINT u;
  465. int i;
  466. FONTRANGE* qfrT;
  467. // clear flag because this option can occur > 1 time
  468. ClearUlFlag(options.lOptionInitFlags, OPT_MAPFONTSIZE);
  469. if (MAX_FONTRANGE <= options.iFontRangeMac) {
  470. VReportError(HCERR_TOO_MANY_FONT_RANGES, &errHpj);
  471. return;
  472. }
  473. if (!FGetUnsigned(pszRHS, &pszRHS, &u)) {
  474. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  475. return;
  476. }
  477. halfptInMin = (HALFPT) (u * 2); // REVIEW - more range checking?
  478. if (*pszRHS == '-') {
  479. pszRHS = FirstNonSpace(pszRHS + 1, fDBCSSystem);
  480. if (!FGetUnsigned(pszRHS, &pszRHS, &u)) {
  481. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  482. return;
  483. }
  484. halfptInMost = (HALFPT) (u * 2); // REVIEW - more range checking?
  485. if (halfptInMost < halfptInMin) {
  486. HALFPT halfptT = halfptInMost;
  487. halfptInMost = halfptInMin;
  488. halfptInMin = halfptT;
  489. }
  490. if (':' != *pszRHS) {
  491. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  492. return;
  493. }
  494. pszRHS = FirstNonSpace(pszRHS, fDBCSSystem);
  495. }
  496. else if (*pszRHS == ':')
  497. halfptInMost = halfptInMin;
  498. else {
  499. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  500. return;
  501. }
  502. pszRHS = FirstNonSpace(pszRHS + 1, fDBCSSystem);
  503. if (!FGetUnsigned(pszRHS, &pszRHS, &u)) {
  504. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  505. return;
  506. }
  507. halfptOut = (HALFPT) (2 * u); // REVIEW - more range checking?
  508. if (*FirstNonSpace(pszRHS, fDBCSSystem)) {
  509. VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine);
  510. return;
  511. }
  512. // Check for font range overlap.
  513. for (i = options.iFontRangeMac - 1, qfrT = options.rgFontRange;
  514. i >= 0;
  515. i--, qfrT++) {
  516. if (! (halfptInMin > qfrT->halfptInMost ||
  517. halfptInMost < qfrT->halfptInMin)) {
  518. VReportError(HCERR_FONT_OVERLAP, &errHpj, pszLine);
  519. return;
  520. }
  521. }
  522. options.rgFontRange[options.iFontRangeMac].halfptInMin = halfptInMin;
  523. options.rgFontRange[options.iFontRangeMac].halfptInMost = halfptInMost;
  524. options.rgFontRange[options.iFontRangeMac++].halfptOut = halfptOut;
  525. }
  526. // ParsePath - Adds filenames in szPath to the specified table.
  527. // ptbl - table to add filenames to
  528. // szPath - string containing zero or more filenames, separated
  529. // by commas or tabs
  530. // opt - option we're parsing the path for (used for error reporting)
  531. //
  532. // Modified:
  533. // 2/28/95 (niklasb): removed space from pchDelimeter because
  534. // long filenames may contain spaces (fixes bug 9605).
  535. // Also added this nifty comment.
  536. void STDCALL ParsePath(CTable* ptbl, PSTR szPath, OPT opt)
  537. {
  538. PSTR psz;
  539. char szNewPath[_MAX_PATH];
  540. HCE hce;
  541. BOOL fBadPath = FALSE;
  542. PSTR pszDir = SzGetDriveAndDir(errHpj.lpszFile, NULL);
  543. // REVIEW (niklasb): this is broken if we every have multiple
  544. // filenames separated by spaces, but we just have to make
  545. // sure that never occurs because LFNs can contain spaces.
  546. static PSTR pchDelimeter = ",\t";
  547. for (psz = StrToken(szPath, pchDelimeter);
  548. psz != NULL;
  549. psz = StrToken(NULL, pchDelimeter)) {
  550. if (strlen(psz) >= _MAX_PATH) {
  551. fBadPath = TRUE;
  552. VReportError(HCERR_PATH_TOO_LONG, &errHpj, psz);
  553. continue;
  554. }
  555. // If the current .HPJ file isn't in the current directory,
  556. // we need to resolve sz to the dir containing it.
  557. hce = HceResolveFileNameSz(psz, pszDir, szNewPath);
  558. if (hce == HCE_OK) {
  559. DWORD attribute = GetFileAttributes(szNewPath);
  560. if (attribute == HFILE_ERROR)
  561. hce = HCE_FILE_NOT_FOUND;
  562. else if (attribute & FILE_ATTRIBUTE_DIRECTORY)
  563. hce = HCE_FILE_IS_DIRECTORY;
  564. }
  565. if (hce == HCE_FILE_IS_DIRECTORY) {
  566. if (!ptbl->AddString(szNewPath)) {
  567. OOM();
  568. break;
  569. }
  570. }
  571. else {
  572. fBadPath = TRUE;
  573. if (hce == HCE_OK || hce == HCE_FILE_NOT_FOUND)
  574. hce = HCE_INVALID_PATH;
  575. VReportError(HCERR_INVALID_PATH, &errHpj, psz, ppszOptions[opt]);
  576. break;
  577. }
  578. }
  579. lcFree(pszDir);
  580. }
  581. /***************************************************************************\
  582. *
  583. - Function: RcParseOptionsSz( sz)
  584. -
  585. * Purpose: Parse a line in the [options] section. (see grammar)
  586. *
  587. * ASSUMES
  588. *
  589. * args IN: sz - line to parse, with comments stripped
  590. *
  591. * PROMISES
  592. *
  593. * returns: success code. Meaning?
  594. *
  595. * +++
  596. * Method: All options are of the form <keyword> = [smag].
  597. * We strip out the keyword and look it up in a table.
  598. * Then we parse the smag as appropriate.
  599. *
  600. \***************************************************************************/
  601. RC_TYPE STDCALL RcParseOptionsSz(PSTR pszLine)
  602. {
  603. HCE hce;
  604. PSTR psz;
  605. PSTR pszEq = StrChr(pszLine, '=', fDBCSSystem);
  606. if (pszEq == NULL) {
  607. VReportError(HCERR_MISSING_VALUE, &errHpj, pszLine);
  608. return RC_Success;
  609. }
  610. *pszEq = '\0';
  611. SzTrimSz(pszLine);
  612. PSTR pszOptionValue = FirstNonSpace(pszEq + 1, fDBCSSystem);
  613. if (!*pszOptionValue) {
  614. VReportError(HCERR_MISSING_VALUE, &errHpj, pszLine);
  615. return RC_Success;
  616. }
  617. ASSERT(MAX_OPT >= OPT_INDEX);
  618. int opt;
  619. for (opt = 0; opt < MAX_OPT; opt++) {
  620. if (_stricmp(pszLine, ppszOptions[opt]) == 0)
  621. break;
  622. }
  623. if (opt == MAX_OPT) {
  624. VReportError(HCERR_UNKNOWN_OPTION, &errHpj, pszLine);
  625. return RC_Success;
  626. }
  627. // Nag if the we've seen the option before, but otherwise continue
  628. if (FTestUlFlag(options.lOptionInitFlags, opt))
  629. VReportError(HCERR_DUPLICATE_OPTION, &errHpj, pszLine);
  630. if (opt != OPT_BMROOT && opt != OPT_ROOT)
  631. SetUlFlag(options.lOptionInitFlags, opt);
  632. switch (opt) {
  633. case OPT_BMROOT:
  634. if (!options.ptblBmpRoot)
  635. options.ptblBmpRoot = new CTable;
  636. ParsePath(options.ptblBmpRoot, pszOptionValue, (OPT) opt);
  637. break;
  638. case OPT_BUILD:
  639. /*
  640. * don't use strdup() because we need space for the extra ")"
  641. * tacked on in parsebld.c.
  642. */
  643. if (options.szBuildExp)
  644. lcFree(options.szBuildExp);
  645. options.szBuildExp = (PSTR) lcMalloc(strlen(pszOptionValue) + 2);
  646. strcpy(options.szBuildExp, pszOptionValue);
  647. break;
  648. case OPT_COMPRESS:
  649. if (isdigit(*pszOptionValue)) {
  650. if (!FGetUnsigned(pszOptionValue, &pszOptionValue,
  651. &options.fsCompress)) {
  652. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  653. options.fsCompress = COMPRESS_NONE;
  654. }
  655. }
  656. else {
  657. switch (YesNo(pszOptionValue)) {
  658. case IDYES:
  659. options.fsCompress = COMPRESS_MAXIMUM;
  660. break;
  661. case IDNO:
  662. options.fsCompress = COMPRESS_NONE;
  663. break;
  664. default:
  665. // Check for special compress options
  666. if (!_stricmp(pszOptionValue, txtLow))
  667. options.fsCompress = COMPRESS_TEXT_ZECK;
  668. else if (!_stricmp(pszOptionValue, txtHigh))
  669. options.fsCompress = COMPRESS_MAXIMUM;
  670. else if (!_stricmp(pszOptionValue, txtNone))
  671. options.fsCompress = COMPRESS_NONE;
  672. // Medium compression is the same as low for text, but
  673. // with maximum compression for bitmaps
  674. else if (!_stricmp(pszOptionValue, txtMedium))
  675. options.fsCompress = COMPRESS_TEXT_ZECK |
  676. (COMPRESS_BMP_RLE | COMPRESS_BMP_ZECK);
  677. else
  678. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  679. break;
  680. }
  681. }
  682. /*
  683. * Phrase and Hall compression cannot be used simultaneously, so
  684. * if both are specified we default to Hall compression.
  685. */
  686. if (options.fsCompress & COMPRESS_TEXT_PHRASE &&
  687. options.fsCompress & COMPRESS_TEXT_HALL)
  688. options.fsCompress &= ~COMPRESS_TEXT_PHRASE;
  689. // Maximum text compression also means maximum bitmap compression
  690. if (options.fsCompress &
  691. (COMPRESS_TEXT_PHRASE | COMPRESS_TEXT_HALL | COMPRESS_TEXT_ZECK))
  692. options.fsCompress |= (COMPRESS_BMP_RLE | COMPRESS_BMP_ZECK);
  693. else
  694. options.fsCompress |= COMPRESS_BMP_RLE;
  695. break;
  696. case OPT_CDROM: // OPTCDROM = -> optimize |topic alignment
  697. switch (YesNo(pszOptionValue)) {
  698. case IDYES:
  699. options.fOptCdRom = TRUE;
  700. break;
  701. case IDNO:
  702. options.fOptCdRom = FALSE;
  703. break;
  704. default:
  705. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  706. break;
  707. }
  708. break;
  709. case OPT_ERRORLOG:
  710. {
  711. // REVIEW: If we can't open the error log, should we terminate?
  712. char szFile[_MAX_PATH];
  713. HCE hce = HceResolveFileNameSz(pszOptionValue, NULL, szFile);
  714. if (hce == HCE_OK || hce == HCE_FILE_NOT_FOUND) {
  715. if (pLogFile)
  716. delete pLogFile;
  717. pLogFile = new COutput(szFile, 2 * 1024);
  718. if (!pLogFile->fInitialized)
  719. VReportError(HCERR_CANNOT_WRITE, &errHpj, szFile);
  720. else {
  721. DispSignOn();
  722. pLogFile->outstring_eol(pfs->rgfe[0].rgchFile);
  723. }
  724. }
  725. else
  726. VReportError(HCERR_CANNOT_WRITE, &errHpj, szFile);
  727. break;
  728. }
  729. case OPT_FORCEFONT:
  730. if (strlen(pszOptionValue) >= MAX4_FONTNAME - 2)
  731. VReportError(HCERR_FONTNAME_TOO_LONG, &errHpj, pszOptionValue);
  732. else {
  733. if (options.pszForceFont)
  734. lcFree(options.pszForceFont);
  735. options.pszForceFont = lcStrDup(pszOptionValue);
  736. SetForcedFont(options.pszForceFont);
  737. }
  738. break;
  739. case OPT_ICON:
  740. {
  741. if (options.pszIcon)
  742. ReportDuplicateOption(&options.pszIcon, ppszOptions[opt]);
  743. FILE *pf;
  744. char szBuf[_MAX_PATH];
  745. if (options.ptblFileRoot)
  746. hce = HceResolveTableDir(pszOptionValue, options.ptblFileRoot,
  747. szBuf, (FILE **) &pf);
  748. else {
  749. PSTR szDir = SzGetDriveAndDir(errHpj.lpszFile, NULL);
  750. hce = HceResolveFileNameSz(pszOptionValue, szDir, szBuf, &pf);
  751. lcFree(szDir);
  752. }
  753. if (hce != HCE_OK) {
  754. VReportError(HCERR_CANNOT_OPEN, &errHpj, pszOptionValue);
  755. }
  756. else {
  757. if (!FIsIconPf(pf)) {
  758. VReportError(HCERR_INVALID_ICON, &errHpj, pszOptionValue);
  759. }
  760. else {
  761. options.pszIcon = lcStrDup(szBuf);
  762. }
  763. fclose(pf);
  764. }
  765. break;
  766. }
  767. case OPT_HLP:
  768. strcpy(szHlpFile, pszOptionValue);
  769. break;
  770. case OPT_HCW:
  771. break; // ignore it -- it's used by HCW
  772. case OPT_FTS:
  773. if (!FGetNum(pszOptionValue, NULL, &options.fsFTS)) {
  774. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  775. }
  776. break;
  777. case OPT_CONTENTS:
  778. // REVIEW - unfinished: check syntax
  779. if (options.pszContentsTopic)
  780. ReportDuplicateOption(&options.pszContentsTopic, ppszOptions[opt]);
  781. if (pszOptionValue)
  782. options.pszContentsTopic = lcStrDup(pszOptionValue);
  783. break;
  784. case OPT_LANGUAGE:
  785. {
  786. VReportError(HCERR_OLD_SORT, &errHpj);
  787. for (int i = 0; i < MAX_SORT; i++) {
  788. if (!_stricmp(pszOptionValue, rgszSortOrder[i]))
  789. break;
  790. }
  791. if (MAX_SORT == i)
  792. VReportError(HCERR_UNKNOWN_LANGUAGE, &errHpj, pszOptionValue);
  793. else
  794. options.sortorder = (SORTORDER) i;
  795. switch (options.sortorder) {
  796. /*
  797. * Note that the only case-sensitive key types are for
  798. * standard and NLS comparisons. The rest of the sorts
  799. * are case-insensitive only. They are also here only
  800. * for backwards compatibility. All 4.0 help files should
  801. * be using either standard or NLS sorting.
  802. */
  803. case SORT_ENGLISH:
  804. ktKeywordi = KT_SZI;
  805. ktKeyword = KT_SZ;
  806. break;
  807. case SORT_SCANDINAVIAN:
  808. // REVIEW: is this Swedish?
  809. ktKeywordi = KT_SZISCAND;
  810. ktKeyword = KT_SZISCAND;
  811. break;
  812. // REVIEW: the following only work with WinHelp 4.0.
  813. case SORT_CHINESE:
  814. kwlcid.langid = 0x0404;
  815. options.fDBCS = TRUE;
  816. goto NLS;
  817. case SORT_JAPANESE:
  818. kwlcid.langid = 0x0411;
  819. options.fDBCS = TRUE;
  820. goto NLS;
  821. case SORT_KOREAN:
  822. kwlcid.langid = 0x0412;
  823. options.fDBCS = TRUE;
  824. goto NLS;
  825. case SORT_POLISH:
  826. kwlcid.langid = 0x0415;
  827. goto NLS;
  828. case SORT_HUNGARIAN:
  829. kwlcid.langid = 0x040E;
  830. goto NLS;
  831. case SORT_RUSSIAN:
  832. kwlcid.langid = 0x0419;
  833. goto NLS;
  834. case SORT_CZECH:
  835. kwlcid.langid = 0x0405;
  836. goto NLS;
  837. case SORT_NLS:
  838. /*
  839. * This is the prefered language flag -- it uses
  840. * whatever the locale id is for the current
  841. * environment. This ensures that the sorting order will
  842. * remain unchanged no matter what language windows is
  843. * running when the help file is displayed.
  844. */
  845. NLS:
  846. ktKeywordi = KT_NLSI;
  847. ktKeyword = KT_NLS;
  848. if (!kwlcid.langid)
  849. kwlcid.langid = GetUserDefaultLangID();
  850. lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT);
  851. break;
  852. default:
  853. ASSERT(FALSE);
  854. break;
  855. }
  856. }
  857. break;
  858. case OPT_MAPFONTSIZE:
  859. ParseMapFontSizeOption(pszOptionValue, pszLine);
  860. break;
  861. case OPT_MULTIKEY:
  862. while (*pszOptionValue)
  863. HceAddPkwiCh(*pszOptionValue++);
  864. // clear flag because this option can occur > 1 time
  865. ClearUlFlag(options.lOptionInitFlags, OPT_MULTIKEY);
  866. break;
  867. case OPT_REPORT:
  868. switch (YesNo(pszOptionValue)) {
  869. case IDYES:
  870. options.fReport = TRUE;
  871. break;
  872. case IDNO:
  873. options.fReport = FALSE;
  874. break;
  875. default:
  876. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  877. break;
  878. }
  879. break;
  880. case OPT_ROOT:
  881. if (!options.ptblFileRoot)
  882. options.ptblFileRoot = new CTable;
  883. ParsePath(options.ptblFileRoot, pszOptionValue, (OPT) opt);
  884. break;
  885. case OPT_TITLE:
  886. if (options.pszTitle)
  887. ReportDuplicateOption(&options.pszTitle, ppszOptions[opt]);
  888. if (strlen(pszOptionValue) > CBMAXTITLE) {
  889. pszOptionValue[CBMAXTITLE + 1] = '\0';
  890. VReportError(HCERR_TITLE_TOO_BIG, &errHpj, pszOptionValue);
  891. }
  892. options.pszTitle = lcStrDup(pszOptionValue);
  893. break;
  894. case OPT_PHRASE:
  895. switch (YesNo(pszOptionValue)) {
  896. case IDYES:
  897. options.fUsePhrase = TRUE;
  898. break;
  899. case IDNO:
  900. options.fUsePhrase = FALSE;
  901. break;
  902. default:
  903. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  904. break;
  905. }
  906. break;
  907. case OPT_WARNING:
  908. // We simply ignore this
  909. break;
  910. case OPT_COPYRIGHT:
  911. if (options.pszCopyright)
  912. ReportDuplicateOption(&options.pszCopyright, ppszOptions[opt]);
  913. if (strlen(pszOptionValue) > CBMAXCOPYRIGHT) {
  914. pszOptionValue[CBMAXCOPYRIGHT + 1] = '\0';
  915. VReportError(HCERR_CPRIGHT_TOO_BIG, &errHpj, pszOptionValue);
  916. }
  917. {
  918. PSTR psz;
  919. if ((psz = strstr(pszOptionValue, "%date"))) {
  920. time_t ltime;
  921. time(&ltime);
  922. *psz = '\0';
  923. CStr csz(pszOptionValue);
  924. if (pszOptionValue != psz)
  925. csz += "\r\n";
  926. csz += ctime(&ltime);
  927. PSTR pszCr = strrchr(csz.psz, '\n');
  928. if (pszCr)
  929. *pszCr = '\0';
  930. psz = FirstNonSpace(psz + 5);
  931. if ((psz = strstr(psz, "%ver"))) {
  932. csz += "\r\n";
  933. csz += GetStringResource(IDS_VERSION);
  934. psz = FirstNonSpace(psz + 4);
  935. }
  936. csz += psz;
  937. options.pszCopyright = lcStrDup(csz.psz);
  938. }
  939. else
  940. options.pszCopyright = lcStrDup(pszOptionValue);
  941. if (!options.pszCopyright) {
  942. OOM();
  943. }
  944. }
  945. break;
  946. case OPT_CNT:
  947. if (options.pszCntFile)
  948. ReportDuplicateOption(&options.pszCntFile, ppszOptions[opt]);
  949. // REVIEW: we should nag if the .CNT file doesn't exist
  950. options.pszCntFile = lcStrDup(pszOptionValue);
  951. if (!options.pszCntFile)
  952. OOM();
  953. break;
  954. case OPT_CITATION:
  955. if (options.pszCitation)
  956. ReportDuplicateOption(&options.pszCitation, ppszOptions[opt]);
  957. options.pszCitation = lcStrDup(pszOptionValue);
  958. break;
  959. case OPT_VERSION:
  960. if (*pszOptionValue != '4' || pszOptionValue[2] != '0')
  961. VReportError(HCERR_INVALID_VERSION, &errHpj, pszOptionValue);
  962. break;
  963. case OPT_NOTE:
  964. switch (YesNo(pszOptionValue)) {
  965. case IDYES:
  966. options.fSupressNotes = FALSE;
  967. break;
  968. case IDNO:
  969. options.fSupressNotes = TRUE;
  970. break;
  971. default:
  972. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  973. break;
  974. }
  975. break;
  976. case OPT_LCID: // new to 4.0
  977. // LCID = lcid [case-sensitive flags] [case-insensitive flags]
  978. // This option automatically uses KT_NLS for keyword and browse
  979. // sorts
  980. if (kwlcid.langid)
  981. VReportError(HCERR_DUPLICATE_OPTION, &errHpj, ppszOptions[opt]);
  982. if (!isdigit((BYTE) *pszOptionValue)) {
  983. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  984. break;
  985. }
  986. if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &kwlcid.langid)) {
  987. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  988. break;
  989. }
  990. if (!IsEmptyString(pszOptionValue)) {
  991. if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &kwlcid.fsCompareI)) {
  992. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  993. break;
  994. }
  995. if (!IsEmptyString(pszOptionValue)) {
  996. if (!FGetUnsigned(pszOptionValue, &pszOptionValue,
  997. &kwlcid.fsCompare)) {
  998. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  999. break;
  1000. }
  1001. }
  1002. }
  1003. if (kwlcid.langid) {
  1004. lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT);
  1005. ktKeywordi = KT_NLSI;
  1006. ktKeyword = KT_NLS;
  1007. fValidLcid = FALSE;
  1008. EnumSystemLocales(EnumLocalesProc, LCID_SUPPORTED);
  1009. if (!fValidLcid) {
  1010. char szLcid[20];
  1011. wsprintf(szLcid, "0x%x", kwlcid.langid);
  1012. VReportError(HCERR_INVALID_LCID, &errHpj, szLcid);
  1013. lcid = 0; // shouldn't get here, but just in case...
  1014. }
  1015. }
  1016. else
  1017. lcid = 0;
  1018. break;
  1019. case OPT_DBCS:
  1020. switch (YesNo(pszOptionValue)) {
  1021. case IDYES:
  1022. options.fDBCS = TRUE;
  1023. break;
  1024. case IDNO:
  1025. options.fDBCS = FALSE;
  1026. break;
  1027. default:
  1028. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  1029. break;
  1030. }
  1031. break;
  1032. case OPT_TMPDIR:
  1033. if (options.pszTmpDir)
  1034. ReportDuplicateOption(&options.pszTmpDir, ppszOptions[opt]);
  1035. if (_access(options.pszTmpDir, 0) != 0) {
  1036. VReportError(HCERR_INVALID_TMP_DIR, &errHpj,
  1037. pszOptionValue);
  1038. break;
  1039. }
  1040. AddTrailingBackslash(pszOptionValue);
  1041. options.pszTmpDir = lcStrDup(pszOptionValue);
  1042. if (hwndParent) {
  1043. CreateSharedMemory();
  1044. strcpy(pszMap, options.pszTmpDir);
  1045. SendMessage(hwndParent, WMP_SET_TMPDIR, 0, 0);
  1046. }
  1047. break;
  1048. case OPT_REPLACE:
  1049. {
  1050. if (options.pszReplace) {
  1051. ReportDuplicateOption(&options.pszReplace,
  1052. ppszOptions[opt]);
  1053. lcClearFree(&options.pszReplaceWith);
  1054. }
  1055. psz = StrRChr(pszOptionValue, '=', fDBCSSystem);
  1056. if (!psz) {
  1057. VReportError(HCERR_INVALID_REPLACE, &errHpj,
  1058. pszOptionValue);
  1059. break;
  1060. }
  1061. *psz++ = '\0';
  1062. SzTrimSz(psz);
  1063. options.pszReplaceWith = lcStrDup(psz);
  1064. SzTrimSz(pszOptionValue);
  1065. options.pszReplace = lcStrDup(pszOptionValue);
  1066. }
  1067. break;
  1068. case OPT_CHARSET:
  1069. {
  1070. DWORD val;
  1071. if (!FGetNum(pszOptionValue, NULL, &val))
  1072. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  1073. else
  1074. defCharSet = (BYTE) val;
  1075. }
  1076. break;
  1077. case OPT_DEFFONT:
  1078. if (options.pszDefFont)
  1079. ReportDuplicateOption(&options.pszIndexSeparators, ppszOptions[opt]);
  1080. options.pszDefFont = (PSTR) lcMalloc(strlen(pszOptionValue));
  1081. psz = StrChr(pszOptionValue, ',', fDBCSSystem);
  1082. if (!psz || !isdigit((BYTE) psz[1])) {
  1083. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  1084. lcClearFree(&options.pszDefFont);
  1085. break;
  1086. }
  1087. /*
  1088. * The first byte is the point size, the second byte is the
  1089. * charset, and the font name comes after that.
  1090. */
  1091. options.pszDefFont[0] = (BYTE) atoi(psz + 1);
  1092. *psz = '\0';
  1093. psz = StrChr(psz + 2, ',', fDBCSSystem);
  1094. if (!psz || !isdigit((BYTE) psz[1])) {
  1095. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  1096. lcClearFree(&options.pszDefFont);
  1097. break;
  1098. }
  1099. options.pszDefFont[1] = (BYTE) atoi(psz + 1);
  1100. strcpy(options.pszDefFont + 2, SzTrimSz(pszOptionValue));
  1101. break;
  1102. case OPT_PREFIX:
  1103. if (!ptblCtxPrefixes)
  1104. ptblCtxPrefixes = new CTable;
  1105. psz = StrToken(pszOptionValue, ", ");
  1106. while (psz) {
  1107. psz = SzTrimSz(psz);
  1108. if (*psz)
  1109. ptblCtxPrefixes->AddString(psz);
  1110. psz = StrToken(NULL, ", ");
  1111. }
  1112. break;
  1113. case OPT_REVISIONS:
  1114. switch (YesNo(pszOptionValue)) {
  1115. case IDYES:
  1116. options.fAcceptRevions = TRUE;
  1117. break;
  1118. case IDNO:
  1119. options.fAcceptRevions = FALSE;
  1120. break;
  1121. default:
  1122. ReportBadOption(pszOptionValue, ppszOptions[opt]);
  1123. break;
  1124. }
  1125. break;
  1126. case OPT_INDEX:
  1127. if (options.pszIndexSeparators)
  1128. ReportDuplicateOption(&options.pszIndexSeparators, ppszOptions[opt]);
  1129. if (IsQuote(*pszOptionValue)) {
  1130. psz = pszOptionValue + 1;
  1131. while (!IsQuote(*psz) && *psz)
  1132. psz = CharNext(psz);
  1133. *psz = '\0';
  1134. SzTrimSz(pszOptionValue + 1);
  1135. options.pszIndexSeparators = lcStrDup(pszOptionValue + 1);
  1136. }
  1137. else
  1138. options.pszIndexSeparators = lcStrDup(pszOptionValue);
  1139. break;
  1140. default:
  1141. return RC_Failure;
  1142. break;
  1143. }
  1144. return RC_Success;
  1145. }
  1146. __inline BOOL STDCALL FVerifyBuildTag(PSTR psz)
  1147. {
  1148. for (; *psz; ++psz) {
  1149. if (!IsCharAlphaNumeric(*psz) && *psz != '_')
  1150. return FALSE;
  1151. }
  1152. return TRUE;
  1153. }
  1154. /***************************************************************************\
  1155. *
  1156. - Function: RcParseBuildTagsSz( sz)
  1157. -
  1158. * Purpose: Parse a line in the [buildtags] section. (see grammar)
  1159. *
  1160. * ASSUMES
  1161. *
  1162. * args IN: sz - line to parse, with comments stripped.
  1163. * Valid build tags consist of the characters
  1164. * [A-Za-z0-9_.], with lower mapped to upper case.
  1165. *
  1166. * PROMISES
  1167. *
  1168. * returns: success code. REVIEW
  1169. *
  1170. * +++
  1171. * Method:
  1172. *
  1173. \***************************************************************************/
  1174. RC_TYPE STDCALL RcParseBuildTagsSz(PSTR pszBuildTag)
  1175. {
  1176. if (lcid) {
  1177. int cbSrc = strlen(pszBuildTag);
  1178. int cbDst;
  1179. CMem mem(cbSrc + 16);
  1180. if ((cbDst = LCMapString(lcid, LCMAP_UPPERCASE, pszBuildTag, cbSrc,
  1181. mem.psz, cbSrc + 16)) > 0) {
  1182. strncpy(pszBuildTag, mem.psz, cbDst);
  1183. pszBuildTag[cbDst + 1] ='\0';
  1184. }
  1185. }
  1186. else {
  1187. CharUpper(pszBuildTag);
  1188. }
  1189. if (!FVerifyBuildTag(pszBuildTag))
  1190. VReportError(HCERR_INVALID_BUILD_TAG, &errHpj, pszBuildTag);
  1191. else if (ptblBuildtags && ptblBuildtags->IsStringInTable(pszBuildTag))
  1192. VReportError(HCERR_DUP_BUILD_TAG, &errHpj, pszBuildTag);
  1193. else {
  1194. if (!ptblBuildtags)
  1195. ptblBuildtags = new CTable;
  1196. if (!ptblBuildtags->AddString(pszBuildTag))
  1197. OOM();
  1198. }
  1199. return RC_Success;
  1200. }
  1201. /***************************************************************************\
  1202. *
  1203. - Function: RcParseFilesSz( sz )
  1204. -
  1205. * Purpose: Parse a line in the [files] section (filename, see grammar).
  1206. * Verify that the filename is properly formed, resolve it
  1207. * relative to root, verify that the file exists and isn't
  1208. * a directory or device. Store the file name.
  1209. * ASSUMES
  1210. *
  1211. * args IN: sz - line to parse, with comments stripped.
  1212. *
  1213. *
  1214. * PROMISES
  1215. *
  1216. * returns: success code. REVIEW
  1217. *
  1218. * +++
  1219. * Method:
  1220. *
  1221. \***************************************************************************/
  1222. RC_TYPE STDCALL RcParseFilesSz(PSTR sz)
  1223. {
  1224. HCE hce;
  1225. char szFile[_MAX_PATH];
  1226. if (options.ptblFileRoot)
  1227. hce = HceResolveTableDir(sz, options.ptblFileRoot, szFile, NULL);
  1228. else {
  1229. PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL);
  1230. hce = HceResolveFileNameSz(sz, pszRoot, szFile);
  1231. lcFree(pszRoot);
  1232. }
  1233. if (hce != HCE_OK)
  1234. VReportError(HCERR_CANNOT_OPEN, &errHpj, sz);
  1235. else
  1236. ptblRtfFiles->AddString(szFile);
  1237. return RC_Success;
  1238. }
  1239. /***************************************************************************\
  1240. *
  1241. - Function: RcParseBaggageSz( sz )
  1242. -
  1243. * Purpose: Parse a line in the [baggage] section (filename, see grammar).
  1244. * Verify that the filename is properly formed, resolve it
  1245. * relative to root, verify that the file exists and isn't
  1246. * a directory or device. Store the file name.
  1247. * ASSUMES
  1248. *
  1249. * args IN: sz - line to parse, with comments stripped.
  1250. *
  1251. *
  1252. * PROMISES
  1253. *
  1254. * returns: success code. REVIEW
  1255. *
  1256. * +++
  1257. * Method:
  1258. *
  1259. \***************************************************************************/
  1260. RC_TYPE STDCALL RcParseBaggageSz(PSTR psz)
  1261. {
  1262. HCE hce = HCE_FILE_NOT_FOUND;
  1263. char szFile[_MAX_PATH];
  1264. if (options.ptblFileRoot)
  1265. hce = HceResolveTableDir(psz, options.ptblFileRoot, szFile, NULL);
  1266. if (hce != HCE_OK) {
  1267. PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL);
  1268. hce = HceResolveFileNameSz(psz, pszRoot, szFile);
  1269. lcFree(pszRoot);
  1270. }
  1271. if (hce != HCE_OK)
  1272. VReportError(HCERR_CANNOT_OPEN, &errHpj, psz);
  1273. else {
  1274. if (!ptblBaggage)
  1275. ptblBaggage = new CTable;
  1276. CharUpper(szFile);
  1277. if (ptblBaggage->IsCSStringInTable(szFile))
  1278. return RC_Success; // we already have it
  1279. if (!ptblBaggage->AddString(szFile))
  1280. OOM(); // this won't return
  1281. }
  1282. return RC_Success;
  1283. }
  1284. /***************************************************************************\
  1285. *
  1286. - Function: RcParseBitmapsSz( sz )
  1287. -
  1288. * Purpose: Parse a line in the [bitmaps] section (filename, see grammar).
  1289. * Verify that the filename is properly formed, resolve it
  1290. * relative to BMroot, verify that the file exists and isn't
  1291. * a directory or device. Store the file name.
  1292. * ASSUMES
  1293. *
  1294. * args IN: sz - line to parse, with comments stripped.
  1295. *
  1296. *
  1297. * PROMISES
  1298. *
  1299. * returns: success code. REVIEW
  1300. *
  1301. * +++
  1302. * Method:
  1303. *
  1304. \***************************************************************************/
  1305. RC_TYPE STDCALL RcParseBitmapsSz(PSTR psz)
  1306. {
  1307. AddBitmap(psz, NULL, FALSE);
  1308. return RC_Success;
  1309. }
  1310. /***************************************************************************\
  1311. *
  1312. - Function: RcParseMapSz( sz )
  1313. -
  1314. * Purpose: Parse a line in the [map] section. (see grammar)
  1315. *
  1316. * <map statement> :== <context string> <number> |
  1317. * "#define" <context string> <number>
  1318. *
  1319. * ASSUMES
  1320. *
  1321. * args IN: sz - line to parse, with comments stripped.
  1322. *
  1323. *
  1324. * PROMISES
  1325. *
  1326. * returns: success code. REVIEW
  1327. *
  1328. * +++
  1329. * Method:
  1330. *
  1331. \***************************************************************************/
  1332. RC_TYPE STDCALL RcParseMapSz(PSTR pszLine)
  1333. {
  1334. MAP map;
  1335. PSTR pszTmp;
  1336. DWORD imap, cmap;
  1337. QMAP qmap;
  1338. // If this #define was used for a #ifdef or #ifndef then ignore it
  1339. if (nstrisubcmp(pszLine, txtDefine) && ptblDefine->IsStringInTable(
  1340. FirstNonSpace(pszLine + strlen(txtDefine) + 1, fDBCSSystem)))
  1341. return RC_Success;
  1342. if (!pdrgMap)
  1343. pdrgMap = new CDrg(sizeof(MAP), 5, 5);
  1344. if (!ptblMap) {
  1345. ptblMap = new CTable;
  1346. ptblMap->SetSorting(lcid, kwlcid.fsCompareI, kwlcid.fsCompare);
  1347. }
  1348. // Assume RcGetLogicalLine() has removed all comments within a line,
  1349. // and never returns a blank line.
  1350. ASSERT(*pszLine);
  1351. /*
  1352. * New for 4.0 is to look for an '='. This is the only way you can
  1353. * map a context string containing a space.
  1354. */
  1355. if (!(pszTmp = StrChr(pszLine, CH_EQUAL, fDBCSSystem)))
  1356. pszTmp = SkipToEndOfWord(pszLine);
  1357. if (!pszTmp || !*pszTmp) {
  1358. VReportError(HCERR_NO_MAP_VALUE, &errHpj, pszLine);
  1359. return RC_Success;
  1360. }
  1361. if (nstrisubcmp(pszLine, txtDefine)) {
  1362. pszLine = FirstNonSpace(pszLine + strlen(txtDefine), fDBCSSystem);
  1363. if (*pszTmp != CH_EQUAL)
  1364. pszTmp = SkipToEndOfWord(pszLine);
  1365. if (!*pszTmp) {
  1366. VReportError(HCERR_NO_MAP_VALUE, &errHpj, pszLine);
  1367. return RC_Success;
  1368. }
  1369. }
  1370. *pszTmp = '\0';
  1371. SzTrimSz(pszLine);
  1372. if (!FValidContextSz(pszLine)) {
  1373. VReportError(HCERR_INVALID_CTX, &errHpj, pszLine);
  1374. return RC_Success;
  1375. }
  1376. // Special case no-help constant
  1377. if (nstrsubcmp(FirstNonSpace(pszTmp + 1, fDBCSSystem), "((DWORD) -1)"))
  1378. return RC_Success;
  1379. map.hash = HashFromSz(pszLine);
  1380. if (ptblMap->IsHashInTable(map.hash)) {
  1381. VReportError(HCERR_MAP_USED, &errHpj, pszLine);
  1382. return RC_Success;
  1383. }
  1384. PSTR pszSave = pszLine;
  1385. pszLine = SzTrimSz(pszTmp + 1);
  1386. if (!FGetUnsigned(pszLine, &pszTmp, &map.ctx)) {
  1387. VReportError(HCERR_INVALID_MAP_NUMBER, &errHpj, pszLine, pszSave);
  1388. return RC_Success;
  1389. }
  1390. if (*pszTmp != '\0') // Nag, but otherwise continue
  1391. VReportError(HCERR_MAP_TEXT_UNEXPECT, &errHpj, pszSave, pszTmp);
  1392. // make sure this CTX isn't already mapped to another context string
  1393. if ((cmap = pdrgMap->Count()) != 0) {
  1394. for (imap = 0, qmap = (QMAP) pdrgMap->GetBasePtr();
  1395. imap < cmap; imap++, qmap++) {
  1396. if (qmap->ctx == map.ctx) {
  1397. // REVIEW: we should nag, but supposedly, it's okay to do this
  1398. VReportError(HCERR_MAP_VALUE_DUP, &errHpj, pszSave,
  1399. (PCSTR) ptblMap->GetPointer(qmap->pos) + sizeof(HASH));
  1400. return RC_Success;
  1401. }
  1402. }
  1403. }
  1404. ASSERT(map.hash == HashFromSz(pszSave));
  1405. map.pos = ptblMap->AddString(map.hash, pszSave);
  1406. pdrgMap->Add(&map);
  1407. return RC_Success;
  1408. }
  1409. /***************************************************************************\
  1410. *
  1411. - Function: RcParseAliasSz( sz)
  1412. -
  1413. * Purpose: Parse a line in the [alias] section. (see grammar)
  1414. *
  1415. * <alias statement> :== <context string> "=" <context string>
  1416. *
  1417. * <context string> :== [A-Za-z0-9_.]{[A-Za-z0-9_.!]}
  1418. *
  1419. * ASSUMES
  1420. *
  1421. * args IN: sz - line to parse, with comments stripped.
  1422. *
  1423. *
  1424. * PROMISES
  1425. *
  1426. * returns: success code. REVIEW
  1427. *
  1428. * +++
  1429. * Method:
  1430. *
  1431. \***************************************************************************/
  1432. RC_TYPE STDCALL RcParseAliasSz(PSTR pszLine)
  1433. {
  1434. PSTR pszAlias;
  1435. ALIAS alias;
  1436. DWORD ialias, calias;
  1437. QALIAS qalias;
  1438. ASSERT(!StrChr(pszLine, CH_SEMICOLON, fDBCSSystem));
  1439. if (!(pszAlias = StrChr(pszLine, CH_EQUAL, fDBCSSystem))) {
  1440. VReportError(HCERR_MISS_ALIAS_EQ, &errHpj, pszLine);
  1441. return RC_Success;
  1442. }
  1443. CStr cszOrg(SzTrimSz(pszLine));
  1444. *pszAlias++ = '\0';
  1445. SzTrimSz(pszLine);
  1446. if (!FValidContextSz(pszLine)) {
  1447. VReportError(HCERR_INVALID_CTX, &errHpj, pszLine);
  1448. return RC_Success;
  1449. }
  1450. alias.hashAlias = HashFromSz(pszLine);
  1451. SzTrimSz(pszAlias);
  1452. if (!FValidContextSz(pszAlias)) {
  1453. VReportError(HCERR_INVALID_CTX, &errHpj, pszAlias);
  1454. return RC_Success;
  1455. }
  1456. alias.hashCtx = HashFromSz(pszAlias);
  1457. if (alias.hashAlias == alias.hashCtx) {
  1458. VReportError(HCERR_ALIAS_EQ_CTX, &errHpj, cszOrg.psz);
  1459. return RC_Success;
  1460. }
  1461. if (!pdrgAlias)
  1462. pdrgAlias = new CDrg(sizeof(ALIAS), 5, 5);
  1463. if ((calias = pdrgAlias->Count()) > 0) {
  1464. for (ialias = 0, qalias = (QALIAS) pdrgAlias->GetBasePtr();
  1465. ialias < calias;
  1466. ialias++, qalias++) {
  1467. if (alias.hashAlias == qalias->hashAlias) {
  1468. VReportError(HCERR_DEFINED_ALIAS, &errHpj, pszLine,
  1469. cszOrg.psz);
  1470. return RC_Success;
  1471. }
  1472. else if (alias.hashAlias == qalias->hashCtx) {
  1473. VReportError(HCERR_DUP_ALIAS, &errHpj, pszLine,
  1474. cszOrg.psz);
  1475. return RC_Success;
  1476. }
  1477. else if (alias.hashCtx == qalias->hashAlias)
  1478. // REVIEW: 27-Mar-1994 [ralphw] Does this work?
  1479. alias.hashCtx = qalias->hashCtx;
  1480. }
  1481. }
  1482. alias.szCtx = lcStrDup(pszAlias);
  1483. pdrgAlias->Add(&alias);
  1484. return RC_Success;
  1485. }
  1486. /***************************************************************************\
  1487. *
  1488. - Function: RcParseConfigSz( sz )
  1489. -
  1490. * Purpose: Parse a line in the [config] section.
  1491. * The config section has structure unknown to this
  1492. * parser. All we do is strip comments. Currently
  1493. * we don't recognize quoted strings (to allow ;).
  1494. *
  1495. * ASSUMES
  1496. *
  1497. * args IN: sz - line to parse, with comments stripped.
  1498. *
  1499. *
  1500. * PROMISES
  1501. *
  1502. * returns: success code. REVIEW
  1503. *
  1504. * +++
  1505. * Method: Each macro is added as a null terminated string
  1506. * to ptblConfig.
  1507. *
  1508. \***************************************************************************/
  1509. RC_TYPE STDCALL RcParseConfigSz(PSTR psz)
  1510. {
  1511. if (!ptblConfig)
  1512. ptblConfig = new CTable;
  1513. if (Execute(psz) == wMACRO_EXPANSION) {
  1514. ASSERT((size_t) lcSize(psz) > strlen(GetMacroExpansion()));
  1515. strcpy(psz, GetMacroExpansion());
  1516. }
  1517. // Add the macro even if it generated an error
  1518. if (!ptblConfig->AddString(psz))
  1519. OOM();
  1520. return RC_Success;
  1521. }
  1522. /***************************************************************************
  1523. FUNCTION: RcParseSecondaryConfigSz
  1524. PURPOSE: Adds configuration information to a secondary window
  1525. PARAMETERS:
  1526. psz
  1527. RETURNS:
  1528. COMMENTS:
  1529. Assumes curConfig points to the current config number in the
  1530. array of configuration tables
  1531. MODIFICATION DATES:
  1532. 10-Jul-1994 [ralphw]
  1533. ***************************************************************************/
  1534. RC_TYPE STDCALL RcParseSecondaryConfigSz(PSTR psz)
  1535. {
  1536. if (!pptblConfig[curConfig])
  1537. pptblConfig[curConfig] = new CTable;
  1538. if (Execute(psz) == wMACRO_EXPANSION) {
  1539. ASSERT((size_t) lcSize(psz) > strlen(GetMacroExpansion()));
  1540. strcpy(psz, GetMacroExpansion());
  1541. }
  1542. // Add the macro even if it generated an error
  1543. if (!pptblConfig[curConfig]->AddString(psz))
  1544. OOM();
  1545. return RC_Success;
  1546. }
  1547. /***************************************************************************\
  1548. *
  1549. - Function: FReadInt16()
  1550. -
  1551. * Purpose: Read an int from a buffer.
  1552. * Legal syntax is an int followed by a comma or by nothing.
  1553. * On success, advance the pointer past the following comma.
  1554. * A missing number isn't an error.
  1555. * If a number is actually read, set a flag.
  1556. *
  1557. * ASSUMES
  1558. * args IN: psz - pointer to sz containing number
  1559. * pi - pointer to int
  1560. * pgrf - pointer to flag word
  1561. * f - flag to set in pgrf
  1562. *
  1563. * PROMISES
  1564. * returns: WINDOW_READ_INVALID - syntax error
  1565. * WINDOW_READ_MISSING - no number, but syntax OK
  1566. * WINDOW_READ_OK - a valid number was read
  1567. *
  1568. * args OUT: psz - sz advanced past following comma
  1569. * pi - value of int copied here on success
  1570. * pgrf - *pgrf != flag if number is present
  1571. *
  1572. \***************************************************************************/
  1573. static UINT STDCALL FReadInt16(PSTR *psz, WORD* pi, WORD *pgrf, UINT f)
  1574. {
  1575. PSTR pszTmp;
  1576. pszTmp = FirstNonSpace(*psz, fDBCSSystem);
  1577. if (*pszTmp == 'f')
  1578. pszTmp++;
  1579. if (*pszTmp == '\0')
  1580. goto UseDefault;
  1581. if (*pszTmp == ',') {
  1582. *psz = pszTmp + 1;
  1583. UseDefault:
  1584. if (f == FWSMAG_X || f == FWSMAG_Y || f == FWSMAG_DX ||
  1585. f == FWSMAG_DY) {
  1586. *pi = (WORD) -1;
  1587. return WINDOW_READ_OK;
  1588. }
  1589. else
  1590. return WINDOW_READ_MISSING;
  1591. }
  1592. LONG l;
  1593. if (!FGetNum(pszTmp, &pszTmp, &l))
  1594. return WINDOW_READ_INVALID;
  1595. if (l == -1 && (f == FWSMAG_X || f == FWSMAG_Y || f == FWSMAG_DX ||
  1596. f == FWSMAG_DY)) {
  1597. *pi = (WORD) -1;
  1598. return WINDOW_READ_OK;
  1599. }
  1600. *pi = (WORD) l;
  1601. // hack to include new possible flags for wsmag.wMax
  1602. if (f == FWSMAG_MAXIMIZE) {
  1603. if (*pi & FWSMAG_WMAX_MAXIMIZE)
  1604. *pgrf |= f;
  1605. }
  1606. else
  1607. *pgrf |= f;
  1608. pszTmp = FirstNonSpace(pszTmp, fDBCSSystem);
  1609. if (*pszTmp == ',') {
  1610. *psz = pszTmp + 1;
  1611. return WINDOW_READ_OK;
  1612. }
  1613. *psz = pszTmp;
  1614. return (*pszTmp == '\0') ? WINDOW_READ_OK : WINDOW_READ_INVALID;
  1615. }
  1616. /***************************************************************************\
  1617. *
  1618. - Function: FReadRgb( psz, pl, pgrf, f )
  1619. -
  1620. * Purpose: Read an RGB triple from a buffer.
  1621. * Legal syntax is nothing, or a parenthesized list
  1622. * of three ints (all must be there).
  1623. * On success, advance the pointer past the following comma.
  1624. * A missing triple isn't an error.
  1625. * If a triple is actually read, set a flag.
  1626. *
  1627. * ASSUMES
  1628. * args IN: psz - pointer to sz containing (r,g,b)
  1629. * pl - pointer to long
  1630. * pgrf - pointer to flag word
  1631. * f - flag to set in pgrf
  1632. *
  1633. * PROMISES
  1634. * returns: WINDOW_READ_INVALID - syntax error
  1635. * WINDOW_READ_MISSING - no number, but syntax OK
  1636. * WINDOW_READ_OK - a valid number was read
  1637. *
  1638. * args OUT: psz - sz advanced past following comma
  1639. * pl - value of rgb copied here on success.
  1640. * We use the RGB() macro above (REVIEW!!)
  1641. * pgrf - *pgrf != flag if number is present
  1642. *
  1643. \***************************************************************************/
  1644. static UINT STDCALL FReadRgb(PSTR *ppsz, LONG *pl, UINT *pgrf, UINT f)
  1645. {
  1646. UINT grf = 0;
  1647. WORD r, g, b;
  1648. PSTR psz = FirstNonSpace(*ppsz, fDBCSSystem);
  1649. PSTR pszTmp;
  1650. if (*psz == '\0')
  1651. return WINDOW_READ_MISSING;
  1652. if (*psz == ',') {
  1653. *ppsz = psz + 1;
  1654. return WINDOW_READ_MISSING;
  1655. }
  1656. if (*psz == '(') {
  1657. psz = FirstNonSpace(psz + 1, fDBCSSystem);
  1658. if (!(pszTmp = StrChr(psz, CH_CLOSE_PAREN, fDBCSSystem)))
  1659. return WINDOW_READ_INVALID;
  1660. *pszTmp = '\0';
  1661. pszTmp = FirstNonSpace(pszTmp + 1, fDBCSSystem);
  1662. if (*pszTmp == ',')
  1663. ++pszTmp;
  1664. *ppsz = pszTmp;
  1665. if (*psz == 'r') { // short format produced by HCW
  1666. *pl = atol(psz + 1);
  1667. *pgrf |= f;
  1668. return WINDOW_READ_OK;
  1669. }
  1670. if (WINDOW_READ_OK != FReadInt16(&psz, &r, (WORD *) pgrf, f) ||
  1671. WINDOW_READ_OK != FReadInt16(&psz, &g, (WORD *) pgrf, f) ||
  1672. WINDOW_READ_OK != FReadInt16(&psz, &b, (WORD *) pgrf, f)) {
  1673. *pgrf &= ~f;
  1674. return WINDOW_READ_INVALID;
  1675. }
  1676. if (r < 0 || r >= 256 || g < 0 || g >= 256 || b < 0 || b >= 256)
  1677. return WINDOW_READ_INVALID; // REVIEW - different error code?
  1678. *pl = RGB(r, g, b);
  1679. return WINDOW_READ_OK;
  1680. }
  1681. else
  1682. return WINDOW_READ_INVALID;
  1683. }
  1684. /***************************************************************************\
  1685. *
  1686. - Function: RcParseWindowsSz( sz )
  1687. -
  1688. * Purpose: Parse a line in the [windows] section. Syntax:
  1689. *
  1690. * <member> = ["caption"],(X,Y,dX,dy),[fMax],[(r,g,b)],[(r,g,b)]
  1691. *
  1692. * If member is "main", class is also "main" and position
  1693. * is not required.
  1694. * Otherwise class is "secondary" and position is mandatory.
  1695. * ASSUMES
  1696. *
  1697. * args IN: sz - line to parse, with comments stripped
  1698. *
  1699. * PROMISES
  1700. * returns: success code.
  1701. *
  1702. * Notes: The WSMAG structure has provisions for multiple
  1703. * secondary window classes and multiple members of
  1704. * each class. This parser doesn't allow it, though.
  1705. * +++
  1706. *
  1707. * Method:
  1708. *
  1709. \***************************************************************************/
  1710. RC_TYPE STDCALL RcParseWindowsSz(PSTR pszLine)
  1711. {
  1712. PSTR pszTmp, pszRHS, pszMember, pszCaption;
  1713. WSMAG wsmag;
  1714. CStr szOriginal(pszLine);
  1715. memset(&wsmag, 0, sizeof(wsmag)); // clear out all values
  1716. pszRHS = StrChr(pszLine, '=', fDBCSSystem);
  1717. if (pszRHS == NULL) {
  1718. VReportError(HCERR_NOEQ_IN_WIN, &errHpj, pszLine);
  1719. return RC_Success;
  1720. }
  1721. else {
  1722. *pszRHS++ = '\0';
  1723. SzTrimSz(pszRHS);
  1724. }
  1725. // Deal with "member=" case for the whiners.
  1726. if (*pszRHS == '\0') {
  1727. VReportError(HCERR_NOTHING_AFTER_EQ, &errHpj, pszLine);
  1728. return RC_Success;
  1729. }
  1730. pszMember = SzTrimSz(pszLine);
  1731. // REVIEW: don't allow a window name to begin with an '@' -- WinHelp
  1732. // special-cases this character.
  1733. PCSTR pszClass = (PCSTR) ((_stricmp(pszMember, txtMainWindow) == 0) ?
  1734. txtMainWindow : txtSecondary);
  1735. if (strlen(pszMember) >= MAX_WINDOW_NAME) {
  1736. // If the window name is too long, truncate it and complain
  1737. CStr cstr(pszMember);
  1738. pszMember[MAX_WINDOW_NAME] = '\0';
  1739. VReportError(HCERR_WIN_NAME_TOO_LONG, &errHpj,
  1740. (PCSTR) cstr, pszMember);
  1741. }
  1742. else if (!*pszMember) {
  1743. VReportError(HCERR_NOTHING_AFTER_EQ, &errHpj, szOriginal);
  1744. return RC_Success;
  1745. }
  1746. strcpy(wsmag.rgchClass, pszClass);
  1747. strcpy(wsmag.rgchMember, pszMember);
  1748. CharLower(wsmag.rgchMember); // for consistency
  1749. if (!ptblWindowNames)
  1750. ptblWindowNames = new CTable;
  1751. if (ptblWindowNames->IsCSStringInTable(wsmag.rgchMember)) {
  1752. VReportError(HCERR_DUPLICATE_NAME, &errHpj, wsmag.rgchMember);
  1753. return RC_Success;
  1754. }
  1755. ptblWindowNames->AddString(wsmag.rgchMember);
  1756. wsmag.grf = FWSMAG_CLASS | FWSMAG_MEMBER;
  1757. if (*pszRHS == CH_QUOTE) {
  1758. // Caption: "string"
  1759. pszCaption = pszRHS + 1;
  1760. pszTmp = StrChr(pszCaption, CH_QUOTE, fDBCSSystem);
  1761. if (pszTmp == NULL) {
  1762. VReportError(HCERR_MISSING_CAPT_QUOTE, &errHpj, szOriginal);
  1763. return RC_Success;
  1764. }
  1765. else {
  1766. *pszTmp = '\0';
  1767. if (strlen(pszCaption) >= MAX_WINDOWCAPTION)
  1768. VReportError(HCERR_CAPTION_TOO_LONG, &errHpj, szOriginal);
  1769. wsmag.grf |= FWSMAG_CAPTION;
  1770. strncpy(wsmag.rgchCaption, pszCaption, MAX_WINDOWCAPTION);
  1771. wsmag.rgchCaption[MAX_WINDOWCAPTION] = '\0';
  1772. }
  1773. pszRHS = FirstNonSpace(pszTmp + 1, fDBCSSystem);
  1774. }
  1775. /*
  1776. * New to HCW, we no longer require the leading comma if a window
  1777. * title isn't specified.
  1778. */
  1779. if (*pszRHS != ',') {
  1780. if (*pszRHS == '\0') {
  1781. pszLine = pszRHS;
  1782. goto normal_return;
  1783. }
  1784. else {
  1785. pszRHS--; // so that next check will skip over this
  1786. }
  1787. }
  1788. pszLine = FirstNonSpace(pszRHS + 1, fDBCSSystem);
  1789. // Position: (int, int, int, int) or (0) for default
  1790. if (*pszLine == '(') {
  1791. PSTR pszSaveLine = pszLine; // save the position for error reporting
  1792. ++pszLine;
  1793. pszTmp = StrChr(pszLine, CH_CLOSE_PAREN, fDBCSSystem);
  1794. if (!pszTmp)
  1795. goto error_return;
  1796. *pszTmp = '\0';
  1797. // REVIEW: will this handle missing items?
  1798. if (FReadInt16(&pszLine, &wsmag.x, &wsmag.grf, FWSMAG_X)
  1799. != WINDOW_READ_OK ||
  1800. FReadInt16(&pszLine, &wsmag.y, &wsmag.grf, FWSMAG_Y)
  1801. != WINDOW_READ_OK ||
  1802. FReadInt16(&pszLine, &wsmag.dx, &wsmag.grf, FWSMAG_DX)
  1803. != WINDOW_READ_OK ||
  1804. FReadInt16(&pszLine, &wsmag.dy, &wsmag.grf, FWSMAG_DY)
  1805. != WINDOW_READ_OK) {
  1806. *pszTmp++ = CH_CLOSE_PAREN;
  1807. *pszTmp = '\0';
  1808. VReportError(HCERR_INVALID_WIN_POS, &errHpj, pszSaveLine);
  1809. return RC_Success;
  1810. }
  1811. pszLine = FirstNonSpace(pszTmp + 1, fDBCSSystem);
  1812. }
  1813. if (*pszLine == ',')
  1814. pszLine = FirstNonSpace(pszLine + 1, fDBCSSystem);
  1815. else if (*pszLine == '\0')
  1816. goto normal_return;
  1817. else {
  1818. VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal);
  1819. return RC_Success;
  1820. }
  1821. // Max, rgbMain, rgbNsr: [int], [(int,int,int)], [(int,int,int)]
  1822. if (!FReadInt16(&pszLine, &wsmag.wMax, &wsmag.grf, FWSMAG_MAXIMIZE)) {
  1823. VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal);
  1824. return RC_Success;
  1825. }
  1826. // version 3 files only understand maximize flag
  1827. if (version < 4)
  1828. wsmag.wMax &= 1;
  1829. if (!FReadRgb(&pszLine, &wsmag.rgbMain, (UINT*) &wsmag.grf,
  1830. FWSMAG_RGBMAIN) || !FReadRgb(&pszLine, &wsmag.rgbNSR,
  1831. (UINT*) &wsmag.grf, FWSMAG_RGBNSR)) {
  1832. VReportError(HCERR_INVALID_RGB, &errHpj, szOriginal);
  1833. return RC_Success;
  1834. }
  1835. {
  1836. WORD fsWin = 0;
  1837. if (!FReadInt16(&pszLine, &fsWin, &wsmag.grf, 0)) {
  1838. VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal);
  1839. return RC_Success;
  1840. }
  1841. if (fsWin & AUTHOR_WINDOW_ON_TOP)
  1842. wsmag.grf |= FWSMAG_ON_TOP;
  1843. if (fsWin & AUTHOR_AUTO_SIZE)
  1844. wsmag.grf |= FWSMAG_AUTO_SIZE;
  1845. if (fsWin & AUTHOR_ABSOLUTE)
  1846. wsmag.grf |= FWSMAG_ABSOLUTE;
  1847. }
  1848. normal_return:
  1849. ASSERT(wsmag.grf & FWSMAG_CLASS);
  1850. // Check for trailing garbage
  1851. if (*FirstNonSpace(pszLine, fDBCSSystem) != '\0') {
  1852. VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal);
  1853. return RC_Success;
  1854. }
  1855. if (wsmag.wMax & 1)
  1856. wsmag.grf |= FWSMAG_MAXIMIZE;
  1857. // range check position
  1858. if (!(wsmag.grf & AUTHOR_ABSOLUTE) &&
  1859. (wsmag.x >= dxVirtScreen ||
  1860. wsmag.y >= dyVirtScreen ||
  1861. wsmag.x + wsmag.dx >= dxVirtScreen ||
  1862. wsmag.y + wsmag.dy >= dyVirtScreen)) {
  1863. VReportError(HCERR_INVALID_WIN_RANGE, &errHpj, szOriginal);
  1864. return RC_Success;
  1865. }
  1866. if (!pdrgWsmag)
  1867. pdrgWsmag = new CDrg(sizeof(WSMAG), 1);
  1868. if ((cwsmag = pdrgWsmag->Count()) > 0) {
  1869. if (cwsmag > MAX_WINDOWS) {
  1870. VReportError(HCERR_256_WINDOWS, &errHpj);
  1871. return RC_Success;
  1872. }
  1873. }
  1874. pdrgWsmag->Add(&wsmag);
  1875. if (wsmag.wMax & FWSMAG_WMAX_BROWSE)
  1876. fBrowseButtonSet = TRUE;
  1877. // Keep track of the number of windows added, so that we know how many
  1878. // configuration sections to write out.
  1879. cwsmag = pdrgWsmag->Count();
  1880. return RC_Success;
  1881. error_return:
  1882. return RC_Success;
  1883. }
  1884. /***************************************************************************\
  1885. *
  1886. - Function: FPushFilePfs( pfs, szFile, perr )
  1887. -
  1888. * Purpose: Open and push an #included file onto the file stack.
  1889. * Emit an error message if it doesn't work.
  1890. *
  1891. * ASSUMES
  1892. *
  1893. * args IN: pfs - file stack
  1894. * szFile - file name
  1895. *
  1896. * PROMISES
  1897. *
  1898. * returns: TRUE on success; FALSE on failure
  1899. *
  1900. * args OUT: pfs - file is pushed if no stack overflow and file can be
  1901. * opened
  1902. *
  1903. * Side Effects: Possible errors:
  1904. *
  1905. * hceFileStackOverflow - #includes nested too deeply
  1906. * hceFileNameTooLong - file name too long
  1907. * etc. (REVIEW)
  1908. * +++
  1909. * Method: pfs->ifeTop == -1 if the stack is empty.
  1910. * Otherwise it is the index of the currently open file entry.
  1911. *
  1912. * Unless szFile is absolute (drive or rooted), it is taken
  1913. * to be relative to the path of the previous file on the
  1914. * stack. For the first file pushed, it is relative to the
  1915. * current directory.
  1916. *
  1917. \***************************************************************************/
  1918. static BOOL STDCALL FPushFilePfs(PSTR szFile)
  1919. {
  1920. ASSERT(pfs->ifeTop >= -1);
  1921. ASSERT(pfs->ifeTop <= MAX_INCLUDE);
  1922. if (++pfs->ifeTop == MAX_INCLUDE) {
  1923. VReportError(HCERR_TOO_MANY_INCLUDES, &errHpj, szFile);
  1924. --pfs->ifeTop;
  1925. return FALSE;
  1926. }
  1927. else {
  1928. PFE pfe = &pfs->rgfe[pfs->ifeTop];
  1929. PSTR pszRoot;
  1930. char szDir[_MAX_DIR];
  1931. if (pfs->ifeTop == 0)
  1932. pszRoot = NULL;
  1933. else
  1934. pszRoot = SzGetDriveAndDir(pfe[-1].rgchFile, szDir);
  1935. HCE hce = HceResolveFileNameSz(szFile, pszRoot, pfe->rgchFile,
  1936. NULL, &pfe->pinput);
  1937. if (hce != HCE_OK) {
  1938. pfs->ifeTop--;
  1939. VReportError(HCERR_CANNOT_OPEN, &errHpj, szFile);
  1940. return FALSE;
  1941. }
  1942. pfe->iLine = 0;
  1943. errHpj.lpszFile = pfe->rgchFile;
  1944. errHpj.iLine = pfe->iLine;
  1945. }
  1946. return TRUE;
  1947. }
  1948. /***************************************************************************\
  1949. *
  1950. - Function: FPopPfs( pfs, perr )
  1951. -
  1952. * Purpose: Remove the top file entry from the file stack.
  1953. *
  1954. * ASSUMES
  1955. *
  1956. * args IN: pfs - file stack
  1957. * perr - error info struct
  1958. *
  1959. * PROMISES
  1960. *
  1961. * returns: TRUE on success; FALSE on failure
  1962. *
  1963. * args OUT: pfs->rgfe[ pfs->ifeTop ].pf gets closed.
  1964. * pfs->ifeTop is decremented if stack nonempty
  1965. * perr - pchFile and iLine set equal to top of stack
  1966. * +++
  1967. *
  1968. * Method: This can be a macro.
  1969. *
  1970. \***************************************************************************/
  1971. __inline BOOL STDCALL FPopPfs(void)
  1972. {
  1973. if (pfs->ifeTop >= 0) {
  1974. ASSERT(PfTopPfs() != NULL);
  1975. delete PfTopPfs();
  1976. --pfs->ifeTop;
  1977. PFE pfe = &pfs->rgfe[pfs->ifeTop];
  1978. if (pfs->ifeTop >= 0) {
  1979. errHpj.lpszFile = pfe->rgchFile;
  1980. errHpj.iLine = pfe->iLine;
  1981. }
  1982. return TRUE;
  1983. }
  1984. return FALSE;
  1985. }
  1986. /***************************************************************************\
  1987. *
  1988. - Function: PfTopPfs( pfs )
  1989. -
  1990. * Purpose: Return pointer to FILE * on top of stack.
  1991. *
  1992. * ASSUMES
  1993. *
  1994. * args IN: pfs
  1995. *
  1996. * PROMISES
  1997. *
  1998. * returns: success: valid FILE * from top of stack
  1999. * failure: NULL (when stack is empty)
  2000. *
  2001. * +++
  2002. *
  2003. * Method: This can be a macro.
  2004. *
  2005. \***************************************************************************/
  2006. __inline CInput* STDCALL PfTopPfs(void)
  2007. {
  2008. return (pfs->ifeTop >= 0) ? pfs->rgfe[pfs->ifeTop].pinput : NULL;
  2009. }
  2010. /***************************************************************************
  2011. FUNCTION: RcGetLogicalLine
  2012. PURPOSE: Read a line, stripping comments, blank lines, and handling
  2013. nested include files.
  2014. PARAMETERS:
  2015. pcszDst -- CStr object which will expand as necessary to fit the
  2016. longest line.
  2017. RETURNS:
  2018. COMMENTS:
  2019. MODIFICATION DATES:
  2020. 05-Sep-1994 [ralphw]
  2021. ***************************************************************************/
  2022. static char txtInclude[] = "#include";
  2023. static RC_TYPE STDCALL RcGetLogicalLine(CStr* pcszDst)
  2024. {
  2025. CInput* pin = PfTopPfs();
  2026. if (!pin)
  2027. return RC_EOF;
  2028. for (;;) {
  2029. if (!pin->getline(pcszDst)) {
  2030. // Close current file, continue with nested file if there is one
  2031. FPopPfs();
  2032. if (!(pin = PfTopPfs()))
  2033. return RC_EOF;
  2034. continue;
  2035. }
  2036. PSTR pszDst = pcszDst->psz; // purely for our notational convenience
  2037. if (*pszDst == CH_SPACE || *pszDst == CH_TAB)
  2038. strcpy(pszDst, FirstNonSpace(pszDst, fDBCSSystem));
  2039. PSTR psz = pszDst;
  2040. switch (*psz) {
  2041. case 0:
  2042. case ';':
  2043. continue;
  2044. case '#':
  2045. if (nstrisubcmp(psz, txtInclude)) {
  2046. // process #include
  2047. psz = FirstNonSpace(pszDst + strlen(txtInclude), fDBCSSystem);
  2048. if (!psz) {
  2049. VReportError(HCERR_NOINCLUDE_FILE, &errHpj);
  2050. continue;
  2051. }
  2052. PSTR pszEnd;
  2053. if (*psz == CH_QUOTE || *psz == '<') {
  2054. char ch = (*psz == CH_QUOTE) ? CH_QUOTE : '>';
  2055. psz++;
  2056. pszEnd = StrChrDBCS(psz, ch);
  2057. if (*pszEnd)
  2058. *pszEnd = '\0';
  2059. }
  2060. if (pszEnd = StrChrDBCS(psz, ';'))
  2061. *pszEnd = '\0';
  2062. if (pszEnd = strstr(psz, "//"))
  2063. *pszEnd = '\0';
  2064. if (pszEnd = strstr(psz, "/*"))
  2065. *pszEnd = '\0';
  2066. SzTrimSz(psz);
  2067. FPushFilePfs(psz);
  2068. if (!(pin = PfTopPfs()))
  2069. return RC_EOF;
  2070. continue;
  2071. }
  2072. else if (nstrisubcmp(psz, txtDefine))
  2073. goto ValidString;
  2074. else if (nstrisubcmp(psz, txtIfDef))
  2075. ptblDefine->AddString(
  2076. FirstNonSpace(psz + strlen(txtIfDef) + 1, fDBCSSystem));
  2077. else if (nstrisubcmp(psz, txtIfnDef))
  2078. ptblDefine->AddString(
  2079. FirstNonSpace(psz + strlen(txtIfnDef) + 1, fDBCSSystem));
  2080. // REVIEW: process #ifdef, #ifndef, #else, #endif
  2081. continue;
  2082. default:
  2083. // We have a valid string.
  2084. if (psz != pszDst)
  2085. strcpy(pszDst, psz);
  2086. // Remove any comments
  2087. ValidString:
  2088. if (options.fDBCS) {
  2089. for (psz = pszDst; *psz; psz = CharNext(psz)) {
  2090. if (IsQuote(*psz)) {
  2091. psz++;
  2092. while (!IsQuote(*psz) && *psz)
  2093. psz = CharNext(psz);
  2094. }
  2095. if (*psz == ';') {
  2096. *psz = '\0';
  2097. break;
  2098. }
  2099. }
  2100. for (psz = pszDst; *psz; psz = CharNext(psz)) {
  2101. if (IsQuote(*psz)) {
  2102. psz++;
  2103. while (!IsQuote(*psz) && *psz)
  2104. psz = CharNext(psz);
  2105. }
  2106. if (*psz == '/' && psz[1] == '/') {
  2107. *psz = '\0';
  2108. break;
  2109. }
  2110. }
  2111. }
  2112. else {
  2113. for (psz = pszDst; *psz; psz++) {
  2114. if (IsQuote(*psz)) {
  2115. psz++;
  2116. while (!IsQuote(*psz) && *psz)
  2117. psz++;
  2118. }
  2119. if (*psz == ';') {
  2120. *psz = '\0';
  2121. break;
  2122. }
  2123. }
  2124. for (psz = pszDst; *psz; psz++) {
  2125. if (IsQuote(*psz)) {
  2126. psz++;
  2127. while (!IsQuote(*psz) && *psz)
  2128. psz++;
  2129. }
  2130. if (*psz == '/' && psz[1] == '/') {
  2131. *psz = '\0';
  2132. break;
  2133. }
  2134. }
  2135. }
  2136. while ((psz = strstr(pszDst, "/*"))) {
  2137. PSTR pszTmp = strstr(psz, "*/");
  2138. if (pszTmp)
  2139. strcpy(psz, FirstNonSpace(pszTmp + 2, fDBCSSystem));
  2140. else {
  2141. char szBuf[512];
  2142. do {
  2143. if (!pin->getline(szBuf)) {
  2144. /*
  2145. * Close current file, continue with nested
  2146. * file if there is one
  2147. */
  2148. FPopPfs();
  2149. if (!(pin = PfTopPfs()))
  2150. return RC_EOF;
  2151. continue;
  2152. }
  2153. } while (!(pszTmp = strstr(szBuf, "*/")));
  2154. strcpy(psz, FirstNonSpace(pszTmp + 2, fDBCSSystem));
  2155. // New line could have comments, so start all over
  2156. goto ValidString;
  2157. }
  2158. }
  2159. SzTrimSz(pszDst);
  2160. if (!*pszDst)
  2161. continue;
  2162. return RC_Success;
  2163. }
  2164. }
  2165. }
  2166. /***************************************************************************\
  2167. *
  2168. - Function: FInitializeHpj()
  2169. -
  2170. * Purpose: Take an HPJ and set to uninitialized state.
  2171. * Assume fields are garbage (i.e. don't try to free stuff).
  2172. *
  2173. * ASSUMES
  2174. *
  2175. *
  2176. * PROMISES
  2177. *
  2178. * returns: TRUE if successful, FALSE if OOM.
  2179. *
  2180. *
  2181. * globals OUT:
  2182. *
  2183. * state OUT:
  2184. *
  2185. * Side Effects:
  2186. *
  2187. * Notes: Should there be different defaults for normal and
  2188. * IsRTF() case?
  2189. *
  2190. * Bugs: Doesn't free memory
  2191. *
  2192. * +++
  2193. *
  2194. * Method:
  2195. *
  2196. * Notes:
  2197. *
  2198. \***************************************************************************/
  2199. BOOL STDCALL FInitializeHpj(void)
  2200. {
  2201. // Error Info
  2202. errHpj.iWarningLevel = 3;
  2203. errHpj.ep = epNoFile;
  2204. // File Stack
  2205. pfs = (PFILESTACK) lcMalloc(sizeof(FILESTACK));
  2206. pfs->ifeTop = -1;
  2207. pfs->ifeComment = -1;
  2208. pfs->iCommentLine = 0;
  2209. // Options
  2210. options.sortorder = SORT_ENGLISH;
  2211. options.pszContentsTopic = NULL;
  2212. options.iWarningLevel = 3;
  2213. options.fUsePhrase = TRUE; // REVIEW
  2214. options.fAcceptRevions = TRUE;
  2215. Ensure(HceAddPkwiCh('K'), HCE_OK);
  2216. Ensure(HceAddPkwiCh('A'), HCE_OK); // for ALinks
  2217. ptblRtfFiles = new CTable;
  2218. // Compile time variables
  2219. nsr = nsrNone;
  2220. return TRUE;
  2221. }
  2222. /***************************************************************************\
  2223. *
  2224. - Function: FParseHpj( szFile )
  2225. -
  2226. * Purpose: Parse the named project file, filling in the HPJ structure.
  2227. *
  2228. * ASSUMES
  2229. *
  2230. * args IN: szFile - name of project file (default .HPJ extension)
  2231. *
  2232. * PROMISES
  2233. *
  2234. * returns: TRUE - parsed OK
  2235. * FALSE - HPJ file unusable, bad file extension
  2236. * - ran out of memory
  2237. *
  2238. *
  2239. * Side Effects: emits error messages
  2240. *
  2241. - Notes:
  2242. -
  2243. - Bugs: Doesn't free discarded and unlock retained smag. REVIEW!
  2244. -
  2245. * +++
  2246. -
  2247. - Method:
  2248. -
  2249. - Notes:
  2250. -
  2251. \***************************************************************************/
  2252. BOOL STDCALL FParseHpj(PSTR pszFile)
  2253. {
  2254. PFPARSE pfparse = NULL;
  2255. RC_TYPE rc = RC_Success;
  2256. PSTR psz;
  2257. if (iflags.fRtfInput) {
  2258. // This is an RTF file, not an HPJ file
  2259. ptblRtfFiles->AddString(pszFile);
  2260. return TRUE;
  2261. }
  2262. for(;;) { // not a real for loop, just gives us something to break out of
  2263. /*
  2264. * If a path was specified, then try to change to that drive and
  2265. * directory. If we can, then remove the path, and just leave the
  2266. * filename. Makes for cleaner error output by removing the path
  2267. * name of the .HPJ file, and allows other files to be relative to
  2268. * the current directory.
  2269. */
  2270. if ((psz = StrRChr(pszFile, CH_BACKSLASH, fDBCSSystem))) {
  2271. *psz = '\0';
  2272. if (_chdir(pszFile) != 0) {
  2273. *psz = CH_BACKSLASH;
  2274. break;
  2275. }
  2276. if (pszFile[1] == ':') {
  2277. if (_chdrive(tolower(pszFile[0]) - ('a' - 1)) != 0) {
  2278. *psz = CH_BACKSLASH;
  2279. break;
  2280. }
  2281. }
  2282. strcpy(pszFile, psz + 1);
  2283. }
  2284. break;
  2285. }
  2286. errHpj.lpszFile = pszFile;
  2287. // extension defaults to szInputExt (.HPJ)
  2288. // REVIEW: why strip off the path? 03-Jan-1994 [ralphw]
  2289. // [olympus 306 - chauv]
  2290. for (psz = pszFile + strlen(pszFile) - 1; psz > pszFile; psz--) {
  2291. // if it's a backslash, check to see if it's a trailing backslash
  2292. if (*psz == '\\') {
  2293. if ( ((psz-1) >= pszFile) && IsDBCSLeadByte(*(psz-1)) )
  2294. psz--;
  2295. else
  2296. break;
  2297. }
  2298. if ( (*psz == '/') || (*psz == ':') || (*psz == '.') )
  2299. break;
  2300. }
  2301. char szHpjName[_MAX_PATH];
  2302. if (*psz != '.') {
  2303. strcpy(szHpjName, pszFile);
  2304. strcat(szHpjName, GetStringResource(IDS_HPJ_EXTENSION));
  2305. pszFile = szHpjName;
  2306. }
  2307. if (FIsRtf(pszFile)) {
  2308. // REVIEW: 23-Jul-1993 [ralphw] why
  2309. // are we allowing them to open an RTF file?
  2310. return FALSE;
  2311. }
  2312. if (!FPushFilePfs(pszFile)) {
  2313. lcFree(pfs);
  2314. return FALSE;
  2315. }
  2316. errHpj.ep = epLine;
  2317. CStr cszDst;
  2318. /*
  2319. * We want automatic deletion of the table, but we want it to be
  2320. * available to other functions while we're processing the .HPJ file, so
  2321. * we simply set a global pointer to the address of our local table.
  2322. * Tacky, but it works.
  2323. */
  2324. CTable tbl;
  2325. ptblDefine = &tbl;
  2326. while (rc == RC_Success) {
  2327. rc = RcGetLogicalLine(&cszDst);
  2328. SpecialParsing:
  2329. if (RC_Success != rc)
  2330. break;
  2331. if (*cszDst.psz == CH_LEFT_BRACKET) {
  2332. psz = StrChr(cszDst, CH_RIGHT_BRACKET, fDBCSSystem);
  2333. if (!psz) {
  2334. VReportError(HCERR_MISSING_SECTION_BRACKET, &errHpj,
  2335. cszDst);
  2336. }
  2337. else {
  2338. *psz = '\0';
  2339. psz = SzTrimSz(cszDst.psz + 1);
  2340. pfparse = PfparseFromSz(psz);
  2341. /*
  2342. * We don't parse the [MACROS] section normally. This
  2343. * section cannot contain a #include line, and cannot contain
  2344. * any comments. So the usual line cleanup don by
  2345. * RcGetLogicalLine() would destroy the information we need.
  2346. * Instead, we let RcParseMacros read its entire section.
  2347. */
  2348. if (pfparse == RcParseMacros) {
  2349. rc = ParseMacros(&cszDst);
  2350. if (rc == RC_Success)
  2351. goto SpecialParsing;
  2352. else { // might have been end of a nested include
  2353. rc = RcGetLogicalLine(&cszDst);
  2354. goto SpecialParsing;
  2355. }
  2356. }
  2357. }
  2358. }
  2359. else {
  2360. if (pfparse == NULL) {
  2361. VReportError(HCERR_MISSING_SECTION, &errHpj, cszDst);
  2362. pfparse = RcParseBogusSz;
  2363. }
  2364. else {
  2365. rc = pfparse(cszDst.psz); // REVIEW - under what conditions term?
  2366. lcHeapCheck();
  2367. if (rc == RC_SkipSection) {
  2368. pfparse = RcParseBogusSz;
  2369. rc = RC_Success;
  2370. }
  2371. }
  2372. }
  2373. }
  2374. if (rc == RC_EOF) {
  2375. rc = RC_Success;
  2376. /*
  2377. * If no compression, we still use RLE compression for bitmaps.
  2378. * This is an insignificant speed hit with a significant size
  2379. * reduction.
  2380. */
  2381. if (options.fsCompress == COMPRESS_NONE)
  2382. options.fsCompress = COMPRESS_BMP_RLE;
  2383. if (kwlcid.langid)
  2384. SetDbcsFlag(kwlcid.langid);
  2385. if (FTestUlFlag(options.lOptionInitFlags, OPT_BUILD)) {
  2386. if (!FTestUFlag(wSectionInitFlags, SECT_BUILDTAGS)) {
  2387. fBldChk = -1;
  2388. VReportError(HCERR_BUILD_TAG_MISSING, &errHpj);
  2389. }
  2390. else if (!FBuildPolishExpFromSz(options.szBuildExp)) {
  2391. fBldChk = -1; // REVIEW - ugly global
  2392. VReportError(HCERR_INVALID_BUILD_EXP, &errHpj);
  2393. }
  2394. else
  2395. fBldChk = 1; // REVIEW - ugly global
  2396. }
  2397. if (!FTestUFlag(wSectionInitFlags, SECT_FILES) ||
  2398. ptblRtfFiles->CountStrings() < 1) {
  2399. VReportError(HCERR_NOFILES_DEFINED, &errHpj);
  2400. rc = RC_Failure;
  2401. goto error_return;
  2402. }
  2403. // Reset error phase.
  2404. errHpj.ep = epNoFile;
  2405. lcFree(pfs);
  2406. return (rc == RC_Success);
  2407. }
  2408. error_return:
  2409. lcFree(pfs);
  2410. return (rc == RC_Success);
  2411. }
  2412. const char txtCopyright[] =
  2413. "Copyright (c) Microsoft Corp 1990 - 1994. All rights reserved.";
  2414. static void STDCALL DispSignOn(void)
  2415. {
  2416. pLogFile->outstring_eol(GetStringResource(IDS_TITLE));
  2417. pLogFile->outstring_eol(GetStringResource(IDS_VERSION));
  2418. pLogFile->outstring_eol((PSTR) txtCopyright);
  2419. }
  2420. PSTR STDCALL SkipToEndOfWord(PSTR psz)
  2421. {
  2422. while (*psz != SPACE && *psz != CHAR_TAB && *psz)
  2423. psz = CharNext(psz);
  2424. return psz;
  2425. }
  2426. /***************************************************************************\
  2427. *
  2428. - Function: SzLoseDriveAndDir( szFile, rgch )
  2429. -
  2430. * Purpose: Return the base name + ext from a file name.
  2431. *
  2432. * ASSUMES
  2433. *
  2434. * args IN: szFile - a filespec that may or may not have drive and/or
  2435. * directory. It isn't NULL or "".
  2436. * rgch - buffer to receive the info. If NULL, a buffer
  2437. * is allocated.
  2438. * PROMISES
  2439. *
  2440. * returns: pointer to buffer where the base + ext are placed.
  2441. * This may be allocated (see rgch above).
  2442. *
  2443. * args OUT: rgch - data put here, except as stated above
  2444. *
  2445. * Side Effects: may allocate memory
  2446. *
  2447. \***************************************************************************/
  2448. void STDCALL SzLoseDriveAndDir(PSTR szFile, PSTR pszDst)
  2449. {
  2450. ASSERT(szFile != NULL);
  2451. // [olympus 306 - chauv]
  2452. // use _splitpath() to do the work
  2453. char fname[_MAX_FNAME], ext[_MAX_EXT];
  2454. _splitpath(szFile, NULL, NULL, fname, ext);
  2455. strcpy(pszDst, fname);
  2456. strcat(pszDst, ext);
  2457. }
  2458. /***************************************************************************\
  2459. *
  2460. - Function: PfparseFromSz( szSection)
  2461. -
  2462. * Purpose: Return a section parsing function based on section name.
  2463. * Emit message on error.
  2464. *
  2465. * ASSUMES
  2466. *
  2467. * args IN: szSection - the section name to look up
  2468. * - if ->wSectionInitFlags section flag already
  2469. * set, emit message and return bogus function
  2470. *
  2471. * globals IN: rgSection - array of section names
  2472. *
  2473. * PROMISES
  2474. *
  2475. * returns: Pointer to a section parsing function. If szSection
  2476. * wasn't a valid section name, return a default function.
  2477. *
  2478. * args OUT: wSectionInitFlags - section flag set
  2479. *
  2480. * +++
  2481. *
  2482. * Method: Linear search. Could use binary search.
  2483. *
  2484. \***************************************************************************/
  2485. static PFPARSE STDCALL PfparseFromSz(PSTR pszSection)
  2486. {
  2487. int sect;
  2488. ASSERT(SECT_MAX < ELEMENTS(rgSection));
  2489. for (sect = 0; sect < SECT_MAX; sect++) {
  2490. if (_stricmp(rgSection[sect].szName, pszSection) == 0)
  2491. break;
  2492. }
  2493. if (sect == SECT_MAX) {
  2494. if (nstrisubcmp(pszSection, "CONFIG")) {
  2495. // Named config section
  2496. if (pszSection[6] == '-') {
  2497. if (pdrgWsmag) {
  2498. for (int i = 0; i < pdrgWsmag->Count(); i++) {
  2499. WSMAG *pwsmag = ((WSMAG *) pdrgWsmag->GetBasePtr()) + i;
  2500. if (!_stricmp(pszSection + 7, pwsmag->rgchMember)) {
  2501. curConfig = i;
  2502. return RcParseSecondaryConfigSz;
  2503. }
  2504. }
  2505. }
  2506. }
  2507. // Numbered config section
  2508. else if (pszSection[6] == ':') {
  2509. if (FGetNum(pszSection + 7, NULL, &curConfig)) {
  2510. return RcParseSecondaryConfigSz;
  2511. }
  2512. }
  2513. }
  2514. VReportError(HCERR_UNKNOWN_SECTION, &errHpj, pszSection);
  2515. }
  2516. if (sect == SECT_OPTIONS && !FTestUFlag(wSectionInitFlags, SECT_OPTIONS) &&
  2517. (FTestUFlag(wSectionInitFlags, SECT_BITMAPS) ||
  2518. FTestUFlag(wSectionInitFlags, SECT_FILES)))
  2519. VReportError(HCERR_SECTION_TOO_SOON, &errHpj, pszSection);
  2520. SetUFlag(wSectionInitFlags, sect); // set even if bogus section
  2521. return rgSection[sect].pfparse;
  2522. }
  2523. RC_TYPE STDCALL RcParseCharSet(PSTR pszLine)
  2524. {
  2525. PSTR pszCharSet;
  2526. if (!(pszCharSet = StrChr(pszLine, CH_EQUAL, fDBCSSystem))) {
  2527. VReportError(HCERR_MISS_CHARSET_EQ, &errHpj, pszLine);
  2528. return RC_Success;
  2529. }
  2530. *pszCharSet = '\0';
  2531. pszCharSet = FirstNonSpace(pszCharSet + 1, fDBCSSystem);
  2532. if (!isdigit((BYTE) *pszCharSet)) {
  2533. VReportError(HCERR_INVALID_CHARSET, &errHpj, pszLine);
  2534. return RC_Success;
  2535. }
  2536. SzTrimSz(pszLine);
  2537. if (!ptblCharSet)
  2538. ptblCharSet = new CTable;
  2539. SzTrimSz(pszCharSet);
  2540. if (atoi(pszCharSet) > 255 || atoi(pszCharSet) < 0) {
  2541. VReportError(HCERR_INVALID_CHARSET, &errHpj, pszLine);
  2542. return RC_Success;
  2543. }
  2544. ptblCharSet->AddString(pszLine, pszCharSet);
  2545. return RC_Success;
  2546. }
  2547. RC_TYPE STDCALL RcParseFonts(PSTR pszLine)
  2548. {
  2549. CFontMap* pMap = new CFontMap(pszLine);
  2550. if (!pMap->IsInitialized())
  2551. delete pMap;
  2552. return RC_Success;
  2553. }
  2554. static void STDCALL ReportDuplicateOption(PSTR* ppszOption, PSTR pszName)
  2555. {
  2556. VReportError(HCERR_DUPLICATE_OPTION, &errHpj, pszName);
  2557. if (ppszOption)
  2558. lcClearFree(ppszOption);
  2559. }
  2560. static void STDCALL ReportBadOption(PSTR pszOptionValue, PSTR pszOption)
  2561. {
  2562. VReportError(HCERR_INVALID_OPTION, &errHpj, pszOption, pszOptionValue);
  2563. }
  2564. /***************************************************************************
  2565. FUNCTION: CreateSharedMemory
  2566. PURPOSE: Create shared memory for communicating with our parent
  2567. PARAMETERS:
  2568. void
  2569. RETURNS:
  2570. COMMENTS:
  2571. MODIFICATION DATES:
  2572. 02-Jul-1994 [ralphw]
  2573. ***************************************************************************/
  2574. void STDCALL CreateSharedMemory(void)
  2575. {
  2576. if (!hfShare) {
  2577. hfShare = CreateFileMapping((HANDLE) -1, NULL, PAGE_READWRITE, 0,
  2578. 4096, txtSharedMem);
  2579. ConfirmOrDie(hfShare);
  2580. pszMap = (PSTR) MapViewOfFile(hfShare, FILE_MAP_WRITE, 0, 0, 0);
  2581. ASSERT(pszMap);
  2582. }
  2583. }
  2584. // This function only exists for match purposes. It is never called
  2585. RC_TYPE STDCALL RcParseMacros(PSTR pszLine)
  2586. {
  2587. ASSERT(!"This function should never be called!");
  2588. return RC_Success;
  2589. }
  2590. /***************************************************************************
  2591. FUNCTION: ParseMacros
  2592. PURPOSE: This creates two tables. The is a double-string table
  2593. containing each macro and title string pair. There is
  2594. a keyword table -- with each keyword having its own entry
  2595. including a position into the associated macro/title table.
  2596. PARAMETERS:
  2597. pcszLine
  2598. RETURNS:
  2599. COMMENTS:
  2600. MODIFICATION DATES:
  2601. 05-Sep-1994 [ralphw]
  2602. ***************************************************************************/
  2603. static RC_TYPE STDCALL ParseMacros(CStr* pcszLine)
  2604. {
  2605. if (!ptblMacKeywords) {
  2606. ptblMacKeywords = new CTable;
  2607. ptblMacroTitles = new CTable;
  2608. }
  2609. CStr cszMacro;
  2610. CStr cszTitle;
  2611. int pos;
  2612. CInput* pin = PfTopPfs();
  2613. if (!pin)
  2614. return RC_Success;
  2615. for (;;) {
  2616. if (!pin->getline(pcszLine)) {
  2617. return RC_EOF;
  2618. }
  2619. if (*pcszLine->psz == CH_LEFT_BRACKET)
  2620. return RC_Success;
  2621. else if (!*pcszLine->psz)
  2622. continue; // blank line -- shouldn't happen, but we'll allow it
  2623. PSTR pszKey = SzParseList(pcszLine->psz);
  2624. if (pszKey == NULL) {
  2625. VReportError(HCERR_NULL_KEYWORD, &errHpj);
  2626. return RC_EOF;
  2627. }
  2628. if (!pin->getline(&cszMacro)) {
  2629. VReportError(HCERR_NULL_KEYWORD, &errHpj);
  2630. return RC_EOF;
  2631. }
  2632. if (!pin->getline(&cszTitle)) {
  2633. VReportError(HCERR_NULL_KEYWORD, &errHpj);
  2634. return RC_EOF;
  2635. }
  2636. pos = ptblMacroTitles->AddString(cszMacro);
  2637. ptblMacroTitles->AddString(cszTitle);
  2638. while (pszKey) {
  2639. ptblMacKeywords->AddIntAndString(pos, pszKey);
  2640. pszKey = SzParseList(NULL);
  2641. }
  2642. }
  2643. }
  2644. static char szLcid[20];
  2645. BOOL CALLBACK EnumLocalesProc(LPSTR pszLocale)
  2646. {
  2647. if (!szLcid[0])
  2648. wsprintf(szLcid, "%08x", kwlcid.langid);
  2649. if (stricmp(pszLocale, szLcid) == 0) {
  2650. fValidLcid = TRUE;
  2651. return FALSE;
  2652. }
  2653. return TRUE;
  2654. }
  2655. /***************************************************************************
  2656. FUNCTION: SetDbcsFlag
  2657. PURPOSE: Try to force DBCS flag based on language ID
  2658. PARAMETERS:
  2659. langid
  2660. RETURNS:
  2661. COMMENTS:
  2662. MODIFICATION DATES:
  2663. 11-Jan-1995 [ralphw]
  2664. ***************************************************************************/
  2665. void STDCALL SetDbcsFlag(LANGID langid)
  2666. {
  2667. switch (langid) {
  2668. case 0x0411: // Japanese
  2669. case 0x0404: // Taiwan
  2670. case 0x1004: // Singapore
  2671. case 0x0C04: // Hong Kong
  2672. options.fDBCS = TRUE;
  2673. break;
  2674. case 0x0409: // American
  2675. case 0x0C09: // Australian
  2676. case 0x0C07: // Austrian
  2677. case 0x042D: // Basque
  2678. case 0x080C: // Belgian
  2679. case 0x0809: // British
  2680. case 0x0402: // Bulgaria
  2681. case 0x1009: // Canadian
  2682. case 0x041A: // Croatian
  2683. case 0x0405: // Czech
  2684. case 0x0406: // Danish
  2685. case 0x0413: // Dutch (Standard)
  2686. case 0x0C01: // Egypt
  2687. case 0x040B: // Finnish
  2688. case 0x040C: // French (Standard)
  2689. case 0x0C0C: // French Canadian
  2690. case 0x0407: // German (Standard)
  2691. case 0x042E: // Germany
  2692. case 0x0408: // Greek
  2693. case 0x040E: // Hungarian
  2694. case 0x040F: // Icelandic
  2695. case 0x0801: // Iraq
  2696. case 0x1809: // Ireland
  2697. case 0x040D: // Israel
  2698. case 0x0410: // Italian (Standard)
  2699. case 0x2C01: // Jordan
  2700. case 0x3401: // Kuwait
  2701. case 0x0426: // Latvia
  2702. case 0x3001: // Lebanon
  2703. case 0x1001: // Libya
  2704. case 0x1407: // Liechtenstein
  2705. case 0x0427: // Lithuania
  2706. case 0x140C: // Luxembourg (French)
  2707. case 0x1007: // Luxembourg (German)
  2708. case 0x042f: // Macedonia
  2709. case 0x080A: // Mexican
  2710. case 0x0819: // Moldavia
  2711. case 0x0818: // Moldavia
  2712. case 0x1801: // Morocco
  2713. case 0x1409: // New Zealand
  2714. case 0x0414: // Norwegian (Bokmal)
  2715. case 0x0814: // Norwegian (Nynorsk)
  2716. case 0x2001: // Oman
  2717. case 0x0415: // Polish
  2718. case 0x0416: // Portuguese (Brazilian)
  2719. case 0x0816: // Portuguese (Standard)
  2720. case 0x0418: // Romania
  2721. case 0x0419: // Russian
  2722. case 0x0401: // Saudi Arabia
  2723. case 0x081A: // Serbian
  2724. case 0x041B: // Slovak
  2725. case 0x0424: // Slovenia
  2726. case 0x0C0A: // Spanish (Modern Sort)
  2727. case 0x040A: // Spanish (Traditional Sort)
  2728. case 0x0430: // Sutu
  2729. case 0x041D: // Swedish
  2730. case 0x100C: // Swiss (French)
  2731. case 0x0807: // Swiss (German)
  2732. case 0x0810: // Swiss (Italian)
  2733. case 0x2801: // Syria
  2734. case 0x041E: // Thailand
  2735. case 0x0431: // Tsonga
  2736. case 0x041f: // Turkish
  2737. case 0x3801: // U.A.E.
  2738. case 0x0422: // Ukraine
  2739. case 0x0420: // Urdu
  2740. case 0x0436: // Zulu
  2741. options.fDBCS = FALSE;
  2742. break;
  2743. }
  2744. }