//+--------------------------------------------------------------------------- // // Microsoft Forms // Copyright (C) Microsoft Corporation, 1994-1997 // // File: ascparse.cxx // // Contents: Tool to build .hsc files from .asc files. // // Does the work of precomputing hash tables for associative // arrays of strings. // //---------------------------------------------------------------------------- #define INCMSG(x) #include "headers.hxx" #ifndef X_LIMITS_H_ #define X_LIMITS_H_ #include #endif #ifndef X_PLATFORM_H_ #define X_PLATFORM_H_ #include #endif #ifndef X_MSHTMDBG_H_ #define X_MSHTMDBG_H_ #undef PERFMETER #include #endif #define ASCPARSE // The following macro definitions allow us to use assoc.cxx // without bringing in the whole CORE directory #ifndef X_TCHAR_H_ #define X_TCHAR_H_ #include "tchar.h" #endif #define THR(x) (x) #define RRETURN(x) return(x) #define _MemAlloc(cb) malloc(cb) #define _MemAllocClear(cb) calloc(1,cb) #define _MemFree(x) free(x) #define MemAlloc(mt,cb) _MemAlloc(cb) #define MemAllocClear(mt,cb) _MemAllocClear(cb) #define MemFree(x) _MemFree(x) #define MemRealloc(mt, ppv, cb) _MemRealloc(ppv, cb) #define _tcsequal(x,y) (!_tcscmp(x,y)) #define Assert(x) if (!(x)) { fprintf(stderr, "%s", #x); exit(1); } #define Verify(x) if (!(x)) { fprintf(stderr, "%s", #x); exit(1); } #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) HRESULT _MemRealloc(void **ppv, size_t cb) { void *pv; if (*ppv == NULL) { *ppv = _MemAlloc(cb); if (*ppv == NULL) return E_OUTOFMEMORY; } else { pv = realloc(*ppv, cb); if (pv == NULL) return E_OUTOFMEMORY; *ppv = pv; } return S_OK; }; void GetSuffix(LPCSTR pAssocString, LPSTR pSuffix, int nNumber) { pSuffix[0] = '\0'; // NULL terminate the suffix // check if the First Character is a Captial letter. if ( islower(pAssocString[0]) ) return; _itoa(nNumber, pSuffix, 10); } #include "assoc.cxx" // end of stubs #define MAX_WORD 64 #define MAX_LINE 4096 class CAscParser { public: CAscParser() { memset(this, 0, sizeof(*this)); } class CAscEntry { public: CAscEntry() { memset(this, 0, sizeof(*this)); } CAscEntry *_pEntryNext; char _achString[MAX_WORD]; int _number; char _achNumber[MAX_WORD]; char _achStringName[MAX_WORD]; char _achAssoc[MAX_WORD]; char _achEnum[MAX_WORD]; BOOL _fNoassoc; BOOL _fNostring; BOOL _fNoenum; const CAssoc *_pAssoc; }; HRESULT ProcessAscFile(char *pchInputFile, char *pchOutputFile); char _achAssocArray[MAX_WORD]; char _achAssocPrefix[MAX_WORD]; char _achEnumType[MAX_WORD]; char _achEnumPrefix[MAX_WORD]; char _achStringNamePrefix[MAX_WORD]; BOOL _fInsensitive; BOOL _fReversible; CAscEntry *_pEntryFirst; CAscEntry *_pEntryLast; }; static BOOL ReadLine(FILE *fp, char *pchBuf, int cchBuf, int *pcchRead); static void SkipSpace(char **ppch); static void SkipNonspace(char **ppch); static void ChopComment(char *pch); static void GetWord(char **ppch, char **ppchWord); int __cdecl main ( int argc, char *argv[] ) { HRESULT hr = E_FAIL; CAscParser np; if (argc != 3) goto Cleanup; hr = np.ProcessAscFile(argv[1], argv[2]); Cleanup: if (hr) printf ( "Error %lx building ASC file\n", hr); exit(hr); } HRESULT CAscParser::ProcessAscFile(char *pchInputFile, char *pchOutputFile) { HRESULT hr; FILE *fpInput = NULL; FILE *fpOutput = NULL; char achBuf[MAX_LINE]; char *pch; char *pchWord; CAscEntry *pEntryNew; CAscEntry *pEntry; CAssocArray nt; nt.Init(); _fReversible = FALSE; // open input file fpInput = fopen(pchInputFile, "r"); if (!fpInput) { hr = E_FAIL; goto Cleanup; } // open output file fpOutput = fopen(pchOutputFile, "w"); if (!fpOutput) { hr = E_FAIL; goto Cleanup; } // phase 1: read the header section hr = E_FAIL; for (;;) { if (!ReadLine(fpInput, achBuf, MAX_LINE, NULL)) goto Cleanup; pch = achBuf; ChopComment(pch); GetWord(&pch, &pchWord); if (!*pchWord) continue; if (!strcmp(pchWord, "assocarray")) { GetWord(&pch, &pchWord); strcpy(_achAssocArray, pchWord); GetWord(&pch, &pchWord); strcpy(_achAssocPrefix, pchWord); } else if (!strcmp(pchWord, "enum")) { GetWord(&pch, &pchWord); strcpy(_achEnumType, pchWord); GetWord(&pch, &pchWord); strcpy(_achEnumPrefix, pchWord); } else if (!strcmp(pchWord, "string")) { GetWord(&pch, &pchWord); strcpy(_achStringNamePrefix, pchWord); } else if (!strcmp(pchWord, "case-insensitive")) { _fInsensitive = TRUE; } else if (!strcmp(pchWord, "case-sensitive")) { _fInsensitive = FALSE; } else if (!strcmp(pchWord, "reversible")) { _fReversible = TRUE; } else if (!strcmp(pchWord, "start")) break; } // phase 2: read the assoc table section hr = S_OK; while (ReadLine(fpInput, achBuf, MAX_LINE, NULL)) { pch = achBuf; ChopComment(pch); GetWord(&pch, &pchWord); if (!*pchWord) continue; // allocate pEntryNew = new CAscEntry; if (!pEntryNew) return E_OUTOFMEMORY; // link up if (!_pEntryLast) { pEntryNew->_number = 0; _pEntryLast = _pEntryFirst = pEntryNew; } else { pEntryNew->_number = _pEntryLast->_number+1; _pEntryLast->_pEntryNext = pEntryNew; _pEntryLast = pEntryNew; } // fill in assoc strcpy(pEntryNew->_achString, pchWord); // fill in other fields for (;;) { GetWord(&pch, &pchWord); if (!*pchWord) break; if (!strcmp(pchWord, "number")) { GetWord(&pch, &pchWord); if (*pchWord == '=') { for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!strcmp(pchWord+1, pEntry->_achString)) { break; } } if (!pEntry) { hr = E_FAIL; goto Cleanup; } pEntryNew->_number = pEntry->_number; strcpy(pEntryNew->_achNumber, pEntry->_achNumber); } else if (*pchWord >= '0' && *pchWord <= '9' || *pchWord == '-') { pEntryNew->_number = atol(pchWord); *pEntryNew->_achNumber = '\0'; } else { pEntryNew->_number = 0; strcpy(pEntryNew->_achNumber, pchWord); } } else if (!strcmp(pchWord, "string")) { GetWord(&pch, &pchWord); strcpy(pEntryNew->_achStringName, pchWord); } else if (!strcmp(pchWord, "enum")) { GetWord(&pch, &pchWord); strcpy(pEntryNew->_achEnum, pchWord); } else if (!strcmp(pchWord, "assoc")) { GetWord(&pch, &pchWord); strcpy(pEntryNew->_achAssoc, pchWord); } else if (!strcmp(pchWord, "noassoc")) { pEntryNew->_fNoassoc = TRUE; } else if (!strcmp(pchWord, "nostring")) { pEntryNew->_fNostring = TRUE; } else if (!strcmp(pchWord, "noenum")) { pEntryNew->_fNoenum = TRUE; } } } // compute assocs for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!pEntry->_fNoassoc) { WCHAR awch[MAX_WORD]; WCHAR *pwch = awch; char *pch = pEntry->_achString; DWORD len; DWORD hash; const CAssoc *passoc; do { *pwch++ = *pch; } while (*pch++); len = _tcslen(awch); if (_fInsensitive) hash = HashStringCi(awch, len, 0); else hash = HashString(awch, len, 0); passoc = nt.AddAssoc((DWORD_PTR)pEntry, awch, len, hash); if (!passoc) { hr = E_FAIL; goto Cleanup; } pEntry->_pAssoc = passoc; } } // phase 3: output header decls enums fprintf(fpOutput, "// %s\n", pchOutputFile); fprintf(fpOutput, "// Generated by ascparse.exe from %s\n", pchInputFile); fprintf(fpOutput, "// Do not modify by hand!\n"); fprintf(fpOutput, "\n#ifndef _cxx_\n\n"); // assocarray and hash fprintf(fpOutput, "class CAssoc;\n"); fprintf(fpOutput, "class CAssocArray;\n\n"); fprintf(fpOutput, "extern const CAssoc * const %s_HashTable[%d];\n", _achAssocArray, nt._mHash); if (_fReversible) { fprintf(fpOutput, "extern const CAssoc * const %s_RevSearch[];\n", _achAssocArray); } fprintf(fpOutput, "extern const CAssocArray %s;\n\n", _achAssocArray); // enums if (*_achEnumType) { fprintf(fpOutput, "enum %s\n{\n", _achEnumType); for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!pEntry->_fNoenum) { if (*pEntry->_achEnum) { fprintf(fpOutput, " %s = %d,\n", pEntry->_achEnum, pEntry->_number); } else { fprintf(fpOutput, " %s%s = %d,\n", _achEnumPrefix, pEntry->_achString, pEntry->_number); } } } fprintf(fpOutput, " %s_FORCE_LONG = LONG_MAX\n", _achEnumType); fprintf(fpOutput, "};\n\n"); } // assocs for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!pEntry->_fNoassoc) { if (!*pEntry->_achAssoc) { char suffix[32]; GetSuffix(pEntry->_achString, suffix, pEntry->_number); fprintf(fpOutput, "extern const CAssoc %s%s%s;\n", _achAssocPrefix, pEntry->_achString, suffix); } else { fprintf(fpOutput, "extern const CAssoc %s;\n", pEntry->_achAssoc); } } } fprintf(fpOutput, "\n\n"); // strings for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!pEntry->_fNostring) { if (!pEntry->_fNoassoc && (*_achStringNamePrefix || *pEntry->_achStringName)) { if (*pEntry->_achStringName) { fprintf(fpOutput, "#define %s ", pEntry->_achStringName); } else { fprintf(fpOutput, "#define %s%s ", _achStringNamePrefix, pEntry->_achString); } if (!*pEntry->_achAssoc) { char suffix[32]; GetSuffix(pEntry->_achString, suffix, pEntry->_number); fprintf(fpOutput, "(%s%s%s._ach)\n", _achAssocPrefix, pEntry->_achString, suffix); } else { fprintf(fpOutput, "(%s._ach)\n", pEntry->_achAssoc); } } else { if (*pEntry->_achStringName) { fprintf(fpOutput, "#define %s (_T(\"%s\"))\n", pEntry->_achStringName, pEntry->_achString); } else if (*_achStringNamePrefix) { fprintf(fpOutput, "#define *%s%s (_T(\"%s\"))\n", _achStringNamePrefix, pEntry->_achString, pEntry->_achString); } } } } // end of header section; start of cxx section fprintf(fpOutput, "\n#else _cxx_\n\n"); fprintf(fpOutput, "\n#undef _cxx_\n\n"); // phase 4: output assocs for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (!pEntry->_fNoassoc) { if (!*pEntry->_achAssoc) { char suffix[32]; GetSuffix(pEntry->_achString, suffix, pEntry->_number); fprintf(fpOutput, "const CAssoc %s%s%s\t\t\t= ", _achAssocPrefix, pEntry->_achString, suffix); } else { fprintf(fpOutput, "const CAssoc %s = ", pEntry->_achAssoc); } if (*pEntry->_achNumber) { fprintf(fpOutput, "{ %12s, 0x%08x, _T(\"%s\") };\n", pEntry->_achNumber, pEntry->_pAssoc->_hash, pEntry->_achString); } else { fprintf(fpOutput, "{ %5d, 0x%08x, _T(\"%s\") };\n", pEntry->_number, pEntry->_pAssoc->_hash, pEntry->_achString); } } } // phase 5: output table // output hash table fprintf(fpOutput, "\n\nconst CAssoc * const %s_HashTable[%d] =\n{\n", _achAssocArray, nt._mHash); { int i; int c; int s; int d; const CAssoc * const *ppAssoc; for (ppAssoc = nt._pHashTable, c=nt._mHash; c; ppAssoc++, c--) { if (!*ppAssoc) { fprintf(fpOutput, " NULL,\n"); } else { i = (*ppAssoc)->Hash() % nt._mHash; s = ((*ppAssoc)->Hash() & nt._sHash) + 1; d = 0; while (nt._pHashTable + i != ppAssoc) { if (i < s) i += nt._mHash; i -= s; d++; } fprintf(fpOutput, "/*%2d */ ",d); pEntry = (CAscEntry*)(*ppAssoc)->Number(); if (!*pEntry->_achAssoc) { char suffix[32]; GetSuffix(pEntry->_achString, suffix, pEntry->_number); fprintf(fpOutput, "&%s%s%s,\n", _achAssocPrefix, pEntry->_achString, suffix); } else { char suffix[32]; GetSuffix(pEntry->_achAssoc, suffix, pEntry->_number); fprintf(fpOutput, "&%s%s,\n", pEntry->_achAssoc, suffix); } } } } fprintf(fpOutput, "};\n\n"); // phase 6: output table for reverse search (if requested) if (_fReversible) { CAscParser::CAscEntry * pFound; CAscParser::CAscEntry * pEntry; int nCurrMin; int nPrevMin; int nCurrVal; fprintf(fpOutput, "const CAssoc * const %s_RevSearch[] =\n{\n", _achAssocArray); // find and print the entries in order from numeric min to max nPrevMin = 0; for(;;) { // find the next entry nCurrMin = INT_MAX; pFound = NULL; for (pEntry = _pEntryFirst; pEntry; pEntry = pEntry->_pEntryNext) { if (*pEntry->_achNumber) { nCurrVal = atol(pEntry->_achNumber); } else { nCurrVal = pEntry->_number; } if (nCurrVal > nPrevMin && nCurrVal < nCurrMin) { nCurrMin = nCurrVal; pFound = pEntry; } } // break out once we've done everything if (pFound == NULL) { break; } // output a pointer to the assoc if (!*pFound->_achAssoc) { char suffix[32]; GetSuffix(pFound->_achString, suffix, pFound->_number); fprintf(fpOutput, "\t&%s%s%s,\n", _achAssocPrefix, pFound->_achString, suffix); } else { fprintf(fpOutput, "\t%s,\n", pFound->_achAssoc); } nPrevMin = nCurrMin; } fprintf(fpOutput, "};\n\n"); } // output assoc table struct itself fprintf(fpOutput, "const CAssocArray %s = {\n", _achAssocArray); fprintf(fpOutput, " %s_HashTable,\n", _achAssocArray); fprintf(fpOutput, " %d,\n", nt._cHash); fprintf(fpOutput, " %d,\n", nt._mHash); fprintf(fpOutput, " %d,\n", nt._sHash); fprintf(fpOutput, " %d,\n", nt._cHash); fprintf(fpOutput, " %d,\n", nt._iSize); fprintf(fpOutput, " TRUE,\n"); fprintf(fpOutput, "};\n\n"); fprintf(fpOutput, "\n#endif _cxx_\n\n"); Cleanup: nt.Deinit(); if (fpInput) fclose(fpInput); if (fpOutput) fclose(fpOutput); return hr; } static BOOL ReadLine(FILE *fp, char *pchBuf, int cchBuf, int *pcchRead) { int cchRead; if (!fgets(pchBuf, cchBuf, fp)) return FALSE; cchRead = strlen(pchBuf); if (!cchRead) return FALSE; if (pcchRead) *pcchRead = cchRead; return TRUE; } static void SkipSpace(char **ppch) { char *pch = *ppch; while (*pch && (*pch == ' ' || *pch == '\t' || *pch == '\r' || *pch == '\n')) pch++; *ppch = pch; } static void SkipNonspace(char **ppch) { char *pch = *ppch; while (*pch && (*pch != ' ' && *pch != '\t' && *pch != '\r' && *pch != '\n')) pch++; *ppch = pch; } static void ChopComment(char *pch) { while (*pch) { if (*pch == '/' && *(pch+1) == '/') { *pch = '\0'; return; } pch++; } } static void GetWord(char **ppch, char **ppchWord) { SkipSpace(ppch); *ppchWord = *ppch; SkipNonspace(ppch); if (**ppch) { **ppch = '\0'; if (*ppch - *ppchWord > MAX_WORD) *(*ppchWord + MAX_WORD-1) = '\0'; (*ppch)++; } }