1324 lines
24 KiB
C
1324 lines
24 KiB
C
/***************************************************************************
|
|
|
|
gbx_string.c
|
|
|
|
(c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>
|
|
|
|
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 <unistd.h>
|
|
#include <pwd.h>
|
|
#include <ctype.h>
|
|
#include <iconv.h>
|
|
|
|
#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_BSD)
|
|
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;
|
|
}
|