/* This is the byte-counted string class for rsyslog. It is a replacement * for classical \0 terminated string functions. We introduce it in * the hope it will make the program more secure, obtain some performance * and, most importantly, lay they foundation for syslog-protocol, which * requires strings to be able to handle embedded \0 characters. * Please see syslogd.c for license information. * All functions in this "class" start with rsCStr (rsyslog Counted String). * begun 2005-09-07 rgerhards * * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH * * This file is part of the rsyslog runtime library. * * The rsyslog runtime library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The rsyslog runtime library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the rsyslog runtime library. If not, see . * * A copy of the GPL can be found in the file "COPYING" in this distribution. * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. */ #include "config.h" #include #include #include #include #include #include "rsyslog.h" #include "stringbuf.h" #include "srUtils.h" #include "regexp.h" #include "obj.h" /* ################################################################# * * private members * * ################################################################# */ /* static data */ DEFobjCurrIf(obj) DEFobjCurrIf(regexp) /* ################################################################# * * public members * * ################################################################# */ rsRetVal rsCStrConstruct(cstr_t **ppThis) { DEFiRet; cstr_t *pThis; ASSERT(ppThis != NULL); if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); rsSETOBJTYPE(pThis, OIDrsCStr); pThis->pBuf = NULL; pThis->pszBuf = NULL; pThis->iBufSize = 0; pThis->iStrLen = 0; pThis->iAllocIncrement = RS_STRINGBUF_ALLOC_INCREMENT; *ppThis = pThis; finalize_it: RETiRet; } /* construct from sz string * rgerhards 2005-09-15 */ rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz) { DEFiRet; cstr_t *pThis; assert(ppThis != NULL); CHKiRet(rsCStrConstruct(&pThis)); pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz); if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { RSFREEOBJ(pThis); ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } /* we do NOT need to copy the \0! */ memcpy(pThis->pBuf, sz, pThis->iStrLen); *ppThis = pThis; finalize_it: RETiRet; } /* construct from CStr object. only the counted string is * copied, not the szString. * rgerhards 2005-10-18 */ rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom) { DEFiRet; cstr_t *pThis; assert(ppThis != NULL); rsCHECKVALIDOBJECT(pFrom, OIDrsCStr); CHKiRet(rsCStrConstruct(&pThis)); pThis->iBufSize = pThis->iStrLen = pFrom->iStrLen; if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { RSFREEOBJ(pThis); ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } /* copy properties */ memcpy(pThis->pBuf, pFrom->pBuf, pThis->iStrLen); *ppThis = pThis; finalize_it: RETiRet; } void rsCStrDestruct(cstr_t **ppThis) { cstr_t *pThis = *ppThis; /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation. * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly * do not know why it was so, I think it was an artifact. Anyhow, I have changed this * now. Should there any issue occur, this comment hopefully will shed some light * on what happened. I re-verified, and this function has never before been called * by anyone. So changing it can have no impact for obvious reasons... * * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where * the destructor receives a pointer to the object, so that it can set it to NULL. */ if(pThis->pBuf != NULL) { free(pThis->pBuf); } if(pThis->pszBuf != NULL) { free(pThis->pszBuf); } RSFREEOBJ(pThis); *ppThis = NULL; } /* extend the string buffer if its size is insufficient. * Param iMinNeeded is the minumum free space needed. If it is larger * than the default alloc increment, space for at least this amount is * allocated. In practice, a bit more is allocated because we envision that * some more characters may be added after these. * rgerhards, 2008-01-07 */ static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded) { DEFiRet; uchar *pNewBuf; size_t iNewSize; /* first compute the new size needed */ if(iMinNeeded > pThis->iAllocIncrement) { /* we allocate "n" iAllocIncrements. Usually, that should * leave some room after the absolutely needed one. It also * reduces memory fragmentation. Note that all of this are * integer operations (very important to understand what is * going on)! Parenthesis are for better readibility. */ iNewSize = ((iMinNeeded / pThis->iAllocIncrement) + 1) * pThis->iAllocIncrement; } else { iNewSize = pThis->iBufSize + pThis->iAllocIncrement; } iNewSize += pThis->iBufSize; /* add current size */ /* and then allocate and copy over */ /* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */ if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize); pThis->iBufSize = iNewSize; if(pThis->pBuf != NULL) { free(pThis->pBuf); } pThis->pBuf = pNewBuf; finalize_it: RETiRet; } /* append a string of known length. In this case, we make sure we do at most * one additional memory allocation. * I optimized this function to use memcpy(), among others. Consider it a * rewrite (which may be good to know in case of bugs) -- rgerhards, 2008-01-07 */ rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen) { DEFiRet; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); assert(psz != NULL); /* does the string fit? */ if(pThis->iStrLen + iStrLen > pThis->iBufSize) { CHKiRet(rsCStrExtendBuf(pThis, iStrLen)); /* need more memory! */ } /* ok, now we always have sufficient continues memory to do a memcpy() */ memcpy(pThis->pBuf + pThis->iStrLen, psz, iStrLen); pThis->iStrLen += iStrLen; finalize_it: RETiRet; } /* changed to be a wrapper to rsCStrAppendStrWithLen() so that * we can save some time when we have the length but do not * need to change existing code. * rgerhards, 2007-07-03 */ rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz) { return rsCStrAppendStrWithLen(pThis, psz, strlen((char*) psz)); } /* append the contents of one cstr_t object to another * rgerhards, 2008-02-25 */ rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend) { return rsCStrAppendStrWithLen(pThis, pstrAppend->pBuf, pstrAppend->iStrLen); } rsRetVal rsCStrAppendInt(cstr_t *pThis, long i) { DEFiRet; uchar szBuf[32]; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), i)); iRet = rsCStrAppendStr(pThis, szBuf); finalize_it: RETiRet; } rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c) { DEFiRet; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(pThis->iStrLen >= pThis->iBufSize) { CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */ } /* ok, when we reach this, we have sufficient memory */ *(pThis->pBuf + pThis->iStrLen++) = c; /* check if we need to invalidate an sz representation! */ if(pThis->pszBuf != NULL) { free(pThis->pszBuf); pThis->pszBuf = NULL; } finalize_it: RETiRet; } /* Sets the string object to the classigal sz-string provided. * Any previously stored vlaue is discarded. If a NULL pointer * the the new value (pszNew) is provided, an empty string is * created (this is NOT an error!). Property iAllocIncrement is * not modified by this function. * rgerhards, 2005-10-18 */ rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew) { rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(pThis->pBuf != NULL) free(pThis->pBuf); if(pThis->pszBuf != NULL) free(pThis->pszBuf); if(pszNew == NULL) { pThis->iStrLen = 0; pThis->iBufSize = 0; pThis->pBuf = NULL; pThis->pszBuf = NULL; } else { pThis->iStrLen = strlen((char*)pszNew); pThis->iBufSize = pThis->iStrLen; pThis->pszBuf = NULL; /* iAllocIncrement is NOT modified! */ /* now save the new value */ if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { RSFREEOBJ(pThis); return RS_RET_OUT_OF_MEMORY; } /* we do NOT need to copy the \0! */ memcpy(pThis->pBuf, pszNew, pThis->iStrLen); } return RS_RET_OK; } /* Converts the CStr object to a classical sz string and returns that. * Same restrictions as in rsCStrGetSzStr() applies (see there!). This * function here guarantees that a valid string is returned, even if * the CStr object currently holds a NULL pointer string buffer. If so, * "" is returned. * rgerhards 2005-10-19 * WARNING: The returned pointer MUST NOT be freed, as it may be * obtained from that constant memory pool (in case of NULL!) */ uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis) { rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(pThis->pBuf == NULL) return (uchar*) ""; else return rsCStrGetSzStr(pThis); } /* Converts the CStr object to a classical zero-terminated C string * and returns that string. The caller must not free it and must not * destroy the CStr object as long as the ascii string is used. * This function may return NULL, if the string is currently NULL. This * is a feature, not a bug. If you need non-NULL in any case, use * rsCStrGetSzStrNoNULL() instead. * rgerhards, 2005-09-15 */ uchar* rsCStrGetSzStr(cstr_t *pThis) { size_t i; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(pThis->pBuf != NULL) if(pThis->pszBuf == NULL) { /* we do not yet have a usable sz version - so create it... */ if((pThis->pszBuf = malloc((pThis->iStrLen + 1) * sizeof(uchar))) == NULL) { /* TODO: think about what to do - so far, I have no bright * idea... rgerhards 2005-09-07 */ } else { /* we can create the sz String */ /* now copy it while doing a sanity check. The string might contain a * \0 byte. There is no way how a sz string can handle this. For * the time being, we simply replace it with space - something that * could definitely be improved (TODO). * 2005-09-15 rgerhards */ for(i = 0 ; i < pThis->iStrLen ; ++i) { if(pThis->pBuf[i] == '\0') pThis->pszBuf[i] = ' '; else pThis->pszBuf[i] = pThis->pBuf[i]; } /* write terminator... */ pThis->pszBuf[i] = '\0'; } } return(pThis->pszBuf); } /* Converts the CStr object to a classical zero-terminated C string, * returns that string and destroys the CStr object. The returned string * MUST be freed by the caller. The function might return NULL if * no memory can be allocated. * * TODO: * This function should at some time become special. The base idea is to * add one extra byte to the end of the regular buffer, so that we can * convert it to an szString without the need to copy. The extra memory * footprint is not hefty, but the performance gain is potentially large. * To get it done now, I am not doing the optimiziation right now. * rgerhards, 2005-09-07 * * rgerhards, 2007-09-04: I have changed the interface of this function. It now * returns an rsRetVal, so that we can communicate back if we have an error. * Using the standard method is much better than returning NULL. Secondly, NULL * was not actually an error - it was in indication if the string was empty. * This was needed in some parts of the code, in others not. I have now added * a second parameter to specify what the caller needs. I hope these changes * will make it less likely that the function is called incorrectly, what * previously happend quite often and was the cause of a number of program * aborts. So the parameters are now: * pointer to the object, pointer to string-pointer to receive string and * bRetNULL: 0 - must not return NULL on empty string, return "" in that * case, 1 - return NULL instead of an empty string. * PLEASE NOTE: the caller must free the memory returned in ppSz in any case * (except, of course, if it is NULL). */ rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL) { DEFiRet; uchar* pRetBuf; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); assert(ppSz != NULL); assert(bRetNULL == 0 || bRetNULL == 1); if(pThis->pBuf == NULL) { if(bRetNULL == 0) { if((pRetBuf = malloc(sizeof(uchar))) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); *pRetBuf = '\0'; } else { pRetBuf = NULL; } } else pRetBuf = rsCStrGetSzStr(pThis); *ppSz = pRetBuf; finalize_it: /* We got it, now free the object ourselfs. Please note * that we can NOT use the rsCStrDestruct function as it would * also free the sz String buffer, which we pass on to the user. */ if(pThis->pBuf != NULL) free(pThis->pBuf); RSFREEOBJ(pThis); RETiRet; } #if STRINGBUF_TRIM_ALLOCSIZE == 1 /* Only in this mode, we need to trim the string. To do * so, we must allocate a new buffer of the exact * string size, and then copy the old one over. */ /* WARNING * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim * memory buffers. This part of the code was inherited from * liblogging (where it is used in a different context) but * never put to use in rsyslog. The reason is that it is hardly * imaginable where the extra performance cost is worth the save * in memory alloc. Then Anders Blomdel rightfully pointed out that * the code does not work at all - and nobody even know that it * probably shouldn't. Rather than removing, I deciced to somewhat * fix the code, so that this feature may be enabled if somebody * really has a need for it. Be warned, however, that I NEVER * tested the fix. So if you intend to use this feature, you must * do full testing before you rely on it. -- rgerhards, 2008-02-12 */ rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis) { DEFiRet; uchar* pBuf; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL) { /* OK, in this case we use the previous buffer. At least * we have it ;) */ } else { /* got the new buffer, so let's use it */ memcpy(pBuf, pThis->pBuf, pThis->iStrLen); pThis->pBuf = pBuf; } RETiRet; } #endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */ void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement) { rsCHECKVALIDOBJECT(pThis, OIDrsCStr); assert(iNewIncrement > 0); pThis->iAllocIncrement = iNewIncrement; } /* return the length of the current string * 2005-09-09 rgerhards * Please note: this is only a function in a debug build. * For release builds, it is a macro defined in stringbuf.h. * This is due to performance reasons. */ #ifndef NDEBUG int rsCStrLen(cstr_t *pThis) { rsCHECKVALIDOBJECT(pThis, OIDrsCStr); return(pThis->iStrLen); } #endif /* Truncate characters from the end of the string. * rgerhards 2005-09-15 */ rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc) { rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(pThis->iStrLen < nTrunc) return RS_TRUNCAT_TOO_LARGE; pThis->iStrLen -= nTrunc; if(pThis->pszBuf != NULL) { /* in this case, we adjust the psz representation * by writing a new \0 terminator - this is by far * the fastest way and outweights the additional memory * required. 2005-9-19 rgerhards. */ pThis->pszBuf[pThis->iStrLen] = '\0'; } return RS_RET_OK; } /* Trim trailing whitespace from a given string */ rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis) { register int i; register uchar *pC; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); i = pThis->iStrLen; pC = pThis->pBuf + i - 1; while(i > 0 && isspace((int)*pC)) { --pC; --i; } /* i now is the new string length! */ pThis->iStrLen = i; return RS_RET_OK; } /* compare two string objects - works like strcmp(), but operates * on CStr objects. Please note that this version here is * faster in the majority of cases, simply because it can * rely on StrLen. * rgerhards 2005-09-19 * fixed bug, in which only the last byte was actually compared * in equal-size strings. * rgerhards, 2005-09-26 */ int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2) { rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); rsCHECKVALIDOBJECT(pCS2, OIDrsCStr); if(pCS1->iStrLen == pCS2->iStrLen) if(pCS1->iStrLen == 0) return 0; /* zero-sized string are equal ;) */ else { /* we now have two non-empty strings of equal * length, so we need to actually check if they * are equal. */ register size_t i; for(i = 0 ; i < pCS1->iStrLen ; ++i) { if(pCS1->pBuf[i] != pCS2->pBuf[i]) return pCS1->pBuf[i] - pCS2->pBuf[i]; } /* if we arrive here, the strings are equal */ return 0; } else return pCS1->iStrLen - pCS2->iStrLen; } /* check if a sz-type string starts with a CStr object. This function * is initially written to support the "startswith" property-filter * comparison operation. Maybe it also has other needs. * This functions is modelled after the strcmp() series, thus a * return value of 0 indicates that the string starts with the * sequence while -1 indicates it does not! * rgerhards 2005-10-19 */ int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) { register int i; int iMax; rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); assert(psz != NULL); assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ if(iLenSz >= pCS1->iStrLen) { /* we need to checkusing pCS1->iStrLen charactes at maximum, thus * we move it to iMax. */ iMax = pCS1->iStrLen; if(iMax == 0) return 0; /* yes, it starts with a zero-sized string ;) */ else { /* we now have something to compare, so let's do it... */ for(i = 0 ; i < iMax ; ++i) { if(psz[i] != pCS1->pBuf[i]) return psz[i] - pCS1->pBuf[i]; } /* if we arrive here, the string actually starts with pCS1 */ return 0; } } else return -1; /* pCS1 is less then psz */ } /* check if a CStr object starts with a sz-type string. * This functions is modelled after the strcmp() series, thus a * return value of 0 indicates that the string starts with the * sequence while -1 indicates it does not! * rgerhards 2005-09-26 */ int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) { register size_t i; rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); assert(psz != NULL); assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ if(pCS1->iStrLen >= iLenSz) { /* we are using iLenSz below, because we need to check * iLenSz characters at maximum (start with!) */ if(iLenSz == 0) return 0; /* yes, it starts with a zero-sized string ;) */ else { /* we now have something to compare, so let's do it... */ for(i = 0 ; i < iLenSz ; ++i) { if(pCS1->pBuf[i] != psz[i]) return pCS1->pBuf[i] - psz[i]; } /* if we arrive here, the string actually starts with psz */ return 0; } } else return -1; /* pCS1 is less then psz */ } /* The same as rsCStrStartsWithSzStr(), but does a case-insensitive * comparison. TODO: consolidate the two. * rgerhards 2008-02-28 */ int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) { register size_t i; rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); assert(psz != NULL); assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ if(pCS1->iStrLen >= iLenSz) { /* we are using iLenSz below, because we need to check * iLenSz characters at maximum (start with!) */ if(iLenSz == 0) return 0; /* yes, it starts with a zero-sized string ;) */ else { /* we now have something to compare, so let's do it... */ for(i = 0 ; i < iLenSz ; ++i) { if(tolower(pCS1->pBuf[i]) != tolower(psz[i])) return tolower(pCS1->pBuf[i]) - tolower(psz[i]); } /* if we arrive here, the string actually starts with psz */ return 0; } } else return -1; /* pCS1 is less then psz */ } /* check if a CStr object matches a regex. * msamia@redhat.com 2007-07-12 * @return returns 0 if matched * bug: doesn't work for CStr containing \0 * rgerhards, 2007-07-16: bug is no real bug, because rsyslogd ensures there * never is a \0 *inside* a property string. * Note that the function returns -1 if regexp functionality is not available. * TODO: change calling interface! -- rgerhards, 2008-03-07 */ int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz) { regex_t preq; int ret; BEGINfunc if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { regexp.regcomp(&preq, (char*) rsCStrGetSzStr(pCS1), 0); ret = regexp.regexec(&preq, (char*) psz, 0, NULL, 0); regexp.regfree(&preq); } else { ret = 1; /* simulate "not found" */ } ENDfunc return ret; } /* compare a rsCStr object with a classical sz string. This function * is almost identical to rsCStrZsStrCmp(), but it also takes an offset * to the CStr object from where the comparison is to start. * I have thought quite a while if it really makes sense to more or * less duplicate the code. After all, if you call it with an offset of * zero, the functionality is exactly the same. So it looks natural to * just have a single function. However, supporting the offset requires * some (few) additional integer operations. While they are few, they * happen at places in the code that is run very frequently. All in all, * I have opted for performance and thus duplicated the code. I hope * this is a good, or at least acceptable, compromise. * rgerhards, 2005-09-26 * This function also has an offset-pointer which allows to * specify *where* the compare operation should begin in * the CStr. If everything is to be compared, it must be set * to 0. If some leading bytes are to be skipped, it must be set * to the first index that is to be compared. It must not be * set higher than the string length (this is considered a * program bug and will lead to unpredictable results and program aborts). * rgerhards 2005-09-26 */ int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz) { BEGINfunc rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); assert(iOffset < pCS1->iStrLen); assert(psz != NULL); assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ if((pCS1->iStrLen - iOffset) == iLenSz) { /* we are using iLenSz below, because the lengths * are equal and iLenSz is faster to access */ if(iLenSz == 0) { return 0; /* zero-sized strings are equal ;) */ ENDfunc } else { /* we now have two non-empty strings of equal * length, so we need to actually check if they * are equal. */ register size_t i; for(i = 0 ; i < iLenSz ; ++i) { if(pCS1->pBuf[i+iOffset] != psz[i]) return pCS1->pBuf[i+iOffset] - psz[i]; } /* if we arrive here, the strings are equal */ return 0; ENDfunc } } else { return pCS1->iStrLen - iOffset - iLenSz; ENDfunc } } /* Converts a string to a number. If the string dos not contain a number, * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. * If all goes well, pNumber contains the number that the string was converted * to. */ rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber) { DEFiRet; number_t n; int bIsNegative; size_t i; ASSERT(pStr != NULL); ASSERT(pNumber != NULL); if(pStr->iStrLen == 0) { /* can be converted to 0! (by convention) */ pNumber = 0; FINALIZE; } /* first skip whitespace (if present) */ for(i = 0 ; i < pStr->iStrLen && isspace(pStr->pBuf[i]) ; ++i) { /*DO NOTHING*/ } /* we have a string, so let's check its syntax */ if(pStr->pBuf[i] == '+') { ++i; /* skip that char */ bIsNegative = 0; } else if(pStr->pBuf[0] == '-') { ++i; /* skip that char */ bIsNegative = 1; } else { bIsNegative = 0; } /* TODO: octal? hex? */ n = 0; while(i < pStr->iStrLen && isdigit(pStr->pBuf[i])) { n = n * 10 + pStr->pBuf[i] * 10; ++i; } if(i < pStr->iStrLen) /* non-digits before end of string? */ ABORT_FINALIZE(RS_RET_NOT_A_NUMBER); if(bIsNegative) n *= -1; /* we got it, so return the number */ *pNumber = n; finalize_it: RETiRet; } /* Converts a string to a boolen. First tries to convert to a number. If * that succeeds, we are done (number is then used as boolean value). If * that fails, we look if the string is "yes" or "true". If so, a value * of 1 is returned. In all other cases, a value of 0 is returned. Please * note that we do not have a specific boolean type, so we return a number. * so, these are * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. * If all goes well, pNumber contains the number that the string was converted * to. */ rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool) { DEFiRet; ASSERT(pStr != NULL); ASSERT(pBool != NULL); iRet = rsCStrConvertToNumber(pStr, pBool); if(iRet != RS_RET_NOT_A_NUMBER) { FINALIZE; /* in any case, we have nothing left to do */ } /* TODO: maybe we can do better than strcasecmp ;) -- overhead! */ if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "true")) { *pBool = 1; } else if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "yes")) { *pBool = 1; } else { *pBool = 0; } finalize_it: RETiRet; } /* compare a rsCStr object with a classical sz string. * Just like rsCStrCStrCmp, just for a different data type. * There must not only the sz string but also its length be * provided. If the caller does not know the length he can * call with * rsCstrSzStrCmp(pCS, psz, strlen((char*)psz)); * we are not doing the strlen((char*)) ourselfs as the caller might * already know the length and in such cases we can save the * overhead of doing it one more time (strelen() is costly!). * The bottom line is that the provided length MUST be correct! * The to sz string pointer must not be NULL! * rgerhards 2005-09-26 */ int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz) { rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); assert(psz != NULL); assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ if(pCS1->iStrLen == iLenSz) /* we are using iLenSz below, because the lengths * are equal and iLenSz is faster to access */ if(iLenSz == 0) return 0; /* zero-sized strings are equal ;) */ else { /* we now have two non-empty strings of equal * length, so we need to actually check if they * are equal. */ register size_t i; for(i = 0 ; i < iLenSz ; ++i) { if(pCS1->pBuf[i] != psz[i]) return pCS1->pBuf[i] - psz[i]; } /* if we arrive here, the strings are equal */ return 0; } else return pCS1->iStrLen - iLenSz; } /* Locate the first occurence of this rsCStr object inside a standard sz string. * Returns the offset (0-bound) of this first occurrence. If not found, -1 is * returned. Both parameters MUST be given (NULL is not allowed). * rgerhards 2005-09-19 */ int rsCStrLocateInSzStr(cstr_t *pThis, uchar *sz) { int i; int iMax; int bFound; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); assert(sz != NULL); if(pThis->iStrLen == 0) return 0; /* compute the largest index where a match could occur - after all, * the to-be-located string must be able to be present in the * searched string (it needs its size ;)). */ iMax = strlen((char*)sz) - pThis->iStrLen; bFound = 0; i = 0; while(i <= iMax && !bFound) { size_t iCheck; uchar *pComp = sz + i; for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) if(*(pComp + iCheck) != *(pThis->pBuf + iCheck)) break; if(iCheck == pThis->iStrLen) bFound = 1; /* found! - else it wouldn't be equal */ else ++i; /* on to the next try */ } return(bFound ? i : -1); } /* This is the same as rsCStrLocateInSzStr(), but does a case-insensitve * comparison. * TODO: over time, consolidate the two. * rgerhards, 2008-02-28 */ int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz) { int i; int iMax; int bFound; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); assert(sz != NULL); if(pThis->iStrLen == 0) return 0; /* compute the largest index where a match could occur - after all, * the to-be-located string must be able to be present in the * searched string (it needs its size ;)). */ iMax = strlen((char*)sz) - pThis->iStrLen; bFound = 0; i = 0; while(i <= iMax && !bFound) { size_t iCheck; uchar *pComp = sz + i; for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) if(tolower(*(pComp + iCheck)) != tolower(*(pThis->pBuf + iCheck))) break; if(iCheck == pThis->iStrLen) bFound = 1; /* found! - else it wouldn't be equal */ else ++i; /* on to the next try */ } return(bFound ? i : -1); } #if 0 /* read comment below why this is commented out. In short: for future use! */ /* locate the first occurence of a standard sz string inside a rsCStr object. * Returns the offset (0-bound) of this first occurrence. If not found, -1 is * returned. * rgerhards 2005-09-19 * WARNING: I accidently created this function (I later noticed I didn't relly * need it... I will not remove the function, as it probably is useful * some time later. However, it is not fully tested, so start with testing * it before you put it to first use). */ int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz) { int iLenSz; int i; int iMax; int bFound; rsCHECKVALIDOBJECT(pThis, OIDrsCStr); if(sz == NULL) return 0; iLenSz = strlen((char*)sz); if(iLenSz == 0) return 0; /* compute the largest index where a match could occur - after all, * the to-be-located string must be able to be present in the * searched string (it needs its size ;)). */ iMax = pThis->iStrLen - iLenSz; bFound = 0; i = 0; while(i < iMax && !bFound) { int iCheck; uchar *pComp = pThis->pBuf + i; for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck) if(*(pComp + iCheck) != *(sz + iCheck)) break; if(iCheck == iLenSz) bFound = 1; /* found! - else it wouldn't be equal */ else ++i; /* on to the next try */ } return(bFound ? i : -1); } #endif /* end comment out */ /* our exit function. TODO: remove once converted to a class * rgerhards, 2008-03-11 */ rsRetVal strExit() { DEFiRet; objRelease(regexp, LM_REGEXP_FILENAME); RETiRet; } /* our init function. TODO: remove once converted to a class */ rsRetVal strInit() { DEFiRet; CHKiRet(objGetObjInterface(&obj)); finalize_it: RETiRet; } /* * Local variables: * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: * vi:set ai: */