/** * @file mprString.c * @brief String routines safe for embedded programming * @overview This module provides safe replacements for the standard * string library. * @remarks Most routines in this file are not thread-safe. It is the callers * responsibility to perform all thread synchronization. */ /* * @copy default * * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. * * This software is distributed under commercial and open source licenses. * You may use the GPL open source license described below or you may acquire * a commercial license from Mbedthis Software. You agree to be fully bound * by the terms of either license. Consult the LICENSE.TXT distributed with * this software for full details. * * This software is open source; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See the GNU General Public License for more * details at: http://www.mbedthis.com/downloads/gplLicense.html * * This program is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This GPL license does NOT permit incorporating this software into * proprietary programs. If you are unable to comply with the GPL, you must * acquire a commercial license to use this software. Commercial licenses * for this software and support services are available from Mbedthis * Software at http://www.mbedthis.com * * @end */ #include "mpr.h" /********************************** Includes **********************************/ /* * We need to use the underlying str(cpy) routines to implement our safe * alternatives */ #if !DOXYGEN #define UNSAFE_FUNCTIONS_OK 1 #endif /******************************************************************************/ /**************************** Safe String Handling ****************************/ /******************************************************************************/ int mprStrcpy(char *dest, int destMax, const char *src) { int len; mprAssert(dest); mprAssert(destMax >= 0); mprAssert(src); len = strlen(src); if (destMax > 0 && len >= destMax && len > 0) { return MPR_ERR_WONT_FIT; } if (len > 0) { memcpy(dest, src, len); dest[len] = '\0'; } else { *dest = '\0'; len = 0; } return len; } /******************************************************************************/ int mprAllocStrcpy(MPR_LOC_DEC(ctx, loc), char **dest, int destMax, const char *src) { int len; mprAssert(dest); mprAssert(destMax >= 0); mprAssert(src); len = strlen(src); if (destMax > 0 && len >= destMax) { mprAssert(0); return MPR_ERR_WONT_FIT; } if (len > 0) { *dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); memcpy(*dest, src, len); (*dest)[len] = '\0'; } else { *dest = (char*) mprAlloc(ctx, 1); *dest = '\0'; len = 0; } return len; } /******************************************************************************/ int mprMemcpy(char *dest, int destMax, const char *src, int nbytes) { mprAssert(dest); mprAssert(destMax <= 0 || destMax >= nbytes); mprAssert(src); mprAssert(nbytes >= 0); if (destMax > 0 && nbytes > destMax) { mprAssert(0); return MPR_ERR_WONT_FIT; } if (nbytes > 0) { memcpy(dest, src, nbytes); return nbytes; } else { return 0; } } /******************************************************************************/ int mprAllocMemcpy(MPR_LOC_DEC(ctx, loc), char **dest, int destMax, const void *src, int nbytes) { mprAssert(dest); mprAssert(src); mprAssert(nbytes > 0); mprAssert(destMax <= 0 || destMax >= nbytes); if (destMax > 0 && nbytes > destMax) { mprAssert(0); return MPR_ERR_WONT_FIT; } if (nbytes > 0) { *dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx,loc), nbytes); if (*dest == 0) { return MPR_ERR_MEMORY; } memcpy(*dest, src, nbytes); } else { *dest = (char*) mprAlloc(ctx, 1); } return nbytes; } /******************************************************************************/ static int mprCoreStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, int existingLen, const char *delim, const char *src, va_list args) { va_list ap; char *dest, *str, *dp; int sepLen, addBytes, required; mprAssert(destp); mprAssert(destMax >= 0); mprAssert(src); dest = *destp; sepLen = (delim) ? strlen(delim) : 0; #ifdef __va_copy __va_copy(ap, args); #else ap = args; #endif addBytes = 0; if (existingLen > 0) { addBytes += sepLen; } str = (char*) src; while (str) { addBytes += strlen(str); str = va_arg(ap, char*); if (str) { addBytes += sepLen; } } required = existingLen + addBytes + 1; if (destMax > 0 && required >= destMax) { mprAssert(0); return MPR_ERR_WONT_FIT; } if (ctx != 0) { if (dest == 0) { dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), required); } else { dest = (char*) mprReallocBlock(MPR_LOC_PASS(ctx, loc), dest, required); } } else { dest = (char*) *destp; } dp = &dest[existingLen]; if (delim && existingLen > 0) { strcpy(dp, delim); dp += sepLen; } if (addBytes > 0) { #ifdef __va_copy __va_copy(ap, args); #else ap = args; #endif str = (char*) src; while (str) { strcpy(dp, str); dp += strlen(str); str = va_arg(ap, char*); if (delim && str) { strcpy(dp, delim); dp += sepLen; } } } else if (dest == 0) { dest = (char*) mprAlloc(ctx, 1); } *dp = '\0'; *destp = dest; mprAssert(dp < &dest[required]); return required - 1; } /***************************************************************************** Note that this VARARGS function must be NULL (not 0, this must be a pointer) terminated */ int mprStrcat(char *dest, int destMax, const char *delim, const char *src, ...) { va_list ap; int rc; mprAssert(dest); mprAssert(src); va_start(ap, src); rc = mprCoreStrcat(MPR_LOC_ARGS(0), &dest, destMax, strlen(dest), delim, src, ap); va_end(ap); return rc; } /***************************************************************************** Note that this VARARGS function must be NULL (not 0, this must be a pointer) terminated */ int mprAllocStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, const char *delim, const char *src, ...) { va_list ap; int rc; mprAssert(destp); mprAssert(src); *destp = 0; va_start(ap, src); rc = mprCoreStrcat(MPR_LOC_PASS(ctx, loc), destp, destMax, 0, delim, src, ap); va_end(ap); return rc; } /***************************************************************************** Note that this VARARGS function must be NULL (not 0, this must be a pointer) terminated */ int mprReallocStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, int existingLen, const char *delim, const char *src,...) { va_list ap; int rc; va_start(ap, src); rc = mprCoreStrcat(MPR_LOC_PASS(ctx, loc), destp, destMax, existingLen, delim, src, ap); va_end(ap); return rc; } /******************************************************************************/ int mprStrlen(const char *src, int max) { int len; len = strlen(src); if (len >= max) { mprAssert(0); return MPR_ERR_WONT_FIT; } return len; } /******************************************************************************/ char *mprStrTrim(char *str, const char *set) { int len, i; if (str == 0 || set == 0) { return str; } i = strspn(str, set); str += i; len = strlen(str); while (strspn(&str[len - 1], set) > 0) { str[len - 1] = '\0'; len--; } return str; } /******************************************************************************/ /* * Map a string to lower case (overwrites original string) */ char *mprStrLower(char *str) { char *cp; mprAssert(str); if (str == 0) { return 0; } for (cp = str; *cp; cp++) { if (isupper(*cp)) { *cp = (char) tolower(*cp); } } return str; } /******************************************************************************/ /* * Map a string to upper case (overwrites buffer) */ char *mprStrUpper(char *str) { char *cp; mprAssert(str); if (str == 0) { return 0; } for (cp = str; *cp; cp++) { if (islower(*cp)) { *cp = (char) toupper(*cp); } } return str; } /******************************************************************************/ /* * Case insensitive string comparison. Stop at the end of str1. */ int mprStrcmpAnyCase(const char *str1, const char *str2) { int rc; if (str1 == 0 || str2 == 0) { return -1; } if (str1 == str2) { return 0; } for (rc = 0; *str1 && rc == 0; str1++, str2++) { rc = tolower(*str1) - tolower(*str2); } if (*str2) { return -1; } return rc; } /******************************************************************************/ /* * Case insensitive string comparison. Limited by length */ int mprStrcmpAnyCaseCount(const char *str1, const char *str2, int len) { int rc; if (str1 == 0 || str2 == 0) { return -1; } if (str1 == str2) { return 0; } for (rc = 0; len-- > 0 && *str1 && rc == 0; str1++, str2++) { rc = tolower(*str1) - tolower(*str2); } return rc; } /******************************************************************************/ /* * Return the last portion of a pathname */ const char *mprGetBaseName(const char *name) { char *cp; cp = strrchr(name, '/'); if (cp == 0) { cp = strrchr(name, '\\'); if (cp == 0) { return name; } } if (cp == name) { if (cp[1] == '\0') { return name; } } else { if (cp[1] == '\0') { return ""; } } return &cp[1]; } /******************************************************************************/ /* * Return the directory portion of a pathname into the users buffer. */ char *mprGetDirName(char *buf, int bufsize, const char *path) { char *cp; int dlen; mprAssert(path); mprAssert(buf); mprAssert(bufsize > 0); cp = strrchr(path, '/'); if (cp == 0) { #if WIN cp = strrchr(path, '\\'); if (cp == 0) #endif { buf[0] = '\0'; return buf; } } if (cp == path && cp[1] == '\0') { strcpy(buf, "."); return buf; } dlen = cp - path; if (dlen < bufsize) { if (dlen == 0) { dlen++; } mprMemcpy(buf, bufsize, path, dlen); buf[dlen] = '\0'; return buf; } return 0; } /******************************************************************************/ /* * Thread-safe wrapping of strtok. Note "str" is modifed as per strtok() */ char *mprStrTok(char *str, const char *delim, char **last) { char *start, *end; int i; start = str ? str : *last; if (start == 0) { return 0; } i = strspn(start, delim); start += i; if (*start == '\0') { *last = 0; return 0; } end = strpbrk(start, delim); if (end) { *end++ = '\0'; i = strspn(end, delim); end += i; } *last = end; return start; } /******************************************************************************/ /* * Split the buffer into word tokens */ char *mprGetWordTok(char *buf, int bufsize, const char *str, const char *delim, const char **tok) { const char *start, *end; int i, len; start = str ? str : *tok; if (start == 0) { return 0; } i = strspn(start, delim); start += i; if (*start =='\0') { *tok = 0; return 0; } end = strpbrk(start, delim); if (end) { len = min(end - start, bufsize - 1); mprMemcpy(buf, bufsize, start, len); buf[len] = '\0'; } else { if (mprStrcpy(buf, bufsize, start) < 0) { buf[bufsize - 1] = '\0'; return 0; } buf[bufsize - 1] = '\0'; } *tok = end; return buf; } /******************************************************************************/ /* * Format a number as a string. */ char *mprItoa(char *buf, int size, int value) { char numBuf[16]; char *cp, *dp, *endp; int negative; cp = &numBuf[sizeof(numBuf)]; *--cp = '\0'; if (value < 0) { negative = 1; value = -value; size--; } else { negative = 0; } do { *--cp = '0' + (value % 10); value /= 10; } while (value > 0); if (negative) { *--cp = '-'; } dp = buf; endp = &buf[size]; while (dp < endp && *cp) { *dp++ = *cp++; } *dp++ = '\0'; return buf; } /******************************************************************************/ /* * Parse an ascii number. Supports radix 10 or 16. */ int mprAtoi(const char *str, int radix) { int c, val, negative; mprAssert(radix == 10 || radix == 16); if (str == 0) { return 0; } val = 0; if (radix == 10 && *str == '-') { negative = 1; str++; } else { negative = 0; } if (radix == 10) { while (*str && isdigit(*str)) { val = (val * radix) + *str - '0'; str++; } } else if (radix == 16) { if (*str == '0' && tolower(str[1]) == 'x') { str += 2; } while (*str) { c = tolower(*str); if (isdigit(c)) { val = (val * radix) + c - '0'; } else if (c >= 'a' && c <= 'f') { val = (val * radix) + c - 'a' + 10; } else { break; } str++; } } return (negative) ? -val: val; } /******************************************************************************/ /* * Make an argv array. Caller must free by calling mprFree(argv) to free * everything. */ int mprMakeArgv(MprCtx ctx, const char *program, const char *cmd, char ***argvp, int *argcp) { char *cp, **argv, *buf, *args; int size, argc; /* * Allocate one buffer for argv and the actual args themselves */ size = strlen(cmd) + 1; buf = (char*) mprAlloc(ctx, (MPR_MAX_ARGC * sizeof(char*)) + size); if (buf == 0) { return MPR_ERR_MEMORY; } args = &buf[MPR_MAX_ARGC * sizeof(char*)]; strcpy(args, cmd); argv = (char**) buf; argc = 0; if (program) { argv[argc++] = (char*) mprStrdup(ctx, program); } for (cp = args; cp && *cp != '\0'; argc++) { if (argc >= MPR_MAX_ARGC) { mprAssert(argc < MPR_MAX_ARGC); mprFree(buf); *argvp = 0; if (argcp) { *argcp = 0; } return MPR_ERR_TOO_MANY; } while (isspace(*cp)) { cp++; } if (*cp == '\0') { break; } if (*cp == '"') { cp++; argv[argc] = cp; while ((*cp != '\0') && (*cp != '"')) { cp++; } } else { argv[argc] = cp; while (*cp != '\0' && !isspace(*cp)) { cp++; } } if (*cp != '\0') { *cp++ = '\0'; } } argv[argc] = 0; if (argcp) { *argcp = argc; } *argvp = argv; return argc; } /******************************************************************************/ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim:tw=78 * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */