/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This 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 2.1 of the license, or (at * your option) any later version. * * This 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 this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This 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 2.1 of the license, or (at * your option) any later version. * * This 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 this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ #include "config.h" #include "ctbase.h" #include "ctstrutils.h" #include #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SYS_TERMIO_H #include #endif CENTERROR CTAllocateString( PCSTR pszInputString, PSTR * ppszOutputString ) { CENTERROR ceError = CENTERROR_SUCCESS; size_t len = 0; PSTR pszOutputString = NULL; if (!pszInputString || !ppszOutputString){ ceError = CENTERROR_INVALID_PARAMETER; BAIL_ON_CENTERIS_ERROR(ceError); } len = strlen(pszInputString); ceError = CTAllocateMemory(len+1, (PVOID *)&pszOutputString); BAIL_ON_CENTERIS_ERROR(ceError); memcpy(pszOutputString, pszInputString, len); pszOutputString[len] = 0; error: *ppszOutputString = pszOutputString; return(ceError); } CENTERROR CTStrndup( PCSTR pszInputString, size_t size2, PSTR * ppszOutputString ) { CENTERROR ceError = CENTERROR_SUCCESS; size_t copylen = 0; PSTR pszOutputString = NULL; if (!pszInputString || !ppszOutputString){ ceError = CENTERROR_INVALID_PARAMETER; BAIL_ON_CENTERIS_ERROR(ceError); } copylen = strlen(pszInputString); if(copylen > size2) copylen = size2; ceError = CTAllocateMemory(copylen+1, (PVOID *)&pszOutputString); BAIL_ON_CENTERIS_ERROR(ceError); memcpy(pszOutputString, pszInputString, copylen); pszOutputString[copylen] = 0; error: *ppszOutputString = pszOutputString; return(ceError); } void CTFreeString( PSTR pszString ) { if (pszString) { CTFreeMemory(pszString); } } void CTFreeStringArray( PSTR * ppStringArray, DWORD dwCount ) { DWORD i; if ( ppStringArray ) { for(i = 0; i < dwCount; i++) { if (ppStringArray[i]) { CTFreeString(ppStringArray[i]); } } CTFreeMemory(ppStringArray); } return; } void CTFreeNullTerminatedStringArray( PSTR * ppStringArray ) { if ( ppStringArray ) { size_t i; for(i = 0; ppStringArray[i] != NULL; i++) { CTFreeString(ppStringArray[i]); } CTFreeMemory(ppStringArray); } return; } void CTStrToUpper( PSTR pszString ) { //PSTR pszTmp = pszString; if (pszString != NULL) { while (*pszString != '\0') { *pszString = toupper((int)*pszString); pszString++; } } } void CTStrToLower( PSTR pszString ) { //PSTR pszTmp = pszString; if (pszString != NULL) { while (*pszString != '\0') { *pszString = tolower((int)*pszString); pszString++; } } } CENTERROR CTEscapeString( PCSTR pszOrig, PSTR * ppszEscapedString ) { CENTERROR ceError = CENTERROR_SUCCESS; int nQuotes = 0; PCSTR pszTmp = pszOrig; PSTR pszNew = NULL; PSTR pszNewTmp = NULL; if ( !ppszEscapedString || !pszOrig ) { ceError = CENTERROR_INVALID_PARAMETER; BAIL_ON_CENTERIS_ERROR(ceError); } while(pszTmp && *pszTmp) { if (*pszTmp=='\'') { nQuotes++; } pszTmp++; } if (!nQuotes) { ceError = CTAllocateString(pszOrig, &pszNew); BAIL_ON_CENTERIS_ERROR(ceError); } else { /* * We are going to escape each single quote and enclose it in two other * single-quotes */ ceError = CTAllocateMemory( strlen(pszOrig)+3*nQuotes+1, (PVOID*)&pszNew ); BAIL_ON_CENTERIS_ERROR(ceError); pszTmp = pszOrig; pszNewTmp = pszNew; while(pszTmp && *pszTmp) { if (*pszTmp=='\'') { *pszNewTmp++='\''; *pszNewTmp++='\\'; *pszNewTmp++='\''; *pszNewTmp++='\''; pszTmp++; } else { *pszNewTmp++ = *pszTmp++; } } *pszNewTmp = '\0'; } *ppszEscapedString = pszNew; pszNew = NULL; error: CT_SAFE_FREE_STRING(pszNew); return ceError; } void CTStripLeadingWhitespace( PSTR pszString ) { PSTR pszNew = pszString; PSTR pszTmp = pszString; if (pszString == NULL || *pszString == '\0' || !isspace((int)*pszString)) { return; } while (pszTmp != NULL && *pszTmp != '\0' && isspace((int)*pszTmp)) { pszTmp++; } while (pszTmp != NULL && *pszTmp != '\0') { *pszNew++ = *pszTmp++; } *pszNew = '\0'; } void CTStripTrailingWhitespace( PSTR pszString ) { PSTR pszLastSpace = NULL; PSTR pszTmp = pszString; if (pszString == NULL || *pszString == '\0') { return; } while (pszTmp != NULL && *pszTmp != '\0') { pszLastSpace = (isspace((int)*pszTmp) ? (pszLastSpace ? pszLastSpace : pszTmp) : NULL); pszTmp++; } if (pszLastSpace != NULL) { *pszLastSpace = '\0'; } } // Remove leading whitespace and newline characters which are added by libxml2 only. void CTRemoveLeadingWhitespacesOnly( PSTR pszString ) { PSTR pszNew = pszString; PSTR pszTmp = pszString; if (pszString == NULL || *pszString == '\0' || !isspace((int)*pszString)) { return; } if( *pszTmp == '\n' ) pszTmp++; while (pszTmp != NULL && *pszTmp != '\0' && isspace((int)*pszTmp)) { if ( *pszTmp == '\n' ) break; pszTmp++; } while (pszTmp != NULL && *pszTmp != '\0') { *pszNew++ = *pszTmp++; } *pszNew = '\0'; } // Remove trailing whitespace and newline characters which are added by libxml2 only. void CTRemoveTrailingWhitespacesOnly( PSTR pszString ) { PSTR pszLastNewLine = NULL; PSTR pszTmp = pszString; if (pszString == NULL || *pszString == '\0') { return; } while (pszTmp != NULL && *pszTmp != '\0') { if(*pszTmp == '\n') pszLastNewLine = pszTmp; pszTmp++; } if (pszLastNewLine != NULL) { if( *(pszLastNewLine-1) != '\n' ){ *pszLastNewLine = '\n'; pszLastNewLine++; } *pszLastNewLine = '\0'; } } void CTStripWhitespace( PSTR pszString ) { if (pszString == NULL || *pszString == '\0') { return; } CTStripLeadingWhitespace(pszString); CTStripTrailingWhitespace(pszString); } CENTERROR CTAllocateStringPrintfV( PSTR* result, PCSTR format, va_list args ) { CENTERROR ceError = CENTERROR_SUCCESS; char *smallBuffer; unsigned int bufsize; int requiredLength; unsigned int newRequiredLength; PSTR outputString = NULL; va_list args2; va_copy(args2, args); bufsize = 4; /* Use a small buffer in case libc does not like NULL */ do { ceError = CTAllocateMemory(bufsize, (PVOID*) &smallBuffer); CLEANUP_ON_CENTERROR(ceError); requiredLength = vsnprintf(smallBuffer, bufsize, format, args); if (requiredLength < 0) { bufsize *= 2; } CTFreeMemory(smallBuffer); } while (requiredLength < 0); if (requiredLength >= (UINT32_MAX - 1)) { ceError = CENTERROR_OUT_OF_MEMORY; CLEANUP_ON_CENTERROR(ceError); } ceError = CTAllocateMemory(requiredLength + 2, (PVOID*)&outputString); CLEANUP_ON_CENTERROR(ceError); newRequiredLength = vsnprintf(outputString, requiredLength + 1, format, args2); if (newRequiredLength < 0) { ceError = CTMapSystemError(errno); CLEANUP_ON_CENTERROR(ceError); } else if (newRequiredLength > requiredLength) { /* unexpected, ideally should log something, or use better error code */ ceError = CENTERROR_OUT_OF_MEMORY; CLEANUP_ON_CENTERROR(ceError); } else if (newRequiredLength < requiredLength) { /* unexpected, ideally should log something -- do not need an error, though */ } cleanup: va_end(args2); if (ceError) { if (outputString) { CTFreeMemory(outputString); outputString = NULL; } } *result = outputString; return ceError; } CENTERROR CTAllocateStringPrintf( PSTR* result, PCSTR format, ... ) { CENTERROR ceError; va_list args; va_start(args, format); ceError = CTAllocateStringPrintfV(result, format, args); va_end(args); return ceError; } BOOLEAN CTStrStartsWith( PCSTR str, PCSTR prefix ) { if(prefix == NULL) return TRUE; if(str == NULL) return FALSE; return strncmp(str, prefix, strlen(prefix)) == 0; } BOOLEAN CTStrEndsWith( PCSTR str, PCSTR suffix ) { size_t strLen, suffixLen; if(suffix == NULL) return TRUE; if(str == NULL) return FALSE; strLen = strlen(str); suffixLen = strlen(suffix); if(suffixLen > strLen) return FALSE; return strcmp(str + strLen - suffixLen, suffix) == 0; } static CENTERROR NullTerminate(StringBuffer *buffer) { CENTERROR ceError = CTArrayAppend(buffer, 1, "\0", 1); CLEANUP_ON_CENTERROR(ceError); buffer->size--; cleanup: return ceError; } CENTERROR CTStringBufferConstruct(StringBuffer* buffer) { CENTERROR ceError = CENTERROR_SUCCESS; BAIL_ON_CENTERIS_ERROR(ceError = CTArrayConstruct(buffer, 1)); error: return ceError; } void CTStringBufferDestroy(StringBuffer* buffer) { CTArrayFree(buffer); } void CTStringBufferClear( StringBuffer* buffer ) { CTArrayRemove(buffer, 0, 1, buffer->size); NullTerminate(buffer); } char* CTStringBufferFreeze(StringBuffer* buffer) { char* data = buffer->data; buffer->size = buffer->capacity = 0; buffer->data = NULL; return data; } CENTERROR EnsureSpace(StringBuffer* buffer, unsigned int space) { CENTERROR ceError = CENTERROR_SUCCESS; if(space <= buffer->capacity) { ceError = CTSetCapacity(buffer, 1, space + 1); CLEANUP_ON_CENTERROR(ceError); ceError = NullTerminate(buffer); CLEANUP_ON_CENTERROR(ceError); } cleanup: return ceError; } CENTERROR CTStringBufferAppend(StringBuffer* buffer, const char* str) { return CTStringBufferAppendLength(buffer, str, strlen(str)); } CENTERROR CTStringBufferAppendLength(StringBuffer* buffer, const char* str, unsigned int length) { CENTERROR ceError = CTArrayAppend(buffer, 1, str, length); CLEANUP_ON_CENTERROR(ceError); ceError = NullTerminate(buffer); CLEANUP_ON_CENTERROR(ceError); cleanup: return ceError; } CENTERROR CTStringBufferAppendChar(StringBuffer* buffer, char c) { return CTStringBufferAppendLength(buffer, &c, 1); } BOOLEAN CTIsAllDigit( PCSTR pszVal ) { while (pszVal && *pszVal) if (!isdigit((int)*pszVal++)) return FALSE; return TRUE; } void CTFreeParseTokenContents(CTParseToken *token) { CT_SAFE_FREE_STRING(token->value); CT_SAFE_FREE_STRING(token->trailingSeparator); } size_t CTGetTokenLen(const CTParseToken *token) { size_t len = 0; if(token->value != NULL) { len += strlen(token->value); } if(token->trailingSeparator != NULL) { len += strlen(token->trailingSeparator); } return len; } void CTAppendTokenString(char **pos, const CTParseToken *token) { size_t len; if(token->value != NULL) { len = strlen(token->value); memcpy(*pos, token->value, len); *pos += len; } if(token->trailingSeparator != NULL) { len = strlen(token->trailingSeparator); memcpy(*pos, token->trailingSeparator, len); *pos += len; } } CENTERROR CTReadToken(const char **pos, CTParseToken *store, const char *includeSeparators, const char *excludeSeparators, const char *trimBack) { CENTERROR ceError = CENTERROR_SUCCESS; char const * token_start = *pos; char const * white_start, * white_end; memset(store, 0, sizeof(*store)); while(**pos != '\0' && strchr(includeSeparators, **pos) == NULL && strchr(excludeSeparators, **pos) == NULL) { (*pos)++; } white_start = *pos; while (**pos != '\0' && strchr(includeSeparators, **pos) != NULL) { (*pos)++; } white_end = *pos; while (white_start > token_start && white_start[-1] != '\0' && strchr(trimBack, white_start[-1]) != NULL) { white_start--; } if(token_start != white_start) { ceError = CTStrndup(token_start, white_start - token_start, &store->value); BAIL_ON_CENTERIS_ERROR(ceError); } if(white_start != white_end) { ceError = CTStrndup(white_start, white_end - white_start, &store->trailingSeparator); BAIL_ON_CENTERIS_ERROR(ceError); } error: if(!CENTERROR_IS_OK(ceError)) CTFreeParseTokenContents(store); return ceError; } CENTERROR CTCopyTokenContents(CTParseToken *dest, const CTParseToken *source) { CENTERROR ceError = CENTERROR_SUCCESS; memset(dest, 0, sizeof(*dest)); if(source->value != NULL) { ceError = CTDupOrNullStr(source->value, &dest->value); BAIL_ON_CENTERIS_ERROR(ceError); } if(source->trailingSeparator != NULL) { ceError = CTDupOrNullStr(source->trailingSeparator, &dest->trailingSeparator); BAIL_ON_CENTERIS_ERROR(ceError); } error: if(!CENTERROR_IS_OK(ceError)) CTFreeParseTokenContents(dest); return ceError; } CENTERROR CTDupOrNullStr(const char *src, char **dest) { if(src == NULL) { *dest = NULL; return CENTERROR_SUCCESS; } return CTStrdup(src, dest); } CENTERROR CTWriteToken(FILE *file, CTParseToken *token) { const char *value = token->value; const char *white = token->trailingSeparator; if(value == NULL) value = ""; if(white == NULL) white = ""; return CTFilePrintf(file, "%s%s", value, white); } void CTFreeParseToken(CTParseToken **token) { if(*token != NULL) { CTFreeParseTokenContents(*token); CT_SAFE_FREE_MEMORY(*token); } } CENTERROR CTCopyToken(const CTParseToken *source, CTParseToken **dest) { CENTERROR ceError = CENTERROR_SUCCESS; *dest = NULL; if(source != NULL) { BAIL_ON_CENTERIS_ERROR(ceError = CTAllocateMemory(sizeof(CTParseToken), (void **) dest)); BAIL_ON_CENTERIS_ERROR(ceError = CTCopyTokenContents(*dest, source)); } error: if(!CENTERROR_IS_OK(ceError)) { CTFreeParseToken(dest); } return ceError; } CENTERROR CTGetTerminalWidth(int terminalFid, int *width) { CENTERROR ceError = CENTERROR_SUCCESS; #ifdef TIOCGWINSZ struct winsize size = {0}; const char *fromEnv = getenv("COLUMNS"); if(ioctl(terminalFid, TIOCGWINSZ, &size) == -1 || size.ws_col == 0) { if(fromEnv != NULL) { size.ws_col = atoi(fromEnv); } else { ceError = CTMapSystemError(errno); CLEANUP_ON_CENTERROR(ceError); } } if(size.ws_col < 1) { ceError = CENTERROR_INVALID_OPERATION; CLEANUP_ON_CENTERROR(ceError); } *width = size.ws_col; #else *width = -1; #endif cleanup: return ceError; } static int CharLen(char c, int tabWidth) { switch(c) { case '\t': return tabWidth; default: return 1; } } //Find out how far the next line is indented static int NextLineIndent(PCSTR input, int tabWidth) { int indent = 0; PCSTR nextLine1 = strchr(input, '\n'); PCSTR nextLine2 = strchr(input, '\r'); if(nextLine2 != NULL && (nextLine1 == NULL || nextLine2 < nextLine1)) nextLine1 = nextLine2; //This is the last line if(nextLine1 == NULL) return -1; if(*nextLine1 == '\r') nextLine1++; if(*nextLine1 == '\n') nextLine1++; while(*nextLine1 == ' ' || *nextLine1 == '\t') { indent += CharLen(*nextLine1, tabWidth); nextLine1++; } if(*nextLine1 == '\r' || *nextLine1 == '\n' || *nextLine1 == '\0') { //Blank lines don't have indents indent = -1; } return indent; } CENTERROR CTWordWrap(PCSTR input, PSTR *output, int tabWidth, int columns) { CENTERROR ceError = CENTERROR_SUCCESS; StringBuffer result; int pos = 0; int start; int column; int i; int previousIndent = -1; int nextIndent; memset(&result, 0, sizeof(result)); while(input[pos] != '\0') { int indentColumns; int lastWhite = 0; start = pos; column = 0; while(input[pos] == ' ' || input[pos] == '\t') { column += CharLen(input[pos], tabWidth); pos++; } indentColumns = column; nextIndent = NextLineIndent(input + pos, tabWidth); if(indentColumns > 0 && (nextIndent == indentColumns || previousIndent == indentColumns)) { //The line following this one is indented the same amount. If this //line wraps, we'll indent it an extra tab to differentiate the //two lines. indentColumns += tabWidth; } if(input[pos] == '\r' && input[pos] == '\n') { //Blank lines don't have indents previousIndent = -1; } else previousIndent = column; for(i = 0; i < column; i++) { ceError = CTStringBufferAppendLength(&result, " ", 1); CLEANUP_ON_CENTERROR(ceError); } start = pos; while(input[pos] != '\n' && input[pos] != '\r' && input[pos] != '\0') { if(input[pos] == '\f') { //This marks how far to indent the text ceError = CTStringBufferAppendLength(&result, input + start, pos - start); CLEANUP_ON_CENTERROR(ceError); indentColumns = column; pos++; start = pos; } if(column + CharLen(input[pos], tabWidth) > columns && columns != -1) { //Wrap the line //If there was any white space in this line, break it there, //otherwise break it in the middle of the word if(lastWhite > start) pos = lastWhite; //First the text in this line ceError = CTStringBufferAppendLength(&result, input + start, pos - start); CLEANUP_ON_CENTERROR(ceError); //Now add the newline ceError = CTStringBufferAppendLength(&result, "\n", 1); CLEANUP_ON_CENTERROR(ceError); //Finally the indent for the new line for(column = 0; column < indentColumns; column++) { ceError = CTStringBufferAppendLength(&result, " ", 1); CLEANUP_ON_CENTERROR(ceError); } //Skip any whitespace while(input[pos] == ' ' || input[pos] == '\t') { pos++; } start = pos; continue; } if(isspace((int)input[pos])) lastWhite = pos; if(input[pos] == '\t') { //First add the text before the tab ceError = CTStringBufferAppendLength(&result, input + start, pos - start); CLEANUP_ON_CENTERROR(ceError); //Now expand the tab into spaces for(i = 0; i < tabWidth; i++) { ceError = CTStringBufferAppendLength(&result, " ", 1); CLEANUP_ON_CENTERROR(ceError); } start = pos + 1; } column += CharLen(input[pos], tabWidth); pos++; } if(input[pos] == '\n') pos++; if(input[pos] == '\r') pos++; //Copy the line ceError = CTStringBufferAppendLength(&result, input + start, pos - start); CLEANUP_ON_CENTERROR(ceError); } ceError = CTStringBufferAppendLength(&result, "\0", 1); CLEANUP_ON_CENTERROR(ceError); *output = result.data; result.data = NULL; cleanup: CTStringBufferDestroy(&result); return ceError; }