root/src/common/strlib.c @ 22

Revision 1, 26.2 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/cbasetypes.h"
5#include "../common/malloc.h"
6#include "../common/showmsg.h"
7#include "strlib.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13
14
15#define J_MAX_MALLOC_SIZE 65535
16
17// escapes a string in-place (' -> \' , \ -> \\ , % -> _)
18char* jstrescape (char* pt)
19{
20        //copy from here
21        char *ptr;
22        int i = 0, j = 0;
23
24        //copy string to temporary
25        CREATE(ptr, char, J_MAX_MALLOC_SIZE);
26        strcpy(ptr,pt);
27
28        while (ptr[i] != '\0') {
29                switch (ptr[i]) {
30                        case '\'':
31                                pt[j++] = '\\';
32                                pt[j++] = ptr[i++];
33                                break;
34                        case '\\':
35                                pt[j++] = '\\';
36                                pt[j++] = ptr[i++];
37                                break;
38                        case '%':
39                                pt[j++] = '_'; i++;
40                                break;
41                        default:
42                                pt[j++] = ptr[i++];
43                }
44        }
45        pt[j++] = '\0';
46        aFree(ptr);
47        return pt;
48}
49
50// escapes a string into a provided buffer
51char* jstrescapecpy (char* pt, const char* spt)
52{
53        //copy from here
54        //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time
55        //a escape character is found, the target's final length increases! [Skotlex]
56        int i =0, j=0;
57
58        if (!spt) {     //Return an empty string [Skotlex]
59                pt[0] = '\0';
60                return &pt[0];
61        }
62       
63        while (spt[i] != '\0') {
64                switch (spt[i]) {
65                        case '\'':
66                                pt[j++] = '\\';
67                                pt[j++] = spt[i++];
68                                break;
69                        case '\\':
70                                pt[j++] = '\\';
71                                pt[j++] = spt[i++];
72                                break;
73                        case '%':
74                                pt[j++] = '_'; i++;
75                                break;
76                        default:
77                                pt[j++] = spt[i++];
78                }
79        }
80        pt[j++] = '\0';
81        return &pt[0];
82}
83
84// escapes exactly 'size' bytes of a string into a provided buffer
85int jmemescapecpy (char* pt, const char* spt, int size)
86{
87        //copy from here
88        int i =0, j=0;
89
90        while (i < size) {
91                switch (spt[i]) {
92                        case '\'':
93                                pt[j++] = '\\';
94                                pt[j++] = spt[i++];
95                                break;
96                        case '\\':
97                                pt[j++] = '\\';
98                                pt[j++] = spt[i++];
99                                break;
100                        case '%':
101                                pt[j++] = '_'; i++;
102                                break;
103                        default:
104                                pt[j++] = spt[i++];
105                }
106        }
107        // copy size is 0 ~ (j-1)
108        return j;
109}
110
111// Function to suppress control characters in a string.
112int remove_control_chars(char* str)
113{
114        int i;
115        int change = 0;
116
117        for(i = 0; str[i]; i++) {
118                if (ISCNTRL(str[i])) {
119                        str[i] = '_';
120                        change = 1;
121                }
122        }
123
124        return change;
125}
126
127// Removes characters identified by ISSPACE from the start and end of the string
128// NOTE: make sure the string is not const!!
129char* trim(char* str)
130{
131        size_t start;
132        size_t end;
133
134        if( str == NULL )
135                return str;
136
137        // get start position
138        for( start = 0; str[start] && ISSPACE(str[start]); ++start )
139                ;
140        // get end position
141        for( end = strlen(str); start < end && str[end-1] && ISSPACE(str[end-1]); --end )
142                ;
143        // trim
144        if( start == end )
145                *str = '\0';// empty string
146        else
147        {// move string with nul terminator
148                str[end] = '\0';
149                memmove(str,str+start,end-start+1);
150        }
151        return str;
152}
153
154// Converts one or more consecutive occurences of the delimiters into a single space
155// and removes such occurences from the beginning and end of string
156// NOTE: make sure the string is not const!!
157char* normalize_name(char* str,const char* delims)
158{
159        char* in = str;
160        char* out = str;
161        int put_space = 0;
162
163        if( str == NULL || delims == NULL )
164                return str;
165
166        // trim start of string
167        while( *in && strchr(delims,*in) )
168                ++in;
169        while( *in )
170        {
171                if( put_space )
172                {// replace trim characters with a single space
173                        *out = ' ';
174                        ++out;
175                }
176                // copy non trim characters
177                while( *in && !strchr(delims,*in) )
178                {
179                        *out = *in;
180                        ++out;
181                        ++in;
182                }
183                // skip trim characters
184                while( *in && strchr(delims,*in) )
185                        ++in;
186                put_space = 1;
187        }
188        *out = '\0';
189        return str;
190}
191
192//stristr: Case insensitive version of strstr, code taken from
193//http://www.daniweb.com/code/snippet313.html, Dave Sinkula
194//
195const char* stristr(const char* haystack, const char* needle)
196{
197        if ( !*needle )
198        {
199                return haystack;
200        }
201        for ( ; *haystack; ++haystack )
202        {
203                if ( TOUPPER(*haystack) == TOUPPER(*needle) )
204                {
205                        // matched starting char -- loop through remaining chars
206                        const char *h, *n;
207                        for ( h = haystack, n = needle; *h && *n; ++h, ++n )
208                        {
209                                if ( TOUPPER(*h) != TOUPPER(*n) )
210                                {
211                                        break;
212                                }
213                        }
214                        if ( !*n ) // matched all of 'needle' to null termination
215                        {
216                                return haystack; // return the start of the match
217                        }
218                }
219        }
220        return 0;
221}
222
223#ifdef __WIN32
224char* _strtok_r(char *s1, const char *s2, char **lasts)
225{
226        char *ret;
227
228        if (s1 == NULL)
229                s1 = *lasts;
230        while(*s1 && strchr(s2, *s1))
231                ++s1;
232        if(*s1 == '\0')
233                return NULL;
234        ret = s1;
235        while(*s1 && !strchr(s2, *s1))
236                ++s1;
237        if(*s1)
238                *s1++ = '\0';
239        *lasts = s1;
240        return ret;
241}
242#endif
243
244#if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(CYGWIN)
245/* Find the length of STRING, but scan at most MAXLEN characters.
246   If no '\0' terminator is found in that many characters, return MAXLEN.  */
247size_t strnlen (const char* string, size_t maxlen)
248{
249  const char* end = memchr (string, '\0', maxlen);
250  return end ? (size_t) (end - string) : maxlen;
251}
252#endif
253
254//----------------------------------------------------
255// E-mail check: return 0 (not correct) or 1 (valid).
256//----------------------------------------------------
257int e_mail_check(char* email)
258{
259        char ch;
260        char* last_arobas;
261        size_t len = strlen(email);
262
263        // athena limits
264        if (len < 3 || len > 39)
265                return 0;
266
267        // part of RFC limits (official reference of e-mail description)
268        if (strchr(email, '@') == NULL || email[len-1] == '@')
269                return 0;
270
271        if (email[len-1] == '.')
272                return 0;
273
274        last_arobas = strrchr(email, '@');
275
276        if (strstr(last_arobas, "@.") != NULL || strstr(last_arobas, "..") != NULL)
277                return 0;
278
279        for(ch = 1; ch < 32; ch++)
280                if (strchr(last_arobas, ch) != NULL)
281                        return 0;
282
283        if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL)
284                return 0;
285
286        // all correct
287        return 1;
288}
289
290//--------------------------------------------------
291// Return numerical value of a switch configuration
292// on/off, english, français, deutsch, español
293//--------------------------------------------------
294int config_switch(const char* str)
295{
296        if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
297                return 1;
298        if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
299                return 0;
300
301        return (int)strtol(str, NULL, 0);
302}
303
304/// always nul-terminates the string
305char* safestrncpy(char* dst, const char* src, size_t n)
306{
307        char* ret;
308        ret = strncpy(dst, src, n);
309        if( ret != NULL )
310                ret[n - 1] = '\0';
311        return ret;
312}
313
314/// doesn't crash on null pointer
315size_t safestrnlen(const char* string, size_t maxlen)
316{
317        return ( string != NULL ) ? strnlen(string, maxlen) : 0;
318}
319
320/// Works like snprintf, but always nul-terminates the buffer.
321/// Returns the size of the string (without nul-terminator)
322/// or -1 if the buffer is too small.
323///
324/// @param buf Target buffer
325/// @param sz Size of the buffer (including nul-terminator)
326/// @param fmt Format string
327/// @param ... Format arguments
328/// @return The size of the string or -1 if the buffer is too small
329int safesnprintf(char* buf, size_t sz, const char* fmt, ...)
330{
331        va_list ap;
332        int ret;
333
334        va_start(ap,fmt);
335        ret = vsnprintf(buf, sz, fmt, ap);
336        va_end(ap);
337        if( ret < 0 || (size_t)ret >= sz )
338        {// overflow
339                buf[sz-1] = '\0';// always nul-terminate
340                return -1;
341        }
342        return ret;
343}
344
345/// Returns the line of the target position in the string.
346/// Lines start at 1.
347int strline(const char* str, size_t pos)
348{
349        const char* target;
350        int line;
351
352        if( str == NULL || pos == 0 )
353                return 1;
354
355        target = str+pos;
356        for( line = 1; ; ++line )
357        {
358                str = strchr(str, '\n');
359                if( str == NULL || target <= str )
360                        break;// found target line
361                ++str;// skip newline
362        }
363        return line;
364}
365
366
367
368/////////////////////////////////////////////////////////////////////
369/// Parses a delim-separated string.
370/// Starts parsing at startoff and fills the pos array with position pairs.
371/// out_pos[0] and out_pos[1] are the start and end of line.
372/// Other position pairs are the start and end of fields.
373/// Returns the number of fields found or -1 if an error occurs.
374///
375/// out_pos can be NULL.
376/// If a line terminator is found, the end position is placed there.
377/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
378/// for the seconds field and so on.
379/// Unfilled positions are set to -1.
380///
381/// @param str String to parse
382/// @param len Length of the string
383/// @param startoff Where to start parsing
384/// @param delim Field delimiter
385/// @param out_pos Array of resulting positions
386/// @param npos Size of the pos array
387/// @param opt Options that determine the parsing behaviour
388/// @return Number of fields found in the string or -1 if an error occured
389int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt)
390{
391        int i;
392        int count;
393        enum {
394                START_OF_FIELD,
395                PARSING_FIELD,
396                PARSING_C_ESCAPE,
397                END_OF_FIELD,
398                TERMINATE,
399                END
400        } state;
401
402        // check pos/npos
403        if( out_pos == NULL ) npos = 0;
404        for( i = 0; i < npos; ++i )
405                out_pos[i] = -1;
406
407        // check opt
408        if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
409        {
410                ShowError("sv_parse: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n");
411                return -1;// error
412        }
413        if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) )
414        {
415                ShowError("sv_parse: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
416                return -1;// error
417        }
418
419        // check str
420        if( str == NULL )
421                return 0;// nothing to parse
422
423#define IS_END() ( i >= len )
424#define IS_DELIM() ( str[i] == delim )
425#define IS_TERMINATOR() ( \
426        ((opt&SV_TERMINATE_LF) && str[i] == '\n') || \
427        ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \
428        ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') )
429#define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
430#define SET_FIELD_START() if( npos > count*2+2 ) out_pos[count*2+2] = i
431#define SET_FIELD_END() if( npos > count*2+3 ) out_pos[count*2+3] = i; ++count
432
433        i = startoff;
434        count = 0;
435        state = START_OF_FIELD;
436        if( npos > 0 ) out_pos[0] = startoff;// start
437        while( state != END )
438        {
439                if( npos > 1 ) out_pos[1] = i;// end
440                switch( state )
441                {
442                case START_OF_FIELD:// record start of field and start parsing it
443                        SET_FIELD_START();
444                        state = PARSING_FIELD;
445                        break;
446
447                case PARSING_FIELD:// skip field character
448                        if( IS_END() || IS_DELIM() || IS_TERMINATOR() )
449                                state = END_OF_FIELD;
450                        else if( IS_C_ESCAPE() )
451                                state = PARSING_C_ESCAPE;
452                        else
453                                ++i;// normal character
454                        break;
455
456                case PARSING_C_ESCAPE:// skip escape sequence (validates it too)
457                        {
458                                ++i;// '\\'
459                                if( IS_END() )
460                                {
461                                        ShowError("sv_parse: empty escape sequence\n");
462                                        return -1;
463                                }
464                                if( str[i] == 'x' )
465                                {// hex escape
466                                        ++i;// 'x'
467                                        if( IS_END() || !ISXDIGIT(str[i]) )
468                                        {
469                                                ShowError("sv_parse: \\x with no following hex digits\n");
470                                                return -1;
471                                        }
472                                        do{
473                                                ++i;// hex digit
474                                        }while( !IS_END() && ISXDIGIT(str[i]));
475                                }
476                                else if( str[i] == '0' || str[i] == '1' || str[i] == '2' )
477                                {// octal escape
478                                        ++i;// octal digit
479                                        if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
480                                                ++i;// octal digit
481                                        if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
482                                                ++i;// octal digit
483                                }
484                                else if( strchr(SV_ESCAPE_C_SUPPORTED, str[i]) )
485                                {// supported escape character
486                                        ++i;
487                                }
488                                else
489                                {
490                                        ShowError("sv_parse: unknown escape sequence \\%c\n", str[i]);
491                                        return -1;
492                                }
493                                state = PARSING_FIELD;
494                                break;
495                        }
496
497                case END_OF_FIELD:// record end of field and continue
498                        SET_FIELD_END();
499                        if( IS_END() )
500                                state = END;
501                        else if( IS_DELIM() )
502                        {
503                                ++i;// delim
504                                state = START_OF_FIELD;
505                        }
506                        else if( IS_TERMINATOR() )
507                                state = TERMINATE;
508                        else
509                                state = START_OF_FIELD;
510                        break;
511
512                case TERMINATE:
513#if 0
514                        // skip line terminator
515                        if( (opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n' )
516                                i += 2;// CRLF
517                        else
518                                ++i;// CR or LF
519#endif
520                        state = END;
521                        break;
522                }
523        }
524
525#undef IS_END
526#undef IS_DELIM
527#undef IS_TERMINATOR
528#undef IS_C_ESCAPE
529#undef SET_FIELD_START
530#undef SET_FIELD_END
531
532        return count;
533}
534
535/// Splits a delim-separated string.
536/// WARNING: this function modifies the input string
537/// Starts splitting at startoff and fills the out_fields array.
538/// out_fields[0] is the start of the next line.
539/// Other entries are the start of fields (nul-teminated).
540/// Returns the number of fields found or -1 if an error occurs.
541///
542/// out_fields can be NULL.
543/// Fields that don't fit in out_fields are not nul-terminated.
544/// Extra entries in out_fields are filled with the end of the last field (empty string).
545///
546/// @param str String to parse
547/// @param len Length of the string
548/// @param startoff Where to start parsing
549/// @param delim Field delimiter
550/// @param out_fields Array of resulting fields
551/// @param nfields Size of the field array
552/// @param opt Options that determine the parsing behaviour
553/// @return Number of fields found in the string or -1 if an error occured
554int sv_split(char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt)
555{
556        int pos[1024];
557        int i;
558        int done;
559        char* end;
560        int ret = sv_parse(str, len, startoff, delim, pos, ARRAYLENGTH(pos), opt);
561
562        if( ret == -1 || out_fields == NULL || nfields <= 0 )
563                return ret; // nothing to do
564
565        // next line
566        end = str + pos[1];
567        if( end[0] == '\0' )
568        {
569                *out_fields = end;
570        }
571        else if( (opt&SV_TERMINATE_LF) && end[0] == '\n' )
572        {
573                if( !(opt&SV_KEEP_TERMINATOR) )
574                        end[0] = '\0';
575                *out_fields = end + 1;
576        }
577        else if( (opt&SV_TERMINATE_CRLF) && end[0] == '\r' && end[1] == '\n' )
578        {
579                if( !(opt&SV_KEEP_TERMINATOR) )
580                        end[0] = end[1] = '\0';
581                *out_fields = end + 2;
582        }
583        else if( (opt&SV_TERMINATE_LF) && end[0] == '\r' )
584        {
585                if( !(opt&SV_KEEP_TERMINATOR) )
586                        end[0] = '\0';
587                *out_fields = end + 1;
588        }
589        else
590        {
591                ShowError("sv_split: unknown line delimiter 0x02%x.\n", (unsigned char)end[0]);
592                return -1;// error
593        }
594        ++out_fields;
595        --nfields;
596
597        // fields
598        i = 2;
599        done = 0;
600        while( done < ret && nfields > 0 )
601        {
602                if( i < ARRAYLENGTH(pos) )
603                {// split field
604                        *out_fields = str + pos[i];
605                        end = str + pos[i+1];
606                        *end = '\0';
607                        // next field
608                        i += 2;
609                        ++done;
610                        ++out_fields;
611                        --nfields;
612                }
613                else
614                {// get more fields
615                        sv_parse(str, len, pos[i-1] + 1, delim, pos, ARRAYLENGTH(pos), opt);
616                        i = 2;
617                }
618        }
619        // remaining fields
620        for( i = 0; i < nfields; ++i )
621                out_fields[i] = end;
622        return ret;
623}
624
625/// Escapes src to out_dest according to the format of the C compiler.
626/// Returns the length of the escaped string.
627/// out_dest should be len*4+1 in size.
628///
629/// @param out_dest Destination buffer
630/// @param src Source string
631/// @param len Length of the source string
632/// @param escapes Extra characters to be escaped
633/// @return Length of the escaped string
634size_t sv_escape_c(char* out_dest, const char* src, size_t len, const char* escapes)
635{
636        size_t i;
637        size_t j;
638
639        if( out_dest == NULL )
640                return 0;// nothing to do
641        if( src == NULL )
642        {// nothing to escape
643                *out_dest = 0;
644                return 0;
645        }
646        if( escapes == NULL )
647                escapes = "";
648
649        for( i = 0, j = 0; i < len; ++i )
650        {
651                switch( src[i] )
652                {
653                case '\0':// octal 0
654                        out_dest[j++] = '\\';
655                        out_dest[j++] = '0';
656                        out_dest[j++] = '0';
657                        out_dest[j++] = '0';
658                        break;
659                case '\r':// carriage return
660                        out_dest[j++] = '\\';
661                        out_dest[j++] = 'r';
662                        break;
663                case '\n':// line feed
664                        out_dest[j++] = '\\';
665                        out_dest[j++] = 'n';
666                        break;
667                case '\\':// escape character
668                        out_dest[j++] = '\\';
669                        out_dest[j++] = '\\';
670                        break;
671                default:
672                        if( strchr(escapes,src[i]) )
673                        {// escapes to octal
674                                out_dest[j++] = '\\';
675                                out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6));
676                                out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3));
677                                out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0007)   ));
678                        }
679                        else
680                                out_dest[j++] = src[i];
681                        break;
682                }
683        }
684        out_dest[j] = 0;
685        return j;
686}
687
688/// Unescapes src to out_dest according to the format of the C compiler.
689/// Returns the length of the unescaped string.
690/// out_dest should be len+1 in size and can be the same buffer as src.
691///
692/// @param out_dest Destination buffer
693/// @param src Source string
694/// @param len Length of the source string
695/// @return Length of the escaped string
696size_t sv_unescape_c(char* out_dest, const char* src, size_t len)
697{
698        static unsigned char low2hex[256] = {
699                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x0?
700                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x1?
701                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x2?
702                0,  1,  2,  3,  4,  5,  6, 7, 8, 9, 0, 0, 0, 0, 0, 0,// 0x3?
703                0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x4?
704                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x5?
705                0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x6?
706                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x7?
707                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x8?
708                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x9?
709                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xA?
710                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xB?
711                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xC?
712                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xD?
713                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xE?
714                0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF?
715        };
716        size_t i;
717        size_t j;
718
719        for( i = 0, j = 0; i < len; )
720        {
721                if( src[i] == '\\' )
722                {
723                        ++i;// '\\'
724                        if( i >= len )
725                                ShowWarning("sv_unescape_c: empty escape sequence\n");
726                        else if( src[i] == 'x' )
727                        {// hex escape sequence
728                                unsigned char c = 0;
729                                unsigned char inrange = 1;
730
731                                ++i;// 'x'
732                                if( i >= len || !ISXDIGIT(src[i]) )
733                                {
734                                        ShowWarning("sv_unescape_c: \\x with no following hex digits\n");
735                                        continue;
736                                }
737                                do{
738                                        if( c > 0x0F && inrange )
739                                        {
740                                                ShowWarning("sv_unescape_c: hex escape sequence out of range\n");
741                                                inrange = 0;
742                                        }
743                                        c = (c<<4)|low2hex[(unsigned char)src[i]];// hex digit
744                                        ++i;
745                                }while( i < len && ISXDIGIT(src[i]) );
746                                out_dest[j++] = (char)c;
747                        }
748                        else if( src[i] == '0' || src[i] == '1' || src[i] == '2' || src[i] == '3' )
749                        {// octal escape sequence (255=0377)
750                                unsigned char c = src[i]-'0';
751                                ++i;// '0', '1', '2' or '3'
752                                if( i < len && src[i] >= '0' && src[i] <= '7' )
753                                {
754                                        c = (c<<3)|(src[i]-'0');
755                                        ++i;// octal digit
756                                }
757                                if( i < len && src[i] >= '0' && src[i] <= '7' )
758                                {
759                                        c = (c<<3)|(src[i]-'0');
760                                        ++i;// octal digit
761                                }
762                                out_dest[j++] = (char)c;
763                        }
764                        else
765                        {// other escape sequence
766                                if( strchr(SV_ESCAPE_C_SUPPORTED, src[i]) == NULL )
767                                        ShowWarning("sv_unescape_c: unknown escape sequence \\%c\n", src[i]);
768                                switch( src[i] )
769                                {
770                                case 'a': out_dest[j++] = '\a'; break;
771                                case 'b': out_dest[j++] = '\b'; break;
772                                case 't': out_dest[j++] = '\t'; break;
773                                case 'n': out_dest[j++] = '\n'; break;
774                                case 'v': out_dest[j++] = '\v'; break;
775                                case 'f': out_dest[j++] = '\f'; break;
776                                case 'r': out_dest[j++] = '\r'; break;
777                                case '?': out_dest[j++] = '\?'; break;
778                                default: out_dest[j++] = src[i]; break;
779                                }
780                                ++i;// escaped character
781                        }
782                }
783                else
784                        out_dest[j++] = src[i++];// normal character
785        }
786        out_dest[j] = 0;
787        return j;
788}
789
790/// Skips a C escape sequence (starting with '\\').
791const char* skip_escaped_c(const char* p)
792{
793        if( p && *p == '\\' )
794        {
795                ++p;
796                switch( *p )
797                {
798                case 'x':// hexadecimal
799                        ++p;
800                        while( ISXDIGIT(*p) )
801                                ++p;
802                        break;
803                case '0':
804                case '1':
805                case '2':
806                case '3':// octal
807                        ++p;
808                        if( *p >= '0' && *p <= '7' )
809                                ++p;
810                        if( *p >= '0' && *p <= '7' )
811                                ++p;
812                        break;
813                default:
814                        if( *p && strchr(SV_ESCAPE_C_SUPPORTED, *p) )
815                                ++p;
816                }
817        }
818        return p;
819}
820
821
822/// Opens and parses a file containing delim-separated columns, feeding them to the specified callback function row by row.
823/// Tracks the progress of the operation (current line number, number of successfully processed rows).
824/// Returns 'true' if it was able to process the specified file, or 'false' if it could not be read.
825///
826/// @param directory Directory
827/// @param filename File to process
828/// @param delim Field delimiter
829/// @param mincols Minimum number of columns of a valid row
830/// @param maxcols Maximum number of columns of a valid row
831/// @param parseproc User-supplied row processing function
832/// @return true on success, false if file could not be opened
833bool sv_readdb(const char* directory, const char* filename, char delim, int mincols, int maxcols, int maxrows, bool (*parseproc)(char* fields[], int columns, int current))
834{
835        FILE* fp;
836        int lines = 0;
837        int entries = 0;
838        char* fields[64]; // room for 63 fields ([0] is reserved)
839        int columns;
840        char path[1024], line[1024];
841
842        if( maxcols > ARRAYLENGTH(fields)-1 )
843        {
844                ShowError("sv_readdb: Insufficient column storage in parser for file \"%s\" (want %d, have only %d). Increase the capacity in the source code please.\n", path, maxcols, ARRAYLENGTH(fields)-1);
845                return false;
846        }
847
848        // open file
849        snprintf(path, sizeof(path), "%s/%s", directory, filename);
850        fp = fopen(path, "r");
851        if( fp == NULL )
852        {
853                ShowError("sv_readdb: can't read %s\n", path);
854                return false;
855        }
856
857        // process rows one by one
858        while( fgets(line, sizeof(line), fp) )
859        {
860                lines++;
861                if( line[0] == '/' && line[1] == '/' )
862                        continue;
863                //TODO: strip trailing // comment
864                //TODO: strip trailing whitespace
865                if( line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
866                        continue;
867
868                columns = sv_split(line, strlen(line), 0, delim, fields, ARRAYLENGTH(fields), (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF));
869
870                if( columns < mincols )
871                {
872                        ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols);
873                        continue; // not enough columns
874                }
875                if( columns > maxcols )
876                {
877                        ShowError("sv_readdb: Too many columns in line %d of \"%s\" (found %d, maximum is %d).\n", lines, path, columns, maxcols );
878                        continue; // too many columns
879                }
880                if( entries == maxrows )
881                {
882                        ShowError("sv_readdb: Reached the maximum allowed number of entries (%d) when parsing file \"%s\".\n", maxrows, path);
883                        break;
884                }
885
886                // parse this row
887                if( !parseproc(fields+1, columns, entries) )
888                {
889                        ShowError("sv_readdb: Could not process contents of line %d of \"%s\".\n", lines, path);
890                        continue; // invalid row contents
891                }
892
893                // success!
894                entries++;
895        }
896
897        fclose(fp);
898        ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path);
899
900        return true;
901}
902
903
904/////////////////////////////////////////////////////////////////////
905// StringBuf - dynamic string
906//
907// @author MouseJstr (original)
908
909/// Allocates a StringBuf
910StringBuf* StringBuf_Malloc() 
911{
912        StringBuf* self;
913        CREATE(self, StringBuf, 1);
914        StringBuf_Init(self);
915        return self;
916}
917
918/// Initializes a previously allocated StringBuf
919void StringBuf_Init(StringBuf* self)
920{
921        self->max_ = 1024;
922        self->ptr_ = self->buf_ = (char*)aMallocA(self->max_ + 1);
923}
924
925/// Appends the result of printf to the StringBuf
926int StringBuf_Printf(StringBuf* self, const char* fmt, ...)
927{
928        int len;
929        va_list ap;
930
931        va_start(ap, fmt);
932        len = StringBuf_Vprintf(self, fmt, ap);
933        va_end(ap);
934
935        return len;
936}
937
938/// Appends the result of vprintf to the StringBuf
939int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap)
940{
941        int n, size, off;
942
943        for(;;)
944        {
945                /* Try to print in the allocated space. */
946                size = self->max_ - (self->ptr_ - self->buf_);
947                n = vsnprintf(self->ptr_, size, fmt, ap);
948                /* If that worked, return the length. */
949                if( n > -1 && n < size )
950                {
951                        self->ptr_ += n;
952                        return (int)(self->ptr_ - self->buf_);
953                }
954                /* Else try again with more space. */
955                self->max_ *= 2; // twice the old size
956                off = (int)(self->ptr_ - self->buf_);
957                self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
958                self->ptr_ = self->buf_ + off;
959        }
960}
961
962/// Appends the contents of another StringBuf to the StringBuf
963int StringBuf_Append(StringBuf* self, const StringBuf* sbuf)
964{
965        int available = self->max_ - (self->ptr_ - self->buf_);
966        int needed = (int)(sbuf->ptr_ - sbuf->buf_);
967
968        if( needed >= available )
969        {
970                int off = (int)(self->ptr_ - self->buf_);
971                self->max_ += needed;
972                self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
973                self->ptr_ = self->buf_ + off;
974        }
975
976        memcpy(self->ptr_, sbuf->buf_, needed);
977        self->ptr_ += needed;
978        return (int)(self->ptr_ - self->buf_);
979}
980
981// Appends str to the StringBuf
982int StringBuf_AppendStr(StringBuf* self, const char* str) 
983{
984        int available = self->max_ - (self->ptr_ - self->buf_);
985        int needed = (int)strlen(str);
986
987        if( needed >= available )
988        {// not enough space, expand the buffer (minimum expansion = 1024)
989                int off = (int)(self->ptr_ - self->buf_);
990                self->max_ += max(needed, 1024);
991                self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
992                self->ptr_ = self->buf_ + off;
993        }
994
995        memcpy(self->ptr_, str, needed);
996        self->ptr_ += needed;
997        return (int)(self->ptr_ - self->buf_);
998}
999
1000// Returns the length of the data in the Stringbuf
1001int StringBuf_Length(StringBuf* self) 
1002{
1003        return (int)(self->ptr_ - self->buf_);
1004}
1005
1006/// Returns the data in the StringBuf
1007char* StringBuf_Value(StringBuf* self) 
1008{
1009        *self->ptr_ = '\0';
1010        return self->buf_;
1011}
1012
1013/// Clears the contents of the StringBuf
1014void StringBuf_Clear(StringBuf* self) 
1015{
1016        self->ptr_ = self->buf_;
1017}
1018
1019/// Destroys the StringBuf
1020void StringBuf_Destroy(StringBuf* self)
1021{
1022        aFree(self->buf_);
1023        self->ptr_ = self->buf_ = 0;
1024        self->max_ = 0;
1025}
1026
1027// Frees a StringBuf returned by StringBuf_Malloc
1028void StringBuf_Free(StringBuf* self) 
1029{
1030        StringBuf_Destroy(self);
1031        aFree(self);
1032}
Note: See TracBrowser for help on using the browser.