|
|
/***************************************************************************
* * HPJ.C * * Copyright (C) Microsoft Corporation 1990 - 1994. * All Rights reserved. * **************************************************************************** * * Module Intent * * This module contains routines for parsing the .HPJ file. * ****************************************************************************/ #include "stdafx.h"
#include "cfontmap.h"
#include "..\common\coutput.h"
#include <errno.h> // ERANGE
#include <io.h>
#include <direct.h>
#include <time.h>
#include "..\common\resource.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
/*****************************************************************************
* * * Defines * * * *****************************************************************************/
// Parser states
#define CCOM ((STATE) 1) // inside a C-style comment
#define LEADING ((STATE) 2) // still eating leading white space
#define COM_EOL ((STATE) 3) // inside a comment to EOL (either ; or //)
#define SLASH ((STATE) 4) // seen a /
#define CCOMSTAR ((STATE) 5) // maybe about to end a C-style comment
#define MEAT ((STATE) 6) // something on this line isn't a comment
const char SPACE = ' '; const char CHAR_TAB = '\t';
// Constants for Window Smag stuff
// three possible outcomes of trying to read something out of the buffer:
// bad syntax, read ok, or value wasn't there before the comma
const int WINDOW_READ_INVALID = 0; const int WINDOW_READ_OK = 1; const int WINDOW_READ_MISSING = 2;
/*****************************************************************************
* * * Typedefs * * * *****************************************************************************/
typedef RC_TYPE (STDCALL NEAR* PFPARSE)(PSTR);
/*
File Entry structure (for file stack) REVIEW - currently the full path spec is stored for each file. */
typedef struct { CInput* pinput; char rgchFile[_MAX_PATH]; int iLine; // line number
} FE, *PFE;
/*
File Stack This is used to store the file handles, names, and current line numbers of the open .HPJ file and files included therein. */
typedef struct { int ifeTop; // index to current file (-1 for empty)
int ifeComment; // file in which C-style comment started
int iCommentLine; // line on which C-style comment started
FE rgfe[MAX_INCLUDE]; } FILESTACK; typedef FILESTACK* PFILESTACK;
/*****************************************************************************
* * * Static Variables * * * *****************************************************************************/
static PFILESTACK pfs;
const char txtSecondary[] = "secondary"; static const char txtDefine[] = "#define"; static const char txtIfDef[] = "#ifdef"; static const char txtIfnDef[] = "#ifndef"; static int curConfig = 0;
// section table
// The section parsing functions
RC_TYPE STDCALL RcParseAliasSz(PSTR); RC_TYPE STDCALL RcParseBaggageSz(PSTR); RC_TYPE STDCALL RcParseBitmapsSz(PSTR); RC_TYPE STDCALL RcParseBogusSz(PSTR); RC_TYPE STDCALL RcParseBuildTagsSz(PSTR); RC_TYPE STDCALL RcParseCharSet(PSTR); RC_TYPE STDCALL RcParseConfigSz(PSTR); RC_TYPE STDCALL RcParseFilesSz(PSTR); RC_TYPE STDCALL RcParseFonts(PSTR); RC_TYPE STDCALL RcParseMacros(PSTR); RC_TYPE STDCALL RcParseMapSz(PSTR); RC_TYPE STDCALL RcParseOptionsSz(PSTR); RC_TYPE STDCALL RcParseSecondaryConfigSz(PSTR); RC_TYPE STDCALL RcParseWindowsSz(PSTR);
// Associates a section name with a section parsing function.
typedef struct { PSTR szName; PFPARSE pfparse; } SECTION;
SECTION rgSection[] = { { "ALIAS", RcParseAliasSz }, { "BAGGAGE", RcParseBaggageSz }, { "BITMAPS", RcParseBitmapsSz }, { "BUILDTAGS", RcParseBuildTagsSz }, { "CONFIG", RcParseConfigSz }, { "FILES", RcParseFilesSz }, { "MAP", RcParseMapSz }, { "OPTIONS", RcParseOptionsSz }, { "WINDOWS", RcParseWindowsSz },
// New to 4.0
{ "CHARSET", RcParseCharSet }, { "MACROS", RcParseMacros }, { "FONTS", RcParseFonts },
{ NULL, RcParseBogusSz }, };
// Strings used for COMPRESS option in addition to yes/no strings
const char txtLow[] = "low"; const char txtMedium[] = "medium"; const char txtHigh[] = "high"; const char txtNone[] = "none"; const char txtJohn[] = "JOHN";
// Language stuff
char *rgszSortOrder[] = { "ENGLISH", "SCANDINAVIAN", "JAPANESE", "KOREAN", "CHINESE", "CZECH", "POLISH", "HUNGARIAN", "RUSSIAN", "EASTERN EUROPE", "NLS", };
#define MAX_SORT ELEMENTS(rgszSortOrder)
COutput* pLogFile; static CTable* ptblDefine;
/*****************************************************************************
* * * Prototypes * * * *****************************************************************************/
static void STDCALL DispSignOn(void); static BOOL STDCALL FIsIconPf(FILE* pf); static BOOL STDCALL FIsRtf(PSTR); static BOOL STDCALL FPushFilePfs(PSTR szFile); static UINT STDCALL FReadInt16(PSTR *psz, WORD* pi, WORD *pgrf, UINT f); static UINT STDCALL FReadRgb(PSTR *psz, LONG *pl, UINT *pgrf, UINT f); static void STDCALL ParseMapFontSizeOption(PSTR, PCSTR); static PFPARSE STDCALL PfparseFromSz(PSTR); static RC_TYPE STDCALL RcGetLogicalLine(CStr*); static RC_TYPE STDCALL RcLoadBitmapFm(FM fmSource, FM FAR* pfmDest, UINT FAR* pwObjrg, BOOL fsCompress); static void STDCALL ReportDuplicateOption(PSTR* pszOption, PSTR pszName); static void STDCALL ReportBadOption(PSTR pszOptionValue, PSTR pszOption); static RC_TYPE STDCALL ParseMacros(CStr* pcszLine);
__inline CInput* STDCALL PfTopPfs(void); __inline BOOL STDCALL FVerifyBuildTag(PSTR psz); __inline BOOL STDCALL FPopPfs(void);
/***************************************************************************
FUNCTION: SzGetDriveAndDir
PURPOSE: Return the drive letter and directory from a file spec
PARAMETERS: pszFile - a filespec that may or may not have drive and/or directory. It isn't NULL or "". pszDst - buffer to receive the info. If NULL, a buffer is allocated.
RETURNS: pointer to buffer where the drive and dir are placed. This may be allocated (see pszDst above).
COMMENTS:
MODIFICATION DATES: 03-Jul-1993 [ralphw]
***************************************************************************/
// REVIEW: allocating memory is risky if caller forgets to free it
PSTR STDCALL SzGetDriveAndDir(PCSTR pszFile, PSTR pszDst) { if (!pszFile) return lcStrDup("."); ASSERT(pszFile != NULL);
char szFileCopy[_MAX_FNAME]; strcpy(szFileCopy, pszFile);
PSTR psz;
for (psz = szFileCopy + strlen(szFileCopy) - 1; psz > szFileCopy && ((*psz != ':' && *psz != '\\' && *psz != '/') || IsFirstByte(*(psz - 1))); --psz);
int cb = psz - szFileCopy + 1;
if (pszDst == NULL) pszDst = (PSTR) lcMalloc(cb + 1);
if (cb > 1) memcpy(pszDst, szFileCopy, cb); else cb = 0;
pszDst[cb] = '\0';
return pszDst; }
/***************************************************************************\
* - Function: FIsRtf( szFile ) - * Purpose: Determine whether szFile is an RTF file (or looks like one). * * ASSUMES * * args IN: szFile - filename * * PROMISES * * returns: TRUE if file contains a normal looking header * FALSE if there is some i/o error or it doesn't * contain rtf header info. * * Side Effects: File is opened and closed. * \***************************************************************************/
static BOOL STDCALL FIsRtf(PSTR pszFile) { char szBuf[_MAX_PATH];
// easy way to check for devices (aux, prn, com1)
if (HceResolveFileNameSz(pszFile, NULL, szBuf) != HCE_OK) return FALSE;
CRead cr(szBuf); if (cr.hf == HFILE_ERROR) return FALSE; cr.read(szBuf, 5); if (_stricmp(szBuf, "{\\rtf") == 0) // is this an RTF file?
return TRUE; else return FALSE; }
/***************************************************************************\
* - Function: FIsIconPf( pf ) - * Purpose: Determine whether pf is an icon file (or looks like one). * * ASSUMES * * args IN: pf - file pointer * * PROMISES * * returns: TRUE if file contains a normal looking header * FALSE if there is some i/o error or it doesn't * contain icon header info. * * Method: Read the first four bytes and check that they match * what an .ICO file has. * * Note: This takes a FILE * rather than a filename because * I have one handle. * \***************************************************************************/
static BOOL STDCALL FIsIconPf(FILE* pf) { char szBuf[4];
return (fread(szBuf, 1, 4, pf) == 4 && szBuf[0] == 0 && szBuf[1] == 0 && szBuf[2] == 1 && szBuf[3] == 0); }
/***************************************************************************\
* - Function: HceResolveFileNameSz( szFile, szRoot, szBuffer, ppf ) - * Purpose: Resolve a filename relative to a given root directory. * If the filename is absolute (either drive letter or * leading '\' given), the root directory is ignored. * * ASSUMES * * args IN: szFile - the file name (non-null) * szRoot - the home directory name; full path with drive * (NULL OK) * ppf - pointer to FILE* for open file (NULL to not open) * * PROMISES * * returns: HCE_OK * hceFileNameTooLong * hcePathNameTooLong * hceBadlyFormed * hceFileNotFound * hceFileIsDirectory * hceFileIsDevice * * args OUT: szBuffer - resolved file name goes here if successful * ppf - open FILE * placed here * * * Notes: Doesn't check for badly formed file names, such as * filenames containing spaces, multiple colons, etc. * Such bogus names will just come back as an error * opening the file. * * stat() doesn't tell me if the file is a device. * I use isatty(). * \***************************************************************************/
HCE STDCALL HceResolveFileNameSz(PSTR szFile, PSTR pszRoot, PSTR pszBuffer, FILE** ppf, CInput** ppinput) { ASSERT(pszBuffer);
int cbRoot = (pszRoot == NULL) ? 0 : strlen(pszRoot); if (strlen(szFile) + cbRoot >= _MAX_PATH) return HCE_NAME_TOO_LONG;
if (*szFile == '\\' || szFile[1] == ':' || pszRoot == NULL || *pszRoot == '\0') pszBuffer[0] = '\0';
else {
// last char of pszRoot if any
strcpy(pszBuffer, pszRoot); // [olympus 306 - chauv]
// changed code to check for trailing backslash
int i = strlen(pszBuffer); if ( ((i > 1) && (pszBuffer[i-1] == '\\') && (IsDBCSLeadByte(pszBuffer[i-2]))) || ( (i > 0) && (pszBuffer[i-1] != '\\') && (pszBuffer[i-1] != ':')) ) strcat(pszBuffer, "\\"); }
strcat(pszBuffer, szFile);
/*
* If we have a replacement string, and we have a path specification, * and the first part of that path matches our replace string, then * perform a substitution. Its up to the help author to specify a valid * replacement string. */
if (options.pszReplace && (StrChr(pszBuffer, CH_BACKSLASH, fDBCSSystem) || StrChr(pszBuffer, CH_COLON, fDBCSSystem))) { if (nstrisubcmp(pszBuffer, options.pszReplace)) { CStr szTmp(pszBuffer); strcpy(pszBuffer, options.pszReplaceWith); strcat(pszBuffer, (PSTR) szTmp + strlen(options.pszReplace)); } }
if (ppinput) { *ppinput = new CInput(pszBuffer); if (!(*ppinput)->fInitialized) { delete *ppinput; *ppinput = NULL; DWORD attribute = GetFileAttributes(pszBuffer); if (attribute == HFILE_ERROR) return HCE_FILE_NOT_FOUND; else if (attribute == FILE_ATTRIBUTE_DIRECTORY) return HCE_FILE_IS_DIRECTORY; else return HCE_CANNOT_OPEN; } return HCE_OK; }
FILE *pf;
if (!iflags.fTrusted || ppf) {
/*
* REVIEW: 12-Apr-1994 [ralphw] if ppf == NULL, the only reason * for opening this is to see if the file is a device (isatty). For * very small RTF files, this is a significant time hit (believe it * or not!) Might be worthwhile to simply remove it and simply check * for known device names. */
if (!ppf) { if (GetFileAttributes(pszBuffer) != HFILE_ERROR) { return HCE_OK; } else { return HCE_FILE_NOT_FOUND; } }
pf = fopen(pszBuffer, "r");
if (pf == NULL) { DWORD attribute = GetFileAttributes(pszBuffer); if (attribute == HFILE_ERROR) return HCE_FILE_NOT_FOUND; else if (attribute == FILE_ATTRIBUTE_DIRECTORY) return HCE_FILE_IS_DIRECTORY; else return HCE_NO_PERMISSION; // REVIEW - can this be anything else?
} if (_isatty(_fileno(pf))) { fclose(pf); return HCE_FILE_IS_DEVICE; } if (ppf == NULL) fclose(pf); else *ppf = pf; }
return HCE_OK; }
HCE STDCALL HceResolveTableDir(PSTR pszFile, CTable *ptblDir, PSTR pszBuffer, FILE** ppf) { HCE hce;
if (!ptblDir) return HceResolveFileNameSz(pszFile, NULL, pszBuffer, ppf);
if (ptblDir->CountStrings() > 0) { for (int pos = 1; pos <= ptblDir->CountStrings(); pos++) { hce = HceResolveFileNameSz(pszFile, ptblDir->GetPointer(pos), pszBuffer, ppf); if (hce != HCE_FILE_NOT_FOUND) return hce; } }
return HCE_FILE_NOT_FOUND; }
/***************************************************************************\
* - Function: RcParseBogusSz( sz) - * Status: stub * * Purpose: Parse an invalid line. (is this function really needed? maybe, if don't want to spew endless error messages.... * * ASSUMES * * args IN: sz - string * * PROMISES * * returns: success code (seems meaningless in this instance) * \***************************************************************************/
RC_TYPE STDCALL RcParseBogusSz(PSTR sz) { Unreferenced(sz);
return RC_Success; }
/***************************************************************************\
* - Function: ParseMapFontSizeOption( pszRHS, poptions, perr ) - * Purpose: Parse the string into the FONTRANGE struct. * * "mapfontsize" "=" <number> "-" <number> ":" <number> | * "mapfontsize" "=" <number> ":" <number> * <number> refers to size in points. * * ASSUMES * * args IN: pszRHS - * poptions - * perr - * * PROMISES * * args OUT: poptions - iFontRangeMac, rgFontRange updated. * * Side Effects: May emit one of the following errors: * * hceInvalidFontRangeFormat * hceTooManyFontRanges * hceFontRangeOverlap * * Notes: Doesn't recognize fractional points. * \***************************************************************************/
static void STDCALL ParseMapFontSizeOption(PSTR pszRHS, PCSTR pszLine) { HALFPT halfptInMin, halfptInMost, halfptOut; UINT u; int i; FONTRANGE* qfrT;
// clear flag because this option can occur > 1 time
ClearUlFlag(options.lOptionInitFlags, OPT_MAPFONTSIZE);
if (MAX_FONTRANGE <= options.iFontRangeMac) { VReportError(HCERR_TOO_MANY_FONT_RANGES, &errHpj); return; }
if (!FGetUnsigned(pszRHS, &pszRHS, &u)) { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; }
halfptInMin = (HALFPT) (u * 2); // REVIEW - more range checking?
if (*pszRHS == '-') { pszRHS = FirstNonSpace(pszRHS + 1, fDBCSSystem);
if (!FGetUnsigned(pszRHS, &pszRHS, &u)) { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; } halfptInMost = (HALFPT) (u * 2); // REVIEW - more range checking?
if (halfptInMost < halfptInMin) { HALFPT halfptT = halfptInMost; halfptInMost = halfptInMin; halfptInMin = halfptT; }
if (':' != *pszRHS) { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; } pszRHS = FirstNonSpace(pszRHS, fDBCSSystem); } else if (*pszRHS == ':') halfptInMost = halfptInMin; else { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; }
pszRHS = FirstNonSpace(pszRHS + 1, fDBCSSystem);
if (!FGetUnsigned(pszRHS, &pszRHS, &u)) { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; } halfptOut = (HALFPT) (2 * u); // REVIEW - more range checking?
if (*FirstNonSpace(pszRHS, fDBCSSystem)) { VReportError(HCERR_INVALID_FONT_RANGE, &errHpj, pszLine); return; }
// Check for font range overlap.
for (i = options.iFontRangeMac - 1, qfrT = options.rgFontRange; i >= 0; i--, qfrT++) { if (! (halfptInMin > qfrT->halfptInMost || halfptInMost < qfrT->halfptInMin)) { VReportError(HCERR_FONT_OVERLAP, &errHpj, pszLine); return; } } options.rgFontRange[options.iFontRangeMac].halfptInMin = halfptInMin; options.rgFontRange[options.iFontRangeMac].halfptInMost = halfptInMost; options.rgFontRange[options.iFontRangeMac++].halfptOut = halfptOut; }
// ParsePath - Adds filenames in szPath to the specified table.
// ptbl - table to add filenames to
// szPath - string containing zero or more filenames, separated
// by commas or tabs
// opt - option we're parsing the path for (used for error reporting)
//
// Modified:
// 2/28/95 (niklasb): removed space from pchDelimeter because
// long filenames may contain spaces (fixes bug 9605).
// Also added this nifty comment.
void STDCALL ParsePath(CTable* ptbl, PSTR szPath, OPT opt) { PSTR psz; char szNewPath[_MAX_PATH]; HCE hce; BOOL fBadPath = FALSE; PSTR pszDir = SzGetDriveAndDir(errHpj.lpszFile, NULL);
// REVIEW (niklasb): this is broken if we every have multiple
// filenames separated by spaces, but we just have to make
// sure that never occurs because LFNs can contain spaces.
static PSTR pchDelimeter = ",\t";
for (psz = StrToken(szPath, pchDelimeter); psz != NULL; psz = StrToken(NULL, pchDelimeter)) { if (strlen(psz) >= _MAX_PATH) { fBadPath = TRUE; VReportError(HCERR_PATH_TOO_LONG, &errHpj, psz); continue; }
// If the current .HPJ file isn't in the current directory,
// we need to resolve sz to the dir containing it.
hce = HceResolveFileNameSz(psz, pszDir, szNewPath); if (hce == HCE_OK) { DWORD attribute = GetFileAttributes(szNewPath); if (attribute == HFILE_ERROR) hce = HCE_FILE_NOT_FOUND; else if (attribute & FILE_ATTRIBUTE_DIRECTORY) hce = HCE_FILE_IS_DIRECTORY; }
if (hce == HCE_FILE_IS_DIRECTORY) { if (!ptbl->AddString(szNewPath)) { OOM(); break; } } else { fBadPath = TRUE; if (hce == HCE_OK || hce == HCE_FILE_NOT_FOUND) hce = HCE_INVALID_PATH; VReportError(HCERR_INVALID_PATH, &errHpj, psz, ppszOptions[opt]); break; } }
lcFree(pszDir); }
/***************************************************************************\
* - Function: RcParseOptionsSz( sz) - * Purpose: Parse a line in the [options] section. (see grammar) * * ASSUMES * * args IN: sz - line to parse, with comments stripped * * PROMISES * * returns: success code. Meaning? * * +++ * Method: All options are of the form <keyword> = [smag]. * We strip out the keyword and look it up in a table. * Then we parse the smag as appropriate. * \***************************************************************************/
RC_TYPE STDCALL RcParseOptionsSz(PSTR pszLine) { HCE hce; PSTR psz;
PSTR pszEq = StrChr(pszLine, '=', fDBCSSystem);
if (pszEq == NULL) { VReportError(HCERR_MISSING_VALUE, &errHpj, pszLine); return RC_Success; }
*pszEq = '\0'; SzTrimSz(pszLine); PSTR pszOptionValue = FirstNonSpace(pszEq + 1, fDBCSSystem);
if (!*pszOptionValue) { VReportError(HCERR_MISSING_VALUE, &errHpj, pszLine); return RC_Success; }
ASSERT(MAX_OPT >= OPT_INDEX);
int opt; for (opt = 0; opt < MAX_OPT; opt++) { if (_stricmp(pszLine, ppszOptions[opt]) == 0) break; }
if (opt == MAX_OPT) { VReportError(HCERR_UNKNOWN_OPTION, &errHpj, pszLine); return RC_Success; }
// Nag if the we've seen the option before, but otherwise continue
if (FTestUlFlag(options.lOptionInitFlags, opt)) VReportError(HCERR_DUPLICATE_OPTION, &errHpj, pszLine);
if (opt != OPT_BMROOT && opt != OPT_ROOT) SetUlFlag(options.lOptionInitFlags, opt);
switch (opt) { case OPT_BMROOT: if (!options.ptblBmpRoot) options.ptblBmpRoot = new CTable; ParsePath(options.ptblBmpRoot, pszOptionValue, (OPT) opt); break;
case OPT_BUILD:
/*
* don't use strdup() because we need space for the extra ")" * tacked on in parsebld.c. */
if (options.szBuildExp) lcFree(options.szBuildExp);
options.szBuildExp = (PSTR) lcMalloc(strlen(pszOptionValue) + 2); strcpy(options.szBuildExp, pszOptionValue); break;
case OPT_COMPRESS: if (isdigit(*pszOptionValue)) { if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &options.fsCompress)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); options.fsCompress = COMPRESS_NONE; } } else { switch (YesNo(pszOptionValue)) { case IDYES: options.fsCompress = COMPRESS_MAXIMUM; break;
case IDNO: options.fsCompress = COMPRESS_NONE; break;
default:
// Check for special compress options
if (!_stricmp(pszOptionValue, txtLow)) options.fsCompress = COMPRESS_TEXT_ZECK; else if (!_stricmp(pszOptionValue, txtHigh)) options.fsCompress = COMPRESS_MAXIMUM; else if (!_stricmp(pszOptionValue, txtNone)) options.fsCompress = COMPRESS_NONE;
// Medium compression is the same as low for text, but
// with maximum compression for bitmaps
else if (!_stricmp(pszOptionValue, txtMedium)) options.fsCompress = COMPRESS_TEXT_ZECK | (COMPRESS_BMP_RLE | COMPRESS_BMP_ZECK);
else ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } }
/*
* Phrase and Hall compression cannot be used simultaneously, so * if both are specified we default to Hall compression. */
if (options.fsCompress & COMPRESS_TEXT_PHRASE && options.fsCompress & COMPRESS_TEXT_HALL) options.fsCompress &= ~COMPRESS_TEXT_PHRASE;
// Maximum text compression also means maximum bitmap compression
if (options.fsCompress & (COMPRESS_TEXT_PHRASE | COMPRESS_TEXT_HALL | COMPRESS_TEXT_ZECK)) options.fsCompress |= (COMPRESS_BMP_RLE | COMPRESS_BMP_ZECK); else options.fsCompress |= COMPRESS_BMP_RLE; break;
case OPT_CDROM: // OPTCDROM = -> optimize |topic alignment
switch (YesNo(pszOptionValue)) { case IDYES: options.fOptCdRom = TRUE; break;
case IDNO: options.fOptCdRom = FALSE; break;
default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_ERRORLOG: {
// REVIEW: If we can't open the error log, should we terminate?
char szFile[_MAX_PATH];
HCE hce = HceResolveFileNameSz(pszOptionValue, NULL, szFile);
if (hce == HCE_OK || hce == HCE_FILE_NOT_FOUND) { if (pLogFile) delete pLogFile;
pLogFile = new COutput(szFile, 2 * 1024);
if (!pLogFile->fInitialized) VReportError(HCERR_CANNOT_WRITE, &errHpj, szFile); else { DispSignOn(); pLogFile->outstring_eol(pfs->rgfe[0].rgchFile); } } else VReportError(HCERR_CANNOT_WRITE, &errHpj, szFile);
break; }
case OPT_FORCEFONT: if (strlen(pszOptionValue) >= MAX4_FONTNAME - 2) VReportError(HCERR_FONTNAME_TOO_LONG, &errHpj, pszOptionValue); else { if (options.pszForceFont) lcFree(options.pszForceFont); options.pszForceFont = lcStrDup(pszOptionValue); SetForcedFont(options.pszForceFont); } break;
case OPT_ICON: { if (options.pszIcon) ReportDuplicateOption(&options.pszIcon, ppszOptions[opt]);
FILE *pf; char szBuf[_MAX_PATH];
if (options.ptblFileRoot) hce = HceResolveTableDir(pszOptionValue, options.ptblFileRoot, szBuf, (FILE **) &pf);
else { PSTR szDir = SzGetDriveAndDir(errHpj.lpszFile, NULL);
hce = HceResolveFileNameSz(pszOptionValue, szDir, szBuf, &pf); lcFree(szDir); }
if (hce != HCE_OK) { VReportError(HCERR_CANNOT_OPEN, &errHpj, pszOptionValue); } else { if (!FIsIconPf(pf)) { VReportError(HCERR_INVALID_ICON, &errHpj, pszOptionValue); } else { options.pszIcon = lcStrDup(szBuf); } fclose(pf); }
break; }
case OPT_HLP: strcpy(szHlpFile, pszOptionValue); break;
case OPT_HCW: break; // ignore it -- it's used by HCW
case OPT_FTS: if (!FGetNum(pszOptionValue, NULL, &options.fsFTS)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); } break;
case OPT_CONTENTS:
// REVIEW - unfinished: check syntax
if (options.pszContentsTopic) ReportDuplicateOption(&options.pszContentsTopic, ppszOptions[opt]); if (pszOptionValue) options.pszContentsTopic = lcStrDup(pszOptionValue); break;
case OPT_LANGUAGE: { VReportError(HCERR_OLD_SORT, &errHpj);
for (int i = 0; i < MAX_SORT; i++) { if (!_stricmp(pszOptionValue, rgszSortOrder[i])) break; } if (MAX_SORT == i) VReportError(HCERR_UNKNOWN_LANGUAGE, &errHpj, pszOptionValue); else options.sortorder = (SORTORDER) i;
switch (options.sortorder) {
/*
* Note that the only case-sensitive key types are for * standard and NLS comparisons. The rest of the sorts * are case-insensitive only. They are also here only * for backwards compatibility. All 4.0 help files should * be using either standard or NLS sorting. */
case SORT_ENGLISH: ktKeywordi = KT_SZI; ktKeyword = KT_SZ; break;
case SORT_SCANDINAVIAN: // REVIEW: is this Swedish?
ktKeywordi = KT_SZISCAND; ktKeyword = KT_SZISCAND; break;
// REVIEW: the following only work with WinHelp 4.0.
case SORT_CHINESE: kwlcid.langid = 0x0404; options.fDBCS = TRUE; goto NLS;
case SORT_JAPANESE: kwlcid.langid = 0x0411; options.fDBCS = TRUE; goto NLS;
case SORT_KOREAN: kwlcid.langid = 0x0412; options.fDBCS = TRUE; goto NLS;
case SORT_POLISH: kwlcid.langid = 0x0415; goto NLS;
case SORT_HUNGARIAN: kwlcid.langid = 0x040E; goto NLS;
case SORT_RUSSIAN: kwlcid.langid = 0x0419; goto NLS;
case SORT_CZECH: kwlcid.langid = 0x0405; goto NLS;
case SORT_NLS:
/*
* This is the prefered language flag -- it uses * whatever the locale id is for the current * environment. This ensures that the sorting order will * remain unchanged no matter what language windows is * running when the help file is displayed. */ NLS: ktKeywordi = KT_NLSI; ktKeyword = KT_NLS; if (!kwlcid.langid) kwlcid.langid = GetUserDefaultLangID(); lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT); break;
default: ASSERT(FALSE); break; } } break;
case OPT_MAPFONTSIZE: ParseMapFontSizeOption(pszOptionValue, pszLine); break;
case OPT_MULTIKEY: while (*pszOptionValue) HceAddPkwiCh(*pszOptionValue++);
// clear flag because this option can occur > 1 time
ClearUlFlag(options.lOptionInitFlags, OPT_MULTIKEY); break;
case OPT_REPORT: switch (YesNo(pszOptionValue)) { case IDYES: options.fReport = TRUE; break;
case IDNO: options.fReport = FALSE; break;
default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_ROOT: if (!options.ptblFileRoot) options.ptblFileRoot = new CTable; ParsePath(options.ptblFileRoot, pszOptionValue, (OPT) opt); break;
case OPT_TITLE: if (options.pszTitle) ReportDuplicateOption(&options.pszTitle, ppszOptions[opt]); if (strlen(pszOptionValue) > CBMAXTITLE) { pszOptionValue[CBMAXTITLE + 1] = '\0'; VReportError(HCERR_TITLE_TOO_BIG, &errHpj, pszOptionValue); } options.pszTitle = lcStrDup(pszOptionValue); break;
case OPT_PHRASE: switch (YesNo(pszOptionValue)) { case IDYES: options.fUsePhrase = TRUE; break;
case IDNO: options.fUsePhrase = FALSE; break;
default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_WARNING:
// We simply ignore this
break;
case OPT_COPYRIGHT: if (options.pszCopyright) ReportDuplicateOption(&options.pszCopyright, ppszOptions[opt]); if (strlen(pszOptionValue) > CBMAXCOPYRIGHT) { pszOptionValue[CBMAXCOPYRIGHT + 1] = '\0'; VReportError(HCERR_CPRIGHT_TOO_BIG, &errHpj, pszOptionValue); } { PSTR psz; if ((psz = strstr(pszOptionValue, "%date"))) { time_t ltime; time(<ime); *psz = '\0'; CStr csz(pszOptionValue); if (pszOptionValue != psz) csz += "\r\n"; csz += ctime(<ime); PSTR pszCr = strrchr(csz.psz, '\n'); if (pszCr) *pszCr = '\0'; psz = FirstNonSpace(psz + 5);
if ((psz = strstr(psz, "%ver"))) { csz += "\r\n"; csz += GetStringResource(IDS_VERSION); psz = FirstNonSpace(psz + 4); } csz += psz; options.pszCopyright = lcStrDup(csz.psz);
} else options.pszCopyright = lcStrDup(pszOptionValue); if (!options.pszCopyright) { OOM(); } } break;
case OPT_CNT:
if (options.pszCntFile) ReportDuplicateOption(&options.pszCntFile, ppszOptions[opt]);
// REVIEW: we should nag if the .CNT file doesn't exist
options.pszCntFile = lcStrDup(pszOptionValue); if (!options.pszCntFile) OOM(); break;
case OPT_CITATION: if (options.pszCitation) ReportDuplicateOption(&options.pszCitation, ppszOptions[opt]); options.pszCitation = lcStrDup(pszOptionValue); break;
case OPT_VERSION: if (*pszOptionValue != '4' || pszOptionValue[2] != '0') VReportError(HCERR_INVALID_VERSION, &errHpj, pszOptionValue); break;
case OPT_NOTE: switch (YesNo(pszOptionValue)) { case IDYES: options.fSupressNotes = FALSE; break;
case IDNO: options.fSupressNotes = TRUE; break;
default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_LCID: // new to 4.0
// LCID = lcid [case-sensitive flags] [case-insensitive flags]
// This option automatically uses KT_NLS for keyword and browse
// sorts
if (kwlcid.langid) VReportError(HCERR_DUPLICATE_OPTION, &errHpj, ppszOptions[opt]); if (!isdigit((BYTE) *pszOptionValue)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); break; }
if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &kwlcid.langid)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } if (!IsEmptyString(pszOptionValue)) { if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &kwlcid.fsCompareI)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } if (!IsEmptyString(pszOptionValue)) { if (!FGetUnsigned(pszOptionValue, &pszOptionValue, &kwlcid.fsCompare)) { ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } } } if (kwlcid.langid) { lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT); ktKeywordi = KT_NLSI; ktKeyword = KT_NLS; fValidLcid = FALSE; EnumSystemLocales(EnumLocalesProc, LCID_SUPPORTED); if (!fValidLcid) { char szLcid[20]; wsprintf(szLcid, "0x%x", kwlcid.langid); VReportError(HCERR_INVALID_LCID, &errHpj, szLcid); lcid = 0; // shouldn't get here, but just in case...
} } else lcid = 0; break;
case OPT_DBCS: switch (YesNo(pszOptionValue)) { case IDYES: options.fDBCS = TRUE; break;
case IDNO: options.fDBCS = FALSE; break; default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_TMPDIR: if (options.pszTmpDir) ReportDuplicateOption(&options.pszTmpDir, ppszOptions[opt]);
if (_access(options.pszTmpDir, 0) != 0) { VReportError(HCERR_INVALID_TMP_DIR, &errHpj, pszOptionValue); break; } AddTrailingBackslash(pszOptionValue); options.pszTmpDir = lcStrDup(pszOptionValue); if (hwndParent) { CreateSharedMemory(); strcpy(pszMap, options.pszTmpDir); SendMessage(hwndParent, WMP_SET_TMPDIR, 0, 0); } break;
case OPT_REPLACE: { if (options.pszReplace) { ReportDuplicateOption(&options.pszReplace, ppszOptions[opt]); lcClearFree(&options.pszReplaceWith); } psz = StrRChr(pszOptionValue, '=', fDBCSSystem); if (!psz) { VReportError(HCERR_INVALID_REPLACE, &errHpj, pszOptionValue); break; } *psz++ = '\0'; SzTrimSz(psz); options.pszReplaceWith = lcStrDup(psz); SzTrimSz(pszOptionValue); options.pszReplace = lcStrDup(pszOptionValue); } break;
case OPT_CHARSET: { DWORD val; if (!FGetNum(pszOptionValue, NULL, &val)) ReportBadOption(pszOptionValue, ppszOptions[opt]); else defCharSet = (BYTE) val; } break;
case OPT_DEFFONT: if (options.pszDefFont) ReportDuplicateOption(&options.pszIndexSeparators, ppszOptions[opt]); options.pszDefFont = (PSTR) lcMalloc(strlen(pszOptionValue)); psz = StrChr(pszOptionValue, ',', fDBCSSystem); if (!psz || !isdigit((BYTE) psz[1])) { ReportBadOption(pszOptionValue, ppszOptions[opt]); lcClearFree(&options.pszDefFont); break; }
/*
* The first byte is the point size, the second byte is the * charset, and the font name comes after that. */
options.pszDefFont[0] = (BYTE) atoi(psz + 1); *psz = '\0'; psz = StrChr(psz + 2, ',', fDBCSSystem); if (!psz || !isdigit((BYTE) psz[1])) { ReportBadOption(pszOptionValue, ppszOptions[opt]); lcClearFree(&options.pszDefFont); break; } options.pszDefFont[1] = (BYTE) atoi(psz + 1); strcpy(options.pszDefFont + 2, SzTrimSz(pszOptionValue)); break;
case OPT_PREFIX: if (!ptblCtxPrefixes) ptblCtxPrefixes = new CTable; psz = StrToken(pszOptionValue, ", "); while (psz) { psz = SzTrimSz(psz); if (*psz) ptblCtxPrefixes->AddString(psz); psz = StrToken(NULL, ", "); } break;
case OPT_REVISIONS: switch (YesNo(pszOptionValue)) { case IDYES: options.fAcceptRevions = TRUE; break;
case IDNO: options.fAcceptRevions = FALSE; break;
default: ReportBadOption(pszOptionValue, ppszOptions[opt]); break; } break;
case OPT_INDEX: if (options.pszIndexSeparators) ReportDuplicateOption(&options.pszIndexSeparators, ppszOptions[opt]); if (IsQuote(*pszOptionValue)) { psz = pszOptionValue + 1; while (!IsQuote(*psz) && *psz) psz = CharNext(psz); *psz = '\0'; SzTrimSz(pszOptionValue + 1); options.pszIndexSeparators = lcStrDup(pszOptionValue + 1); } else options.pszIndexSeparators = lcStrDup(pszOptionValue); break;
default: return RC_Failure; break; }
return RC_Success; }
__inline BOOL STDCALL FVerifyBuildTag(PSTR psz) { for (; *psz; ++psz) { if (!IsCharAlphaNumeric(*psz) && *psz != '_') return FALSE; } return TRUE; }
/***************************************************************************\
* - Function: RcParseBuildTagsSz( sz) - * Purpose: Parse a line in the [buildtags] section. (see grammar) * * ASSUMES * * args IN: sz - line to parse, with comments stripped. * Valid build tags consist of the characters * [A-Za-z0-9_.], with lower mapped to upper case. * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseBuildTagsSz(PSTR pszBuildTag) { if (lcid) { int cbSrc = strlen(pszBuildTag); int cbDst; CMem mem(cbSrc + 16); if ((cbDst = LCMapString(lcid, LCMAP_UPPERCASE, pszBuildTag, cbSrc, mem.psz, cbSrc + 16)) > 0) { strncpy(pszBuildTag, mem.psz, cbDst); pszBuildTag[cbDst + 1] ='\0'; } } else { CharUpper(pszBuildTag); }
if (!FVerifyBuildTag(pszBuildTag)) VReportError(HCERR_INVALID_BUILD_TAG, &errHpj, pszBuildTag); else if (ptblBuildtags && ptblBuildtags->IsStringInTable(pszBuildTag)) VReportError(HCERR_DUP_BUILD_TAG, &errHpj, pszBuildTag); else { if (!ptblBuildtags) ptblBuildtags = new CTable; if (!ptblBuildtags->AddString(pszBuildTag)) OOM(); }
return RC_Success; }
/***************************************************************************\
* - Function: RcParseFilesSz( sz ) - * Purpose: Parse a line in the [files] section (filename, see grammar). * Verify that the filename is properly formed, resolve it * relative to root, verify that the file exists and isn't * a directory or device. Store the file name. * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseFilesSz(PSTR sz) { HCE hce; char szFile[_MAX_PATH];
if (options.ptblFileRoot) hce = HceResolveTableDir(sz, options.ptblFileRoot, szFile, NULL); else { PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL); hce = HceResolveFileNameSz(sz, pszRoot, szFile); lcFree(pszRoot); }
if (hce != HCE_OK) VReportError(HCERR_CANNOT_OPEN, &errHpj, sz); else ptblRtfFiles->AddString(szFile);
return RC_Success; }
/***************************************************************************\
* - Function: RcParseBaggageSz( sz ) - * Purpose: Parse a line in the [baggage] section (filename, see grammar). * Verify that the filename is properly formed, resolve it * relative to root, verify that the file exists and isn't * a directory or device. Store the file name. * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseBaggageSz(PSTR psz) { HCE hce = HCE_FILE_NOT_FOUND; char szFile[_MAX_PATH];
if (options.ptblFileRoot) hce = HceResolveTableDir(psz, options.ptblFileRoot, szFile, NULL);
if (hce != HCE_OK) { PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL); hce = HceResolveFileNameSz(psz, pszRoot, szFile); lcFree(pszRoot); }
if (hce != HCE_OK) VReportError(HCERR_CANNOT_OPEN, &errHpj, psz); else { if (!ptblBaggage) ptblBaggage = new CTable;
CharUpper(szFile); if (ptblBaggage->IsCSStringInTable(szFile)) return RC_Success; // we already have it
if (!ptblBaggage->AddString(szFile)) OOM(); // this won't return
}
return RC_Success; }
/***************************************************************************\
* - Function: RcParseBitmapsSz( sz ) - * Purpose: Parse a line in the [bitmaps] section (filename, see grammar). * Verify that the filename is properly formed, resolve it * relative to BMroot, verify that the file exists and isn't * a directory or device. Store the file name. * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseBitmapsSz(PSTR psz) { AddBitmap(psz, NULL, FALSE);
return RC_Success; }
/***************************************************************************\
* - Function: RcParseMapSz( sz ) - * Purpose: Parse a line in the [map] section. (see grammar) * * <map statement> :== <context string> <number> | * "#define" <context string> <number> * * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseMapSz(PSTR pszLine) { MAP map; PSTR pszTmp; DWORD imap, cmap; QMAP qmap;
// If this #define was used for a #ifdef or #ifndef then ignore it
if (nstrisubcmp(pszLine, txtDefine) && ptblDefine->IsStringInTable( FirstNonSpace(pszLine + strlen(txtDefine) + 1, fDBCSSystem))) return RC_Success;
if (!pdrgMap) pdrgMap = new CDrg(sizeof(MAP), 5, 5);
if (!ptblMap) { ptblMap = new CTable; ptblMap->SetSorting(lcid, kwlcid.fsCompareI, kwlcid.fsCompare); }
// Assume RcGetLogicalLine() has removed all comments within a line,
// and never returns a blank line.
ASSERT(*pszLine);
/*
* New for 4.0 is to look for an '='. This is the only way you can * map a context string containing a space. */
if (!(pszTmp = StrChr(pszLine, CH_EQUAL, fDBCSSystem))) pszTmp = SkipToEndOfWord(pszLine);
if (!pszTmp || !*pszTmp) { VReportError(HCERR_NO_MAP_VALUE, &errHpj, pszLine); return RC_Success; }
if (nstrisubcmp(pszLine, txtDefine)) { pszLine = FirstNonSpace(pszLine + strlen(txtDefine), fDBCSSystem); if (*pszTmp != CH_EQUAL) pszTmp = SkipToEndOfWord(pszLine);
if (!*pszTmp) { VReportError(HCERR_NO_MAP_VALUE, &errHpj, pszLine); return RC_Success; } } *pszTmp = '\0'; SzTrimSz(pszLine);
if (!FValidContextSz(pszLine)) { VReportError(HCERR_INVALID_CTX, &errHpj, pszLine); return RC_Success; }
// Special case no-help constant
if (nstrsubcmp(FirstNonSpace(pszTmp + 1, fDBCSSystem), "((DWORD) -1)")) return RC_Success;
map.hash = HashFromSz(pszLine); if (ptblMap->IsHashInTable(map.hash)) { VReportError(HCERR_MAP_USED, &errHpj, pszLine); return RC_Success; }
PSTR pszSave = pszLine;
pszLine = SzTrimSz(pszTmp + 1);
if (!FGetUnsigned(pszLine, &pszTmp, &map.ctx)) { VReportError(HCERR_INVALID_MAP_NUMBER, &errHpj, pszLine, pszSave); return RC_Success; }
if (*pszTmp != '\0') // Nag, but otherwise continue
VReportError(HCERR_MAP_TEXT_UNEXPECT, &errHpj, pszSave, pszTmp);
// make sure this CTX isn't already mapped to another context string
if ((cmap = pdrgMap->Count()) != 0) { for (imap = 0, qmap = (QMAP) pdrgMap->GetBasePtr(); imap < cmap; imap++, qmap++) { if (qmap->ctx == map.ctx) { // REVIEW: we should nag, but supposedly, it's okay to do this
VReportError(HCERR_MAP_VALUE_DUP, &errHpj, pszSave, (PCSTR) ptblMap->GetPointer(qmap->pos) + sizeof(HASH)); return RC_Success; } } }
ASSERT(map.hash == HashFromSz(pszSave)); map.pos = ptblMap->AddString(map.hash, pszSave); pdrgMap->Add(&map);
return RC_Success; }
/***************************************************************************\
* - Function: RcParseAliasSz( sz) - * Purpose: Parse a line in the [alias] section. (see grammar) * * <alias statement> :== <context string> "=" <context string> * * <context string> :== [A-Za-z0-9_.]{[A-Za-z0-9_.!]} * * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseAliasSz(PSTR pszLine) { PSTR pszAlias; ALIAS alias; DWORD ialias, calias; QALIAS qalias;
ASSERT(!StrChr(pszLine, CH_SEMICOLON, fDBCSSystem));
if (!(pszAlias = StrChr(pszLine, CH_EQUAL, fDBCSSystem))) { VReportError(HCERR_MISS_ALIAS_EQ, &errHpj, pszLine); return RC_Success; }
CStr cszOrg(SzTrimSz(pszLine));
*pszAlias++ = '\0';
SzTrimSz(pszLine);
if (!FValidContextSz(pszLine)) { VReportError(HCERR_INVALID_CTX, &errHpj, pszLine); return RC_Success; }
alias.hashAlias = HashFromSz(pszLine);
SzTrimSz(pszAlias);
if (!FValidContextSz(pszAlias)) { VReportError(HCERR_INVALID_CTX, &errHpj, pszAlias); return RC_Success; } alias.hashCtx = HashFromSz(pszAlias); if (alias.hashAlias == alias.hashCtx) { VReportError(HCERR_ALIAS_EQ_CTX, &errHpj, cszOrg.psz); return RC_Success; }
if (!pdrgAlias) pdrgAlias = new CDrg(sizeof(ALIAS), 5, 5);
if ((calias = pdrgAlias->Count()) > 0) { for (ialias = 0, qalias = (QALIAS) pdrgAlias->GetBasePtr(); ialias < calias; ialias++, qalias++) { if (alias.hashAlias == qalias->hashAlias) { VReportError(HCERR_DEFINED_ALIAS, &errHpj, pszLine, cszOrg.psz); return RC_Success; } else if (alias.hashAlias == qalias->hashCtx) { VReportError(HCERR_DUP_ALIAS, &errHpj, pszLine, cszOrg.psz); return RC_Success; } else if (alias.hashCtx == qalias->hashAlias)
// REVIEW: 27-Mar-1994 [ralphw] Does this work?
alias.hashCtx = qalias->hashCtx; } }
alias.szCtx = lcStrDup(pszAlias);
pdrgAlias->Add(&alias);
return RC_Success; }
/***************************************************************************\
* - Function: RcParseConfigSz( sz ) - * Purpose: Parse a line in the [config] section. * The config section has structure unknown to this * parser. All we do is strip comments. Currently * we don't recognize quoted strings (to allow ;). * * ASSUMES * * args IN: sz - line to parse, with comments stripped. * * * PROMISES * * returns: success code. REVIEW * * +++ * Method: Each macro is added as a null terminated string * to ptblConfig. * \***************************************************************************/
RC_TYPE STDCALL RcParseConfigSz(PSTR psz) { if (!ptblConfig) ptblConfig = new CTable;
if (Execute(psz) == wMACRO_EXPANSION) { ASSERT((size_t) lcSize(psz) > strlen(GetMacroExpansion())); strcpy(psz, GetMacroExpansion()); } // Add the macro even if it generated an error
if (!ptblConfig->AddString(psz)) OOM();
return RC_Success; }
/***************************************************************************
FUNCTION: RcParseSecondaryConfigSz
PURPOSE: Adds configuration information to a secondary window
PARAMETERS: psz
RETURNS:
COMMENTS: Assumes curConfig points to the current config number in the array of configuration tables
MODIFICATION DATES: 10-Jul-1994 [ralphw]
***************************************************************************/
RC_TYPE STDCALL RcParseSecondaryConfigSz(PSTR psz) { if (!pptblConfig[curConfig]) pptblConfig[curConfig] = new CTable;
if (Execute(psz) == wMACRO_EXPANSION) { ASSERT((size_t) lcSize(psz) > strlen(GetMacroExpansion())); strcpy(psz, GetMacroExpansion()); }
// Add the macro even if it generated an error
if (!pptblConfig[curConfig]->AddString(psz)) OOM();
return RC_Success; }
/***************************************************************************\
* - Function: FReadInt16() - * Purpose: Read an int from a buffer. * Legal syntax is an int followed by a comma or by nothing. * On success, advance the pointer past the following comma. * A missing number isn't an error. * If a number is actually read, set a flag. * * ASSUMES * args IN: psz - pointer to sz containing number * pi - pointer to int * pgrf - pointer to flag word * f - flag to set in pgrf * * PROMISES * returns: WINDOW_READ_INVALID - syntax error * WINDOW_READ_MISSING - no number, but syntax OK * WINDOW_READ_OK - a valid number was read * * args OUT: psz - sz advanced past following comma * pi - value of int copied here on success * pgrf - *pgrf != flag if number is present * \***************************************************************************/
static UINT STDCALL FReadInt16(PSTR *psz, WORD* pi, WORD *pgrf, UINT f) { PSTR pszTmp;
pszTmp = FirstNonSpace(*psz, fDBCSSystem); if (*pszTmp == 'f') pszTmp++; if (*pszTmp == '\0') goto UseDefault; if (*pszTmp == ',') { *psz = pszTmp + 1;
UseDefault: if (f == FWSMAG_X || f == FWSMAG_Y || f == FWSMAG_DX || f == FWSMAG_DY) { *pi = (WORD) -1; return WINDOW_READ_OK; } else return WINDOW_READ_MISSING; }
LONG l; if (!FGetNum(pszTmp, &pszTmp, &l)) return WINDOW_READ_INVALID; if (l == -1 && (f == FWSMAG_X || f == FWSMAG_Y || f == FWSMAG_DX || f == FWSMAG_DY)) { *pi = (WORD) -1; return WINDOW_READ_OK; }
*pi = (WORD) l;
// hack to include new possible flags for wsmag.wMax
if (f == FWSMAG_MAXIMIZE) { if (*pi & FWSMAG_WMAX_MAXIMIZE) *pgrf |= f; } else *pgrf |= f;
pszTmp = FirstNonSpace(pszTmp, fDBCSSystem);
if (*pszTmp == ',') { *psz = pszTmp + 1; return WINDOW_READ_OK; } *psz = pszTmp; return (*pszTmp == '\0') ? WINDOW_READ_OK : WINDOW_READ_INVALID; }
/***************************************************************************\
* - Function: FReadRgb( psz, pl, pgrf, f ) - * Purpose: Read an RGB triple from a buffer. * Legal syntax is nothing, or a parenthesized list * of three ints (all must be there). * On success, advance the pointer past the following comma. * A missing triple isn't an error. * If a triple is actually read, set a flag. * * ASSUMES * args IN: psz - pointer to sz containing (r,g,b) * pl - pointer to long * pgrf - pointer to flag word * f - flag to set in pgrf * * PROMISES * returns: WINDOW_READ_INVALID - syntax error * WINDOW_READ_MISSING - no number, but syntax OK * WINDOW_READ_OK - a valid number was read * * args OUT: psz - sz advanced past following comma * pl - value of rgb copied here on success. * We use the RGB() macro above (REVIEW!!) * pgrf - *pgrf != flag if number is present * \***************************************************************************/
static UINT STDCALL FReadRgb(PSTR *ppsz, LONG *pl, UINT *pgrf, UINT f) { UINT grf = 0; WORD r, g, b; PSTR psz = FirstNonSpace(*ppsz, fDBCSSystem); PSTR pszTmp;
if (*psz == '\0') return WINDOW_READ_MISSING; if (*psz == ',') { *ppsz = psz + 1; return WINDOW_READ_MISSING; } if (*psz == '(') { psz = FirstNonSpace(psz + 1, fDBCSSystem); if (!(pszTmp = StrChr(psz, CH_CLOSE_PAREN, fDBCSSystem))) return WINDOW_READ_INVALID; *pszTmp = '\0'; pszTmp = FirstNonSpace(pszTmp + 1, fDBCSSystem); if (*pszTmp == ',') ++pszTmp; *ppsz = pszTmp;
if (*psz == 'r') { // short format produced by HCW
*pl = atol(psz + 1); *pgrf |= f; return WINDOW_READ_OK; }
if (WINDOW_READ_OK != FReadInt16(&psz, &r, (WORD *) pgrf, f) || WINDOW_READ_OK != FReadInt16(&psz, &g, (WORD *) pgrf, f) || WINDOW_READ_OK != FReadInt16(&psz, &b, (WORD *) pgrf, f)) { *pgrf &= ~f; return WINDOW_READ_INVALID; }
if (r < 0 || r >= 256 || g < 0 || g >= 256 || b < 0 || b >= 256) return WINDOW_READ_INVALID; // REVIEW - different error code?
*pl = RGB(r, g, b); return WINDOW_READ_OK; } else return WINDOW_READ_INVALID; }
/***************************************************************************\
* - Function: RcParseWindowsSz( sz ) - * Purpose: Parse a line in the [windows] section. Syntax: * * <member> = ["caption"],(X,Y,dX,dy),[fMax],[(r,g,b)],[(r,g,b)] * * If member is "main", class is also "main" and position * is not required. * Otherwise class is "secondary" and position is mandatory. * ASSUMES * * args IN: sz - line to parse, with comments stripped * * PROMISES * returns: success code. * * Notes: The WSMAG structure has provisions for multiple * secondary window classes and multiple members of * each class. This parser doesn't allow it, though. * +++ * * Method: * \***************************************************************************/
RC_TYPE STDCALL RcParseWindowsSz(PSTR pszLine) { PSTR pszTmp, pszRHS, pszMember, pszCaption; WSMAG wsmag;
CStr szOriginal(pszLine);
memset(&wsmag, 0, sizeof(wsmag)); // clear out all values
pszRHS = StrChr(pszLine, '=', fDBCSSystem); if (pszRHS == NULL) { VReportError(HCERR_NOEQ_IN_WIN, &errHpj, pszLine); return RC_Success; } else { *pszRHS++ = '\0'; SzTrimSz(pszRHS); }
// Deal with "member=" case for the whiners.
if (*pszRHS == '\0') { VReportError(HCERR_NOTHING_AFTER_EQ, &errHpj, pszLine); return RC_Success; }
pszMember = SzTrimSz(pszLine);
// REVIEW: don't allow a window name to begin with an '@' -- WinHelp
// special-cases this character.
PCSTR pszClass = (PCSTR) ((_stricmp(pszMember, txtMainWindow) == 0) ? txtMainWindow : txtSecondary);
if (strlen(pszMember) >= MAX_WINDOW_NAME) {
// If the window name is too long, truncate it and complain
CStr cstr(pszMember); pszMember[MAX_WINDOW_NAME] = '\0'; VReportError(HCERR_WIN_NAME_TOO_LONG, &errHpj, (PCSTR) cstr, pszMember); } else if (!*pszMember) { VReportError(HCERR_NOTHING_AFTER_EQ, &errHpj, szOriginal); return RC_Success; }
strcpy(wsmag.rgchClass, pszClass); strcpy(wsmag.rgchMember, pszMember); CharLower(wsmag.rgchMember); // for consistency
if (!ptblWindowNames) ptblWindowNames = new CTable;
if (ptblWindowNames->IsCSStringInTable(wsmag.rgchMember)) { VReportError(HCERR_DUPLICATE_NAME, &errHpj, wsmag.rgchMember); return RC_Success; } ptblWindowNames->AddString(wsmag.rgchMember);
wsmag.grf = FWSMAG_CLASS | FWSMAG_MEMBER;
if (*pszRHS == CH_QUOTE) {
// Caption: "string"
pszCaption = pszRHS + 1; pszTmp = StrChr(pszCaption, CH_QUOTE, fDBCSSystem); if (pszTmp == NULL) { VReportError(HCERR_MISSING_CAPT_QUOTE, &errHpj, szOriginal); return RC_Success; } else { *pszTmp = '\0'; if (strlen(pszCaption) >= MAX_WINDOWCAPTION) VReportError(HCERR_CAPTION_TOO_LONG, &errHpj, szOriginal); wsmag.grf |= FWSMAG_CAPTION;
strncpy(wsmag.rgchCaption, pszCaption, MAX_WINDOWCAPTION); wsmag.rgchCaption[MAX_WINDOWCAPTION] = '\0'; } pszRHS = FirstNonSpace(pszTmp + 1, fDBCSSystem); }
/*
* New to HCW, we no longer require the leading comma if a window * title isn't specified. */
if (*pszRHS != ',') { if (*pszRHS == '\0') { pszLine = pszRHS; goto normal_return; } else { pszRHS--; // so that next check will skip over this
} }
pszLine = FirstNonSpace(pszRHS + 1, fDBCSSystem);
// Position: (int, int, int, int) or (0) for default
if (*pszLine == '(') { PSTR pszSaveLine = pszLine; // save the position for error reporting
++pszLine; pszTmp = StrChr(pszLine, CH_CLOSE_PAREN, fDBCSSystem); if (!pszTmp) goto error_return; *pszTmp = '\0';
// REVIEW: will this handle missing items?
if (FReadInt16(&pszLine, &wsmag.x, &wsmag.grf, FWSMAG_X) != WINDOW_READ_OK || FReadInt16(&pszLine, &wsmag.y, &wsmag.grf, FWSMAG_Y) != WINDOW_READ_OK || FReadInt16(&pszLine, &wsmag.dx, &wsmag.grf, FWSMAG_DX) != WINDOW_READ_OK || FReadInt16(&pszLine, &wsmag.dy, &wsmag.grf, FWSMAG_DY) != WINDOW_READ_OK) { *pszTmp++ = CH_CLOSE_PAREN; *pszTmp = '\0'; VReportError(HCERR_INVALID_WIN_POS, &errHpj, pszSaveLine); return RC_Success; } pszLine = FirstNonSpace(pszTmp + 1, fDBCSSystem); }
if (*pszLine == ',') pszLine = FirstNonSpace(pszLine + 1, fDBCSSystem); else if (*pszLine == '\0') goto normal_return; else { VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal); return RC_Success; }
// Max, rgbMain, rgbNsr: [int], [(int,int,int)], [(int,int,int)]
if (!FReadInt16(&pszLine, &wsmag.wMax, &wsmag.grf, FWSMAG_MAXIMIZE)) { VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal); return RC_Success; }
// version 3 files only understand maximize flag
if (version < 4) wsmag.wMax &= 1;
if (!FReadRgb(&pszLine, &wsmag.rgbMain, (UINT*) &wsmag.grf, FWSMAG_RGBMAIN) || !FReadRgb(&pszLine, &wsmag.rgbNSR, (UINT*) &wsmag.grf, FWSMAG_RGBNSR)) { VReportError(HCERR_INVALID_RGB, &errHpj, szOriginal); return RC_Success; }
{ WORD fsWin = 0; if (!FReadInt16(&pszLine, &fsWin, &wsmag.grf, 0)) { VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal); return RC_Success; }
if (fsWin & AUTHOR_WINDOW_ON_TOP) wsmag.grf |= FWSMAG_ON_TOP; if (fsWin & AUTHOR_AUTO_SIZE) wsmag.grf |= FWSMAG_AUTO_SIZE; if (fsWin & AUTHOR_ABSOLUTE) wsmag.grf |= FWSMAG_ABSOLUTE; }
normal_return:
ASSERT(wsmag.grf & FWSMAG_CLASS);
// Check for trailing garbage
if (*FirstNonSpace(pszLine, fDBCSSystem) != '\0') { VReportError(HCERR_INVALID_WIN_SYNTAX, &errHpj, szOriginal); return RC_Success; }
if (wsmag.wMax & 1) wsmag.grf |= FWSMAG_MAXIMIZE;
// range check position
if (!(wsmag.grf & AUTHOR_ABSOLUTE) && (wsmag.x >= dxVirtScreen || wsmag.y >= dyVirtScreen || wsmag.x + wsmag.dx >= dxVirtScreen || wsmag.y + wsmag.dy >= dyVirtScreen)) { VReportError(HCERR_INVALID_WIN_RANGE, &errHpj, szOriginal); return RC_Success; }
if (!pdrgWsmag) pdrgWsmag = new CDrg(sizeof(WSMAG), 1);
if ((cwsmag = pdrgWsmag->Count()) > 0) { if (cwsmag > MAX_WINDOWS) { VReportError(HCERR_256_WINDOWS, &errHpj); return RC_Success; } }
pdrgWsmag->Add(&wsmag); if (wsmag.wMax & FWSMAG_WMAX_BROWSE) fBrowseButtonSet = TRUE;
// Keep track of the number of windows added, so that we know how many
// configuration sections to write out.
cwsmag = pdrgWsmag->Count();
return RC_Success;
error_return: return RC_Success; }
/***************************************************************************\
* - Function: FPushFilePfs( pfs, szFile, perr ) - * Purpose: Open and push an #included file onto the file stack. * Emit an error message if it doesn't work. * * ASSUMES * * args IN: pfs - file stack * szFile - file name * * PROMISES * * returns: TRUE on success; FALSE on failure * * args OUT: pfs - file is pushed if no stack overflow and file can be * opened * * Side Effects: Possible errors: * * hceFileStackOverflow - #includes nested too deeply * hceFileNameTooLong - file name too long * etc. (REVIEW) * +++ * Method: pfs->ifeTop == -1 if the stack is empty. * Otherwise it is the index of the currently open file entry. * * Unless szFile is absolute (drive or rooted), it is taken * to be relative to the path of the previous file on the * stack. For the first file pushed, it is relative to the * current directory. * \***************************************************************************/
static BOOL STDCALL FPushFilePfs(PSTR szFile) { ASSERT(pfs->ifeTop >= -1); ASSERT(pfs->ifeTop <= MAX_INCLUDE);
if (++pfs->ifeTop == MAX_INCLUDE) { VReportError(HCERR_TOO_MANY_INCLUDES, &errHpj, szFile); --pfs->ifeTop; return FALSE; } else { PFE pfe = &pfs->rgfe[pfs->ifeTop];
PSTR pszRoot; char szDir[_MAX_DIR]; if (pfs->ifeTop == 0) pszRoot = NULL; else pszRoot = SzGetDriveAndDir(pfe[-1].rgchFile, szDir);
HCE hce = HceResolveFileNameSz(szFile, pszRoot, pfe->rgchFile, NULL, &pfe->pinput);
if (hce != HCE_OK) { pfs->ifeTop--; VReportError(HCERR_CANNOT_OPEN, &errHpj, szFile); return FALSE; }
pfe->iLine = 0;
errHpj.lpszFile = pfe->rgchFile; errHpj.iLine = pfe->iLine; }
return TRUE; }
/***************************************************************************\
* - Function: FPopPfs( pfs, perr ) - * Purpose: Remove the top file entry from the file stack. * * ASSUMES * * args IN: pfs - file stack * perr - error info struct * * PROMISES * * returns: TRUE on success; FALSE on failure * * args OUT: pfs->rgfe[ pfs->ifeTop ].pf gets closed. * pfs->ifeTop is decremented if stack nonempty * perr - pchFile and iLine set equal to top of stack * +++ * * Method: This can be a macro. * \***************************************************************************/
__inline BOOL STDCALL FPopPfs(void) { if (pfs->ifeTop >= 0) { ASSERT(PfTopPfs() != NULL); delete PfTopPfs();
--pfs->ifeTop; PFE pfe = &pfs->rgfe[pfs->ifeTop];
if (pfs->ifeTop >= 0) { errHpj.lpszFile = pfe->rgchFile; errHpj.iLine = pfe->iLine; } return TRUE; } return FALSE; }
/***************************************************************************\
* - Function: PfTopPfs( pfs ) - * Purpose: Return pointer to FILE * on top of stack. * * ASSUMES * * args IN: pfs * * PROMISES * * returns: success: valid FILE * from top of stack * failure: NULL (when stack is empty) * * +++ * * Method: This can be a macro. * \***************************************************************************/
__inline CInput* STDCALL PfTopPfs(void) { return (pfs->ifeTop >= 0) ? pfs->rgfe[pfs->ifeTop].pinput : NULL; }
/***************************************************************************
FUNCTION: RcGetLogicalLine
PURPOSE: Read a line, stripping comments, blank lines, and handling nested include files.
PARAMETERS: pcszDst -- CStr object which will expand as necessary to fit the longest line.
RETURNS:
COMMENTS:
MODIFICATION DATES: 05-Sep-1994 [ralphw]
***************************************************************************/
static char txtInclude[] = "#include";
static RC_TYPE STDCALL RcGetLogicalLine(CStr* pcszDst) { CInput* pin = PfTopPfs();
if (!pin) return RC_EOF;
for (;;) { if (!pin->getline(pcszDst)) {
// Close current file, continue with nested file if there is one
FPopPfs(); if (!(pin = PfTopPfs())) return RC_EOF; continue; } PSTR pszDst = pcszDst->psz; // purely for our notational convenience
if (*pszDst == CH_SPACE || *pszDst == CH_TAB) strcpy(pszDst, FirstNonSpace(pszDst, fDBCSSystem));
PSTR psz = pszDst;
switch (*psz) { case 0: case ';': continue;
case '#': if (nstrisubcmp(psz, txtInclude)) {
// process #include
psz = FirstNonSpace(pszDst + strlen(txtInclude), fDBCSSystem); if (!psz) { VReportError(HCERR_NOINCLUDE_FILE, &errHpj); continue; }
PSTR pszEnd; if (*psz == CH_QUOTE || *psz == '<') { char ch = (*psz == CH_QUOTE) ? CH_QUOTE : '>'; psz++; pszEnd = StrChrDBCS(psz, ch); if (*pszEnd) *pszEnd = '\0'; } if (pszEnd = StrChrDBCS(psz, ';')) *pszEnd = '\0'; if (pszEnd = strstr(psz, "//")) *pszEnd = '\0'; if (pszEnd = strstr(psz, "/*")) *pszEnd = '\0'; SzTrimSz(psz);
FPushFilePfs(psz); if (!(pin = PfTopPfs())) return RC_EOF; continue; } else if (nstrisubcmp(psz, txtDefine)) goto ValidString;
else if (nstrisubcmp(psz, txtIfDef)) ptblDefine->AddString( FirstNonSpace(psz + strlen(txtIfDef) + 1, fDBCSSystem)); else if (nstrisubcmp(psz, txtIfnDef)) ptblDefine->AddString( FirstNonSpace(psz + strlen(txtIfnDef) + 1, fDBCSSystem));
// REVIEW: process #ifdef, #ifndef, #else, #endif
continue;
default:
// We have a valid string.
if (psz != pszDst) strcpy(pszDst, psz);
// Remove any comments
ValidString: if (options.fDBCS) { for (psz = pszDst; *psz; psz = CharNext(psz)) { if (IsQuote(*psz)) { psz++; while (!IsQuote(*psz) && *psz) psz = CharNext(psz); } if (*psz == ';') { *psz = '\0'; break; } } for (psz = pszDst; *psz; psz = CharNext(psz)) { if (IsQuote(*psz)) { psz++; while (!IsQuote(*psz) && *psz) psz = CharNext(psz); } if (*psz == '/' && psz[1] == '/') { *psz = '\0'; break; } } } else { for (psz = pszDst; *psz; psz++) { if (IsQuote(*psz)) { psz++; while (!IsQuote(*psz) && *psz) psz++; } if (*psz == ';') { *psz = '\0'; break; } } for (psz = pszDst; *psz; psz++) { if (IsQuote(*psz)) { psz++; while (!IsQuote(*psz) && *psz) psz++; } if (*psz == '/' && psz[1] == '/') { *psz = '\0'; break; } } }
while ((psz = strstr(pszDst, "/*"))) { PSTR pszTmp = strstr(psz, "*/"); if (pszTmp) strcpy(psz, FirstNonSpace(pszTmp + 2, fDBCSSystem)); else { char szBuf[512]; do { if (!pin->getline(szBuf)) {
/*
* Close current file, continue with nested * file if there is one */
FPopPfs(); if (!(pin = PfTopPfs())) return RC_EOF; continue; } } while (!(pszTmp = strstr(szBuf, "*/"))); strcpy(psz, FirstNonSpace(pszTmp + 2, fDBCSSystem));
// New line could have comments, so start all over
goto ValidString; } } SzTrimSz(pszDst); if (!*pszDst) continue; return RC_Success; } } }
/***************************************************************************\
* - Function: FInitializeHpj() - * Purpose: Take an HPJ and set to uninitialized state. * Assume fields are garbage (i.e. don't try to free stuff). * * ASSUMES * * * PROMISES * * returns: TRUE if successful, FALSE if OOM. * * * globals OUT: * * state OUT: * * Side Effects: * * Notes: Should there be different defaults for normal and * IsRTF() case? * * Bugs: Doesn't free memory * * +++ * * Method: * * Notes: * \***************************************************************************/
BOOL STDCALL FInitializeHpj(void) { // Error Info
errHpj.iWarningLevel = 3; errHpj.ep = epNoFile;
// File Stack
pfs = (PFILESTACK) lcMalloc(sizeof(FILESTACK)); pfs->ifeTop = -1; pfs->ifeComment = -1; pfs->iCommentLine = 0;
// Options
options.sortorder = SORT_ENGLISH;
options.pszContentsTopic = NULL; options.iWarningLevel = 3; options.fUsePhrase = TRUE; // REVIEW
options.fAcceptRevions = TRUE;
Ensure(HceAddPkwiCh('K'), HCE_OK); Ensure(HceAddPkwiCh('A'), HCE_OK); // for ALinks
ptblRtfFiles = new CTable;
// Compile time variables
nsr = nsrNone;
return TRUE; }
/***************************************************************************\
* - Function: FParseHpj( szFile ) - * Purpose: Parse the named project file, filling in the HPJ structure. * * ASSUMES * * args IN: szFile - name of project file (default .HPJ extension) * * PROMISES * * returns: TRUE - parsed OK * FALSE - HPJ file unusable, bad file extension * - ran out of memory * * * Side Effects: emits error messages * - Notes: - - Bugs: Doesn't free discarded and unlock retained smag. REVIEW! - * +++ - - Method: - - Notes: - \***************************************************************************/
BOOL STDCALL FParseHpj(PSTR pszFile) { PFPARSE pfparse = NULL; RC_TYPE rc = RC_Success; PSTR psz;
if (iflags.fRtfInput) {
// This is an RTF file, not an HPJ file
ptblRtfFiles->AddString(pszFile);
return TRUE; }
for(;;) { // not a real for loop, just gives us something to break out of
/*
* If a path was specified, then try to change to that drive and * directory. If we can, then remove the path, and just leave the * filename. Makes for cleaner error output by removing the path * name of the .HPJ file, and allows other files to be relative to * the current directory. */
if ((psz = StrRChr(pszFile, CH_BACKSLASH, fDBCSSystem))) { *psz = '\0'; if (_chdir(pszFile) != 0) { *psz = CH_BACKSLASH; break; } if (pszFile[1] == ':') { if (_chdrive(tolower(pszFile[0]) - ('a' - 1)) != 0) { *psz = CH_BACKSLASH; break; } } strcpy(pszFile, psz + 1); } break; }
errHpj.lpszFile = pszFile;
// extension defaults to szInputExt (.HPJ)
// REVIEW: why strip off the path? 03-Jan-1994 [ralphw]
// [olympus 306 - chauv]
for (psz = pszFile + strlen(pszFile) - 1; psz > pszFile; psz--) { // if it's a backslash, check to see if it's a trailing backslash
if (*psz == '\\') { if ( ((psz-1) >= pszFile) && IsDBCSLeadByte(*(psz-1)) ) psz--; else break; } if ( (*psz == '/') || (*psz == ':') || (*psz == '.') ) break; }
char szHpjName[_MAX_PATH];
if (*psz != '.') { strcpy(szHpjName, pszFile); strcat(szHpjName, GetStringResource(IDS_HPJ_EXTENSION)); pszFile = szHpjName; }
if (FIsRtf(pszFile)) {
// REVIEW: 23-Jul-1993 [ralphw] why
// are we allowing them to open an RTF file?
return FALSE; }
if (!FPushFilePfs(pszFile)) { lcFree(pfs); return FALSE; } errHpj.ep = epLine;
CStr cszDst;
/*
* We want automatic deletion of the table, but we want it to be * available to other functions while we're processing the .HPJ file, so * we simply set a global pointer to the address of our local table. * Tacky, but it works. */
CTable tbl; ptblDefine = &tbl;
while (rc == RC_Success) { rc = RcGetLogicalLine(&cszDst); SpecialParsing: if (RC_Success != rc) break;
if (*cszDst.psz == CH_LEFT_BRACKET) { psz = StrChr(cszDst, CH_RIGHT_BRACKET, fDBCSSystem); if (!psz) { VReportError(HCERR_MISSING_SECTION_BRACKET, &errHpj, cszDst); } else { *psz = '\0';
psz = SzTrimSz(cszDst.psz + 1);
pfparse = PfparseFromSz(psz);
/*
* We don't parse the [MACROS] section normally. This * section cannot contain a #include line, and cannot contain * any comments. So the usual line cleanup don by * RcGetLogicalLine() would destroy the information we need. * Instead, we let RcParseMacros read its entire section. */
if (pfparse == RcParseMacros) { rc = ParseMacros(&cszDst); if (rc == RC_Success) goto SpecialParsing; else { // might have been end of a nested include
rc = RcGetLogicalLine(&cszDst); goto SpecialParsing; } } } } else { if (pfparse == NULL) { VReportError(HCERR_MISSING_SECTION, &errHpj, cszDst); pfparse = RcParseBogusSz; } else { rc = pfparse(cszDst.psz); // REVIEW - under what conditions term?
lcHeapCheck(); if (rc == RC_SkipSection) { pfparse = RcParseBogusSz; rc = RC_Success; } } } }
if (rc == RC_EOF) { rc = RC_Success;
/*
* If no compression, we still use RLE compression for bitmaps. * This is an insignificant speed hit with a significant size * reduction. */
if (options.fsCompress == COMPRESS_NONE) options.fsCompress = COMPRESS_BMP_RLE;
if (kwlcid.langid) SetDbcsFlag(kwlcid.langid);
if (FTestUlFlag(options.lOptionInitFlags, OPT_BUILD)) { if (!FTestUFlag(wSectionInitFlags, SECT_BUILDTAGS)) { fBldChk = -1; VReportError(HCERR_BUILD_TAG_MISSING, &errHpj); } else if (!FBuildPolishExpFromSz(options.szBuildExp)) { fBldChk = -1; // REVIEW - ugly global
VReportError(HCERR_INVALID_BUILD_EXP, &errHpj); } else fBldChk = 1; // REVIEW - ugly global
}
if (!FTestUFlag(wSectionInitFlags, SECT_FILES) || ptblRtfFiles->CountStrings() < 1) { VReportError(HCERR_NOFILES_DEFINED, &errHpj); rc = RC_Failure; goto error_return; }
// Reset error phase.
errHpj.ep = epNoFile;
lcFree(pfs);
return (rc == RC_Success); }
error_return:
lcFree(pfs);
return (rc == RC_Success); }
const char txtCopyright[] = "Copyright (c) Microsoft Corp 1990 - 1994. All rights reserved.";
static void STDCALL DispSignOn(void) { pLogFile->outstring_eol(GetStringResource(IDS_TITLE)); pLogFile->outstring_eol(GetStringResource(IDS_VERSION)); pLogFile->outstring_eol((PSTR) txtCopyright); }
PSTR STDCALL SkipToEndOfWord(PSTR psz) { while (*psz != SPACE && *psz != CHAR_TAB && *psz) psz = CharNext(psz); return psz; }
/***************************************************************************\
* - Function: SzLoseDriveAndDir( szFile, rgch ) - * Purpose: Return the base name + ext from a file name. * * ASSUMES * * args IN: szFile - a filespec that may or may not have drive and/or * directory. It isn't NULL or "".
* rgch - buffer to receive the info. If NULL, a buffer * is allocated. * PROMISES * * returns: pointer to buffer where the base + ext are placed. * This may be allocated (see rgch above). * * args OUT: rgch - data put here, except as stated above * * Side Effects: may allocate memory * \***************************************************************************/
void STDCALL SzLoseDriveAndDir(PSTR szFile, PSTR pszDst) { ASSERT(szFile != NULL);
// [olympus 306 - chauv]
// use _splitpath() to do the work
char fname[_MAX_FNAME], ext[_MAX_EXT]; _splitpath(szFile, NULL, NULL, fname, ext); strcpy(pszDst, fname); strcat(pszDst, ext); }
/***************************************************************************\
* - Function: PfparseFromSz( szSection) - * Purpose: Return a section parsing function based on section name. * Emit message on error. * * ASSUMES * * args IN: szSection - the section name to look up * - if ->wSectionInitFlags section flag already * set, emit message and return bogus function * * globals IN: rgSection - array of section names * * PROMISES * * returns: Pointer to a section parsing function. If szSection * wasn't a valid section name, return a default function. * * args OUT: wSectionInitFlags - section flag set * * +++ * * Method: Linear search. Could use binary search. * \***************************************************************************/
static PFPARSE STDCALL PfparseFromSz(PSTR pszSection) { int sect;
ASSERT(SECT_MAX < ELEMENTS(rgSection));
for (sect = 0; sect < SECT_MAX; sect++) { if (_stricmp(rgSection[sect].szName, pszSection) == 0) break; }
if (sect == SECT_MAX) { if (nstrisubcmp(pszSection, "CONFIG")) {
// Named config section
if (pszSection[6] == '-') { if (pdrgWsmag) { for (int i = 0; i < pdrgWsmag->Count(); i++) { WSMAG *pwsmag = ((WSMAG *) pdrgWsmag->GetBasePtr()) + i; if (!_stricmp(pszSection + 7, pwsmag->rgchMember)) { curConfig = i; return RcParseSecondaryConfigSz; } } } }
// Numbered config section
else if (pszSection[6] == ':') { if (FGetNum(pszSection + 7, NULL, &curConfig)) { return RcParseSecondaryConfigSz; } } } VReportError(HCERR_UNKNOWN_SECTION, &errHpj, pszSection); }
if (sect == SECT_OPTIONS && !FTestUFlag(wSectionInitFlags, SECT_OPTIONS) && (FTestUFlag(wSectionInitFlags, SECT_BITMAPS) || FTestUFlag(wSectionInitFlags, SECT_FILES))) VReportError(HCERR_SECTION_TOO_SOON, &errHpj, pszSection);
SetUFlag(wSectionInitFlags, sect); // set even if bogus section
return rgSection[sect].pfparse; }
RC_TYPE STDCALL RcParseCharSet(PSTR pszLine) { PSTR pszCharSet;
if (!(pszCharSet = StrChr(pszLine, CH_EQUAL, fDBCSSystem))) { VReportError(HCERR_MISS_CHARSET_EQ, &errHpj, pszLine); return RC_Success; }
*pszCharSet = '\0'; pszCharSet = FirstNonSpace(pszCharSet + 1, fDBCSSystem);
if (!isdigit((BYTE) *pszCharSet)) { VReportError(HCERR_INVALID_CHARSET, &errHpj, pszLine); return RC_Success; }
SzTrimSz(pszLine); if (!ptblCharSet) ptblCharSet = new CTable; SzTrimSz(pszCharSet); if (atoi(pszCharSet) > 255 || atoi(pszCharSet) < 0) { VReportError(HCERR_INVALID_CHARSET, &errHpj, pszLine); return RC_Success; }
ptblCharSet->AddString(pszLine, pszCharSet);
return RC_Success; }
RC_TYPE STDCALL RcParseFonts(PSTR pszLine) { CFontMap* pMap = new CFontMap(pszLine); if (!pMap->IsInitialized()) delete pMap;
return RC_Success; }
static void STDCALL ReportDuplicateOption(PSTR* ppszOption, PSTR pszName) { VReportError(HCERR_DUPLICATE_OPTION, &errHpj, pszName); if (ppszOption) lcClearFree(ppszOption); }
static void STDCALL ReportBadOption(PSTR pszOptionValue, PSTR pszOption) { VReportError(HCERR_INVALID_OPTION, &errHpj, pszOption, pszOptionValue); }
/***************************************************************************
FUNCTION: CreateSharedMemory
PURPOSE: Create shared memory for communicating with our parent
PARAMETERS: void
RETURNS:
COMMENTS:
MODIFICATION DATES: 02-Jul-1994 [ralphw]
***************************************************************************/
void STDCALL CreateSharedMemory(void) { if (!hfShare) { hfShare = CreateFileMapping((HANDLE) -1, NULL, PAGE_READWRITE, 0, 4096, txtSharedMem); ConfirmOrDie(hfShare); pszMap = (PSTR) MapViewOfFile(hfShare, FILE_MAP_WRITE, 0, 0, 0); ASSERT(pszMap); } }
// This function only exists for match purposes. It is never called
RC_TYPE STDCALL RcParseMacros(PSTR pszLine) { ASSERT(!"This function should never be called!"); return RC_Success; }
/***************************************************************************
FUNCTION: ParseMacros
PURPOSE: This creates two tables. The is a double-string table containing each macro and title string pair. There is a keyword table -- with each keyword having its own entry including a position into the associated macro/title table.
PARAMETERS: pcszLine
RETURNS:
COMMENTS:
MODIFICATION DATES: 05-Sep-1994 [ralphw]
***************************************************************************/
static RC_TYPE STDCALL ParseMacros(CStr* pcszLine) { if (!ptblMacKeywords) { ptblMacKeywords = new CTable; ptblMacroTitles = new CTable; }
CStr cszMacro; CStr cszTitle; int pos;
CInput* pin = PfTopPfs();
if (!pin) return RC_Success;
for (;;) { if (!pin->getline(pcszLine)) { return RC_EOF; } if (*pcszLine->psz == CH_LEFT_BRACKET) return RC_Success; else if (!*pcszLine->psz) continue; // blank line -- shouldn't happen, but we'll allow it
PSTR pszKey = SzParseList(pcszLine->psz);
if (pszKey == NULL) { VReportError(HCERR_NULL_KEYWORD, &errHpj); return RC_EOF; }
if (!pin->getline(&cszMacro)) { VReportError(HCERR_NULL_KEYWORD, &errHpj); return RC_EOF; }
if (!pin->getline(&cszTitle)) { VReportError(HCERR_NULL_KEYWORD, &errHpj); return RC_EOF; }
pos = ptblMacroTitles->AddString(cszMacro); ptblMacroTitles->AddString(cszTitle);
while (pszKey) { ptblMacKeywords->AddIntAndString(pos, pszKey); pszKey = SzParseList(NULL); } } }
static char szLcid[20];
BOOL CALLBACK EnumLocalesProc(LPSTR pszLocale) { if (!szLcid[0]) wsprintf(szLcid, "%08x", kwlcid.langid);
if (stricmp(pszLocale, szLcid) == 0) { fValidLcid = TRUE; return FALSE; } return TRUE; }
/***************************************************************************
FUNCTION: SetDbcsFlag
PURPOSE: Try to force DBCS flag based on language ID
PARAMETERS: langid
RETURNS:
COMMENTS:
MODIFICATION DATES: 11-Jan-1995 [ralphw]
***************************************************************************/
void STDCALL SetDbcsFlag(LANGID langid) { switch (langid) { case 0x0411: // Japanese
case 0x0404: // Taiwan
case 0x1004: // Singapore
case 0x0C04: // Hong Kong
options.fDBCS = TRUE; break;
case 0x0409: // American
case 0x0C09: // Australian
case 0x0C07: // Austrian
case 0x042D: // Basque
case 0x080C: // Belgian
case 0x0809: // British
case 0x0402: // Bulgaria
case 0x1009: // Canadian
case 0x041A: // Croatian
case 0x0405: // Czech
case 0x0406: // Danish
case 0x0413: // Dutch (Standard)
case 0x0C01: // Egypt
case 0x040B: // Finnish
case 0x040C: // French (Standard)
case 0x0C0C: // French Canadian
case 0x0407: // German (Standard)
case 0x042E: // Germany
case 0x0408: // Greek
case 0x040E: // Hungarian
case 0x040F: // Icelandic
case 0x0801: // Iraq
case 0x1809: // Ireland
case 0x040D: // Israel
case 0x0410: // Italian (Standard)
case 0x2C01: // Jordan
case 0x3401: // Kuwait
case 0x0426: // Latvia
case 0x3001: // Lebanon
case 0x1001: // Libya
case 0x1407: // Liechtenstein
case 0x0427: // Lithuania
case 0x140C: // Luxembourg (French)
case 0x1007: // Luxembourg (German)
case 0x042f: // Macedonia
case 0x080A: // Mexican
case 0x0819: // Moldavia
case 0x0818: // Moldavia
case 0x1801: // Morocco
case 0x1409: // New Zealand
case 0x0414: // Norwegian (Bokmal)
case 0x0814: // Norwegian (Nynorsk)
case 0x2001: // Oman
case 0x0415: // Polish
case 0x0416: // Portuguese (Brazilian)
case 0x0816: // Portuguese (Standard)
case 0x0418: // Romania
case 0x0419: // Russian
case 0x0401: // Saudi Arabia
case 0x081A: // Serbian
case 0x041B: // Slovak
case 0x0424: // Slovenia
case 0x0C0A: // Spanish (Modern Sort)
case 0x040A: // Spanish (Traditional Sort)
case 0x0430: // Sutu
case 0x041D: // Swedish
case 0x100C: // Swiss (French)
case 0x0807: // Swiss (German)
case 0x0810: // Swiss (Italian)
case 0x2801: // Syria
case 0x041E: // Thailand
case 0x0431: // Tsonga
case 0x041f: // Turkish
case 0x3801: // U.A.E.
case 0x0422: // Ukraine
case 0x0420: // Urdu
case 0x0436: // Zulu
options.fDBCS = FALSE; break;
} }
|