/*! \file namevaluetree.c * \brief Implemetation of the NameValueTree helper object * (includes XML parser). * * \author Rainer Gerhards * \date 2003-08-04 * Initial version as part of liblogging 0.2 begun. * * Copyright 2002-2003 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Adiscon GmbH or Rainer Gerhards * nor the names of its contributors may be used to * endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "config.h" #include "liblogging.h" #include "namevaluetree.h" #include "stringbuf.h" /* ################################################################# * * private members * * ################################################################# */ static srRetVal sbNVTXMLProcessXMLSTREAM(char **ppXML, sbNVTRObj *pRoot); /** * We can not use the usual sbNVTSearchKeySZ() method in this * destructor, as we would invalidate the previous pointer while * walking & deleting the list. So we need to walk it by using * the raw pointers. */ void sbNVTRDestroy(sbNVTRObj* pThis) { sbNVTEObj* pEntry; sbNVTEObj* pNext; sbNVTRCHECKVALIDOBJECT(pThis); /* debug: printf("sbNVTRDestroy: %8.8x\n", pThis); */ pEntry = pThis->pFirst; while(pEntry != NULL) { pNext = pEntry->pNext; sbNVTEDestroy(pEntry); pEntry = pNext; } /* unlink from parent! */ sbNVTRUnlinkFromParent(pThis); SRFREEOBJ(pThis); } void sbNVTEDestroy(sbNVTEObj* pThis) { sbNVTECHECKVALIDOBJECT(pThis); /* debug: printf("sbNVTEDestroy: %8.8x\n", pThis); */ if(pThis->pszKey != NULL) free(pThis->pszKey); if(pThis->pszValue != NULL) free(pThis->pszValue); if(pThis->pCDATA != NULL) free(pThis->pCDATA); if(pThis->pXMLProps != NULL) sbNVTRDestroy(pThis->pXMLProps); if(pThis->pChild != NULL) sbNVTRDestroy(pThis->pChild); if(pThis->pUsr != NULL) (pThis->pUsrDestroy)(pThis->pUsr); SRFREEOBJ(pThis); } /** * Remove WHITESPACE (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. */ static void sbNVTXMLEatWHITESPACE(char **ppXML) { assert(ppXML != NULL); while(isspace(**ppXML)) (*ppXML)++; } /** * Retrieve an ESCSEQ (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. * \retval pointer to text of ESCSEQ or NULL, if a memory allocation error occured. */ static char* sbNTVXMLReadESCSEQ(char **ppXML) { sbStrBObj *pStrB; assert(ppXML != NULL); if((pStrB = sbStrBConstruct()) == NULL) return NULL; sbStrBSetAllocIncrement(pStrB, 16); while( (**ppXML != ';') && (**ppXML != '\0')) { sbStrBAppendChar(pStrB, **ppXML); (*ppXML)++; } if(**ppXML != '\0') (*ppXML)++; /* need to eat the ";" */ return sbStrBFinish(pStrB); } /** * Retrieve an XMLVALUE (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. * \param cTerm [in] terminating character. This is the counterpart to the * character the initiated the call to XMLVALUE. With BEEP, * we expect it to be ', but it may also be ". * \retval pointer to XMLVALUE or NULL, if an error occured. */ static char* sbNVTXMLReadXMLVALUE(char **ppXML, char cTerm) { sbStrBObj *pStrB; char *pszESC; assert(ppXML != NULL); if((pStrB = sbStrBConstruct()) == NULL) return NULL; while( (**ppXML != cTerm) && (**ppXML != '\0')) { if(**ppXML == '&') { /* we have an escape sequence, need to handle this */ (*ppXML)++; /* eat the "&" */ if((pszESC = sbNTVXMLReadESCSEQ(ppXML)) == NULL) return NULL; if(!strcmp(pszESC, "gt")) sbStrBAppendChar(pStrB, '>'); else if(!strcmp(pszESC, "lt")) sbStrBAppendChar(pStrB, '<'); else if(!strcmp(pszESC, "amp")) sbStrBAppendChar(pStrB, '&'); else if(!strcmp(pszESC, "apos")) sbStrBAppendChar(pStrB, ';'); else if(!strcmp(pszESC, "quot")) sbStrBAppendChar(pStrB, '"'); else { free(pszESC); return NULL; } free(pszESC); } else { /* just a plain character */ sbStrBAppendChar(pStrB, **ppXML); (*ppXML)++; } } return sbStrBFinish(pStrB); } /** * Retrieve an XMLNAME (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. * \retval pointer to XMLNAME or NULL, if a memory allocation error occured. */ static char* sbNVTXMLReadXMLNAME(char **ppXML) { sbStrBObj *pStrB; assert(ppXML != NULL); if((pStrB = sbStrBConstruct()) == NULL) return NULL; sbStrBSetAllocIncrement(pStrB, 64); while( !isspace(**ppXML) && (**ppXML != '\0') && (**ppXML != '<') && (**ppXML != '>') && (**ppXML != '=') && (**ppXML != ';') && (**ppXML != '/')) { sbStrBAppendChar(pStrB, **ppXML); (*ppXML)++; } return sbStrBFinish(pStrB); } /** * Retrieve a CDATAVALUE (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. * \retval pointer to CDATA value or NULL, if a memory allocation error occured. */ static char* sbNVTXMLReadCDATAVALUE(char **ppXML) { sbStrBObj *pStrB; assert(ppXML != NULL); if((pStrB = sbStrBConstruct()) == NULL) return NULL; while( !isspace(**ppXML) && (**ppXML != '\0') && (**ppXML != ']')) { sbStrBAppendChar(pStrB, **ppXML); (*ppXML)++; } return sbStrBFinish(pStrB); } /** * Retrieve a TAG (see ABNF \ref sbNVTRParseXML) from the XML stream. * * \param pp [in/out] pointer to XML stream. Updated to reflect new position. * \retval pointer to XMLNAME or NULL, if a memory allocation error occured. */ static char* sbNVTXMLReadTAG(char **ppXML) { return(sbNVTXMLReadXMLNAME(ppXML)); } /** * Process a PARAM (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * \param pRoot Pointer to the list the element is to be added to. */ static srRetVal sbNVTXMLProcessPARAM(char **ppXML, sbNVTRObj *pRoot) { char* pName; char *pValue; char cTerm; sbNVTEObj *pEntry; assert(ppXML != NULL); sbNVTRCHECKVALIDOBJECT(pRoot); if((pName = sbNVTXMLReadXMLNAME(ppXML)) == NULL) return SR_RET_OUT_OF_MEMORY; /* check if we have a value and, if yes, retrieve it */ if(**ppXML == '=') { (*ppXML)++; /* eat "=" */ if(**ppXML == '\'') cTerm = '\''; else if(**ppXML == '"') cTerm = '"'; else return SR_RET_XML_INVALID_PARAMTAG; (*ppXML)++; /* eat delimiter */ if((pValue = sbNVTXMLReadXMLVALUE(ppXML, cTerm)) == NULL) return SR_RET_OUT_OF_MEMORY; /* check terminating character */ if(**ppXML != cTerm) return SR_RET_XML_INVALID_TERMINATOR; (*ppXML)++; /* eat terminating char */ } else pValue = NULL; /* OK, we have name and value. Now let's add it to the list. */ if((pEntry = sbNVTAddEntry(pRoot)) == NULL) return SR_RET_OUT_OF_MEMORY; /* the string buffers are handed over to the list, * so they MUST NOT be freed. */ sbNVTESetKeySZ(pEntry, pName, FALSE); sbNVTESetValueSZ(pEntry, pValue, FALSE); return SR_RET_OK; } /** * Process a TAGwPARAMS (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * \param pEntry Pointer to the current list entry. */ static srRetVal sbNVTXMLProcessTAGwPARAMS(char **ppXML, sbNVTEObj *pEntry) { char *pTag; sbNVTRObj *pParamRoot = NULL; assert(ppXML != NULL); sbNVTECHECKVALIDOBJECT(pEntry); if((pTag = sbNVTXMLReadTAG(ppXML)) == NULL) return SR_RET_OUT_OF_MEMORY; sbNVTESetKeySZ(pEntry, pTag, FALSE); sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ /* now check if we have params */ while((**ppXML != '\0') && (**ppXML != '/') && (**ppXML != '>')) { /* OK, we have params, so now let's retrieve * and store them. We need to create a new list * root for the parameter tree now if we do not * have one already. */ if(pParamRoot == NULL) if((pParamRoot = sbNVTRConstruct()) == NULL) return SR_RET_OUT_OF_MEMORY; pEntry->pXMLProps = pParamRoot; sbNVTXMLProcessPARAM(ppXML, pParamRoot); sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ } return SR_RET_OK; } srRetVal sbNVTXMLEscapePCDATAintoStrB(char *pszToEscape, sbStrBObj *pStr) { srRetVal iRet; sbSTRBCHECKVALIDOBJECT(pStr); if(pszToEscape == NULL) return SR_RET_OK; while(*pszToEscape) { if(*pszToEscape == '<') { if((iRet = sbStrBAppendStr(pStr, "<")) != SR_RET_OK) return iRet; } else if(*pszToEscape == '&') { if((iRet = sbStrBAppendStr(pStr, """)) != SR_RET_OK) return iRet; } else { if((iRet = sbStrBAppendChar(pStr, *pszToEscape)) != SR_RET_OK) return iRet; } pszToEscape++; } return SR_RET_OK; } char* sbNVTXMLEscapePCDATA(char *pszToEscape) { sbStrBObj *pStrBuf; if(pszToEscape == NULL) return NULL; if((pStrBuf = sbStrBConstruct()) == NULL) return NULL; if(sbNVTXMLEscapePCDATAintoStrB(pszToEscape, pStrBuf) != SR_RET_OK) { sbStrBDestruct(pStrBuf); return NULL; } return sbStrBFinish(pStrBuf); } /** * Process a XMLNODE and a XMLCONTAINER (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * \note Due to the syntaxes, we can not distinguish if we have a plain XMLNODE * or a container at this stage. As such, this method checks if we have * a container (no closing tag) and if so, it processes the container. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * \param pEntry Pointer to the current list entry. */ static srRetVal sbNVTXMLProcessXMLNODE(char **ppXML, sbNVTEObj *pEntry) { int iRet; char *pCloseTag; char *pValue; char **ppXMLSave; sbNVTRObj *pChildRoot = NULL; assert(ppXML != NULL); sbNVTECHECKVALIDOBJECT(pEntry); if((iRet = sbNVTXMLProcessTAGwPARAMS(ppXML, pEntry)) != SR_RET_OK) return iRet; if(*((*ppXML)++) == '/') /* we have a plain node */ if(**ppXML == '>') { (*ppXML)++; return SR_RET_OK; } else return SR_RET_MISSING_CLOSE_BRACE; /* We now need to check if we have an XMLVALUE or an XMLCONTAINER. * to do so, we look at the first non-whitespace chartert. If it * is '<', then we have an XMLCONTAINER. If it is not, we have an * XMLVALUE. In the later case, we need to go back to the front of * the whitespace, as the whitespaces, too, belongs to the * XMLVALUE. We use a temporary pointer to do this. */ ppXMLSave = ppXML; sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ if(**ppXML == '<') { /* OK, we have a container */ if((pChildRoot = sbNVTRConstruct()) == NULL) return SR_RET_OUT_OF_MEMORY; if((iRet = sbNVTESetChild(pEntry, pChildRoot)) != SR_RET_OK) return iRet; if((iRet = sbNVTXMLProcessXMLSTREAM(ppXML, pChildRoot)) != SR_RET_OK) return iRet; } else { /* we have an XMLVALUE and need to retrieve that */ ppXML = ppXMLSave; /* restore previous char position */ if((pValue = sbNVTXMLReadXMLVALUE(ppXML, '<')) == NULL) return SR_RET_OUT_OF_MEMORY; if((iRet = sbNVTESetValueSZ(pEntry, pValue, FALSE)) != SR_RET_OK) return iRet; } /* now checking the close tag */ if(**ppXML != '<') return SR_RET_XML_MISSING_CLOSETAG; (*ppXML)++; if(**ppXML != '/') return SR_RET_XML_MISSING_CLOSETAG; (*ppXML)++; /* OK, now retrieve the closing tag name */ sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ if((pCloseTag = sbNVTXMLReadTAG(ppXML)) == NULL) return SR_RET_OUT_OF_MEMORY; if(**ppXML != '>') return SR_RET_MISSING_CLOSE_BRACE; (*ppXML)++; /* eat ">" */ /* check if start and end tag match */ if(strcmp(pEntry->pszKey, pCloseTag)) return SR_RET_XML_TAG_MISMATCH; free(pCloseTag); /* no longer needed */ return SR_RET_OK; } /** * Process a CDATA (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * Needs to point at the first [ in the stream. * \param pEntry Pointer to the current list entry. */ static srRetVal sbNVTXMLProcessCDATA(char **ppXML, sbNVTEObj *pEntry) { assert(ppXML != NULL); sbNVTECHECKVALIDOBJECT(pEntry); /* check header */ if(**ppXML != '[') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != 'C') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != 'D') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != 'A') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != 'T') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != 'A') return SR_RET_XML_INVALID_CDATA_HDR; if(*(++*ppXML) != '[') return SR_RET_XML_INVALID_CDATA_HDR; (*ppXML)++; /* eat '[' */ /* Now retrieve & set CDATA pointer. We should eventually * create a specific method for this, but on the other * hand it is only done in exactly this one case... */ if((pEntry->pCDATA = sbNVTXMLReadCDATAVALUE(ppXML)) == NULL) return SR_RET_OUT_OF_MEMORY; /* check trailer */ if(**ppXML != ']') return SR_RET_XML_INVALID_CDATA_TRAIL; if(*(++*ppXML) != ']') return SR_RET_XML_INVALID_CDATA_TRAIL; if(*(++*ppXML) != '>') return SR_RET_XML_INVALID_CDATA_TRAIL; (*ppXML)++; /* eat '<' */ return SR_RET_OK; } /** * Process a XMLELEMENT (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * \param pEntry Pointer to the current list entry. */ static srRetVal sbNVTXMLProcessXMLELEMENT(char **ppXML, sbNVTEObj *pEntry) { srRetVal iRet; assert(ppXML != NULL); sbNVTECHECKVALIDOBJECT(pEntry); if(**ppXML != '<') return SR_RET_XML_MISSING_OPENTAG; (*ppXML)++; /* eat '<' */ if(**ppXML == '!') { (*ppXML)++; /* eat '!' */ if((iRet = sbNVTXMLProcessCDATA(ppXML, pEntry)) != SR_RET_OK) return iRet; } else if((iRet = sbNVTXMLProcessXMLNODE(ppXML, pEntry)) != SR_RET_OK) return iRet; sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ return SR_RET_OK; } /** * Process a XMLSTREAM (see ABNF \ref sbNVTRParseXML) from the XML stream * and add it to the NameValueTree. * * This creates one new entry element for each entree it finds. * * \param ppXML [in/out] pointer to XML stream. Updated to reflect new position. * \param pRoot Pointer to the current list root. */ static srRetVal sbNVTXMLProcessXMLSTREAM(char **ppXML, sbNVTRObj *pRoot) { srRetVal iRet; sbNVTEObj *pEntry; assert(ppXML != NULL); sbNVTRCHECKVALIDOBJECT(pRoot); while((**ppXML != '\0') && !((**ppXML == '<') && (*(*ppXML + 1)) == '/')) { sbNVTXMLEatWHITESPACE(ppXML); /* eat whitespace, if any */ if(**ppXML == '\0') { return SR_RET_OK; } if((pEntry = sbNVTAddEntry(pRoot)) == NULL) return SR_RET_OUT_OF_MEMORY; if((iRet = sbNVTXMLProcessXMLELEMENT(ppXML, pEntry)) != SR_RET_OK) return iRet; } return SR_RET_OK; } /* ################################################################# * * public members * * ################################################################# */ sbNVTRObj* sbNVTRConstruct(void) { sbNVTRObj *pThis; if((pThis = calloc(1, sizeof(sbNVTRObj))) == NULL) return NULL; pThis->OID = OIDsbNVTR; pThis->pFirst = NULL; pThis->pLast = NULL; pThis->pParent = NULL; /* debug: printf("sbNVTRConstuct: %8.8x\n", pThis); */ return(pThis); } sbNVTEObj* sbNVTEConstruct(void) { sbNVTEObj *pThis; if((pThis = calloc(1, sizeof(sbNVTEObj))) == NULL) return NULL; pThis->OID = OIDsbNVTE; pThis->pCDATA = NULL; pThis->pChild = NULL; pThis->pNext = NULL; pThis->pszKey = NULL; pThis->pszValue = NULL; pThis->pUsr = NULL; pThis->pXMLProps = NULL; pThis->uKey = 0; pThis->uKeyPresent = FALSE; pThis->uValue = 0; pThis->bIsSetUValue = FALSE; /* debug: printf("sbNVTeConstuct: %8.8x\n", pThis); */ return(pThis); } srRetVal sbNVTESetChild(sbNVTEObj *pEntry, sbNVTRObj *pChildRoot) { sbNVTECHECKVALIDOBJECT(pEntry); sbNVTRCHECKVALIDOBJECT(pChildRoot); pEntry->pChild = pChildRoot; return SR_RET_OK; } sbNVTEObj* sbNVTAddEntry(sbNVTRObj* pRoot) { sbNVTEObj * pThis; sbNVTRCHECKVALIDOBJECT(pRoot); if((pThis = sbNVTEConstruct()) == NULL) return NULL; if(pRoot->pLast == NULL) { /* empty list */ pRoot->pLast = pRoot->pFirst = pThis; } else { /* add to list */ sbNVTECHECKVALIDOBJECT(pRoot->pLast); assert(pRoot->pLast->pNext == NULL); pRoot->pLast->pNext = pThis; pRoot->pLast = pThis; } return(pThis); } srRetVal sbNVTRRemoveFirst(sbNVTRObj* pRoot) { sbNVTEObj *pEntry; sbNVTRCHECKVALIDOBJECT(pRoot); if((pEntry = sbNVTUnlinkElement(pRoot)) != NULL) sbNVTEDestroy(pEntry); return SR_RET_OK; } sbNVTEObj* sbNVTUnlinkElement(sbNVTRObj* pRoot) { sbNVTEObj *pThis; sbNVTRCHECKVALIDOBJECT(pRoot); if(pRoot->pFirst == NULL) return NULL; pThis = pRoot->pFirst; pRoot->pFirst = pThis->pNext; if(pRoot->pFirst == NULL) pRoot->pLast = NULL; return(pThis); } sbNVTEObj* sbNVTSearchKeySZ(sbNVTRObj* pRoot, sbNVTEObj* pStart, char* pszSearch) { sbNVTEObj *pThis; sbNVTRCHECKVALIDOBJECT(pRoot); pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext; while(pThis != NULL) { if(pszSearch == NULL) break; /* attention: order of conditions is vitally important! */ if((pThis->pszKey != NULL) && !strcmp(pThis->pszKey, pszSearch)) break; /* found it! */ pThis = pThis->pNext; } return(pThis); } sbNVTEObj* sbNVTSearchKeyU(sbNVTRObj* pRoot, sbNVTEObj* pStart, unsigned uSearch) { sbNVTEObj *pThis; sbNVTRCHECKVALIDOBJECT(pRoot); pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext; while(pThis != NULL) { if((pThis->uKeyPresent == TRUE) && (pThis->uKey == uSearch)) break; /* found it! */ pThis = pThis->pNext; } return(pThis); } sbNVTEObj* sbNVTSearchKeyUAndPrev(sbNVTRObj* pRoot, sbNVTEObj* pStart, unsigned uSearch, sbNVTEObj** ppPrevEntry) { sbNVTEObj *pThis; sbNVTEObj *pPrev = NULL; sbNVTRCHECKVALIDOBJECT(pRoot); pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext; while(pThis != NULL) { if((pThis->uKeyPresent == TRUE) && (pThis->uKey == uSearch)) break; /* found it! */ pPrev = pThis; pThis = pThis->pNext; } *ppPrevEntry = pPrev; return(pThis); } sbNVTEObj* sbNVTSearchpUsrAndPrev(sbNVTRObj* pRoot, sbNVTEObj* pStart, void *pUsr, sbNVTEObj** ppPrevEntry) { sbNVTEObj *pThis; sbNVTEObj *pPrev = NULL; sbNVTRCHECKVALIDOBJECT(pRoot); pThis = (pStart == NULL) ? pRoot->pFirst : pStart->pNext; while(pThis != NULL) { if(pThis->pUsr == pUsr) break; /* found it! */ pPrev = pThis; pThis = pThis->pNext; } *ppPrevEntry = pPrev; return(pThis); } char* sbNVTEUtilStrDup(char *pszStrToDup) { size_t iLen; char* pszBuf; assert(pszStrToDup != NULL); iLen = strlen(pszStrToDup); /* we use malloc() below for performance * reasons - after all, the buffer is immediately * overwritten. */ if((pszBuf = malloc((iLen+1) * sizeof(char))) == NULL) return NULL; memcpy(pszBuf, pszStrToDup, (iLen+1) * sizeof(char)); return(pszBuf); } srRetVal sbNVTESetKeySZ(sbNVTEObj* pThis, char* pszKey, int bCopy) { char *pszBuf; sbNVTECHECKVALIDOBJECT(pThis); if(bCopy == TRUE) { if((pszBuf = sbNVTEUtilStrDup(pszKey)) == NULL) return SR_RET_ERR; } else pszBuf = pszKey; if(pThis->pszKey != NULL) free(pThis->pszKey); pThis->pszKey = pszBuf; return SR_RET_OK; } srRetVal sbNVTESetValueSZ(sbNVTEObj* pThis, char* pszVal, int bCopy) { char *pszBuf; sbNVTECHECKVALIDOBJECT(pThis); if(bCopy == TRUE) { if((pszBuf = sbNVTEUtilStrDup(pszVal)) == NULL) return SR_RET_ERR; } else pszBuf = pszVal; if(pThis->pszValue != NULL) free(pThis->pszValue); pThis->pszValue = pszBuf; return SR_RET_OK; } srRetVal sbNVTESetKeyU(sbNVTEObj* pThis, unsigned uKey) { sbNVTECHECKVALIDOBJECT(pThis); pThis->uKey = uKey; pThis->uKeyPresent = TRUE; return SR_RET_OK; } srRetVal sbNVTEUnsetKeyU(sbNVTEObj* pThis) { sbNVTECHECKVALIDOBJECT(pThis); pThis->uKeyPresent = FALSE; return SR_RET_OK; } srRetVal sbNVTESetUsrPtr(sbNVTEObj* pThis, void *pPtr, void(pPtrDestroy)(void*)) { sbNVTECHECKVALIDOBJECT(pThis); if(pPtrDestroy == NULL) return SR_RET_INVALID_DESTRUCTOR; pThis->pUsr = pPtr; pThis->pUsrDestroy = pPtrDestroy; return SR_RET_OK; } srRetVal sbNVTESetValueU(sbNVTEObj* pThis, unsigned uVal) { sbNVTECHECKVALIDOBJECT(pThis); pThis->uValue = uVal; pThis->bIsSetUValue = TRUE; return SR_RET_OK; } srRetVal sbNVTEGetValueU(sbNVTEObj* pThis, unsigned* puValue) { char *pBuf; unsigned u; sbNVTECHECKVALIDOBJECT(pThis); assert(puValue != NULL); if(pThis->bIsSetUValue) { *puValue = pThis->uValue; } else { /* try to convert the szValue */ if(pThis->pszValue == NULL) return SR_RET_NO_VALUE; else { pBuf = pThis->pszValue; u = 0; while(*pBuf) { if(!isdigit(*pBuf)) return SR_RET_NO_VALUE; u = u * 10 + (*pBuf - '0'); pBuf++; } pThis->uValue = u; /* Please note: this can cause an error if the szValue * is being re-set. Things then go out of sync. This is * no issue for the current library, but may be worth * looking into if the lib use is extended. */ pThis->bIsSetUValue = TRUE; *puValue = u; } } return SR_RET_OK; } void sbNVTEUnsetUsrPtr(sbNVTEObj *pEntry) { sbNVTECHECKVALIDOBJECT(pEntry); pEntry->pUsr = NULL; pEntry->pUsrDestroy = NULL; } void sbNVTRUnlinkFromParent(sbNVTRObj *pRoot) { sbNVTRCHECKVALIDOBJECT(pRoot); if(pRoot->pParent != NULL) pRoot->pParent->pChild = NULL; } void sbNVTEUnlinkFromList(sbNVTRObj *pRoot, sbNVTEObj* pEntry, sbNVTEObj* pPrev) { sbNVTRCHECKVALIDOBJECT(pRoot); sbNVTECHECKVALIDOBJECT(pEntry); if(pPrev == NULL) { pRoot->pFirst = pEntry->pNext; if(pRoot->pLast == pEntry) pRoot->pLast = NULL; } else { pPrev->pNext = pEntry->pNext; if(pRoot->pLast == pEntry) pRoot->pLast = pPrev; } } srRetVal sbNVTRRemoveKeyU(sbNVTRObj *pRoot, unsigned uKey) { sbNVTEObj *pEntry, *pPrev; sbNVTRCHECKVALIDOBJECT(pRoot); if((pEntry = sbNVTSearchKeyUAndPrev(pRoot, NULL, uKey, &pPrev)) == NULL) return SR_RET_NOT_FOUND; sbNVTEUnlinkFromList(pRoot, pEntry, pPrev); sbNVTEDestroy(pEntry); return SR_RET_OK; } srRetVal sbNVTRRemovEntryWithpUsr(sbNVTRObj *pRoot, void* pUsr) { sbNVTEObj *pEntry, *pPrev; sbNVTRCHECKVALIDOBJECT(pRoot); if((pEntry = sbNVTSearchpUsrAndPrev(pRoot, NULL, pUsr, &pPrev)) == NULL) return SR_RET_NOT_FOUND; sbNVTEUnlinkFromList(pRoot, pEntry, pPrev); sbNVTEDestroy(pEntry); return SR_RET_OK; } srRetVal sbNVTRParseXML(sbNVTRObj *pRoot, char *pszXML) { srRetVal iRet; sbNVTRCHECKVALIDOBJECT(pRoot); if(pszXML == NULL) /* this does not make much sense, but is * a valid case. So let's do it ;) */ return SR_RET_OK; if((iRet = sbNVTXMLProcessXMLSTREAM(&pszXML, pRoot)) != SR_RET_OK) return iRet; return SR_RET_OK; } #if DEBUGLEVEL /* Helper to srNVTDebugPrintTree - indents a new line * by iLevel*2 spaces. */ static void sbNVTDebugPrintTreeSpacer(int iLevel) { int i; for(i = 0 ; i < iLevel * 2 ; ++i) { putchar(' '); } } void sbNVTDebugPrintTree(sbNVTRObj *pRoot, int iLevel) { sbNVTEObj *pEntry = NULL; while((pEntry = sbNVTSearchKeySZ(pRoot, pEntry, NULL)) != NULL) { sbNVTDebugPrintTreeSpacer(iLevel); printf("KeySZ: '%s', ValueSZ '%s'\n", pEntry->pszKey, pEntry->pszValue); if(pEntry->uKeyPresent) { sbNVTDebugPrintTreeSpacer(iLevel); printf("KeyU: '%d', ValueU '%d'\n", pEntry->uKey, pEntry->uValue); } if(pEntry->pCDATA != NULL) { sbNVTDebugPrintTreeSpacer(iLevel); printf("CDATA: '%s'\n", pEntry->pCDATA); } if(pEntry->pXMLProps != NULL) { sbNVTDebugPrintTreeSpacer(iLevel); printf("HAS XML Properties:\n"); sbNVTDebugPrintTree(pEntry->pXMLProps, iLevel + 1); } if(pEntry->pChild != NULL) { sbNVTDebugPrintTreeSpacer(iLevel); printf("HAS Child:\n"); sbNVTDebugPrintTree(pEntry->pChild, iLevel + 1); } } } /* #endif for #if DEBUGLEVEL */ #endif sbNVTEObj *sbNVTRHasElement(sbNVTRObj* pRoot, char *pEltname, int bMustBeOnly) { sbNVTEObj *pEntry; sbNVTRCHECKVALIDOBJECT(pRoot); assert(pEltname != NULL); if((bMustBeOnly == TRUE) && (pRoot->pFirst != pRoot->pLast)) return NULL; if((pEntry = sbNVTSearchKeySZ(pRoot, NULL, pEltname)) == NULL) return NULL; return pEntry; }