/*************************************************************************** gbx_string.c (c) 2000-2017 Benoît Minisini This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #define __STRING_C #include "gb_common.h" #include "gb_common_buffer.h" #include "gb_common_case.h" #include "gb_common_string.h" #include "gb_error.h" #include "gbx_value.h" #include "gbx_debug.h" #include "gbx_local.h" #include "gbx_project.h" #include "gbx_exec.h" #include #include #include #include #include "gbx_string.h" //#define DEBUG_ME #if DEBUG_STRING #define DEBUG_ME #endif #if DEBUG_STRING char *STRING_watch = NULL; #endif /*#ifdef DEBUG_ME extern FILE *MEMORY_log; #undef stderr #define stderr MEMORY_log static void print_where() { fprintf(MEMORY_log, "%s: ", DEBUG_get_current_position()); } #define DEBUG_where print_where #endif*/ #if DEBUG_MEMORY static void *_my_malloc(int size) { void *ptr; ALLOC(&ptr, size); return ptr; } static void _my_free(void *ptr) { IFREE(ptr); } static void *_my_realloc(void *ptr, int size) { REALLOC(&ptr, size); return ptr; } #else #define _my_malloc my_malloc #define _my_free my_free #define _my_realloc my_realloc #endif #define STRING_last_count 32 static char *STRING_last[STRING_last_count] = { 0 }; const char STRING_char_table[512] = "\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0A\x00\x0B\x00\x0C\x00\x0D\x00\x0E\x00\x0F\x00" "\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1A\x00\x1B\x00\x1C\x00\x1D\x00\x1E\x00\x1F\x00" "\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x27\x00\x28\x00\x29\x00\x2A\x00\x2B\x00\x2C\x00\x2D\x00\x2E\x00\x2F\x00" "\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00\x38\x00\x39\x00\x3A\x00\x3B\x00\x3C\x00\x3D\x00\x3E\x00\x3F\x00" "\x40\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x48\x00\x49\x00\x4A\x00\x4B\x00\x4C\x00\x4D\x00\x4E\x00\x4F\x00" "\x50\x00\x51\x00\x52\x00\x53\x00\x54\x00\x55\x00\x56\x00\x57\x00\x58\x00\x59\x00\x5A\x00\x5B\x00\x5C\x00\x5D\x00\x5E\x00\x5F\x00" "\x60\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00\x69\x00\x6A\x00\x6B\x00\x6C\x00\x6D\x00\x6E\x00\x6F\x00" "\x70\x00\x71\x00\x72\x00\x73\x00\x74\x00\x75\x00\x76\x00\x77\x00\x78\x00\x79\x00\x7A\x00\x7B\x00\x7C\x00\x7D\x00\x7E\x00\x7F\x00" "\x80\x00\x81\x00\x82\x00\x83\x00\x84\x00\x85\x00\x86\x00\x87\x00\x88\x00\x89\x00\x8A\x00\x8B\x00\x8C\x00\x8D\x00\x8E\x00\x8F\x00" "\x90\x00\x91\x00\x92\x00\x93\x00\x94\x00\x95\x00\x96\x00\x97\x00\x98\x00\x99\x00\x9A\x00\x9B\x00\x9C\x00\x9D\x00\x9E\x00\x9F\x00" "\xA0\x00\xA1\x00\xA2\x00\xA3\x00\xA4\x00\xA5\x00\xA6\x00\xA7\x00\xA8\x00\xA9\x00\xAA\x00\xAB\x00\xAC\x00\xAD\x00\xAE\x00\xAF\x00" "\xB0\x00\xB1\x00\xB2\x00\xB3\x00\xB4\x00\xB5\x00\xB6\x00\xB7\x00\xB8\x00\xB9\x00\xBA\x00\xBB\x00\xBC\x00\xBD\x00\xBE\x00\xBF\x00" "\xC0\x00\xC1\x00\xC2\x00\xC3\x00\xC4\x00\xC5\x00\xC6\x00\xC7\x00\xC8\x00\xC9\x00\xCA\x00\xCB\x00\xCC\x00\xCD\x00\xCE\x00\xCF\x00" "\xD0\x00\xD1\x00\xD2\x00\xD3\x00\xD4\x00\xD5\x00\xD6\x00\xD7\x00\xD8\x00\xD9\x00\xDA\x00\xDB\x00\xDC\x00\xDD\x00\xDE\x00\xDF\x00" "\xE0\x00\xE1\x00\xE2\x00\xE3\x00\xE4\x00\xE5\x00\xE6\x00\xE7\x00\xE8\x00\xE9\x00\xEA\x00\xEB\x00\xEC\x00\xED\x00\xEE\x00\xEF\x00" "\xF0\x00\xF1\x00\xF2\x00\xF3\x00\xF4\x00\xF5\x00\xF6\x00\xF7\x00\xF8\x00\xF9\x00\xFA\x00\xFB\x00\xFC\x00\xFD\x00\xFE\x00\xFF\x00"; static int _index = 0; STRING_MAKE STRING_make_buffer; #define _make STRING_make_buffer static iconv_t _conv_unicode_utf8 = (iconv_t)-1; static iconv_t _conv_utf8_unicode = (iconv_t)-1; /**************************************************************************** String pool management ****************************************************************************/ #define SIZE_INC 16 #define SIZE_INC2 256 #define REAL_SIZE(_len) ((_len) >= 256 ? (((_len) + (SIZE_INC2 - 1)) & ~(SIZE_INC2 - 1)) : (((_len) + (SIZE_INC - 1)) & ~(SIZE_INC - 1))) #define POOL_SIZE 16 #define POOL_MAX 64 #define POOL_MAX_LEN (POOL_SIZE * SIZE_INC) static STRING *_pool[POOL_SIZE] = { 0 }; static char _pool_count[POOL_SIZE] = { 0 }; #ifdef DEBUG_ME static STRING *alloc_string(int _len) \ { \ STRING *str; \ int size = REAL_SIZE((_len) + 1 + sizeof(STRING)); \ int pool = (size / SIZE_INC) - 1; \ \ MEMORY_count++; \ fprintf(stderr, "[%d]\n", MEMORY_count); \ \ if (pool < POOL_SIZE && (_pool_count[pool])) \ { \ str = _pool[pool]; \ fprintf(stderr, "alloc_string: (%p) %d bytes from pool %d [%d]\n", str, size, pool, _pool_count[pool]); \ _pool[pool] = *((STRING **)str); \ _pool_count[pool]--; \ } \ else \ { \ str = _my_malloc(size); \ if (!str) \ THROW_MEMORY(); \ } \ str->len = (_len); \ str->ref = 1; \ return str; \ } #else #define alloc_string(_len) \ ({ \ STRING *str; \ int pool = (((_len) + 1 + sizeof(STRING) + (SIZE_INC - 1)) / SIZE_INC) - 1; \ \ MEMORY_count++; \ \ if (pool < POOL_SIZE && (_pool_count[pool])) \ { \ str = _pool[pool]; \ _pool[pool] = *((STRING **)str); \ _pool_count[pool]--; \ } \ else \ { \ str = _my_malloc(REAL_SIZE((_len) + 1 + sizeof(STRING))); \ if (!str) \ THROW_MEMORY(); \ } \ str->len = (_len); \ str->ref = 1; \ str; \ }) #endif extern char *STRING_utf8_current; void STRING_free_real(char *ptr) { STRING *str = STRING_from_ptr(ptr); int size = REAL_SIZE(str->len + 1 + sizeof(STRING)); int pool = (size / SIZE_INC) - 1; if (STRING_utf8_current == ptr) { //fprintf(stderr, "free STRING_utf8_current (%p)\n", ptr); STRING_utf8_current = NULL; } MEMORY_count--; #ifdef DEBUG_ME fprintf(stderr, "[%d]\n", MEMORY_count); #endif if (pool < POOL_SIZE) { if (_pool_count[pool] < POOL_MAX) { #ifdef DEBUG_ME fprintf(stderr, "STRING_free_real: (%p / %p) %d bytes to pool %d\n", str, ptr, size, pool); str->ref = 0x87654321; str->len = 0x87654321; #endif *((STRING **)str) = _pool[pool]; _pool[pool] = str; _pool_count[pool]++; return; } } _my_free(str); } static STRING *realloc_string(STRING *str, int new_len) { int size; int new_size; if (new_len == str->len) return str; size = REAL_SIZE(str->len + 1 + sizeof(STRING)); new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); if (new_size != size) { if (new_len == 0) { STRING_free_real(str->data); return NULL; } else if (size > POOL_MAX_LEN && new_size > POOL_MAX_LEN) { str = _my_realloc(str, new_size); //REALLOC(&str, new_size, "realloc_string"); } else { STRING *nstr = alloc_string(new_len); if (new_len < str->len) memcpy(nstr->data, str->data, new_len); else memcpy(nstr->data, str->data, str->len); if (str->ref == 1) STRING_free_real(str->data); else str->ref--; str = nstr; } } str->len = new_len; return str; } static void clear_pool(void) { int i; STRING *str, *next; for (i = 0; i < POOL_SIZE; i++) { #ifdef DEBUG_ME fprintf(stderr, "clear_pool: clear pool #%d\n", i); #endif str = _pool[i]; while (str) { next = *((STRING **)str); #ifdef DEBUG_ME fprintf(stderr, "%p\n", str); #endif _my_free(str); str = next; } } } /**************************************************************************** String routines ****************************************************************************/ char *STRING_new(const char *src, int len) { STRING *str; char *data; if (len == 0) return NULL; //ALLOC(&str, REAL_SIZE(len + 1 + sizeof(STRING)), "STRING_new"); str = alloc_string(len); data = str->data; if (src) memcpy(data, src, len); data[len] = 0; #ifdef DEBUG_ME DEBUG_where(); fprintf(stderr, "STRING_new %p ( 0 ) \"%.*s\"\n", data, len, src); fflush(stderr); #endif return data; } char *STRING_free_later(char *ptr) { /*if (NLast >= MAX_LAST_STRING) THROW(E_STRING);*/ //static int nfl = 0; if (ptr) { //nfl++; //fprintf(stderr, "% 8d % 6d\n", nfl, STRING_length(ptr)); #ifdef DEBUG_ME if (STRING_last[_index]) { DEBUG_where(); fprintf(stderr, "STRING_free_later: release temp: %p '%s'\n", STRING_last[_index], STRING_last[_index]); fflush(stderr); } #endif //if (STRING_last[_index] && STRING_length(STRING_last[_index]) >= 1024) // fprintf(stderr, "STRING_free_later: free [%d] %d\n", _index, STRING_length(STRING_last[_index])); STRING_unref(&STRING_last[_index]); #ifdef DEBUG_ME fprintf(stderr, "STRING_free_later: post temp: %p '%s'\n", ptr, ptr); fflush(stderr); #endif STRING_last[_index] = ptr; //if (STRING_length(ptr) >= 1024) // fprintf(stderr, "STRING_free_later: [%d] = %d\n", _index, STRING_length(ptr)); _index++; if (_index >= STRING_last_count) _index = 0; } return ptr; } int STRING_get_free_index(void) { return _index; } void STRING_clear_cache(void) { int i; for (i = 0; i < STRING_last_count; i++) { #ifdef DEBUG_ME if (STRING_last[i]) fprintf(stderr, "release temp %p '%s'\n", STRING_last[i], STRING_last[i]); #endif STRING_unref(&STRING_last[i]); STRING_last[i] = NULL; } _index = 0; clear_pool(); } void STRING_exit(void) { STRING_clear_cache(); #ifdef DEBUG_ME fprintf(stderr, "STRING_exit\n"); #endif if (_conv_unicode_utf8 != ((iconv_t)-1)) iconv_close(_conv_unicode_utf8); if (_conv_utf8_unicode != ((iconv_t)-1)) iconv_close(_conv_utf8_unicode); } char *STRING_extend(char *str, int new_len) { STRING *sstr; if (!str) { sstr = alloc_string(new_len); #ifdef DEBUG_ME fprintf(stderr, "STRING_extend: NULL -> %p / %p\n", sstr, sstr->data); #endif } else { sstr = realloc_string(STRING_from_ptr(str), new_len); #ifdef DEBUG_ME fprintf(stderr, "STRING_extend: %p / %p -> %p / %p\n", STRING_from_ptr(str), str, sstr, sstr->data); #endif } return sstr ? sstr->data : NULL; } bool STRING_extend_will_realloc(char *str, int new_len) { STRING *sstr; int size; int new_size; if (!str) return new_len != 0; sstr = STRING_from_ptr(str); if (new_len == sstr->len) return FALSE; size = REAL_SIZE(sstr->len + 1 + sizeof(STRING)); new_size = REAL_SIZE(new_len + 1 + sizeof(STRING)); return size != new_size; } void STRING_new_temp_value(VALUE *value, const char *src, int len) { value->_string.addr = STRING_new_temp(src, len); value->_string.len = len; //STRING_length(value->_string.addr); value->_string.start = 0; value->type = T_STRING; } void STRING_new_constant_value(VALUE *value, const char *src, int len) { value->_string.addr = (char *)src; value->_string.len = ((len < 0) ? strlen(src) : len); value->_string.start = 0; value->type = T_CSTRING; } void STRING_void_value(VALUE *value) { value->type = T_CSTRING; value->_string.addr = NULL; value->_string.start = 0; value->_string.len = 0; } #if DEBUG_STRING void STRING_free(char **ptr) { if (*ptr == NULL) return; #ifdef DEBUG_ME DEBUG_where(); fprintf(stderr, "STRING_free: %p\n", *ptr); fflush(stderr); #endif STRING_free_real(*ptr); *ptr = NULL; } void STRING_ref_real(char *ptr) { STRING *str; if (ptr == NULL) return; if (ptr == STRING_watch) BREAKPOINT(); str = STRING_from_ptr(ptr); #ifdef DEBUG_ME DEBUG_where(); fprintf(stderr, "STRING_ref: %p ( %d -> %d )\n", ptr, str->ref, str->ref + 1); if (str->ref < 0 || str->ref > 10000) fprintf(stderr, "*** BAD\n"); fflush(stderr); #endif str->ref++; } void STRING_unref_real(char **ptr) { STRING *str; if (*ptr == NULL) return; if (*ptr == STRING_watch) BREAKPOINT(); str = STRING_from_ptr(*ptr); #ifdef DEBUG_ME DEBUG_where(); fprintf(stderr, "STRING_unref: %p ( %d -> %d )\n", *ptr, str->ref, str->ref - 1); if (str->ref < 1 || str->ref > 10000) { fprintf(stderr, "*** BAD\n"); BREAKPOINT(); } fflush(stderr); #endif if ((--str->ref) <= 0) STRING_free(ptr); } #endif void STRING_unref_keep(char **ptr) { STRING *str; if (*ptr == NULL) return; str = STRING_from_ptr(*ptr); if (str->ref > 1) str->ref--; else STRING_free_later(*ptr); } /* The get_param argument index starts at 1, not 0! */ #define INDEX_AT 0 #define INDEX_IGNORE (-1) #define INDEX_ERROR (-2) static int get_param_index(const char *str, int len, uint *pos, int *len_pattern) { uint i; int index; bool err; uchar d; i = *pos + 1; d = str[i]; if (d == '&') { index = INDEX_AT; } else if (d >= '1' && d <= '9') { index = d - '0'; } else if (d == '{') { err = FALSE; index = 0; for(;;) { i++; if (i >= len) break; d = str[i]; if (d == '}') break; if (d >= '0' && d <= '9') index = index * 10 + d - '0'; else err = TRUE; } if (err || index < 1 || index >= 64) index = INDEX_ERROR; } else { index = INDEX_IGNORE; } if (len_pattern) *len_pattern = i - *pos + 1; *pos = i; return index; } char *STRING_subst(const char *str, int len, SUBST_FUNC get_param) { uint i; uchar c; int np, lenp; int len_subst; char *subst; char *ps; char *p[64]; int lp[64]; if (!str) return NULL; if (len <= 0) len = strlen(str); // Comment to force a commit because svn unexpectedly lost my log if (len == 0) return NULL; // Calculate the length len_subst = len; for (i = 0; i < len; i++) { c = str[i]; if (c == '&') { np = get_param_index(str, len, &i, &lenp); len_subst -= lenp; switch (np) { case INDEX_AT: len_subst++; break; case INDEX_IGNORE: len_subst += lenp; break; case INDEX_ERROR: break; default: np--; (*get_param)(np + 1, &p[np], &lp[np]); if (lp[np] < 0) lp[np] = strlen(p[np]); len_subst += lp[np]; } } } if (!len_subst) return NULL; subst = STRING_new(NULL, len_subst); ps = subst; for (i = 0; i < len; i++) { c = str[i]; if (c == '&') { np = get_param_index(str, len, &i, &lenp); switch (np) { case INDEX_AT: *ps++ = '&'; break; case INDEX_IGNORE: *ps++ = '&'; *ps++ = str[i]; break; case INDEX_ERROR: break; default: np--; memcpy(ps, p[np], lp[np]); ps += lp[np]; } } else *ps++ = c; } *ps = 0; return STRING_free_later(subst); } char *STRING_subst_add(const char *str, int len, SUBST_ADD_FUNC add_param) { uint i; char c; int np; if (!str) return NULL; if (len <= 0) len = strlen(str); STRING_start_len(len); for (i = 0; i < len; i++) { c = str[i]; if (c == '&') { np = get_param_index(str, len, &i, NULL); switch (np) { case INDEX_AT: STRING_make_char('&'); break; case INDEX_IGNORE: STRING_make_char('&'); STRING_make_char(str[i]); break; case INDEX_ERROR: break; default: (*add_param)(np); } } else STRING_make_char(c); } return STRING_end_temp(); } char *STRING_add(char *str, const char *src, int len) { int old_len; if (len <= 0 && src != NULL) len = strlen(src); if (len <= 0) return str; if (!str) return STRING_new(src, len); old_len = STRING_length(str); str = STRING_extend(str, old_len + len); if (src) { memcpy(&str[old_len], src, len); str[old_len + len] = 0; } return str; } char *STRING_add_char(char *str, char c) { int len = STRING_length(str); char *p; str = STRING_extend(str, len + 1); p = str + len; p[0] = c; p[1] = 0; return str; } void STRING_start_len(int len) { _make.inc = 32; if (len == 0) len = 32; _make.buffer = STRING_new(NULL, len); _make.max = len; _make.len = 0; _make.ptr = _make.buffer; _make.ntemp = 0; } void STRING_make_dump() { int n = _make.ntemp; _make.ntemp = 0; STRING_make(_make.temp, n); } // len == 0 est possible ! On peut vouloir ajouter une chaîne vide. void STRING_make(const char *src, int len) { int pos; if (!src) return; if (len < 0) len = strlen(src); if (len <= 0) return; if (_make.ntemp) STRING_make_dump(); _make.len += len; if (_make.len >= _make.max) { pos = (_make.len - _make.max) * 4; if (pos < _make.inc) pos = _make.inc; else { _make.inc = (pos + 31) & ~31; if (_make.inc > 1024) _make.inc = 1024; } _make.max += pos; //fprintf(stderr, "STRING_extend: %d\n", _max - STRING_length(SUBST_buffer)); pos = _make.ptr - _make.buffer; _make.buffer = STRING_extend(_make.buffer, _make.max); _make.ptr = _make.buffer + pos; } memcpy(_make.ptr, src, len); _make.ptr += len; } char *STRING_end() { if (_make.ntemp) STRING_make_dump(); if (_make.len) { _make.buffer = STRING_extend(_make.buffer, _make.len); _make.buffer[_make.len] = 0; } else STRING_free(&_make.buffer); return _make.buffer; } char *STRING_end_temp() { STRING_end(); if (_make.buffer) STRING_free_later(_make.buffer); return _make.buffer; } /**************************************************************************** Charset conversion routines ****************************************************************************/ static iconv_t my_iconv_open(const char *dst, const char *src) { const char *osrc, *odst; iconv_t *cache; iconv_t handle; osrc = src; odst = dst; if (dst == SC_UNICODE) dst = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; else if (dst == SC_UTF8) dst = "UTF-8"; else if (!dst || *dst == 0) dst = "ASCII"; if (src == SC_UNICODE) src = EXEC_big_endian ? "UCS-4BE" : "UCS-4LE"; else if (src == SC_UTF8) src = "UTF-8"; else if (!src || *src == 0) src = "ASCII"; if (osrc == SC_UNICODE && odst == SC_UTF8) cache = &_conv_unicode_utf8; else if (osrc == SC_UTF8 && odst == SC_UNICODE) cache = &_conv_utf8_unicode; else cache = NULL; if (cache && *cache != ((iconv_t)-1)) return *cache; //fprintf(stderr, "iconv_open: %s -> %s\n", src, dst); handle = iconv_open(dst, src); if (cache) *cache = handle; return handle; } static void my_iconv_close(iconv_t handle) { if (handle == _conv_unicode_utf8 || handle == _conv_utf8_unicode) return; iconv_close(handle); } int STRING_conv(char **result, const char *str, int len, const char *src, const char *dst, bool throw) { iconv_t handle; bool err; const char *in; char *out; size_t in_len; size_t out_len; size_t ret; int errcode = 0; bool unicode; *result = NULL; in = str; in_len = len; if (len == 0) return errcode; unicode = (dst == SC_UNICODE); handle = my_iconv_open(dst, src); if (handle == (iconv_t)(-1)) { if (errno == EINVAL) errcode = E_UCONV; else errcode = E_CONV; } else { err = FALSE; for(;;) { out = COMMON_buffer; out_len = COMMON_BUF_MAX; #if defined(OS_SOLARIS) || defined(OS_FREEBSD) || defined(OS_OPENBSD) ret = iconv(handle, &in, &in_len, &out, &out_len); #else ret = iconv(handle, (char **)&in, &in_len, &out, &out_len); #endif if (ret != (size_t)(-1) || errno == E2BIG) *result = STRING_add(*result, COMMON_buffer, COMMON_BUF_MAX - out_len); if (ret != (size_t)(-1)) break; if (errno != E2BIG) { err = TRUE; break; } } my_iconv_close(handle); if (unicode) *result = STRING_add(*result, "\0\0\0", 3); STRING_extend_end(*result); if (err) errcode = E_CONV; } if (throw && errcode) THROW(errcode); return errcode; } char *STRING_conv_to_UTF8(const char *name, int len) { char *result = NULL; if (!name) return ""; if (LOCAL_is_UTF8) { if (len <= 0) result = (char *)name; else result = STRING_new_temp(name, len); } else { if (len <= 0) len = strlen(name); STRING_conv(&result, name, len, LOCAL_encoding, SC_UTF8, TRUE); } if (result) return result; else return ""; } char *STRING_conv_file_name(const char *name, int len) { char *result = NULL; int pos; struct passwd *info; char *dir; char *user; int err; if (!name) return ""; if (len <= 0) len = strlen(name); if (len > 0 && *name == '~') { for (pos = 0; pos < len; pos++) { if (name[pos] == '/') break; } if (pos <= 1) dir = FILE_get_home(); else { user = STRING_new_temp(&name[1], pos - 1); info = getpwnam(user); if (info) dir = info->pw_dir; else dir = NULL; } if (dir) { user = STRING_new_zero(dir); if (pos < len) user = STRING_add(user, &name[pos], len - pos); name = user; len = STRING_length(name); STRING_free_later(user); } } if (LOCAL_is_UTF8) result = STRING_new_temp(name, len); else { err = STRING_conv(&result, name, len, SC_UTF8, LOCAL_encoding, FALSE); if (err) result = STRING_new_temp(name, len); } //fprintf(stderr, "STRING_conv_file_name: %s\n", result); if (result) return result; else return ""; } // Warning! Returns the index starting from 1, and 0 if no match is found. int STRING_search(const char *ps, int ls, const char *pp, int lp, int is, bool right, bool nocase) { int pos, ip, apos; char *p; if (lp > ls) return 0; if (is < 0) is += ls; else if (is == 0) is = right ? ls : 1; ls = ls - lp + 1; if (is > ls) { if (!right) return 0; else is = ls; } else if (is < lp) { if (right) return 0; else if (is < 1) is = 1; } is--; if (lp == 1) { char cp = *pp; if (nocase && cp != tolower(cp)) { cp = tolower(cp); if (right) { for (; is >= 0; is--) { if (tolower(ps[is]) == cp) return is + 1; } } else { for (; is < ls; is++) { if (tolower(ps[is]) == cp) return is + 1; } } } else { if (right) { p = memrchr(ps, (uchar)cp, is + 1); return p ? p - ps + 1 : 0; /*for (; is >= 0; is--) { if (ps[is] == cp) return is + 1; }*/ } else { p = memchr(ps + is, (uchar)cp, ls - is); return p ? p - ps + 1 : 0; /*for (; is < ls; is++) { if (ps[is] == cp) return is + 1; }*/ } } return 0; } pos = 0; apos = 0; if (!nocase || pp[0] == tolower(pp[0])) { p = memchr(right ? ps : ps + is, (uchar)pp[0], right ? ls : ls - is); if (!p) goto __FOUND; ls -= (int)(p - ps); is -= (int)(p - ps); if (is < 0) is = 0; apos = (int)(p - ps); ps = p; p = memrchr(ps, (uchar)pp[0], right ? is + 1 : ls); if (!p) goto __FOUND; ls = (int)(p - ps + 1); if (is >= ls) is = ls - 1; } ps += is; if (right) { if (nocase) { for (; is >= 0; is--, ps--) { if (tolower(ps[0]) != tolower(pp[0])) goto __NEXT_RN; for (ip = 1; ip < lp; ip++) { if (tolower(ps[ip]) != tolower(pp[ip])) goto __NEXT_RN; } pos = apos + is + 1; goto __FOUND; __NEXT_RN: ; } } else { for (; is >= 0; is--, ps--) { if (ps[0] != pp[0]) goto __NEXT_R; for (ip = 1; ip < lp; ip++) { if (ps[ip] != pp[ip]) goto __NEXT_R; } pos = apos + is + 1; goto __FOUND; __NEXT_R: ; } } } else { if (nocase) { for (; is < ls; is++, ps++) { if (tolower(ps[0]) != tolower(pp[0])) goto __NEXT_LN; for (ip = 1; ip < lp; ip++) { if (tolower(ps[ip]) != tolower(pp[ip])) goto __NEXT_LN; } pos = apos + is + 1; goto __FOUND; __NEXT_LN: ; } } else { for (; is < ls; is++, ps++) { if (ps[0] != pp[0]) goto __NEXT_L; if (memcmp(ps + 1, pp + 1, lp - 1)) goto __NEXT_L; /*for (ip = 1; ip < lp; ip++) { if (ps[ip] != pp[ip]) goto __NEXT_L; }*/ pos = apos + is + 1; goto __FOUND; __NEXT_L: ; } } } __FOUND: return pos; }