#include #include #include #include "stringbuf.h" #include "rpmbuild.h" #include "xml2rpm.h" #include "xmlstruct.h" #ifdef XML_DEBUG #include "header_internal.h" #endif // This is where our packaged scripts start (or the largest number // of the sources) int g_nMaxSourceNum = 511; static struct ReqComp { const char* m_szCmp; rpmsenseFlags m_rpmCmp; } sReqComp[] = { {"le", RPMSENSE_LESS | RPMSENSE_EQUAL }, {"lt", RPMSENSE_LESS }, {"eq", RPMSENSE_EQUAL }, {"ge", RPMSENSE_GREATER | RPMSENSE_EQUAL}, {"gt", RPMSENSE_GREATER }, {NULL, 0 } }; Spec g_pSpec = NULL; const char* g_szDefLang = "C"; #ifdef XML_DEBUG void printSourceInfo(struct Source* pSource) { if (pSource) { printf("D: Source (%08x){\n", (unsigned int)pSource); printf("D: Source->fullSource (%08x): {%s}\n", (unsigned int)(pSource->fullSource), pSource->fullSource); printf("D: Source->source (%08x): {%s}\n", (unsigned int)(pSource->source), pSource->source); printf("D: Source->flags : {%08x}\n", pSource->flags); printf("D: Source->num : {%d}\n", pSource->num); printf("D: }\n"); } } #endif #ifdef XML_DEBUG void printHeaderInfo(Header pHeader) { int i = 0; int j = 0; int nVal = 0; indexEntry pIdx; const struct headerTagTableEntry_s* pEntry; char* pData; int nCnt; char szType[20]; if (pHeader) { printf("D: Header->indexUsed : {%d}\n", pHeader->indexUsed); printf("D: Header->indexAlloced : {%d}\n", pHeader->indexAlloced); printf("D: Header->flags : {%d}\n", pHeader->flags); printf("D: Header->nrefs : {%d}\n", pHeader->nrefs); printf("D: Header->hv {\n"); printf("D: HV.hdrvecs : {%08x}\n", (unsigned int)(pHeader->hv.hdrvecs)); printf("D: HV.hdrdata : {%08x}\n", (unsigned int)(pHeader->hv.hdrdata)); printf("D: HV.hdrversion : {%d}\n", pHeader->hv.hdrversion); printf("D: }\n"); printf("D: Header->blob (%08x): {\n", (unsigned int)(pHeader->blob)); pIdx = pHeader->index; for (i = 0; i < pHeader->indexUsed; i++, pIdx++) { pEntry = rpmTagTable; while (pEntry->name && pEntry->val != pIdx->info.tag) pEntry++; nCnt = pIdx->info.count; pData = pIdx->data; switch (pIdx->info.type) { case RPM_STRING_TYPE: sprintf(szType, "STRING"); break; case RPM_STRING_ARRAY_TYPE: sprintf(szType, "STRING_ARRAY"); break; case RPM_I18NSTRING_TYPE: sprintf(szType, "STRING_I18N"); break; case RPM_CHAR_TYPE: sprintf(szType, "CHAR"); break; case RPM_INT8_TYPE: sprintf(szType, "INT8"); break; case RPM_INT16_TYPE: sprintf(szType, "INT16"); break; case RPM_INT32_TYPE: sprintf(szType, "INT32"); break; default: sprintf(szType, "OTHER"); break; } printf("D: %02d %s(%02d) %s\n", i, pEntry->name, nCnt, szType); switch (pIdx->info.type) { case RPM_STRING_TYPE: case RPM_STRING_ARRAY_TYPE: case RPM_I18NSTRING_TYPE: for (j = 0; j < nCnt; j++) { if (strlen(pData)) printf("D: %02d {%s}\n", j, pData); pData = strchr(pData, 0); pData++; } break; case RPM_CHAR_TYPE: case RPM_INT8_TYPE: case RPM_INT16_TYPE: case RPM_INT32_TYPE: for (j = 0; j < nCnt; j++) { switch (pIdx->info.type) { case RPM_CHAR_TYPE: case RPM_INT8_TYPE: nVal = *(((int_8*)pData)+j); break; case RPM_INT16_TYPE: nVal = *(((uint_16*)pData)+j); break; case RPM_INT32_TYPE: default: nVal = *(((int_32*)pData)+j); break; } printf("D: %02d {%08x}\n", j, nVal); } break; default: break; } } printf("D: }\n"); } } #endif #ifdef XML_DEBUG void printMacroInfo(MacroContext pMacro) { int i = 0; MacroEntry pEntry = NULL; if (pMacro) { for (i = 0; i < pMacro->firstFree; i++) { if ((pEntry = pMacro->macroTable[i]) && (pEntry->level >= RMIL_SPEC)) { printf("D: %03d % 3d %s", i, pEntry->level, pEntry->name); if ((pEntry->opts) && (*pEntry->opts)) printf("(%s)", pEntry->opts); printf("\n"); if ((pEntry->level >= RMIL_SPEC) && (pEntry->body) && (*pEntry->body)) printf("D: {%s}\n", pEntry->body); } } } } #endif #ifdef XML_DEBUG void printSpecInfo(Spec pSpec) { struct Source* pSource = NULL; struct PackageStruct* pPackage = NULL; printf("D: Spec(%08x) {\n", (unsigned int)pSpec); if (pSpec) { printf("D: Spec->specfile (%08x): {%s}\n", (unsigned int)(pSpec->specFile), pSpec->specFile); printf("D: Spec->sourceRpmName (%08x): {%s}\n", (unsigned int)(pSpec->sourceRpmName), pSpec->sourceRpmName); printf("D: Spec->buildRootURL (%08x): {%s}\n", (unsigned int)(pSpec->buildRootURL), pSpec->buildRootURL); printf("D: Spec->buildSubdir (%08x): {%s}\n", (unsigned int)(pSpec->buildSubdir), pSpec->buildSubdir); printf("D: Spec->rootURL (%08x): {%s}\n", (unsigned int)(pSpec->rootURL), pSpec->rootURL); printf("D: Spec->BACount : {%d}\n", pSpec->BACount); printf("D: Spec->recursing : {%d}\n", pSpec->recursing); printf("D: Spec->force : {%d}\n", pSpec->force); printf("D: Spec->anyarch : {%d}\n", pSpec->anyarch); printf("D: Spec->gotBuildRootURL : {%d}\n", pSpec->gotBuildRootURL); printf("D: Spec->timeCheck : {%d}\n", pSpec->timeCheck); printf("D: Spec->passPhrase (%08x): {%s}\n", (unsigned int)(pSpec->passPhrase), pSpec->passPhrase); printf("D: Spec->cookie (%08x): {%s}\n", (unsigned int)(pSpec->cookie), pSpec->cookie); printf("D: Spec->numSources : {%d}\n", pSpec->numSources); printf("D: Spec->noSource : {%d}\n", pSpec->noSource); printf("D: Spec->sources : (%08x)\n", (unsigned int)(pSpec->sources)); pSource = pSpec->sources; while (pSource) { printSourceInfo(pSource); pSource = pSource->next; } printf("D: Spec->buildRestrictions (%08x){\n", (unsigned int)(pSpec->buildRestrictions)); printHeaderInfo(pSpec->buildRestrictions); printf("D: }\n"); printf("D: Spec->sourceHeader (%08x){\n", (unsigned int)(pSpec->sourceHeader)); printHeaderInfo(pSpec->sourceHeader); printf("D: }\n"); printf("D: Spec->packages : {%08x}\n", (unsigned int)(pSpec->packages)); pPackage = pSpec->packages; while (pPackage) { printf("D: Package(%08x) {\n", (unsigned int)pPackage); printf("D: Package->cpioList : {%08x}\n", (unsigned int)(pPackage->cpioList)); printf("D: Package->icon : {%08x}\n", (unsigned int)(pPackage->icon)); printf("D: Package->autoReq : {%d}\n", pPackage->autoReq); printf("D: Package->autoProv : {%d}\n", pPackage->autoProv); printf("D: Package->preInFile (%08x): {%s}\n", (unsigned int)(pPackage->preInFile), pPackage->preInFile); printf("D: Package->postInFile (%08x): {%s}\n", (unsigned int)(pPackage->postInFile), pPackage->postInFile); printf("D: Package->preUnFile (%08x): {%s}\n", (unsigned int)(pPackage->preUnFile), pPackage->preUnFile); printf("D: Package->postUnFile (%08x): {%s}\n", (unsigned int)(pPackage->postUnFile), pPackage->postUnFile); printf("D: Package->triggerFiles : {%08x}\n", (unsigned int)(pPackage->triggerFiles)); printf("D: Package->fileFile (%08x): {%s}\n", (unsigned int)(pPackage->fileFile), pPackage->fileFile); printf("D: Package->fileFile (%08x): {%s}\n", (unsigned int)(pPackage->fileList), pPackage->fileList ? getStringBuf(pPackage->fileList) : NULL); printf("D: Package->header (%08x){\n", (unsigned int)(pPackage->header)); printHeaderInfo(pPackage->header); printf("D: }\n"); pPackage = pPackage->next; printf("D: }\n"); } printf("D: Spec->macros (%08x){\n", (unsigned int)(pSpec->macros)); printMacroInfo(pSpec->macros); printf("D: }\n"); printf("D: Spec->prep (%08x): {%s}\n", (unsigned int)(pSpec->prep), pSpec->prep ? getStringBuf(pSpec->prep) : NULL); printf("D: Spec->build (%08x): {%s}\n", (unsigned int)(pSpec->build), pSpec->build ? getStringBuf(pSpec->build) : NULL); printf("D: Spec->install (%08x): {%s}\n", (unsigned int)(pSpec->install), pSpec->install ? getStringBuf(pSpec->install) : NULL); printf("D: Spec->clean (%08x): {%s}\n", (unsigned int)(pSpec->clean), pSpec->clean ? getStringBuf(pSpec->clean) : NULL); } printf("D: }\n"); } #endif void createRPMSource(const char* szName, int nNum, Spec pSpec, int nType) { struct Source* pSrc = NULL; struct Source* pPrev = NULL; char szTmpName[20]; char szPre[3]; char szType[7]; if (!szName || !pSpec) return; if ((nType & RPMBUILD_ISNO) == RPMBUILD_ISNO) { pSpec->noSource++; sprintf(szPre, "NO"); } else { pSpec->numSources++; szPre[0] = '\0'; } pSrc = malloc(sizeof(struct Source)); pSrc->fullSource = NULL; newStrEx(szName, &(pSrc->fullSource)); if (!(pSrc->source = strrchr(pSrc->fullSource, '/'))) pSrc->source = pSrc->fullSource; else pSrc->source++; pSrc->num = nNum; pSrc->flags = nType; pSrc->next = NULL; if ((pPrev = pSpec->sources)) { while (pPrev->next) pPrev = pPrev->next; pPrev->next = pSrc; } else pSpec->sources = pSrc; if ((nType & RPMBUILD_ISSOURCE) == RPMBUILD_ISSOURCE) sprintf(szType, "SOURCE"); else sprintf(szType, "PATCH"); sprintf(szTmpName, "%s%s%d", szPre, szType, nNum); addMacro(pSpec->macros, szTmpName, NULL, pSrc->fullSource, RMIL_SPEC); sprintf(szTmpName, "%s%sURL%d", szPre, szType, nNum); addMacro(pSpec->macros, szTmpName, NULL, pSrc->source, RMIL_SPEC); if (nNum > g_nMaxSourceNum) g_nMaxSourceNum = nNum; } void convertXMLSource(const t_structXMLSource* pXMLSrc, Spec pSpec, int nType) { if (!pXMLSrc || !pSpec) return; createRPMSource(pXMLSrc->m_szName, pXMLSrc->m_nNum, pSpec, nType); convertXMLSource(pXMLSrc->m_pNext, pSpec, nType); } StringBuf scriptsToStringBuf(const t_structXMLScripts* pScripts, Spec pSpec) { StringBuf pSb = NULL; t_structXMLScript* pScript = NULL; char* szTmp = NULL; if (pScripts) { pSb = newStringBuf(); if ((pScripts->m_szScript) && (szTmp = fileToStr(pScripts->m_szScript, NULL))) { appendLineStringBuf(pSb, szTmp); freeStr(&(szTmp)); createRPMSource(pScripts->m_szScript, g_nMaxSourceNum+1, pSpec, RPMBUILD_ISSOURCE); } pScript = pScripts->m_pScripts; while (pScript) { if ((pScript->m_szScript) && (szTmp = fileToStr(pScript->m_szScript, NULL))) { appendLineStringBuf(pSb, szTmp); freeStr(&(szTmp)); createRPMSource(pScript->m_szScript, g_nMaxSourceNum+1, pSpec, RPMBUILD_ISSOURCE); } if (pScript->m_szEntry) { // we do a newStrEx to make sure we expand all // macros, as is done in the case of the files // (done as part of the fileToStr call) newStrEx(pScript->m_szEntry, (char**)(&szTmp)); appendLineStringBuf(pSb, szTmp); freeStr(&(szTmp)); } pScript = pScript->m_pNext; } } return pSb; } void convertXMLScripts(const t_structXMLScripts* pScripts, struct PackageStruct* pPkg, Spec pSpec, int nScript, int nInterpreter) { StringBuf pSb = NULL; if (!pScripts || !pPkg) return; if ((pSb = scriptsToStringBuf(pScripts, pSpec))) { headerAddEntry(pPkg->header, nScript, RPM_STRING_TYPE, getStringBuf(pSb), 1); freeStringBuf(pSb); } if (pScripts->m_szInterpreter) headerAddEntry(pPkg->header, nInterpreter, RPM_STRING_TYPE, pScripts->m_szInterpreter, 1); } void handleProvObsConf(Header pHeader, t_structXMLRequire* pReqs, rpmsenseFlags fSense) { t_structXMLRequire* pReq = NULL; int nFlags = 0; int i = 0; int nIndex = 0; pReq = pReqs; while (pReq) { if (pReq->m_szName) { nFlags = 0; if (pReq->m_szCompare && pReq->m_szVersion) { for (i = 0; sReqComp[i].m_szCmp; i++) { if (!strcasecmp(sReqComp[i].m_szCmp, pReq->m_szCompare)) { nFlags = (sReqComp[i].m_rpmCmp | RPMSENSE_ANY) & ~RPMSENSE_SENSEMASK; } } } addReqProv(NULL, pHeader, fSense | nFlags, pReq->m_szName, pReq->m_szVersion, nIndex++); } pReq = pReq->m_pNext; } } void convertXMLPackage(const t_structXMLPackage* pXMLPkg, const t_structXMLSpec* pXMLSpec, Spec pSpec) { struct PackageStruct* pPkg = NULL; char* szPlatform = NULL; char* szArch = NULL; char* szOs = NULL; char* szTmp = NULL; if (!pXMLPkg || !pSpec) return; pPkg = newPackage(pSpec); pPkg->autoReq = pXMLPkg->m_nAutoRequire; pPkg->autoProv = pXMLPkg->m_nAutoProvide; pPkg->header = headerNew(); szPlatform = rpmExpand("%{_target_platform}", NULL); szArch = rpmExpand("%{_target_cpu}", NULL); szOs = rpmExpand("%{_target_os}", NULL); headerAddOrAppendEntry(pPkg->header, RPMTAG_OS, RPM_STRING_TYPE, szOs, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_ARCH, RPM_STRING_TYPE, szArch, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_RHNPLATFORM, RPM_STRING_TYPE, szArch, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_PLATFORM, RPM_STRING_TYPE, szPlatform, 1); _free(szPlatform); _free(szArch); _free(szOs); // do the provides, obsoletes, conflicts handleProvObsConf(pPkg->header, pXMLPkg->m_pProvides, RPMSENSE_PROVIDES); handleProvObsConf(pPkg->header, pXMLPkg->m_pObsoletes, RPMSENSE_OBSOLETES); handleProvObsConf(pPkg->header, pXMLPkg->m_pConflicts, RPMSENSE_CONFLICTS); if (pXMLPkg->m_szName) headerAddOrAppendEntry(pPkg->header, RPMTAG_NAME, RPM_STRING_TYPE, pXMLPkg->m_szName, 1); else headerAddOrAppendEntry(pPkg->header, RPMTAG_NAME, RPM_STRING_TYPE, pXMLSpec->m_szName, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_VERSION, RPM_STRING_TYPE, pXMLSpec->m_szVersion, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_RELEASE, RPM_STRING_TYPE, pXMLSpec->m_szRelease, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_GROUP, RPM_STRING_TYPE, pXMLPkg->m_szGroup, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_RELEASE, RPM_STRING_TYPE, pXMLSpec->m_szRelease, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_LICENSE, RPM_STRING_TYPE, pXMLSpec->m_szCopyright, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_URL, RPM_STRING_TYPE, pXMLSpec->m_szURL, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_DISTRIBUTION, RPM_STRING_TYPE, pXMLSpec->m_szDistribution, 1); headerAddOrAppendEntry(pPkg->header, RPMTAG_VENDOR, RPM_STRING_TYPE, pXMLSpec->m_szVendor, 1); headerAddOrAppendEntry(pPkg->header, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, &g_szDefLang, 1); headerAddI18NString(pPkg->header, RPMTAG_SUMMARY, pXMLPkg->m_szSummary, g_szDefLang); headerAddI18NString(pPkg->header, RPMTAG_DESCRIPTION, pXMLPkg->m_szSummary, g_szDefLang); if (pXMLSpec->m_szPackagerEmail || pXMLSpec->m_szPackager) { szTmp = malloc(strlen(pXMLSpec->m_szPackager)+strlen(pXMLSpec->m_szPackagerEmail)+4); if (pXMLSpec->m_szPackagerEmail && pXMLSpec->m_szPackager) sprintf(szTmp, "%s <%s>", pXMLSpec->m_szPackager, pXMLSpec->m_szPackagerEmail); else sprintf(szTmp, "%s", pXMLSpec->m_szPackager ? pXMLSpec->m_szPackager : pXMLSpec->m_szPackagerEmail); headerAddOrAppendEntry(pPkg->header, RPMTAG_PACKAGER, RPM_STRING_TYPE, szTmp, 1); free(szTmp); } if (pXMLPkg->m_pFiles) pPkg->fileList = fileToStrBuf(pXMLPkg->m_pFiles->m_szFileList, NULL); convertXMLScripts(pXMLPkg->m_pPre, pPkg, pSpec, RPMTAG_PREIN, RPMTAG_PREINPROG); convertXMLScripts(pXMLPkg->m_pPost, pPkg, pSpec, RPMTAG_POSTIN, RPMTAG_POSTINPROG); convertXMLScripts(pXMLPkg->m_pPreUn, pPkg, pSpec, RPMTAG_PREUN, RPMTAG_PREUNPROG); convertXMLScripts(pXMLPkg->m_pPostUn, pPkg, pSpec, RPMTAG_POSTUN, RPMTAG_POSTUNPROG); convertXMLScripts(pXMLPkg->m_pVerify, pPkg, pSpec, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG); convertXMLPackage(pXMLPkg->m_pNext, pXMLSpec, pSpec); } void convertXMLMacro(const t_structXMLMacro* pMacro, Spec pSpec) { if (!pMacro || !pSpec) return; addMacroEx(pMacro->m_szName, (char**)&(pMacro->m_szValue), 1); convertXMLMacro(pMacro->m_pNext, pSpec); } void convertXMLBuildRequires(const t_structXMLSpec* pXMLSpec, Spec pSpec) { t_structXMLRequire* pRequire = NULL; char* szTmp = NULL; char* szVal = NULL; int i = 0; int nFlags = 0; int nIndex = 0; char* szVersion = NULL; pRequire = pXMLSpec->m_pBuildRequires; szVal = malloc(1); szVal[0] = '\0'; while (pRequire) { if (pRequire->m_szName) { szTmp = malloc(strlen(szVal)+strlen(pRequire->m_szName)+2); if (strlen(szVal)) sprintf(szTmp, "%s", pRequire->m_szName); else sprintf(szTmp, "%s %s", szVal, pRequire->m_szName); free(szVal); szVal = szTmp; } szVersion = NULL; nFlags = 0; if (pRequire->m_szCompare && pRequire->m_szVersion) { for (i = 0; sReqComp[i].m_szCmp; i++) if (!strcasecmp(sReqComp[i].m_szCmp, pRequire->m_szCompare)) { nFlags = (sReqComp[i].m_rpmCmp | RPMSENSE_ANY) & ~RPMSENSE_SENSEMASK; szVersion = pRequire->m_szVersion; } } addReqProv(pSpec, pSpec->buildRestrictions, nFlags, pRequire->m_szName, pRequire->m_szVersion, nIndex); addReqProv(pSpec, pSpec->sourceHeader, nFlags, pRequire->m_szName, pRequire->m_szVersion, nIndex); pRequire = pRequire->m_pNext; nIndex++; } if (pXMLSpec->m_szPackagerEmail || pXMLSpec->m_szPackager) { szTmp = malloc(strlen(pXMLSpec->m_szPackager)+strlen(pXMLSpec->m_szPackagerEmail)+4); if (pXMLSpec->m_szPackagerEmail && pXMLSpec->m_szPackager) sprintf(szTmp, "%s <%s>", pXMLSpec->m_szPackager, pXMLSpec->m_szPackagerEmail); else sprintf(szTmp, "%s", pXMLSpec->m_szPackager ? pXMLSpec->m_szPackager : pXMLSpec->m_szPackagerEmail); headerAddOrAppendEntry(pSpec->packages->header, RPMTAG_PACKAGER, RPM_STRING_TYPE, szTmp, 1); free(szTmp); } free(szVal); } Spec toRPMStruct(const t_structXMLSpec* pXMLSpec) { Spec pSpec = NULL; char* szTmp = NULL; if (!pXMLSpec) return NULL; pSpec = g_pSpec; newStrEx(pXMLSpec->m_szSpecName, (char**)&(pSpec->specFile)); if (pXMLSpec->m_pPackages) { addMacroEx("group", (char**)&(pXMLSpec->m_pPackages->m_szGroup), RMIL_SPEC); addMacroEx("summary", (char**)&(pXMLSpec->m_pPackages->m_szSummary), RMIL_SPEC); } pSpec->gotBuildRootURL = 1; newStrEx(pXMLSpec->m_szBuildRootDir, (char**)(&(pSpec->buildRootURL))); newStrEx(pXMLSpec->m_szBuildSubdir, (char**)(&(pSpec->buildSubdir))); newStrEx(pXMLSpec->m_szRootDir, (char**)(&(pSpec->rootURL))); convertXMLSource(pXMLSpec->m_pSources, pSpec, RPMBUILD_ISSOURCE); convertXMLSource(pXMLSpec->m_pPatches, pSpec, RPMBUILD_ISPATCH); pSpec->prep = scriptsToStringBuf(pXMLSpec->m_pPrep, pSpec); pSpec->build = scriptsToStringBuf(pXMLSpec->m_pBuild, pSpec); pSpec->install = scriptsToStringBuf(pXMLSpec->m_pInstall, pSpec); pSpec->clean = scriptsToStringBuf(pXMLSpec->m_pClean, pSpec); convertXMLPackage(pXMLSpec->m_pPackages, pXMLSpec, pSpec); initSourceHeader(pSpec); if (pXMLSpec->m_szPackagerEmail || pXMLSpec->m_szPackager) { szTmp = malloc(strlen(pXMLSpec->m_szPackager)+strlen(pXMLSpec->m_szPackagerEmail)+4); if (pXMLSpec->m_szPackagerEmail && pXMLSpec->m_szPackager) sprintf(szTmp, "%s <%s>", pXMLSpec->m_szPackager, pXMLSpec->m_szPackagerEmail); else sprintf(szTmp, "%s", pXMLSpec->m_szPackager ? pXMLSpec->m_szPackager : pXMLSpec->m_szPackagerEmail); headerAddOrAppendEntry(pSpec->sourceHeader, RPMTAG_PACKAGER, RPM_STRING_TYPE, szTmp, 1); free(szTmp); } pSpec->buildRestrictions = headerNew(); convertXMLBuildRequires(pXMLSpec, pSpec); #ifdef XML_DEBUG printSpecInfo(pSpec); #endif return pSpec; }