123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125 |
- // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
- // For more information, see LICENCE in the main folder
- #include "../common/cbasetypes.h"
- #include "../common/malloc.h"
- #include "../common/showmsg.h"
- #include "strlib.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #define J_MAX_MALLOC_SIZE 65535
- // escapes a string in-place (' -> \' , \ -> \\ , % -> _)
- 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;
- }
- // escapes a string into a provided buffer
- 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];
- }
- // escapes exactly 'size' bytes of a string into a provided buffer
- 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;
- }
- // Function to suppress control characters in a string.
- int remove_control_chars(char* str)
- {
- int i;
- int change = 0;
- for(i = 0; str[i]; i++) {
- if (ISCNTRL(str[i])) {
- str[i] = '_';
- change = 1;
- }
- }
- 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)
- {
- 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* 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
- //http://www.daniweb.com/code/snippet313.html, Dave Sinkula
- //
- 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;
- }
- #ifdef __WIN32
- 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;
- }
- #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)
- {
- const char* end = 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 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)
- {
- char ch;
- char* last_arobas;
- size_t len = strlen(email);
- // 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;
- if (email[len-1] == '.')
- return 0;
- last_arobas = strrchr(email, '@');
- 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;
- if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL)
- return 0;
- // 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)
- {
- 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);
- }
- /// strncpy that always nul-terminates the string
- 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;
- }
- /// doesn't crash on null pointer
- size_t safestrnlen(const char* string, size_t maxlen)
- {
- return ( string != NULL ) ? strnlen(string, maxlen) : 0;
- }
- /// Works like snprintf, but always nul-terminates the buffer.
- /// Returns the size of the string (without nul-terminator)
- /// or -1 if the buffer is too small.
- ///
- /// @param buf Target buffer
- /// @param sz Size of the buffer (including nul-terminator)
- /// @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, ...)
- {
- 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)
- {
- 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.
- /// The output buffer must be at least count*2+1 in size.
- /// Returns true on success, false on failure.
- ///
- /// @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)
- {
- 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;
- }
- /////////////////////////////////////////////////////////////////////
- /// Parses a delim-separated string.
- /// Starts parsing at startoff and fills the pos array with position pairs.
- /// 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]
- /// 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
- /// @param delim Field delimiter
- /// @param out_pos Array of resulting positions
- /// @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 i;
- int count;
- enum {
- START_OF_FIELD,
- PARSING_FIELD,
- PARSING_C_ESCAPE,
- END_OF_FIELD,
- TERMINATE,
- END
- } state;
- // check pos/npos
- if( out_pos == NULL ) npos = 0;
- for( i = 0; i < npos; ++i )
- out_pos[i] = -1;
- // check opt
- if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
- {
- ShowError("sv_parse: 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: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
- return -1;// error
- }
- // check str
- if( str == NULL )
- 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') )
- #define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
- #define SET_FIELD_START() if( npos > count*2+2 ) out_pos[count*2+2] = i
- #define SET_FIELD_END() if( npos > count*2+3 ) out_pos[count*2+3] = i; ++count
- i = startoff;
- count = 0;
- state = START_OF_FIELD;
- if( npos > 0 ) out_pos[0] = startoff;// start
- while( state != END )
- {
- if( npos > 1 ) out_pos[1] = i;// 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: empty escape sequence\n");
- return -1;
- }
- if( str[i] == 'x' )
- {// hex escape
- ++i;// 'x'
- if( IS_END() || !ISXDIGIT(str[i]) )
- {
- ShowError("sv_parse: \\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: unknown escape sequence \\%c\n", str[i]);
- return -1;
- }
- state = PARSING_FIELD;
- break;
- }
- case END_OF_FIELD:// record end of field and continue
- SET_FIELD_END();
- if( IS_END() )
- state = END;
- else if( IS_DELIM() )
- {
- ++i;// delim
- state = START_OF_FIELD;
- }
- else if( IS_TERMINATOR() )
- state = TERMINATE;
- else
- state = START_OF_FIELD;
- 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
- #endif
- state = END;
- break;
- }
- }
- #undef IS_END
- #undef IS_DELIM
- #undef IS_TERMINATOR
- #undef IS_C_ESCAPE
- #undef SET_FIELD_START
- #undef SET_FIELD_END
- return count;
- }
- /// Splits a delim-separated string.
- /// WARNING: this function modifies the input string
- /// Starts splitting at startoff and fills the out_fields array.
- /// 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
- /// @param delim Field delimiter
- /// @param out_fields Array of resulting fields
- /// @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 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.
- /// Returns the length of the escaped string.
- /// out_dest should be len*4+1 in size.
- ///
- /// @param out_dest Destination buffer
- /// @param src Source string
- /// @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 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.
- /// Returns the length of the unescaped string.
- /// out_dest should be len+1 in size and can be the same buffer as src.
- ///
- /// @param out_dest Destination buffer
- /// @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)
- {
- 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)
- {
- 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;
- }
- /// Opens and parses a file containing delim-separated columns, feeding them to the specified callback function row by row.
- /// Tracks the progress of the operation (current line number, number of successfully processed rows).
- /// Returns 'true' if it was able to process the specified file, or 'false' if it could not be read.
- ///
- /// @param directory Directory
- /// @param filename File to process
- /// @param delim Field delimiter
- /// @param mincols Minimum number of columns of a valid row
- /// @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))
- {
- 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 = 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;
- }
- /////////////////////////////////////////////////////////////////////
- // StringBuf - dynamic string
- //
- // @author MouseJstr (original)
- /// Allocates a StringBuf
- StringBuf* StringBuf_Malloc()
- {
- StringBuf* self;
- CREATE(self, StringBuf, 1);
- StringBuf_Init(self);
- return self;
- }
- /// Initializes a previously allocated StringBuf
- void StringBuf_Init(StringBuf* self)
- {
- self->max_ = 1024;
- self->ptr_ = self->buf_ = (char*)aMallocA(self->max_ + 1);
- }
- /// Appends the result of printf to the StringBuf
- int StringBuf_Printf(StringBuf* self, const char* fmt, ...)
- {
- int len;
- va_list ap;
- va_start(ap, fmt);
- len = StringBuf_Vprintf(self, fmt, ap);
- va_end(ap);
- return len;
- }
- /// Appends the result of vprintf to the StringBuf
- 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;
- }
- }
- /// Appends the contents of another StringBuf to the StringBuf
- 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_);
- }
- // Appends str to the StringBuf
- 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_);
- }
- // Returns the length of the data in the Stringbuf
- int StringBuf_Length(StringBuf* self)
- {
- return (int)(self->ptr_ - self->buf_);
- }
- /// Returns the data in the StringBuf
- char* StringBuf_Value(StringBuf* self)
- {
- *self->ptr_ = '\0';
- return self->buf_;
- }
- /// Clears the contents of the StringBuf
- void StringBuf_Clear(StringBuf* self)
- {
- self->ptr_ = self->buf_;
- }
- /// Destroys the StringBuf
- void StringBuf_Destroy(StringBuf* self)
- {
- aFree(self->buf_);
- self->ptr_ = self->buf_ = 0;
- self->max_ = 0;
- }
- // Frees a StringBuf returned by StringBuf_Malloc
- void StringBuf_Free(StringBuf* self)
- {
- StringBuf_Destroy(self);
- aFree(self);
- }
|