From 60a426c0742b3e7d8c5b557c7578df6eeeea377b Mon Sep 17 00:00:00 2001 From: brianluau Date: Wed, 5 Dec 2012 02:53:33 +0000 Subject: - Undid r16968: SVN Replaced with source:/trunk/src/@16966 (tid:74924). [16969:16991/trunk/src/] will be re-committed in the next 24 hours. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@16992 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/common/strlib.c | 1729 ++++++++++++++++++++++++++------------------------- 1 file changed, 881 insertions(+), 848 deletions(-) (limited to 'src/common/strlib.c') diff --git a/src/common/strlib.c b/src/common/strlib.c index 89aac3b40..dfacbf136 100644 --- a/src/common/strlib.c +++ b/src/common/strlib.c @@ -14,348 +14,360 @@ #define J_MAX_MALLOC_SIZE 65535 // escapes a string in-place (' -> \' , \ -> \\ , % -> _) -char *jstrescape(char *pt) +char* jstrescape (char* pt) { - //copy from here - char *ptr; - int i = 0, j = 0; - - //copy string to temporary - CREATE(ptr, char, J_MAX_MALLOC_SIZE); - strcpy(ptr,pt); - - while (ptr[i] != '\0') { - switch (ptr[i]) { - case '\'': - pt[j++] = '\\'; - pt[j++] = ptr[i++]; - break; - case '\\': - pt[j++] = '\\'; - pt[j++] = ptr[i++]; - break; - case '%': - pt[j++] = '_'; - i++; - break; - default: - pt[j++] = ptr[i++]; - } - } - pt[j++] = '\0'; - aFree(ptr); - return pt; + //copy from here + char *ptr; + int i = 0, j = 0; + + //copy string to temporary + CREATE(ptr, char, J_MAX_MALLOC_SIZE); + strcpy(ptr,pt); + + while (ptr[i] != '\0') { + switch (ptr[i]) { + case '\'': + pt[j++] = '\\'; + pt[j++] = ptr[i++]; + break; + case '\\': + pt[j++] = '\\'; + pt[j++] = ptr[i++]; + break; + case '%': + pt[j++] = '_'; i++; + break; + default: + pt[j++] = ptr[i++]; + } + } + pt[j++] = '\0'; + aFree(ptr); + return pt; } // escapes a string into a provided buffer -char *jstrescapecpy(char *pt, const char *spt) +char* jstrescapecpy (char* pt, const char* spt) { - //copy from here - //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time - //a escape character is found, the target's final length increases! [Skotlex] - int i =0, j=0; - - if (!spt) { //Return an empty string [Skotlex] - pt[0] = '\0'; - return &pt[0]; - } - - while (spt[i] != '\0') { - switch (spt[i]) { - case '\'': - pt[j++] = '\\'; - pt[j++] = spt[i++]; - break; - case '\\': - pt[j++] = '\\'; - pt[j++] = spt[i++]; - break; - case '%': - pt[j++] = '_'; - i++; - break; - default: - pt[j++] = spt[i++]; - } - } - pt[j++] = '\0'; - return &pt[0]; + //copy from here + //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time + //a escape character is found, the target's final length increases! [Skotlex] + int i =0, j=0; + + if (!spt) { //Return an empty string [Skotlex] + pt[0] = '\0'; + return &pt[0]; + } + + while (spt[i] != '\0') { + switch (spt[i]) { + case '\'': + pt[j++] = '\\'; + pt[j++] = spt[i++]; + break; + case '\\': + pt[j++] = '\\'; + pt[j++] = spt[i++]; + break; + case '%': + pt[j++] = '_'; i++; + break; + default: + pt[j++] = spt[i++]; + } + } + pt[j++] = '\0'; + return &pt[0]; } // escapes exactly 'size' bytes of a string into a provided buffer -int jmemescapecpy(char *pt, const char *spt, int size) +int jmemescapecpy (char* pt, const char* spt, int size) { - //copy from here - int i =0, j=0; - - while (i < size) { - switch (spt[i]) { - case '\'': - pt[j++] = '\\'; - pt[j++] = spt[i++]; - break; - case '\\': - pt[j++] = '\\'; - pt[j++] = spt[i++]; - break; - case '%': - pt[j++] = '_'; - i++; - break; - default: - pt[j++] = spt[i++]; - } - } - // copy size is 0 ~ (j-1) - return j; + //copy from here + int i =0, j=0; + + while (i < size) { + switch (spt[i]) { + case '\'': + pt[j++] = '\\'; + pt[j++] = spt[i++]; + break; + case '\\': + pt[j++] = '\\'; + pt[j++] = spt[i++]; + break; + case '%': + pt[j++] = '_'; i++; + break; + default: + pt[j++] = spt[i++]; + } + } + // copy size is 0 ~ (j-1) + return j; } // Function to suppress control characters in a string. -int remove_control_chars(char *str) +int remove_control_chars(char* str) { - int i; - int change = 0; + int i; + int change = 0; - for (i = 0; str[i]; i++) { - if (ISCNTRL(str[i])) { - str[i] = '_'; - change = 1; - } - } + for(i = 0; str[i]; i++) { + if (ISCNTRL(str[i])) { + str[i] = '_'; + change = 1; + } + } - return change; + return change; } // Removes characters identified by ISSPACE from the start and end of the string // NOTE: make sure the string is not const!! -char *trim(char *str) +char* trim(char* str) { - size_t start; - size_t end; - - if (str == NULL) - return str; - - // get start position - for (start = 0; str[start] && ISSPACE(str[start]); ++start) - ; - // get end position - for (end = strlen(str); start < end && str[end-1] && ISSPACE(str[end-1]); --end) - ; - // trim - if (start == end) - *str = '\0';// empty string - else { - // move string with nul terminator - str[end] = '\0'; - memmove(str,str+start,end-start+1); - } - return str; + size_t start; + size_t end; + + if( str == NULL ) + return str; + + // get start position + for( start = 0; str[start] && ISSPACE(str[start]); ++start ) + ; + // get end position + for( end = strlen(str); start < end && str[end-1] && ISSPACE(str[end-1]); --end ) + ; + // trim + if( start == end ) + *str = '\0';// empty string + else + {// move string with nul terminator + str[end] = '\0'; + memmove(str,str+start,end-start+1); + } + return str; } // Converts one or more consecutive occurences of the delimiters into a single space // and removes such occurences from the beginning and end of string // NOTE: make sure the string is not const!! -char *normalize_name(char *str,const char *delims) +char* normalize_name(char* str,const char* delims) { - char *in = str; - char *out = str; - int put_space = 0; - - if (str == NULL || delims == NULL) - return str; - - // trim start of string - while (*in && strchr(delims,*in)) - ++in; - while (*in) { - if (put_space) { - // replace trim characters with a single space - *out = ' '; - ++out; - } - // copy non trim characters - while (*in && !strchr(delims,*in)) { - *out = *in; - ++out; - ++in; - } - // skip trim characters - while (*in && strchr(delims,*in)) - ++in; - put_space = 1; - } - *out = '\0'; - return str; + char* in = str; + char* out = str; + int put_space = 0; + + if( str == NULL || delims == NULL ) + return str; + + // trim start of string + while( *in && strchr(delims,*in) ) + ++in; + while( *in ) + { + if( put_space ) + {// replace trim characters with a single space + *out = ' '; + ++out; + } + // copy non trim characters + while( *in && !strchr(delims,*in) ) + { + *out = *in; + ++out; + ++in; + } + // skip trim characters + while( *in && strchr(delims,*in) ) + ++in; + put_space = 1; + } + *out = '\0'; + return str; } -//stristr: Case insensitive version of strstr, code taken from +//stristr: Case insensitive version of strstr, code taken from //http://www.daniweb.com/code/snippet313.html, Dave Sinkula // -const char *stristr(const char *haystack, const char *needle) +const char* stristr(const char* haystack, const char* needle) { - if (!*needle) { - return haystack; - } - for (; *haystack; ++haystack) { - if (TOUPPER(*haystack) == TOUPPER(*needle)) { - // matched starting char -- loop through remaining chars - const char *h, *n; - for (h = haystack, n = needle; *h && *n; ++h, ++n) { - if (TOUPPER(*h) != TOUPPER(*n)) { - break; - } - } - if (!*n) { // matched all of 'needle' to null termination - return haystack; // return the start of the match - } - } - } - return 0; + if ( !*needle ) + { + return haystack; + } + for ( ; *haystack; ++haystack ) + { + if ( TOUPPER(*haystack) == TOUPPER(*needle) ) + { + // matched starting char -- loop through remaining chars + const char *h, *n; + for ( h = haystack, n = needle; *h && *n; ++h, ++n ) + { + if ( TOUPPER(*h) != TOUPPER(*n) ) + { + break; + } + } + if ( !*n ) // matched all of 'needle' to null termination + { + return haystack; // return the start of the match + } + } + } + return 0; } #ifdef __WIN32 -char *_strtok_r(char *s1, const char *s2, char **lasts) +char* _strtok_r(char *s1, const char *s2, char **lasts) { - char *ret; - - if (s1 == NULL) - s1 = *lasts; - while (*s1 && strchr(s2, *s1)) - ++s1; - if (*s1 == '\0') - return NULL; - ret = s1; - while (*s1 && !strchr(s2, *s1)) - ++s1; - if (*s1) - *s1++ = '\0'; - *lasts = s1; - return ret; + char *ret; + + if (s1 == NULL) + s1 = *lasts; + while(*s1 && strchr(s2, *s1)) + ++s1; + if(*s1 == '\0') + return NULL; + ret = s1; + while(*s1 && !strchr(s2, *s1)) + ++s1; + if(*s1) + *s1++ = '\0'; + *lasts = s1; + return ret; } #endif #if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN) /* Find the length of STRING, but scan at most MAXLEN characters. If no '\0' terminator is found in that many characters, return MAXLEN. */ -size_t strnlen(const char *string, size_t maxlen) +size_t strnlen (const char* string, size_t maxlen) { - const char *end = (const char *)memchr(string, '\0', maxlen); - return end ? (size_t)(end - string) : maxlen; + const char* end = (const char*)memchr(string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; } #endif #if defined(WIN32) && defined(_MSC_VER) && _MSC_VER <= 1200 -uint64 strtoull(const char *str, char **endptr, int base) +uint64 strtoull(const char* str, char** endptr, int base) { - uint64 result; - int count; - int n; - - if (base == 0) { - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) - base = 16; - else if (str[0] == '0') - base = 8; - else - base = 10; - } - - if (base == 8) - count = sscanf(str, "%I64o%n", &result, &n); - else if (base == 10) - count = sscanf(str, "%I64u%n", &result, &n); - else if (base == 16) - count = sscanf(str, "%I64x%n", &result, &n); - else - count = 0; // fail - - if (count < 1) { - errno = EINVAL; - result = 0; - n = 0; - } - - if (endptr) - *endptr = (char *)str + n; - - return result; + uint64 result; + int count; + int n; + + if( base == 0 ) + { + if( str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + base = 16; + else + if( str[0] == '0' ) + base = 8; + else + base = 10; + } + + if( base == 8 ) + count = sscanf(str, "%I64o%n", &result, &n); + else + if( base == 10 ) + count = sscanf(str, "%I64u%n", &result, &n); + else + if( base == 16 ) + count = sscanf(str, "%I64x%n", &result, &n); + else + count = 0; // fail + + if( count < 1 ) + { + errno = EINVAL; + result = 0; + n = 0; + } + + if( endptr ) + *endptr = (char*)str + n; + + return result; } #endif //---------------------------------------------------- // E-mail check: return 0 (not correct) or 1 (valid). //---------------------------------------------------- -int e_mail_check(char *email) +int e_mail_check(char* email) { - char ch; - char *last_arobas; - size_t len = strlen(email); + char ch; + char* last_arobas; + size_t len = strlen(email); - // athena limits - if (len < 3 || len > 39) - return 0; + // athena limits + if (len < 3 || len > 39) + return 0; - // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[len-1] == '@') - return 0; + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[len-1] == '@') + return 0; - if (email[len-1] == '.') - return 0; + if (email[len-1] == '.') + return 0; - last_arobas = strrchr(email, '@'); + last_arobas = strrchr(email, '@'); - if (strstr(last_arobas, "@.") != NULL || strstr(last_arobas, "..") != NULL) - return 0; + if (strstr(last_arobas, "@.") != NULL || strstr(last_arobas, "..") != NULL) + return 0; - for (ch = 1; ch < 32; ch++) - if (strchr(last_arobas, ch) != NULL) - return 0; + for(ch = 1; ch < 32; ch++) + if (strchr(last_arobas, ch) != NULL) + return 0; - if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL) - return 0; + if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL) + return 0; - // all correct - return 1; + // all correct + return 1; } //-------------------------------------------------- // Return numerical value of a switch configuration // on/off, english, français, deutsch, español //-------------------------------------------------- -int config_switch(const char *str) +int config_switch(const char* str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; - return (int)strtol(str, NULL, 0); + return (int)strtol(str, NULL, 0); } /// strncpy that always nul-terminates the string -char *safestrncpy(char *dst, const char *src, size_t n) +char* safestrncpy(char* dst, const char* src, size_t n) { - if (n > 0) { - char *d = dst; - const char *s = src; - d[--n] = '\0';/* nul-terminate string */ - for (; n > 0; --n) { - if ((*d++ = *s++) == '\0') { - /* nul-pad remaining bytes */ - while (--n > 0) - *d++ = '\0'; - break; - } - } - } - return dst; + if( n > 0 ) + { + char* d = dst; + const char* s = src; + d[--n] = '\0';/* nul-terminate string */ + for( ; n > 0; --n ) + { + if( (*d++ = *s++) == '\0' ) + {/* nul-pad remaining bytes */ + while( --n > 0 ) + *d++ = '\0'; + break; + } + } + } + return dst; } /// doesn't crash on null pointer -size_t safestrnlen(const char *string, size_t maxlen) +size_t safestrnlen(const char* string, size_t maxlen) { - return (string != NULL) ? strnlen(string, maxlen) : 0; + return ( string != NULL ) ? strnlen(string, maxlen) : 0; } /// Works like snprintf, but always nul-terminates the buffer. @@ -367,40 +379,41 @@ size_t safestrnlen(const char *string, size_t maxlen) /// @param fmt Format string /// @param ... Format arguments /// @return The size of the string or -1 if the buffer is too small -int safesnprintf(char *buf, size_t sz, const char *fmt, ...) +int safesnprintf(char* buf, size_t sz, const char* fmt, ...) { - va_list ap; - int ret; - - va_start(ap,fmt); - ret = vsnprintf(buf, sz, fmt, ap); - va_end(ap); - if (ret < 0 || (size_t)ret >= sz) { - // overflow - buf[sz-1] = '\0';// always nul-terminate - return -1; - } - return ret; + va_list ap; + int ret; + + va_start(ap,fmt); + ret = vsnprintf(buf, sz, fmt, ap); + va_end(ap); + if( ret < 0 || (size_t)ret >= sz ) + {// overflow + buf[sz-1] = '\0';// always nul-terminate + return -1; + } + return ret; } /// Returns the line of the target position in the string. /// Lines start at 1. -int strline(const char *str, size_t pos) +int strline(const char* str, size_t pos) { - const char *target; - int line; - - if (str == NULL || pos == 0) - return 1; - - target = str+pos; - for (line = 1; ; ++line) { - str = strchr(str, '\n'); - if (str == NULL || target <= str) - break;// found target line - ++str;// skip newline - } - return line; + const char* target; + int line; + + if( str == NULL || pos == 0 ) + return 1; + + target = str+pos; + for( line = 1; ; ++line ) + { + str = strchr(str, '\n'); + if( str == NULL || target <= str ) + break;// found target line + ++str;// skip newline + } + return line; } /// Produces the hexadecimal representation of the given input. @@ -410,18 +423,19 @@ int strline(const char *str, size_t pos) /// @param output Output string /// @param input Binary input buffer /// @param count Number of bytes to convert -bool bin2hex(char *output, unsigned char *input, size_t count) +bool bin2hex(char* output, unsigned char* input, size_t count) { - char toHex[] = "0123456789abcdef"; - size_t i; - - for (i = 0; i < count; ++i) { - *output++ = toHex[(*input & 0xF0) >> 4]; - *output++ = toHex[(*input & 0x0F) >> 0]; - ++input; - } - *output = '\0'; - return true; + char toHex[] = "0123456789abcdef"; + size_t i; + + for( i = 0; i < count; ++i ) + { + *output++ = toHex[(*input & 0xF0) >> 4]; + *output++ = toHex[(*input & 0x0F) >> 0]; + ++input; + } + *output = '\0'; + return true; } @@ -432,134 +446,146 @@ bool bin2hex(char *output, unsigned char *input, size_t count) /// /// @param sv Parse state /// @return 1 if a field was parsed, 0 if already done, -1 on error. -int sv_parse_next(struct s_svstate *sv) +int sv_parse_next(struct s_svstate* sv) { - enum { - START_OF_FIELD, - PARSING_FIELD, - PARSING_C_ESCAPE, - END_OF_FIELD, - TERMINATE, - END - } state; - const char *str; - int len; - enum e_svopt opt; - char delim; - int i; - - if (sv == NULL) - return -1;// error - - str = sv->str; - len = sv->len; - opt = sv->opt; - delim = sv->delim; - - // check opt - if (delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF))) { - ShowError("sv_parse_next: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n"); - return -1;// error - } - if (delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR))) { - ShowError("sv_parse_next: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n"); - return -1;// error - } - - if (sv->done || str == NULL) { - sv->done = true; - return 0;// nothing to parse - } + enum { + START_OF_FIELD, + PARSING_FIELD, + PARSING_C_ESCAPE, + END_OF_FIELD, + TERMINATE, + END + } state; + const char* str; + int len; + enum e_svopt opt; + char delim; + int i; + + if( sv == NULL ) + return -1;// error + + str = sv->str; + len = sv->len; + opt = sv->opt; + delim = sv->delim; + + // check opt + if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) ) + { + ShowError("sv_parse_next: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n"); + return -1;// error + } + if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) ) + { + ShowError("sv_parse_next: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n"); + return -1;// error + } + + if( sv->done || str == NULL ) + { + sv->done = true; + return 0;// nothing to parse + } #define IS_END() ( i >= len ) #define IS_DELIM() ( str[i] == delim ) #define IS_TERMINATOR() ( \ - ((opt&SV_TERMINATE_LF) && str[i] == '\n') || \ - ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \ - ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') ) + ((opt&SV_TERMINATE_LF) && str[i] == '\n') || \ + ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \ + ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') ) #define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' ) #define SET_FIELD_START() sv->start = i #define SET_FIELD_END() sv->end = i - i = sv->off; - state = START_OF_FIELD; - while (state != END) { - switch (state) { - case START_OF_FIELD:// record start of field and start parsing it - SET_FIELD_START(); - state = PARSING_FIELD; - break; - - case PARSING_FIELD:// skip field character - if (IS_END() || IS_DELIM() || IS_TERMINATOR()) - state = END_OF_FIELD; - else if (IS_C_ESCAPE()) - state = PARSING_C_ESCAPE; - else - ++i;// normal character - break; - - case PARSING_C_ESCAPE: { // skip escape sequence (validates it too) - ++i;// '\\' - if (IS_END()) { - ShowError("sv_parse_next: empty escape sequence\n"); - return -1; - } - if (str[i] == 'x') { - // hex escape - ++i;// 'x' - if (IS_END() || !ISXDIGIT(str[i])) { - ShowError("sv_parse_next: \\x with no following hex digits\n"); - return -1; - } - do { - ++i;// hex digit - } while (!IS_END() && ISXDIGIT(str[i])); - } else if (str[i] == '0' || str[i] == '1' || str[i] == '2') { - // octal escape - ++i;// octal digit - if (!IS_END() && str[i] >= '0' && str[i] <= '7') - ++i;// octal digit - if (!IS_END() && str[i] >= '0' && str[i] <= '7') - ++i;// octal digit - } else if (strchr(SV_ESCAPE_C_SUPPORTED, str[i])) { - // supported escape character - ++i; - } else { - ShowError("sv_parse_next: unknown escape sequence \\%c\n", str[i]); - return -1; - } - state = PARSING_FIELD; - break; - } - - case END_OF_FIELD:// record end of field and stop - SET_FIELD_END(); - state = END; - if (IS_END()) - ;// nothing else - else if (IS_DELIM()) - ++i;// delim - else if (IS_TERMINATOR()) - state = TERMINATE; - break; - - case TERMINATE: + i = sv->off; + state = START_OF_FIELD; + while( state != END ) + { + switch( state ) + { + case START_OF_FIELD:// record start of field and start parsing it + SET_FIELD_START(); + state = PARSING_FIELD; + break; + + case PARSING_FIELD:// skip field character + if( IS_END() || IS_DELIM() || IS_TERMINATOR() ) + state = END_OF_FIELD; + else if( IS_C_ESCAPE() ) + state = PARSING_C_ESCAPE; + else + ++i;// normal character + break; + + case PARSING_C_ESCAPE:// skip escape sequence (validates it too) + { + ++i;// '\\' + if( IS_END() ) + { + ShowError("sv_parse_next: empty escape sequence\n"); + return -1; + } + if( str[i] == 'x' ) + {// hex escape + ++i;// 'x' + if( IS_END() || !ISXDIGIT(str[i]) ) + { + ShowError("sv_parse_next: \\x with no following hex digits\n"); + return -1; + } + do{ + ++i;// hex digit + }while( !IS_END() && ISXDIGIT(str[i])); + } + else if( str[i] == '0' || str[i] == '1' || str[i] == '2' ) + {// octal escape + ++i;// octal digit + if( !IS_END() && str[i] >= '0' && str[i] <= '7' ) + ++i;// octal digit + if( !IS_END() && str[i] >= '0' && str[i] <= '7' ) + ++i;// octal digit + } + else if( strchr(SV_ESCAPE_C_SUPPORTED, str[i]) ) + {// supported escape character + ++i; + } + else + { + ShowError("sv_parse_next: unknown escape sequence \\%c\n", str[i]); + return -1; + } + state = PARSING_FIELD; + break; + } + + case END_OF_FIELD:// record end of field and stop + SET_FIELD_END(); + state = END; + if( IS_END() ) + ;// nothing else + else if( IS_DELIM() ) + ++i;// delim + else if( IS_TERMINATOR() ) + state = TERMINATE; + break; + + case TERMINATE: #if 0 - // skip line terminator - if ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') - i += 2;// CRLF - else - ++i;// CR or LF + // skip line terminator + if( (opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n' ) + i += 2;// CRLF + else + ++i;// CR or LF #endif - sv->done = true; - state = END; - break; - } - } - if (IS_END()) - sv->done = true; - sv->off = i; + sv->done = true; + state = END; + break; + } + } + if( IS_END() ) + sv->done = true; + sv->off = i; #undef IS_END #undef IS_DELIM @@ -568,7 +594,7 @@ int sv_parse_next(struct s_svstate *sv) #undef SET_FIELD_START #undef SET_FIELD_END - return 1; + return 1; } @@ -577,13 +603,13 @@ int sv_parse_next(struct s_svstate *sv) /// out_pos[0] and out_pos[1] are the start and end of line. /// Other position pairs are the start and end of fields. /// Returns the number of fields found or -1 if an error occurs. -/// +/// /// out_pos can be NULL. /// If a line terminator is found, the end position is placed there. -/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5] +/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5] /// for the seconds field and so on. /// Unfilled positions are set to -1. -/// +/// /// @param str String to parse /// @param len Length of the string /// @param startoff Where to start parsing @@ -592,34 +618,35 @@ int sv_parse_next(struct s_svstate *sv) /// @param npos Size of the pos array /// @param opt Options that determine the parsing behaviour /// @return Number of fields found in the string or -1 if an error occured -int sv_parse(const char *str, int len, int startoff, char delim, int *out_pos, int npos, enum e_svopt opt) +int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt) { - struct s_svstate sv; - int count; - - // initialize - if (out_pos == NULL) npos = 0; - for (count = 0; count < npos; ++count) - out_pos[count] = -1; - sv.str = str; - sv.len = len; - sv.off = startoff; - sv.opt = opt; - sv.delim = delim; - sv.done = false; - - // parse - count = 0; - if (npos > 0) out_pos[0] = startoff; - while (!sv.done) { - ++count; - if (sv_parse_next(&sv) <= 0) - return -1;// error - if (npos > count*2) out_pos[count*2] = sv.start; - if (npos > count*2+1) out_pos[count*2+1] = sv.end; - } - if (npos > 1) out_pos[1] = sv.off; - return count; + struct s_svstate sv; + int count; + + // initialize + if( out_pos == NULL ) npos = 0; + for( count = 0; count < npos; ++count ) + out_pos[count] = -1; + sv.str = str; + sv.len = len; + sv.off = startoff; + sv.opt = opt; + sv.delim = delim; + sv.done = false; + + // parse + count = 0; + if( npos > 0 ) out_pos[0] = startoff; + while( !sv.done ) + { + ++count; + if( sv_parse_next(&sv) <= 0 ) + return -1;// error + if( npos > count*2 ) out_pos[count*2] = sv.start; + if( npos > count*2+1 ) out_pos[count*2+1] = sv.end; + } + if( npos > 1 ) out_pos[1] = sv.off; + return count; } /// Splits a delim-separated string. @@ -628,11 +655,11 @@ int sv_parse(const char *str, int len, int startoff, char delim, int *out_pos, i /// out_fields[0] is the start of the next line. /// Other entries are the start of fields (nul-teminated). /// Returns the number of fields found or -1 if an error occurs. -/// +/// /// out_fields can be NULL. /// Fields that don't fit in out_fields are not nul-terminated. /// Extra entries in out_fields are filled with the end of the last field (empty string). -/// +/// /// @param str String to parse /// @param len Length of the string /// @param startoff Where to start parsing @@ -641,64 +668,75 @@ int sv_parse(const char *str, int len, int startoff, char delim, int *out_pos, i /// @param nfields Size of the field array /// @param opt Options that determine the parsing behaviour /// @return Number of fields found in the string or -1 if an error occured -int sv_split(char *str, int len, int startoff, char delim, char **out_fields, int nfields, enum e_svopt opt) +int sv_split(char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt) { - int pos[1024]; - int i; - int done; - char *end; - int ret = sv_parse(str, len, startoff, delim, pos, ARRAYLENGTH(pos), opt); - - if (ret == -1 || out_fields == NULL || nfields <= 0) - return ret; // nothing to do - - // next line - end = str + pos[1]; - if (end[0] == '\0') { - *out_fields = end; - } else if ((opt&SV_TERMINATE_LF) && end[0] == '\n') { - if (!(opt&SV_KEEP_TERMINATOR)) - end[0] = '\0'; - *out_fields = end + 1; - } else if ((opt&SV_TERMINATE_CRLF) && end[0] == '\r' && end[1] == '\n') { - if (!(opt&SV_KEEP_TERMINATOR)) - end[0] = end[1] = '\0'; - *out_fields = end + 2; - } else if ((opt&SV_TERMINATE_CR) && end[0] == '\r') { - if (!(opt&SV_KEEP_TERMINATOR)) - end[0] = '\0'; - *out_fields = end + 1; - } else { - ShowError("sv_split: unknown line delimiter 0x02%x.\n", (unsigned char)end[0]); - return -1;// error - } - ++out_fields; - --nfields; - - // fields - i = 2; - done = 0; - while (done < ret && nfields > 0) { - if (i < ARRAYLENGTH(pos)) { - // split field - *out_fields = str + pos[i]; - end = str + pos[i+1]; - *end = '\0'; - // next field - i += 2; - ++done; - ++out_fields; - --nfields; - } else { - // get more fields - sv_parse(str, len, pos[i-1] + 1, delim, pos, ARRAYLENGTH(pos), opt); - i = 2; - } - } - // remaining fields - for (i = 0; i < nfields; ++i) - out_fields[i] = end; - return ret; + int pos[1024]; + int i; + int done; + char* end; + int ret = sv_parse(str, len, startoff, delim, pos, ARRAYLENGTH(pos), opt); + + if( ret == -1 || out_fields == NULL || nfields <= 0 ) + return ret; // nothing to do + + // next line + end = str + pos[1]; + if( end[0] == '\0' ) + { + *out_fields = end; + } + else if( (opt&SV_TERMINATE_LF) && end[0] == '\n' ) + { + if( !(opt&SV_KEEP_TERMINATOR) ) + end[0] = '\0'; + *out_fields = end + 1; + } + else if( (opt&SV_TERMINATE_CRLF) && end[0] == '\r' && end[1] == '\n' ) + { + if( !(opt&SV_KEEP_TERMINATOR) ) + end[0] = end[1] = '\0'; + *out_fields = end + 2; + } + else if( (opt&SV_TERMINATE_CR) && end[0] == '\r' ) + { + if( !(opt&SV_KEEP_TERMINATOR) ) + end[0] = '\0'; + *out_fields = end + 1; + } + else + { + ShowError("sv_split: unknown line delimiter 0x02%x.\n", (unsigned char)end[0]); + return -1;// error + } + ++out_fields; + --nfields; + + // fields + i = 2; + done = 0; + while( done < ret && nfields > 0 ) + { + if( i < ARRAYLENGTH(pos) ) + {// split field + *out_fields = str + pos[i]; + end = str + pos[i+1]; + *end = '\0'; + // next field + i += 2; + ++done; + ++out_fields; + --nfields; + } + else + {// get more fields + sv_parse(str, len, pos[i-1] + 1, delim, pos, ARRAYLENGTH(pos), opt); + i = 2; + } + } + // remaining fields + for( i = 0; i < nfields; ++i ) + out_fields[i] = end; + return ret; } /// Escapes src to out_dest according to the format of the C compiler. @@ -710,77 +748,69 @@ int sv_split(char *str, int len, int startoff, char delim, char **out_fields, in /// @param len Length of the source string /// @param escapes Extra characters to be escaped /// @return Length of the escaped string -size_t sv_escape_c(char *out_dest, const char *src, size_t len, const char *escapes) +size_t sv_escape_c(char* out_dest, const char* src, size_t len, const char* escapes) { - size_t i; - size_t j; - - if (out_dest == NULL) - return 0;// nothing to do - if (src == NULL) { - // nothing to escape - *out_dest = 0; - return 0; - } - if (escapes == NULL) - escapes = ""; - - for (i = 0, j = 0; i < len; ++i) { - switch (src[i]) { - case '\0':// octal 0 - out_dest[j++] = '\\'; - out_dest[j++] = '0'; - out_dest[j++] = '0'; - out_dest[j++] = '0'; - break; - case '\r':// carriage return - out_dest[j++] = '\\'; - out_dest[j++] = 'r'; - break; - case '\n':// line feed - out_dest[j++] = '\\'; - out_dest[j++] = 'n'; - break; - case '\\':// escape character - out_dest[j++] = '\\'; - out_dest[j++] = '\\'; - break; - default: - if (strchr(escapes,src[i])) { - // escape - out_dest[j++] = '\\'; - switch (src[i]) { - case '\a': - out_dest[j++] = 'a'; - break; - case '\b': - out_dest[j++] = 'b'; - break; - case '\t': - out_dest[j++] = 't'; - break; - case '\v': - out_dest[j++] = 'v'; - break; - case '\f': - out_dest[j++] = 'f'; - break; - case '\?': - out_dest[j++] = '?'; - break; - default:// to octal - out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6)); - out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3)); - out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0007))); - break; - } - } else - out_dest[j++] = src[i]; - break; - } - } - out_dest[j] = 0; - return j; + size_t i; + size_t j; + + if( out_dest == NULL ) + return 0;// nothing to do + if( src == NULL ) + {// nothing to escape + *out_dest = 0; + return 0; + } + if( escapes == NULL ) + escapes = ""; + + for( i = 0, j = 0; i < len; ++i ) + { + switch( src[i] ) + { + case '\0':// octal 0 + out_dest[j++] = '\\'; + out_dest[j++] = '0'; + out_dest[j++] = '0'; + out_dest[j++] = '0'; + break; + case '\r':// carriage return + out_dest[j++] = '\\'; + out_dest[j++] = 'r'; + break; + case '\n':// line feed + out_dest[j++] = '\\'; + out_dest[j++] = 'n'; + break; + case '\\':// escape character + out_dest[j++] = '\\'; + out_dest[j++] = '\\'; + break; + default: + if( strchr(escapes,src[i]) ) + {// escape + out_dest[j++] = '\\'; + switch( src[i] ) + { + case '\a': out_dest[j++] = 'a'; break; + case '\b': out_dest[j++] = 'b'; break; + case '\t': out_dest[j++] = 't'; break; + case '\v': out_dest[j++] = 'v'; break; + case '\f': out_dest[j++] = 'f'; break; + case '\?': out_dest[j++] = '?'; break; + default:// to octal + out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6)); + out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3)); + out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0007) )); + break; + } + } + else + out_dest[j++] = src[i]; + break; + } + } + out_dest[j] = 0; + return j; } /// Unescapes src to out_dest according to the format of the C compiler. @@ -791,135 +821,129 @@ size_t sv_escape_c(char *out_dest, const char *src, size_t len, const char *esca /// @param src Source string /// @param len Length of the source string /// @return Length of the escaped string -size_t sv_unescape_c(char *out_dest, const char *src, size_t len) +size_t sv_unescape_c(char* out_dest, const char* src, size_t len) { - static unsigned char low2hex[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x0? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x1? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x2? - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,// 0x3? - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x4? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x5? - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x6? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x7? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x8? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x9? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xA? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xB? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xC? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xD? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xE? - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF? - }; - size_t i; - size_t j; - - for (i = 0, j = 0; i < len;) { - if (src[i] == '\\') { - ++i;// '\\' - if (i >= len) - ShowWarning("sv_unescape_c: empty escape sequence\n"); - else if (src[i] == 'x') { - // hex escape sequence - unsigned char c = 0; - unsigned char inrange = 1; - - ++i;// 'x' - if (i >= len || !ISXDIGIT(src[i])) { - ShowWarning("sv_unescape_c: \\x with no following hex digits\n"); - continue; - } - do { - if (c > 0x0F && inrange) { - ShowWarning("sv_unescape_c: hex escape sequence out of range\n"); - inrange = 0; - } - c = (c<<4)|low2hex[(unsigned char)src[i]];// hex digit - ++i; - } while (i < len && ISXDIGIT(src[i])); - out_dest[j++] = (char)c; - } else if (src[i] == '0' || src[i] == '1' || src[i] == '2' || src[i] == '3') { - // octal escape sequence (255=0377) - unsigned char c = src[i]-'0'; - ++i;// '0', '1', '2' or '3' - if (i < len && src[i] >= '0' && src[i] <= '7') { - c = (c<<3)|(src[i]-'0'); - ++i;// octal digit - } - if (i < len && src[i] >= '0' && src[i] <= '7') { - c = (c<<3)|(src[i]-'0'); - ++i;// octal digit - } - out_dest[j++] = (char)c; - } else { - // other escape sequence - if (strchr(SV_ESCAPE_C_SUPPORTED, src[i]) == NULL) - ShowWarning("sv_unescape_c: unknown escape sequence \\%c\n", src[i]); - switch (src[i]) { - case 'a': - out_dest[j++] = '\a'; - break; - case 'b': - out_dest[j++] = '\b'; - break; - case 't': - out_dest[j++] = '\t'; - break; - case 'n': - out_dest[j++] = '\n'; - break; - case 'v': - out_dest[j++] = '\v'; - break; - case 'f': - out_dest[j++] = '\f'; - break; - case 'r': - out_dest[j++] = '\r'; - break; - case '?': - out_dest[j++] = '\?'; - break; - default: - out_dest[j++] = src[i]; - break; - } - ++i;// escaped character - } - } else - out_dest[j++] = src[i++];// normal character - } - out_dest[j] = 0; - return j; + static unsigned char low2hex[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x0? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x1? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x2? + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,// 0x3? + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x4? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x5? + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x6? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x7? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x8? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x9? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xA? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xB? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xC? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xD? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xE? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF? + }; + size_t i; + size_t j; + + for( i = 0, j = 0; i < len; ) + { + if( src[i] == '\\' ) + { + ++i;// '\\' + if( i >= len ) + ShowWarning("sv_unescape_c: empty escape sequence\n"); + else if( src[i] == 'x' ) + {// hex escape sequence + unsigned char c = 0; + unsigned char inrange = 1; + + ++i;// 'x' + if( i >= len || !ISXDIGIT(src[i]) ) + { + ShowWarning("sv_unescape_c: \\x with no following hex digits\n"); + continue; + } + do{ + if( c > 0x0F && inrange ) + { + ShowWarning("sv_unescape_c: hex escape sequence out of range\n"); + inrange = 0; + } + c = (c<<4)|low2hex[(unsigned char)src[i]];// hex digit + ++i; + }while( i < len && ISXDIGIT(src[i]) ); + out_dest[j++] = (char)c; + } + else if( src[i] == '0' || src[i] == '1' || src[i] == '2' || src[i] == '3' ) + {// octal escape sequence (255=0377) + unsigned char c = src[i]-'0'; + ++i;// '0', '1', '2' or '3' + if( i < len && src[i] >= '0' && src[i] <= '7' ) + { + c = (c<<3)|(src[i]-'0'); + ++i;// octal digit + } + if( i < len && src[i] >= '0' && src[i] <= '7' ) + { + c = (c<<3)|(src[i]-'0'); + ++i;// octal digit + } + out_dest[j++] = (char)c; + } + else + {// other escape sequence + if( strchr(SV_ESCAPE_C_SUPPORTED, src[i]) == NULL ) + ShowWarning("sv_unescape_c: unknown escape sequence \\%c\n", src[i]); + switch( src[i] ) + { + case 'a': out_dest[j++] = '\a'; break; + case 'b': out_dest[j++] = '\b'; break; + case 't': out_dest[j++] = '\t'; break; + case 'n': out_dest[j++] = '\n'; break; + case 'v': out_dest[j++] = '\v'; break; + case 'f': out_dest[j++] = '\f'; break; + case 'r': out_dest[j++] = '\r'; break; + case '?': out_dest[j++] = '\?'; break; + default: out_dest[j++] = src[i]; break; + } + ++i;// escaped character + } + } + else + out_dest[j++] = src[i++];// normal character + } + out_dest[j] = 0; + return j; } /// Skips a C escape sequence (starting with '\\'). -const char *skip_escaped_c(const char *p) +const char* skip_escaped_c(const char* p) { - if (p && *p == '\\') { - ++p; - switch (*p) { - case 'x':// hexadecimal - ++p; - while (ISXDIGIT(*p)) - ++p; - break; - case '0': - case '1': - case '2': - case '3':// octal - ++p; - if (*p >= '0' && *p <= '7') - ++p; - if (*p >= '0' && *p <= '7') - ++p; - break; - default: - if (*p && strchr(SV_ESCAPE_C_SUPPORTED, *p)) - ++p; - } - } - return p; + if( p && *p == '\\' ) + { + ++p; + switch( *p ) + { + case 'x':// hexadecimal + ++p; + while( ISXDIGIT(*p) ) + ++p; + break; + case '0': + case '1': + case '2': + case '3':// octal + ++p; + if( *p >= '0' && *p <= '7' ) + ++p; + if( *p >= '0' && *p <= '7' ) + ++p; + break; + default: + if( *p && strchr(SV_ESCAPE_C_SUPPORTED, *p) ) + ++p; + } + } + return p; } @@ -934,72 +958,78 @@ const char *skip_escaped_c(const char *p) /// @param maxcols Maximum number of columns of a valid row /// @param parseproc User-supplied row processing function /// @return true on success, false if file could not be opened -bool sv_readdb(const char *directory, const char *filename, char delim, int mincols, int maxcols, int maxrows, bool (*parseproc)(char *fields[], int columns, int current)) +bool sv_readdb(const char* directory, const char* filename, char delim, int mincols, int maxcols, int maxrows, bool (*parseproc)(char* fields[], int columns, int current)) { - FILE *fp; - int lines = 0; - int entries = 0; - char **fields; // buffer for fields ([0] is reserved) - int columns, fields_length; - char path[1024], line[1024]; - char *match; - - snprintf(path, sizeof(path), "%s/%s", directory, filename); - - // open file - fp = fopen(path, "r"); - if (fp == NULL) { - ShowError("sv_readdb: can't read %s\n", path); - return false; - } - - // allocate enough memory for the maximum requested amount of columns plus the reserved one - fields_length = maxcols+1; - fields = (char **)aMalloc(fields_length*sizeof(char *)); - - // process rows one by one - while (fgets(line, sizeof(line), fp)) { - lines++; - - if ((match = strstr(line, "//")) != NULL) { - // strip comments - match[0] = 0; - } - - //TODO: strip trailing whitespace - if (line[0] == '\0' || line[0] == '\n' || line[0] == '\r') - continue; - - columns = sv_split(line, strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF)); - - if (columns < mincols) { - ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols); - continue; // not enough columns - } - if (columns > maxcols) { - ShowError("sv_readdb: Too many columns in line %d of \"%s\" (found %d, maximum is %d).\n", lines, path, columns, maxcols); - continue; // too many columns - } - if (entries == maxrows) { - ShowError("sv_readdb: Reached the maximum allowed number of entries (%d) when parsing file \"%s\".\n", maxrows, path); - break; - } - - // parse this row - if (!parseproc(fields+1, columns, entries)) { - ShowError("sv_readdb: Could not process contents of line %d of \"%s\".\n", lines, path); - continue; // invalid row contents - } - - // success! - entries++; - } - - aFree(fields); - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path); - - return true; + FILE* fp; + int lines = 0; + int entries = 0; + char** fields; // buffer for fields ([0] is reserved) + int columns, fields_length; + char path[1024], line[1024]; + char* match; + + snprintf(path, sizeof(path), "%s/%s", directory, filename); + + // open file + fp = fopen(path, "r"); + if( fp == NULL ) + { + ShowError("sv_readdb: can't read %s\n", path); + return false; + } + + // allocate enough memory for the maximum requested amount of columns plus the reserved one + fields_length = maxcols+1; + fields = (char**)aMalloc(fields_length*sizeof(char*)); + + // process rows one by one + while( fgets(line, sizeof(line), fp) ) + { + lines++; + + if( ( match = strstr(line, "//") ) != NULL ) + {// strip comments + match[0] = 0; + } + + //TODO: strip trailing whitespace + if( line[0] == '\0' || line[0] == '\n' || line[0] == '\r') + continue; + + columns = sv_split(line, strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF)); + + if( columns < mincols ) + { + ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols); + continue; // not enough columns + } + if( columns > maxcols ) + { + ShowError("sv_readdb: Too many columns in line %d of \"%s\" (found %d, maximum is %d).\n", lines, path, columns, maxcols ); + continue; // too many columns + } + if( entries == maxrows ) + { + ShowError("sv_readdb: Reached the maximum allowed number of entries (%d) when parsing file \"%s\".\n", maxrows, path); + break; + } + + // parse this row + if( !parseproc(fields+1, columns, entries) ) + { + ShowError("sv_readdb: Could not process contents of line %d of \"%s\".\n", lines, path); + continue; // invalid row contents + } + + // success! + entries++; + } + + aFree(fields); + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path); + + return true; } @@ -1009,126 +1039,129 @@ bool sv_readdb(const char *directory, const char *filename, char delim, int minc // @author MouseJstr (original) /// Allocates a StringBuf -StringBuf *StringBuf_Malloc() +StringBuf* StringBuf_Malloc() { - StringBuf *self; - CREATE(self, StringBuf, 1); - StringBuf_Init(self); - return self; + StringBuf* self; + CREATE(self, StringBuf, 1); + StringBuf_Init(self); + return self; } /// Initializes a previously allocated StringBuf -void StringBuf_Init(StringBuf *self) +void StringBuf_Init(StringBuf* self) { - self->max_ = 1024; - self->ptr_ = self->buf_ = (char *)aMalloc(self->max_ + 1); + self->max_ = 1024; + self->ptr_ = self->buf_ = (char*)aMalloc(self->max_ + 1); } /// Appends the result of printf to the StringBuf -int StringBuf_Printf(StringBuf *self, const char *fmt, ...) +int StringBuf_Printf(StringBuf* self, const char* fmt, ...) { - int len; - va_list ap; + int len; + va_list ap; - va_start(ap, fmt); - len = StringBuf_Vprintf(self, fmt, ap); - va_end(ap); + va_start(ap, fmt); + len = StringBuf_Vprintf(self, fmt, ap); + va_end(ap); - return len; + return len; } /// Appends the result of vprintf to the StringBuf -int StringBuf_Vprintf(StringBuf *self, const char *fmt, va_list ap) +int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) { - int n, size, off; - - for (;;) { - va_list apcopy; - /* Try to print in the allocated space. */ - size = self->max_ - (self->ptr_ - self->buf_); - va_copy(apcopy, ap); - n = vsnprintf(self->ptr_, size, fmt, apcopy); - va_end(apcopy); - /* If that worked, return the length. */ - if (n > -1 && n < size) { - self->ptr_ += n; - return (int)(self->ptr_ - self->buf_); - } - /* Else try again with more space. */ - self->max_ *= 2; // twice the old size - off = (int)(self->ptr_ - self->buf_); - self->buf_ = (char *)aRealloc(self->buf_, self->max_ + 1); - self->ptr_ = self->buf_ + off; - } + int n, size, off; + + for(;;) + { + va_list apcopy; + /* Try to print in the allocated space. */ + size = self->max_ - (self->ptr_ - self->buf_); + va_copy(apcopy, ap); + n = vsnprintf(self->ptr_, size, fmt, apcopy); + va_end(apcopy); + /* If that worked, return the length. */ + if( n > -1 && n < size ) + { + self->ptr_ += n; + return (int)(self->ptr_ - self->buf_); + } + /* Else try again with more space. */ + self->max_ *= 2; // twice the old size + off = (int)(self->ptr_ - self->buf_); + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } } /// Appends the contents of another StringBuf to the StringBuf -int StringBuf_Append(StringBuf *self, const StringBuf *sbuf) +int StringBuf_Append(StringBuf* self, const StringBuf* sbuf) { - int available = self->max_ - (self->ptr_ - self->buf_); - int needed = (int)(sbuf->ptr_ - sbuf->buf_); - - if (needed >= available) { - int off = (int)(self->ptr_ - self->buf_); - self->max_ += needed; - self->buf_ = (char *)aRealloc(self->buf_, self->max_ + 1); - self->ptr_ = self->buf_ + off; - } - - memcpy(self->ptr_, sbuf->buf_, needed); - self->ptr_ += needed; - return (int)(self->ptr_ - self->buf_); + int available = self->max_ - (self->ptr_ - self->buf_); + int needed = (int)(sbuf->ptr_ - sbuf->buf_); + + if( needed >= available ) + { + int off = (int)(self->ptr_ - self->buf_); + self->max_ += needed; + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } + + memcpy(self->ptr_, sbuf->buf_, needed); + self->ptr_ += needed; + return (int)(self->ptr_ - self->buf_); } // Appends str to the StringBuf -int StringBuf_AppendStr(StringBuf *self, const char *str) +int StringBuf_AppendStr(StringBuf* self, const char* str) { - int available = self->max_ - (self->ptr_ - self->buf_); - int needed = (int)strlen(str); - - if (needed >= available) { - // not enough space, expand the buffer (minimum expansion = 1024) - int off = (int)(self->ptr_ - self->buf_); - self->max_ += max(needed, 1024); - self->buf_ = (char *)aRealloc(self->buf_, self->max_ + 1); - self->ptr_ = self->buf_ + off; - } - - memcpy(self->ptr_, str, needed); - self->ptr_ += needed; - return (int)(self->ptr_ - self->buf_); + int available = self->max_ - (self->ptr_ - self->buf_); + int needed = (int)strlen(str); + + if( needed >= available ) + {// not enough space, expand the buffer (minimum expansion = 1024) + int off = (int)(self->ptr_ - self->buf_); + self->max_ += max(needed, 1024); + self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1); + self->ptr_ = self->buf_ + off; + } + + memcpy(self->ptr_, str, needed); + self->ptr_ += needed; + return (int)(self->ptr_ - self->buf_); } // Returns the length of the data in the Stringbuf -int StringBuf_Length(StringBuf *self) +int StringBuf_Length(StringBuf* self) { - return (int)(self->ptr_ - self->buf_); + return (int)(self->ptr_ - self->buf_); } /// Returns the data in the StringBuf -char *StringBuf_Value(StringBuf *self) +char* StringBuf_Value(StringBuf* self) { - *self->ptr_ = '\0'; - return self->buf_; + *self->ptr_ = '\0'; + return self->buf_; } /// Clears the contents of the StringBuf -void StringBuf_Clear(StringBuf *self) +void StringBuf_Clear(StringBuf* self) { - self->ptr_ = self->buf_; + self->ptr_ = self->buf_; } /// Destroys the StringBuf -void StringBuf_Destroy(StringBuf *self) +void StringBuf_Destroy(StringBuf* self) { - aFree(self->buf_); - self->ptr_ = self->buf_ = 0; - self->max_ = 0; + aFree(self->buf_); + self->ptr_ = self->buf_ = 0; + self->max_ = 0; } // Frees a StringBuf returned by StringBuf_Malloc -void StringBuf_Free(StringBuf *self) +void StringBuf_Free(StringBuf* self) { - StringBuf_Destroy(self); - aFree(self); + StringBuf_Destroy(self); + aFree(self); } -- cgit v1.2.3-60-g2f50