/* $Id: kFileDef.cpp,v 1.13 2002-08-29 07:59:24 bird Exp $ * * kFileDef - Definition files. * * Copyright (c) 1999-2001 knut st. osmundsen * */ /******************************************************************************* * Defined Constants * *******************************************************************************/ #define StringCase(psz, pszMatch) (strnicmp(psz, pszMatch, sizeof(pszMatch)-1) == 0 \ && ( psz[sizeof(pszMatch)-1] == '\0' \ || psz[sizeof(pszMatch)-1] == ' ' \ || psz[sizeof(pszMatch)-1] == '\n' \ || psz[sizeof(pszMatch)-1] == '\r' \ || psz[sizeof(pszMatch)-1] == '\t' \ ) \ ) /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include "kTypes.h" #include "kError.h" #include "kFile.h" #include "kFileFormatBase.h" #include "kFileInterfaces.h" #include "kFileDef.h" /******************************************************************************* * Global Variables * *******************************************************************************/ #if 0 static kFileDef tst((kFile*)NULL); #endif /******************************************************************************* * Internal Functions * *******************************************************************************/ static char *dupeString(const char *psz, KBOOL fSkipFirstWord = FALSE); static char *trim(char *psz); static char *ltrim(const char *psz); static char *removeFnutts(char *pszStr); inline char upcase(char ch); static char *stristr(const char *pszStr, const char *pszSubStr); /** * Duplicates a string. * @returns Pointer to stringcopy. Remeber to delete this! * @param psz Pointer to string to duplicate. * @param fSkipFirstWord Skips the first word before duplicating the string. */ static char *dupeString(const char *psz, KBOOL fSkipFirstWord/* = FALSE*/) { char *pszDupe; if (psz == NULL) return NULL; if (fSkipFirstWord) { while (*psz != ' ' && *psz != '\t' && *psz != '\n' && *psz != '\r' && *psz != '\0') psz++; psz = ltrim(psz); } pszDupe = new char[strlen(psz)+1]; strcpy(pszDupe, psz); if (fSkipFirstWord) return removeFnutts(pszDupe); return pszDupe; } /** * Trims a string, that is removing blank spaces at start and end. * @returns Pointer to first non-blank char. * @param psz Pointer to string. * @result Blank at end of string is removed. ('\0' is moved to the left.) */ static char *trim(char *psz) { int i; if (psz == NULL) return NULL; while (*psz == ' ' || *psz == '\t') psz++; i = strlen(psz) - 1; while (i >= 0 && (psz[i] == ' ' || psz[i] == '\t')) i--; psz[i+1] = '\0'; return psz; } /** * Trims a string, that is removing blank spaces at start and end. * @returns Pointer to first non-blank char. * @param psz Pointer to string. * @result Blank at end of string is removed. ('\0' is moved to the left.) */ static char *ltrim(const char *psz) { if (psz == NULL) return NULL; while (*psz == ' ' || *psz == '\t') psz++; return (char *)psz; } kFileDef::kFileDef(kFile *pFile) throw (kError) : kFileFormatBase(pFile), pszType(NULL), pszModName(NULL), pszBase(NULL), pszCode(NULL), pszData(NULL), pszDescription(NULL), pszExeType(NULL), pszHeapSize(NULL), pszOld(NULL), pszProtmode(NULL), pszStackSize(NULL), pszStub(NULL), pSegments(NULL), pImports(NULL), pExports(NULL), fProgram(FALSE), fLibrary(FALSE), fPhysicalDevice(FALSE), fVirtualDevice(FALSE), fInitInstance(FALSE), fTermInstance(FALSE), fInitGlobal(FALSE), fTermGlobal(FALSE), chAppType(kFileDef::unknown) { pFile->setThrowOnErrors(); pFile->start(); read(pFile); } /** * Destructor. Frees used memory. */ kFileDef::~kFileDef() throw (kError) { if (pszType != NULL) delete pszType; if (pszBase != NULL) delete pszBase; if (pszCode != NULL) delete pszCode; if (pszData != NULL) delete pszData; if (pszDescription != NULL) delete pszDescription; if (pszExeType != NULL) delete pszExeType; if (pszHeapSize != NULL) delete pszHeapSize; if (pszOld != NULL) delete pszOld; if (pszProtmode != NULL) delete pszProtmode; if (pszStackSize != NULL) delete pszStackSize; if (pszStub != NULL) delete pszStub; while (pSegments != NULL) { PDEFSEGMENT p = pSegments; pSegments = pSegments->pNext; if (p->psz != NULL) delete p->psz; delete p; } while (pImports != NULL) { PDEFIMPORT p = pImports; pImports = pImports->pNext; if (p->pszName != NULL) delete p->pszName; if (p->pszDll != NULL) delete p->pszDll; if (p->pszIntName != NULL) delete p->pszIntName; delete p; } while (pExports != NULL) { PDEFEXPORT p = pExports; pExports = p->pNext; if (p->pszName != NULL) delete p->pszName; if (p->pszIntName != NULL) delete p->pszIntName; delete p; } } /** * Read/parse the Definition file. * @param pFile Pointer to fileobject. * @remark throws errorcode on error (TODO: errorhandling) */ void kFileDef::read(kFile *pFile) throw (kError) { char *pszTmp; char *psz; char szBuffer[256]; char ch; /* readloop */ psz = readln(pFile, &szBuffer[0], sizeof(szBuffer)); while (psz != NULL) { KBOOL fNext = TRUE; /* if-switch */ if (StringCase(psz, "LIBRARY")) { if (pszType != NULL) throw (kError(kError::DEF_MULIPLE_MODULE_STATEMENT)); pszType = dupeString(psz); fLibrary = TRUE; if (!setModuleName()) throw (kError(kError::DEF_BAD_LIBRARY_STATEMENT)); fInitInstance = stristr(pszType, "INITINSTANCE") != NULL; fInitGlobal = stristr(pszType, "INITGLOBAL") != NULL; fTermInstance = stristr(pszType, "TERMINSTANCE") != NULL; fTermGlobal = stristr(pszType, "TERMGLOBAL") != NULL || !fTermInstance; } else if (StringCase(psz, "NAME")) { if (pszType != NULL) throw (kError(kError::DEF_MULIPLE_MODULE_STATEMENT)); pszType = dupeString(psz); fProgram = TRUE; setModuleName(); if (stristr(pszType, "WINDOWAPI")) chAppType = kFileDef::pm; else if (stristr(pszType, "NOTWINDOWCOMPAT")) chAppType = kFileDef::fullscreen; else if (stristr(pszType, "WINDOWCOMPAT")) chAppType = kFileDef::pmvio; else chAppType = kFileDef::unknown; } else if (StringCase(psz, "PHYSICAL DEVICE")) //gap is fixed to one space, this may be fixed in readln. { if (pszType != NULL) throw (kError(kError::DEF_MULIPLE_MODULE_STATEMENT)); pszType = dupeString(psz); fPhysicalDevice = TRUE; setModuleName(); } else if (StringCase(psz, "VIRTUAL DEVICE")) //gap is fixed to one space, this may be fixed in readln. { if (pszType != NULL) throw (kError(kError::DEF_MULIPLE_MODULE_STATEMENT)); pszType = dupeString(psz); fVirtualDevice = TRUE; setModuleName(); } else if (StringCase(psz, "BASE")) pszBase = dupeString(psz, TRUE); else if (StringCase(psz, "CODE")) pszCode = dupeString(psz, TRUE); else if (StringCase(psz, "DATA")) pszData = dupeString(psz, TRUE); else if (StringCase(psz, "DESCRIPTION")) pszDescription = dupeString(psz, TRUE); else if (StringCase(psz, "EXETYPE")) pszExeType = dupeString(psz, TRUE); else if (StringCase(psz, "HEAPSIZE")) pszHeapSize = dupeString(psz, TRUE); else if (StringCase(psz, "OLD")) pszOld = dupeString(psz, TRUE); else if (StringCase(psz, "PROTMODE")) pszProtmode = dupeString(psz, TRUE); else if (StringCase(psz, "STACKSIZE")) pszStackSize = dupeString(psz, TRUE); else if (StringCase(psz, "STUB")) pszStub = dupeString(psz, TRUE); else if (StringCase(psz, "SEGMENTS")) { PDEFSEGMENT *pps = &pSegments; while (!isKeyword(psz = readln(pFile, &szBuffer[0], sizeof(szBuffer))) && psz != NULL) { *pps = new DEFSEGMENT; memset(*pps, 0, sizeof(**pps)); (**pps).psz = dupeString(psz); printf("debug: parsing: %s\n", psz); /* seg name */ (**pps).pszName = ltrim((**pps).psz); ch = *(**pps).pszName; if (ch == '\'' || ch == '\"') pszTmp = strchr(++(**pps).pszName, ch); else pszTmp = strpbrk((**pps).pszName, " \t"); if (pszTmp) { *pszTmp = '\0'; /* class ? */ pszTmp = ltrim(pszTmp + 1); if (!strnicmp(pszTmp, "CLASS", 5)) { (**pps).pszClass = ltrim(pszTmp + 5); ch = *(**pps).pszClass; if (ch == '\'' || ch == '\"') pszTmp = strchr(++(**pps).pszClass, ch); else pszTmp = strpbrk((**pps).pszClass, " \t"); if (pszTmp) { *pszTmp++ = '\0'; } } if (pszTmp) { /* attribs */ (**pps).pszAttr = trim(pszTmp); if (!*(**pps).pszAttr) (**pps).pszAttr = NULL; } } printf("debug: pszName=%s\n", (**pps).pszName); printf("debug: pszClass=%s\n", (**pps).pszClass); printf("debug: pszAttr=%s\n", (**pps).pszAttr); /* next */ pps = &(**pps).pNext; } fNext = FALSE; } else if (StringCase(psz, "IMPORTS")) { PDEFIMPORT *ppi = &pImports; while (!isKeyword(psz = readln(pFile, &szBuffer[0], sizeof(szBuffer))) && psz != NULL) { //DOSCALL1.154 or DosQueryHeaderInfo = DOSCALL1.154 *ppi = new DEFIMPORT; memset(*ppi, 0, sizeof(**ppi)); (**ppi).ulOrdinal = 0xffffffffUL; if ((pszTmp = strchr(psz, '=')) != NULL) { *pszTmp = '\0'; (**ppi).pszIntName = dupeString(trim(psz)); psz = pszTmp + 1; } if ((pszTmp = strchr(psz, '.')) != NULL) { *pszTmp = '\0'; (**ppi).pszDll = dupeString(trim(psz)); psz = pszTmp + 1; } psz = trim(psz); if (*psz >= '0' && *psz <= '9') { (**ppi).ulOrdinal = strtol(psz, &pszTmp, 0); if (psz ==pszTmp) throw (kError(kError::DEF_BAD_IMPORT)); } else (**ppi).pszName = dupeString(psz); ppi = &(**ppi).pNext; } fNext = FALSE; } else if (StringCase(psz, "EXPORTS")) { PDEFEXPORT *ppe = &pExports; while (!isKeyword(psz = readln(pFile, &szBuffer[0], sizeof(szBuffer))) && psz != NULL) { /* CloseHandle = CloseHandle@4 @1234 RESIDENTNAME 2 */ *ppe = new DEFEXPORT; memset(*ppe, 0, sizeof(**ppe)); (**ppe).ulOrdinal = 0xffffffffUL; (**ppe).cParam = 0xffffffffUL; /* look for '=' */ pszTmp = strchr(psz, '='); if (pszTmp != NULL) { /* CloseHandle = CloseHandle@4 */ *pszTmp ='\0'; (**ppe).pszName = dupeString(trim(psz)); psz = trim(pszTmp + 1); pszTmp = strchr(psz, ' '); if (pszTmp != NULL) *pszTmp = '\0'; (**ppe).pszIntName = dupeString(trim(psz)); if (pszTmp != NULL) psz = pszTmp + 1; else psz = NULL; } else { /* CloseHandle (no '= CloseHandle@4')*/ pszTmp = strchr(psz, ' '); if (pszTmp != NULL) *pszTmp = '\0'; (**ppe).pszName = dupeString(trim(psz)); if (pszTmp != NULL) psz = pszTmp + 1; else psz = NULL; } if (psz != NULL) { /* @1234 RESIDENTNAME 2 */ pszTmp = strchr(psz, '@'); if (pszTmp) { /* @1234 RESIDENTNAME 2 */ psz = pszTmp + 1; (**ppe).ulOrdinal = strtol(psz, &pszTmp, 0); if (pszTmp == psz) throw (kError(kError::DEF_BAD_EXPORT)); psz = trim(pszTmp); if (*psz != '\0') { /* RESIDENTNAME 2 */ if (StringCase(psz, "RESIDENTNAME")) { (**ppe).fResident = TRUE; psz = trim(psz + sizeof("RESIDENTNAME") - 1); } else if (StringCase(psz, "NONAME")) { (**ppe).fResident = FALSE; psz = trim(psz + sizeof("NONAME") - 1); } } else { (**ppe).fResident = (**ppe).ulOrdinal == 0xffffffffUL; } } if (*psz != '\0') { /* 2 */ (**ppe).cParam = strtol(psz, &pszTmp, 0); if (pszTmp == psz) throw (kError(kError::DEF_BAD_EXPORT)); } } removeFnutts((**ppe).pszIntName); removeFnutts((**ppe).pszName); ppe = &(**ppe).pNext; } fNext = FALSE; } else throw (kError(kError::DEF_UNKOWN_VERB)); /* next ? */ if (fNext) psz = readln(pFile, &szBuffer[0], sizeof(szBuffer)); } /* sanity check */ if (pszType == NULL) throw (kError(kError::DEF_NO_MODULE_STATEMENT)); } /** * Reads first meaning full line from a file into a buffer. * @returns Pointer to buffer on success; NULL on error. * @param pFile Pointer to fileobject to read line from. * @param pszBuffer Pointer to buffer. * @param cbBuffer Size of buffer. * @remark tabs are expanded. string is trimmed. comments removed. */ char *kFileDef::readln(kFile *pFile, char *pszBuffer, int cbBuffer) throw (kError) { int i; int cch; do { /* read line */ if (pFile->readln(pszBuffer, cbBuffer)) { if (!pFile->isEOF()) throw (kError(pFile->getLastError())); return NULL; } /* check for and remove comments, and get string length. */ cch = 0; while (pszBuffer[cch] != '\0' && pszBuffer[cch] != ';') cch++; pszBuffer[cch] = '\0'; if (cch > 0) { /* remove '\t' - tab is trouble some! */ for (i = 0; i < cch; i++) if (pszBuffer[i] == '\t') pszBuffer[i] = ' '; /* trim line - right */ i = cch - 1; while (i >= 0 && ( pszBuffer[i] == '\n' || pszBuffer[i] == '\r' || pszBuffer[i] == ' ' )) i--; pszBuffer[++i] = '\0'; cch = i; /* trim line - left */ i = 0; while (i < cch && pszBuffer[i] == ' ') i++; cch -= i; if (i > 0) memmove(pszBuffer, &pszBuffer[i], cch + 1); } } while ((*pszBuffer == ';' || cch == 0) && !pFile->isEOF()); return !(*pszBuffer == ';' || cch == 0) ? pszBuffer : NULL; } /** * Checks for keyword. * @returns TRUE - keyword; FALSE - not keyword; * @param psz Pointer to word/string to check. * @remark TODO - we are going to check WORDS. */ KBOOL kFileDef::isKeyword(const char *psz) { return psz != NULL && ( StringCase(psz, "LIBRARY") || StringCase(psz, "NAME") || StringCase(psz, "PHYSICAL DEVICE") || //gap is fixed to one space, this may be fixed in readln. StringCase(psz, "VIRTUAL DEVICE") || //gap is fixed to one space, this may be fixed in readln. StringCase(psz, "BASE") || StringCase(psz, "CODE") || StringCase(psz, "DATA") || StringCase(psz, "DESCRIPTION") || StringCase(psz, "EXETYPE") || StringCase(psz, "HEAPSIZE") || StringCase(psz, "OLD") || StringCase(psz, "STACKSIZE") || StringCase(psz, "STUB") || StringCase(psz, "SEGMENTS") || StringCase(psz, "IMPORTS") || StringCase(psz, "EXPORTS") ); } /** * Extracts the module name from the pszType string. * @returns Success indicator. * @param pszBuffer Pointer to string buffer which is to hold the module name upon return. * @remark Assumes that pszBuffer is large enough. */ KBOOL kFileDef::setModuleName(void) { char *pszEnd; char *pszStart; kASSERT(pszType); /* skip the first word */ pszStart = strpbrk(pszType, " \t"); if (pszStart != NULL) { /* if phys/virt device skip second word too */ if (fVirtualDevice || fPhysicalDevice) { pszStart = strpbrk(ltrim(pszStart), " \t"); if (pszStart == NULL) return TRUE; } pszStart = ltrim(pszStart); pszEnd = strpbrk(pszStart, " \t"); if (pszEnd == NULL) pszEnd = pszStart + strlen(pszStart); pszModName = new char[pszEnd - pszStart + 1]; memcpy(pszModName, pszStart, pszEnd - pszStart); pszModName[pszEnd - pszStart] = '\0'; } else return !StringCase(pszType, "LIBRARY"); return TRUE; } /** * Query for the module name. * @returns Success indicator. TRUE / FALSE. * @param pszBuffer Pointer to buffer which to put the name into. * @param cchBuffer Size of the buffer (defaults to 260 chars). */ KBOOL kFileDef::moduleGetName(char *pszBuffer, int cchSize/* = 260*/) { int cch; if (pszModName == NULL) return FALSE; cch = strlen(pszModName) + 1; if (cch > cchSize) return FALSE; memcpy(pszBuffer, pszModName, cch); return TRUE; } /** * Finds the first exports. * @returns Success indicator. TRUE / FALSE. * @param pExport Pointer to export structure. */ KBOOL kFileDef::exportFindFirst(kExportEntry *pExport) { if (pExports != NULL && pExport != NULL) { pExport->ulOrdinal = pExports->ulOrdinal; pExport->achName[0] = '\0'; if (pExports->pszName != NULL) strcpy(&pExport->achName[0], pExports->pszName); pExport->achIntName[0] = '\0'; if (pExports->pszIntName) strcpy(&pExport->achIntName[0], pExports->pszIntName); pExport->ulAddress = pExport->iObject = pExport->ulOffset = ~0UL; pExport->pv = (void*)pExports->pNext; } else return FALSE; return TRUE; } /** * Finds the next export. * @returns Success indicator. TRUE / FALSE. * @param pExport Pointer to export structure. */ KBOOL kFileDef::exportFindNext(kExportEntry *pExport) { if (pExport != NULL && pExport->pv != NULL) { PDEFEXPORT pExp = (PDEFEXPORT)pExport->pv; pExport->ulOrdinal = pExp->ulOrdinal; pExport->achName[0] = '\0'; if (pExp->pszName != NULL) strcpy(&pExport->achName[0], pExp->pszName); pExport->achIntName[0] = '\0'; if (pExp->pszIntName) strcpy(&pExport->achIntName[0], pExp->pszIntName); pExport->ulAddress = pExport->iObject = pExport->ulOffset = ~0UL; pExport->pv = (void*)pExp->pNext; } else return FALSE; return TRUE; } /** * Frees resources associated with the communicatin area. * It's not necessary to call this when exportFindNext has return FALSE. * (We don't allocate anything so it's not a problem ;-) * @param pExport Communication area which has been successfully * processed by findFirstExport. */ void kFileDef::exportFindClose(kExportEntry *pExport) { pExport = pExport; return; } /** * Lookup information on a spesific export given by ordinal number. * @returns Success indicator. * @param pExport Communication area containing export information * on successful return. * @remark stub */ KBOOL kFileDef::exportLookup(unsigned long ulOrdinal, kExportEntry *pExport) { kASSERT(!"not implemented."); ulOrdinal = ulOrdinal; pExport = pExport; return FALSE; } /** * Lookup information on a spesific export given by name. * @returns Success indicator. * @param pExport Communication area containing export information * on successful return. * @remark stub */ KBOOL kFileDef::exportLookup(const char * pszName, kExportEntry *pExport) { kASSERT(!"not implemented."); pszName = pszName; pExport = pExport; return FALSE; } /** * Make a Watcom Linker parameter file addtition of this definition file. * @returns Success indicator. * @param pFile File which we're to write to (append). * Appends at current posistion. * @param enmOS The target OS of the link operation. * @sketch * @status * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no) * @remark */ KBOOL kFileDef::makeWatcomLinkFileAddtion(kFile *pOut, int enmOS) throw(kError) { PDEFSEGMENT pSeg; PDEFIMPORT pImp; PDEFEXPORT pExp; pOut->setThrowOnErrors(); /* * Write a little remark first to tell that converted stuff starts here. */ pOut->printf("#\n# Directives generated from .DEF-file.\n#\n"); /* Format - Module type */ switch (enmOS) { case kFileDef::os2: pOut->printf("FORMAT OS2 LX %s %s %s\n", fLibrary ? "DLL" : (fProgram ? (chAppType == pm ? "PM" : (chAppType == fullscreen ? "FULLSCREEN" : "PMCOMPATIBLE")) : (fVirtualDevice ? "VIRTDEVICE" : "PHYSDEVICE" )), fLibrary ? (fInitGlobal || (!fInitInstance && !fTermInstance) ? "INITGLOBAL" : "INITINSTANCE") : "", fLibrary ? (fTermGlobal || (!fTermInstance && !fInitInstance) ? "TERMGLOBAL" : "TERMINSTANCE") : ""); break; case kFileDef::os2v1: pOut->printf("FORMAT OS2 %s %s %s\n", fLibrary ? "DLL" : (fProgram ? (chAppType == pm ? "PM" : (chAppType == fullscreen ? "FULLSCREEN" : "PMCOMPATIBLE")) : (fVirtualDevice ? "VIRTDEVICE" : "PHYSDEVICE" )), fLibrary ? (fInitGlobal || fTermGlobal ? "INITGLOBAL" /* Not sure how correct these things are! */ : "") : "", /*fLibrary ? (fTermGlobal || fInitGlobal ? "TERMGLOBAL" : "") :*/ ""); break; case kFileDef::win32: if (fLibrary) pOut->printf("FORMAT Window NT DLL %s %s\n" "Runtime Windows\n", fLibrary ? (fInitGlobal ? "INITGLOBAL" : "INITINSTANCE") : "", fLibrary ? (fTermGlobal ? "TERMGLOBAL" : "TERMINSTANCE") : ""); else pOut->printf("FORMAT Window NT\n" "Runtime %s\n", fProgram ? chAppType == pm ? "Windows" : "Console" : "Native"); break; default: return FALSE; } /* Module name */ if (pszModName) pOut->printf("OPTION MODNAME=%s\n", pszModName); /* Description */ if (pszDescription) pOut->printf("OPTION DESCRIPTION '%s'\n", pszDescription); /* Base */ if (pszBase) pOut->printf("OPTION OFFSET=%s\n", pszBase); /* Stub */ if (pszStub) pOut->printf("OPTION STUB='%s'\n", pszStub); /* Old */ if (pszOld) pOut->printf("OPTION OLDLIBRARY=%s\n", pszOld); /* Protected mode */ if (pszProtmode && (enmOS == kFileDef::os2)) pOut->printf("OPTION PROTMODE\n", pszProtmode); /* Stacksize */ if (pszStackSize) pOut->printf("OPTION STACK=%s\n", pszStackSize); /* HeapSize */ if (pszHeapSize) pOut->printf("OPTION HEAPSIZE=%s\n", pszHeapSize); /* Code - not supported */ /* Data - not supported */ /* * Segments. */ pSeg = pSegments; while (pSeg != NULL) { if (pSeg->pszName) pOut->printf("SEGMENT '%s' %s\n", pSeg->pszName, pSeg->pszAttr ? pSeg->pszAttr : ""); if (pSeg->pszClass) pOut->printf("SEGMENT CLASS '%s' %s\n", pSeg->pszClass, pSeg->pszAttr ? pSeg->pszAttr : ""); pSeg = pSeg->pNext; } /* * Imports. */ pImp = pImports; while (pImp != NULL) { if (pImp->pszName == NULL) pOut->printf("IMPORT '%s' '%s'.%d\n", pImp->pszIntName, pImp->pszDll, pImp->ulOrdinal); else pOut->printf("IMPORT '%s' '%s'.'%s'\n", pImp->pszIntName, pImp->pszDll, pImp->pszName); pImp = pImp->pNext; } /* * Exports. */ pExp = pExports; while (pExp != NULL) { pOut->printf("EXPORT '%s'", pExp->pszName); if (pExp->ulOrdinal != ~0UL) pOut->printf(".%d", pExp->ulOrdinal); if (pExp->pszIntName) pOut->printf("='%s'", pExp->pszIntName); if (pExp->fResident && (enmOS == kFileDef::os2 || enmOS == kFileDef::win16)) pOut->printf(" RESIDENT"); if (pExp->cParam != ~0UL) pOut->printf(" %d", pExp->cParam * 2); /* .DEFs this is number of words. Watcom should have bytes. */ pOut->printf("\n"); pExp = pExp->pNext; } return TRUE; } /** * Removes '"' and ''' at start and end of the string. * @returns pszStr! * @param pszStr String that is to have "s and 's removed. */ static char *removeFnutts(char *pszStr) { if (pszStr != NULL) { int cch = strlen(pszStr); if (cch > 0 && (pszStr[cch-1] == '"' || pszStr[cch-1] == '\'')) pszStr[cch-1] = '\0'; if (*pszStr == '"' || *pszStr == '\'') memmove(pszStr, pszStr+1, cch-1); } return pszStr; } /** * Upcases a char. * @returns Upper case of the char given in ch. * @param ch Char to capitalize. */ inline char upcase(char ch) { return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch; } /** * Searches for a substring in a string. * @returns Pointer to start of substring when found, NULL when not found. * @param pszStr String to be searched. * @param pszSubStr String to be searched. * @remark Depends on the upcase function. */ static char *stristr(const char *pszStr, const char *pszSubStr) { int cchSubStr = strlen(pszSubStr); int i = 0; while (*pszStr != '\0' && i < cchSubStr) { i = 0; while (i < cchSubStr && pszStr[i] != '\0' && (upcase(pszStr[i]) == upcase(pszSubStr[i]))) i++; pszStr++; } return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL); }