conf.c
#define EX_UTILS_NO_FUNCS 1
#include "ex_utils.h"
#include "conf.h"
#include <limits.h>
#include "mk.h"
#define CLEN VSTR__AT_COMPILE_STRLEN
#define VPREFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
Conf_parse *conf_parse_make(Vstr_conf *conf)
{
Conf_parse *ret = MK(sizeof(Conf_parse));
Vstr_sects *sects = vstr_sects_make(4);
Vstr_base *s1 = vstr_make_base(conf);
Vstr_base *s2 = vstr_make_base(conf);
unsigned int *ptr = MK(sizeof(unsigned int) * 4);
if (!ret || !s1 || !sects || !ptr)
{
F(ret);
vstr_sects_free(sects);
vstr_free_base(s1);
vstr_free_base(s2);
F(ptr);
return (NULL);
}
ret->sects = sects;
ret->data = s1;
ret->tmp = s2;
ret->types_sz = 4;
ret->types_ptr = ptr;
ret->uvals_sz = 0;
ret->uvals_num = 0;
ret->uvals_ptr = NULL;
ret->state = CONF_PARSE_STATE_BEG;
ret->depth = 0;
return (ret);
}
void conf_parse_free(Conf_parse *conf)
{
if (!conf)
return;
F(conf->types_ptr);
vstr_sects_free(conf->sects);
vstr_free_base(conf->data);
vstr_free_base(conf->tmp);
while (conf->uvals_num)
vstr_ref_del(conf->uvals_ptr[--conf->uvals_num].ref);
F(conf->uvals_ptr);
F(conf);
}
static int conf__parse_type_add(Conf_parse *conf, unsigned int type)
{
unsigned int *ptr = NULL;
ASSERT(conf->types_sz <= conf->sects->sz);
if (conf->types_sz == conf->sects->sz)
goto fin_ok;
if (!MV(conf->types_ptr, ptr, sizeof(unsigned int) * conf->sects->sz))
return (FALSE);
conf->types_sz = conf->sects->sz;
fin_ok:
conf->types_ptr[conf->sects->num - 1] = type;
return (TRUE);
}
static size_t conf__parse_ws(Conf_parse *conf, size_t pos, size_t len)
{
conf->state = CONF_PARSE_STATE_CHOOSE;
return (vstr_spn_cstr_chrs_fwd(conf->data, pos, len, " \t\v\r\n"));
}
static size_t conf__parse_comment(Conf_parse *conf, size_t pos, size_t len)
{
Vstr_sect_node *node = NULL;
ASSERT(conf->sects);
if (conf->sects->malloc_bad)
return (0);
ASSERT(conf->sects->num);
node = VSTR_SECTS_NUM(conf->sects, conf->sects->num);
if (node->len == 1)
{
int byte = vstr_export_chr(conf->data, node->pos);
if ((byte == ';') || (byte == '#'))
{
vstr_sects_del(conf->sects, conf->sects->num);
conf->state = CONF_PARSE_STATE_CHOOSE;
return (vstr_cspn_cstr_chrs_fwd(conf->data, pos, len, "\n"));
}
}
return (0);
}
static int conf__parse_beg_list(Conf_parse *conf, size_t pos,
unsigned int *list_nums)
{
if (conf->depth >= CONF_PARSE_LIST_DEPTH_SZ)
return (FALSE);
vstr_sects_add(conf->sects, pos, 0);
conf__parse_type_add(conf, CONF_TOKEN_TYPE_ERR);
list_nums[conf->depth++] = conf->sects->num;
conf->state = CONF_PARSE_STATE_CHOOSE;
return (TRUE);
}
static int conf__parse_end_list(Conf_parse *conf, unsigned int *list_nums,
int byte)
{
Vstr_sects *sects = conf->sects;
unsigned int depth_beg_num = 0;
Vstr_sect_node *node = NULL;
if (!conf->depth)
return (FALSE);
depth_beg_num = list_nums[--conf->depth];
node = VSTR_SECTS_NUM(sects, depth_beg_num);
if (byte == ']')
{
conf->types_ptr[depth_beg_num - 1] = CONF_TOKEN_TYPE_SLIST;
if (vstr_export_chr(conf->data, node->pos) != '[')
return (FALSE);
}
if (byte == ')')
{
conf->types_ptr[depth_beg_num - 1] = CONF_TOKEN_TYPE_CLIST;
if (vstr_export_chr(conf->data, node->pos) != '(')
return (FALSE);
}
ASSERT(!node->len);
node->len = sects->num - depth_beg_num;
conf->state = CONF_PARSE_STATE_CHOOSE;
return (TRUE);
}
static void conf__parse_beg_quote_d(Conf_parse *conf, size_t pos,
unsigned int *list_nums)
{
ASSERT(conf->depth <= CONF_PARSE_LIST_DEPTH_SZ);
vstr_sects_add(conf->sects, pos, 0);
conf__parse_type_add(conf, CONF_TOKEN_TYPE_QUOTE_D);
list_nums[conf->depth++] = conf->sects->num;
conf->state = CONF_PARSE_STATE_QUOTE_D_BEG;
}
static void conf__parse_beg_quote_s(Conf_parse *conf, size_t pos,
unsigned int *list_nums)
{
ASSERT(conf->depth <= CONF_PARSE_LIST_DEPTH_SZ);
vstr_sects_add(conf->sects, pos, 0);
conf__parse_type_add(conf, CONF_TOKEN_TYPE_QUOTE_S);
list_nums[conf->depth++] = conf->sects->num;
conf->state = CONF_PARSE_STATE_QUOTE_S_BEG;
}
static void conf__parse_beg_quote_xxx(Conf_parse *conf, unsigned int *list_nums,
int three)
{
Vstr_sects *sects = conf->sects;
unsigned int depth_beg_num = 0;
size_t mv_pos = 1;
unsigned int state = 0;
ASSERT(conf->depth);
depth_beg_num = list_nums[conf->depth - 1];
if (conf->state == CONF_PARSE_STATE_QUOTE_S_BEG)
state = CONF_PARSE_STATE_QUOTE_S_END;
if (conf->state == CONF_PARSE_STATE_QUOTE_D_BEG)
state = CONF_PARSE_STATE_QUOTE_D_END;
if (three)
{
mv_pos = 3;
++state;
conf->types_ptr[depth_beg_num - 1] += 2;
}
VSTR_SECTS_NUM(sects, depth_beg_num)->pos += mv_pos;
conf->state = state;
}
static void conf__parse_end_quote_xxx(Conf_parse *conf, size_t pos,
unsigned int *list_nums)
{
Vstr_sects *sects = conf->sects;
unsigned int depth_beg_num = 0;
size_t beg_pos = 0;
ASSERT(conf->depth);
depth_beg_num = list_nums[--conf->depth];
beg_pos = VSTR_SECTS_NUM(sects, depth_beg_num)->pos;
VSTR_SECTS_NUM(sects, depth_beg_num)->pos = beg_pos;
VSTR_SECTS_NUM(sects, depth_beg_num)->len = pos - beg_pos;
conf->state = CONF_PARSE_STATE_LIST_END_OR_WS;
}
static int conf__parse_esc_quote(Conf_parse *conf, unsigned int *list_nums,
size_t pos, size_t len, unsigned int type)
{
Vstr_sects *sects = conf->sects;
unsigned int depth_beg_num = 0;
ASSERT(conf->depth);
if (len < 2)
return (FALSE);
depth_beg_num = list_nums[conf->depth - 1];
if ((pos == VSTR_SECTS_NUM(sects, depth_beg_num)->pos) &&
vstr_cmp_cstr_eq(conf->data, pos, CLEN("\\\n"), "\\\n"))
{
VSTR_SECTS_NUM(sects, depth_beg_num)->pos += 2;
return (TRUE);
}
if (conf->types_ptr[depth_beg_num - 1] == type)
++conf->types_ptr[depth_beg_num - 1];
return (TRUE);
}
#define CONF__SC_PARSE_ESC_QUOTE(x) do { \
ASSERT(vstr_export_chr(data, pos) == '\\'); \
\
if (!conf__parse_esc_quote(conf, list_nums, pos, len, \
CONF_TOKEN_TYPE_QUOTE_ ## x)) \
return (CONF_PARSE_ERR); \
plen = 2; \
} while (FALSE)
int conf_parse_lex(Conf_parse *conf, size_t pos, size_t len)
{
unsigned int list_nums[CONF_PARSE_LIST_DEPTH_SZ + 1];
Vstr_base *data = NULL;
ASSERT(conf && conf->data && conf->sects &&
conf->types_ptr && conf->types_sz);
data = conf->data;
if (conf->state != CONF_PARSE_STATE_BEG)
return (CONF_PARSE_ERR);
while (len)
{
size_t plen = 0;
switch (conf->state)
{
case CONF_PARSE_STATE_BEG:
conf->state = CONF_PARSE_STATE_CHOOSE;
if (VPREFIX(data, pos, len, "#!"))
{
plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\n");
conf->state = CONF_PARSE_STATE_WS;
}
break;
case CONF_PARSE_STATE_WS:
if (!(plen = conf__parse_ws(conf, pos, len)))
return (CONF_PARSE_ERR);
break;
case CONF_PARSE_STATE_LIST_END_OR_WS:
{
int byte = vstr_export_chr(data, pos);
plen = 1;
switch (byte)
{
case ' ':
case '\t':
case '\v':
case '\r':
case '\n':
plen = conf__parse_ws(conf, pos, len);
break;
case ')':
case ']':
if (!conf__parse_end_list(conf, list_nums, byte))
return (CONF_PARSE_ERR);
break;
default:
return (CONF_PARSE_ERR);
}
}
break;
case CONF_PARSE_STATE_CHOOSE:
{
int byte = vstr_export_chr(data, pos);
plen = 1;
switch (byte)
{
case ' ':
case '\t':
case '\v':
case '\r':
case '\n':
plen = conf__parse_ws(conf, pos, len);
break;
case ')':
case ']':
if (!conf__parse_end_list(conf, list_nums, byte))
return (CONF_PARSE_ERR);
break;
case '(':
case '[':
if (!conf__parse_beg_list(conf, pos, list_nums))
return (CONF_PARSE_ERR);
break;
case '"':
conf__parse_beg_quote_d(conf, pos, list_nums);
break;
case '\'':
conf__parse_beg_quote_s(conf, pos, list_nums);
break;
default:
plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, " \t\v\r\n\"'()[]");
conf->state = CONF_PARSE_STATE_SYMBOL_END;
vstr_sects_add(conf->sects, pos, plen);
conf__parse_type_add(conf, CONF_TOKEN_TYPE_SYMBOL);
break;
}
}
break;
case CONF_PARSE_STATE_QUOTE_D_BEG:
{
static const char dd[] = {'"', '"', 0};
if (!VPREFIX(data, pos, len, dd))
conf__parse_beg_quote_xxx(conf, list_nums, FALSE);
else
{
plen = 2;
conf__parse_beg_quote_xxx(conf, list_nums, TRUE);
}
}
break;
case CONF_PARSE_STATE_QUOTE_S_BEG:
if (!VPREFIX(data, pos, len, "''"))
conf__parse_beg_quote_xxx(conf, list_nums, FALSE);
else
{
plen = 2;
conf__parse_beg_quote_xxx(conf, list_nums, TRUE);
}
break;
case CONF_PARSE_STATE_QUOTE_D_END:
if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\")))
{
plen = 1;
if (vstr_export_chr(data, pos) == '"')
conf__parse_end_quote_xxx(conf, pos, list_nums);
else
CONF__SC_PARSE_ESC_QUOTE(D);
}
break;
case CONF_PARSE_STATE_QUOTE_DDD_END:
if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\")))
{
static const char ddd[] = {'"', '"', '"', 0};
plen = 3;
if (VPREFIX(data, pos, len, ddd))
conf__parse_end_quote_xxx(conf, pos, list_nums);
else if (vstr_export_chr(data, pos) != '\\')
plen = 1;
else
CONF__SC_PARSE_ESC_QUOTE(DDD);
}
break;
case CONF_PARSE_STATE_QUOTE_S_END:
if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "'\\")))
{
plen = 1;
if (vstr_export_chr(data, pos) == '\'')
conf__parse_end_quote_xxx(conf, pos, list_nums);
else
CONF__SC_PARSE_ESC_QUOTE(S);
}
break;
case CONF_PARSE_STATE_QUOTE_SSS_END:
if (!(plen = vstr_cspn_cstr_chrs_fwd(data, pos, len, "'\\")))
{
plen = 3;
if (VPREFIX(data, pos, len, "'''"))
conf__parse_end_quote_xxx(conf, pos, list_nums);
else if (vstr_export_chr(data, pos) != '\\')
plen = 1;
else
CONF__SC_PARSE_ESC_QUOTE(SSS);
}
break;
case CONF_PARSE_STATE_SYMBOL_END:
{
int byte = vstr_export_chr(data, pos);
switch (byte)
{
case ' ':
case '\t':
case '\v':
case '\r':
case '\n':
if (!(plen = conf__parse_comment(conf, pos, len)))
plen = conf__parse_ws(conf, pos, len);
break;
case ')':
case ']':
if (!(plen = conf__parse_comment(conf, pos, len)))
{
plen = 1;
if (!conf__parse_end_list(conf, list_nums, byte))
return (CONF_PARSE_ERR);
}
break;
case '(':
case '[':
if (!(plen = conf__parse_comment(conf, pos, len)))
{
plen = 1;
if (!conf__parse_beg_list(conf, pos, list_nums))
return (CONF_PARSE_ERR);
}
break;
case '"':
case '\'':
return (CONF_PARSE_ERR);
default:
assert(FALSE);
return (CONF_PARSE_ERR);
}
}
break;
default:
assert(FALSE);
return (CONF_PARSE_ERR);
}
len -= plen; pos += plen;
}
if (conf->sects->malloc_bad || conf->depth)
return (CONF_PARSE_ERR);
conf->state = CONF_PARSE_STATE_END;
return (CONF_PARSE_FIN);
}
Conf_token *conf_token_make(void)
{
Conf_token dummy = CONF_TOKEN_INIT;
Conf_token *ret = MK(sizeof(Conf_token));
if (!ret)
return (NULL);
*ret = dummy;
return (ret);
}
void conf_token_free(Conf_token *token)
{
F(token);
}
static const char *conf__token_name_map[] = {
"<** Error **>",
"Circular bracket list",
"Square bracket list",
"Quoted string (double)",
"Quoted string (double, with Escaping)",
"Quoted string (3x double)",
"Quoted string (3x double, with Escaping)",
"Quoted string (single)",
"Quoted string (single, with Escaping)",
"Quoted string (3x single)",
"Quoted string (3x single, with Escaping)",
"Symbol"
};
const char *conf_token_name(const Conf_token *token)
{
ASSERT(token);
if (token->type > CONF_TOKEN_TYPE_SYMBOL)
return ("User type");
return (conf__token_name_map[token->type]);
}
int conf_sc_token_parse_uint(const Conf_parse *conf, Conf_token *token,
unsigned int *val)
{
unsigned int num = conf_token_list_num(token, token->depth_num);
int ern = CONF_SC_TYPE_RET_OK;
const Vstr_sect_node *pv = NULL;
unsigned int nflags = VSTR_FLAG02(PARSE_NUM, OVERFLOW, SEP);
size_t len = 0;
ASSERT(val);
if (!num)
return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
conf_parse_token(conf, token);
if (!(pv = conf_token_value(token)))
return (CONF_SC_TYPE_RET_ERR_PARSE);
*val = vstr_parse_uint(conf->data, pv->pos, pv->len, nflags, &len, NULL);
if (len != pv->len)
ern = CONF_SC_TYPE_RET_ERR_PARSE;
return (ern);
}
int conf_sc_token_app_vstr(const Conf_parse *conf, Conf_token *token,
Vstr_base *s1,
const Vstr_base **a_s1, size_t *a_pos, size_t *a_len)
{
unsigned int num = conf_token_list_num(token, token->depth_num);
int ern = CONF_SC_TYPE_RET_OK;
const Vstr_sect_node *pv = NULL;
ASSERT(s1);
if (!num)
return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
conf_parse_token(conf, token);
if (!(pv = conf_token_value(token)))
return (CONF_SC_TYPE_RET_ERR_PARSE);
if (vstr_add_vstr(s1, s1->len, conf->data, pv->pos, pv->len,
VSTR_TYPE_SUB_BUF_REF))
{
*a_s1 = s1;
*a_pos = (s1->len - pv->len) + 1;
*a_len = pv->len;
}
return (ern);
}
int conf_sc_token_sub_vstr(const Conf_parse *conf, Conf_token *token,
Vstr_base *s1, size_t pos, size_t len)
{
unsigned int num = conf_token_list_num(token, token->depth_num);
int ern = CONF_SC_TYPE_RET_OK;
const Vstr_sect_node *pv = NULL;
ASSERT(s1);
if (!num)
return (CONF_SC_TYPE_RET_ERR_NOT_EXIST);
conf_parse_token(conf, token);
if (!(pv = conf_token_value(token)))
return (CONF_SC_TYPE_RET_ERR_PARSE);
vstr_sub_vstr(s1, pos, len, conf->data, pv->pos, pv->len,
VSTR_TYPE_SUB_BUF_REF);
return (ern);
}
#define SUB2(x, y, z) vstr_sub_cstr_buf(x, y, 2, z)
int conf_sc_conv_unesc(Vstr_base *s1, size_t pos, size_t len,
size_t *ret_len)
{
size_t dummy_len;
if (!ret_len) ret_len = &dummy_len;
*ret_len = len;
while (!s1->conf->malloc_bad && (len > 1))
{
size_t plen = vstr_cspn_cstr_chrs_fwd(s1, pos, len, "\\");
if (!plen)
{
char chr = vstr_export_chr(s1, pos + 1);
if (chr == '\n')
{
vstr_del(s1, pos, 2);
len -= 2; *ret_len -= 2;
continue;
}
else if (chr == 't') { SUB2(s1, pos, "\t"); --len; --*ret_len; }
else if (chr == 'v') { SUB2(s1, pos, "\v"); --len; --*ret_len; }
else if (chr == 'r') { SUB2(s1, pos, "\r"); --len; --*ret_len; }
else if (chr == 'n') { SUB2(s1, pos, "\n"); --len; --*ret_len; }
else if (chr == 'b') { SUB2(s1, pos, "\b"); --len; --*ret_len; }
else if (chr == '0')
{
unsigned char val = 0;
unsigned int nflags = VSTR_FLAG02(PARSE_NUM, OVERFLOW, SEP);
if (chr != '0') nflags |= 8;
val = vstr_parse_ushort(s1, pos + 1, len - 1, nflags, &plen, NULL);
vstr_sub_rep_chr(s1, pos, plen + 1, val, 1);
len -= plen;
*ret_len -= plen;
}
else
{
vstr_del(s1, pos, 1);
--len; --*ret_len;
}
plen = 1;
}
len -= plen;
pos += plen;
}
return (!s1->conf->malloc_bad);
}
#undef SUB
int conf_token_set_user_value(Conf_parse *conf, Conf_token *token,
unsigned int type,
Vstr_ref *uval, unsigned int nxt)
{
Vstr_ref *oref = NULL;
ASSERT(conf && token);
ASSERT(type <= (UINT_MAX - CONF_TOKEN_TYPE_USER_BEG));
if ((token->type == CONF_TOKEN_TYPE_CLIST) ||
(token->type == CONF_TOKEN_TYPE_SLIST))
return (FALSE);
if (token->type >= CONF_TOKEN_TYPE_USER_BEG)
oref = conf->uvals_ptr[token->u.uval_num].ref;
else
{
if (conf->uvals_sz <= conf->uvals_num)
{
unsigned int num = (conf->uvals_sz << 1) + 1;
Conf__uval_store *uvals = NULL;
if (num <= conf->uvals_sz)
return (FALSE);
if (!conf->uvals_sz &&
!(conf->uvals_ptr = MK(sizeof(Conf__uval_store) * num)))
return (FALSE);
else if (!MV(conf->uvals_ptr, uvals, sizeof(Conf__uval_store) * num))
return (FALSE);
conf->uvals_sz = num;
}
VSTR_SECTS_NUM(conf->sects, token->num)->pos = conf->uvals_num;
token->u.uval_num = conf->uvals_num;
++conf->uvals_num;
}
token->type = CONF_TOKEN_TYPE_USER_BEG + type;
conf->types_ptr[token->num - 1] = token->type;
conf->uvals_ptr[token->u.uval_num].ref = NULL;
conf->uvals_ptr[token->u.uval_num].nxt = nxt;
if (uval)
conf->uvals_ptr[token->u.uval_num].ref = vstr_ref_add(uval);
vstr_ref_del(oref);
return (TRUE);
}
void conf_parse_compress(Conf_parse *conf)
{
(void)conf;
}
void conf_parse_backtrace(Vstr_base *out, const char *filename,
const Conf_parse *conf, const Conf_token *token)
{
const Vstr_sect_node *val = NULL;
if (!out)
return;
if (conf->state != CONF_PARSE_STATE_END)
{
vstr_add_fmt(out, out->len, "Syntax error in %s, ", filename);
vstr_add_fmt(out, out->len, " State was: %d", conf->state);
return;
}
if (token->type > CONF_TOKEN_TYPE_SYMBOL)
vstr_add_fmt(out, out->len, "Failed parse on node %u (%s %d)",
token->num, conf_token_name(token),
token->type - CONF_TOKEN_TYPE_SYMBOL);
else if (!(val = conf_token_value(token)))
vstr_add_fmt(out, out->len, "Failed parse on node %u [%s]",
token->num, conf_token_name(token));
else
{
Vstr_base *s1 = conf->data;
vstr_add_fmt(out, out->len, "Failed parse on node %u <%s> = ",
token->num, conf_token_name(token));
vstr_add_vstr(out, out->len, s1, val->pos, val->len, VSTR_TYPE_ADD_BUF_REF);
}
}