\n\
<sections>\t<section>[;<sections>]\n\
<section>\t[*]<name>{[<items>]}\n\
-<items>\t\t<item>[;<items>]\n\
-<item>\t\t<static> | @<list>\n\
-<static>\t[-]<type><name>\n\
-<list>\t\t<name>{[<items>]}\n\
+<items>\t\t[-]<item>[;<items>]\n\
+<item>\t\t<static> | <array> | <list>\n\
+<static>\t<type><name>[=<value>]\n\
+<list>\t\t@<name>{[<items>]}\n\
+<array>\t\t<type><name><left-bracket>[<number>]<right-bracket>\n\
+<value>\t\t[a-zA-Z0-9.-/]* | 'string without single quotes'<value> | \"c-like string\"<value>\n\
\n\
Types:\n\
-<empty>\t\tString\n\
+:\t\tString\n\
#\t\t32-bit integer\n\
##\t\t64-bit integer\n\
$\t\tFloating point number\n\
static void
parse_section(struct section *section)
{
+#define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
for (uns sep = 0; ; sep = 1)
{
parse_white();
}
item->cf.cls = CC_STATIC;
item->cf.number = 1;
- switch (*pos)
+ switch (*pos++)
{
case '#':
- if (*++pos == '#')
+ if (*pos == '#')
{
pos++;
item->cf.type = CT_U64;
item->cf.type = CT_INT;
break;
case '$':
- pos++;
item->cf.type = CT_DOUBLE;
break;
- default:
- if (!Cword(*pos))
- die("Invalid type syntax");
+ case ':':
item->cf.type = CT_STRING;
break;
+ default:
+ die("Invalid type syntax");
}
+ parse_white();
item->cf.name = parse_name();
parse_white();
+ if (*pos == '[')
+ {
+ pos++;
+ parse_white();
+ item->cf.cls = CC_DYNAMIC;
+ byte *num = pos;
+ while (*pos && *pos != ']')
+ pos++;
+ if (!*pos)
+ die("Missing ']'");
+ *pos++ = 0;
+ if (!*num)
+ item->cf.number = CF_ANY_NUM;
+ else
+ {
+ int inum;
+ TRY(cf_parse_int(num, &inum));
+ if (!inum)
+ die("Invalid array length");
+ item->cf.number = inum;
+ }
+ parse_white();
+ }
if (*pos == '=')
{
pos++;
parse_white();
if (section->item.cf.cls == CC_LIST)
die("List items can not have default values");
+ if (item->cf.cls == CC_DYNAMIC)
+ die("Arrays can not have default values");
byte *def = pos, *d = def;
while (*pos != ';' && *pos != '}' && !Cspace(*pos))
{
memcpy(buf, def, len);
buf[len] = 0;
switch (item->cf.type)
-#define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
{
case CT_STRING:
item->value.v_ptr = buf;
break;
default:
ASSERT(0);
-#undef TRY
}
}
}
clist_add_tail(§ion->list, &item->node);
section->count++;
}
+#undef TRY
}
static void
static bb_t path;
+static uns
+type_size(enum cf_type type)
+{
+ switch (type)
+ {
+ case CT_INT: return sizeof(int);
+ case CT_U64: return sizeof(u64);
+ case CT_DOUBLE: return sizeof(double);
+ case CT_STRING: return sizeof(byte *);
+ default: ASSERT(0);
+ }
+}
+
+static void
+dump_value(uns array, struct item *item, void *v)
+{
+ byte buf[128], *value = buf;
+ if (!array)
+ printf("CF_%s_%s='", path.ptr, item->cf.name);
+ else
+ printf("CF_%s_%s[%u]='", path.ptr, item->cf.name, ++item->index);
+ switch (item->cf.type)
+ {
+ case CT_INT:
+ sprintf(buf, "%d", *(int *)v);
+ break;
+ case CT_U64:
+ sprintf(buf, "%Lu", *(u64 *)v);
+ break;
+ case CT_DOUBLE:
+ sprintf(buf, "%g", *(double *)v);
+ break;
+ case CT_STRING:
+ if (*(byte **)v)
+ value = *(byte **)v;
+ else
+ *value = 0;
+ break;
+ default:
+ ASSERT(0);
+ }
+ while (*value) {
+ if (*value == '\'')
+ printf("'\\''");
+ else
+ putchar(*value);
+ value++;
+ }
+ printf("'\n");
+}
+
static void
dump_item(struct item *item, void *ptr, uns path_len)
{
if (item->flags & FLAG_HIDE)
return;
- union value *val = (union value *)((addr_int_t)ptr + (addr_int_t)item->cf.ptr);
+ byte *val = (byte *)((addr_int_t)ptr + (addr_int_t)item->cf.ptr);
if (item->cf.cls == CC_LIST)
{
uns len = strlen(item->cf.name);
bb_grow(&path, path_len + len + 1);
path.ptr[path_len] = '_';
memcpy(path.ptr + path_len + 1, item->cf.name, len);
- CLIST_FOR_EACH(cnode *, ptr2, val->list)
+ CLIST_FOR_EACH(cnode *, ptr2, *(clist *)val)
CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
dump_item(item2, ptr2, path_len + len + 1);
}
else
{
- byte *name = item->cf.name;
- byte buf[128], *value = buf;
bb_grow(&path, path_len + 1)[path_len] = 0;
- if (!ptr)
- printf("CF_%s_%s='", path.ptr, name);
+ if (item->cf.cls == CC_STATIC)
+ dump_value(!!ptr, item, val);
else
- printf("CF_%s_%s[%u]='", path.ptr, name, item->index++);
- switch (item->cf.type)
{
- case CT_INT:
- sprintf(buf, "%d", val->v_int);
- break;
- case CT_U64:
- sprintf(buf, "%Lu", val->v_u64);
- break;
- case CT_DOUBLE:
- sprintf(buf, "%g", val->v_double);
- break;
- case CT_STRING:
- if (val->v_ptr)
- value = val->v_ptr;
- else
- *value = 0;
- break;
- default:
- ASSERT(0);
+ val = *(void **)val;
+ uns len = DARY_LEN(val);
+ uns size = type_size(item->cf.type);
+ for (uns i = 0; i < len; i++, val += size)
+ dump_value(1, item, val);
}
- while (*value) {
-#if 0
- if (*value == '\'')
- die("Apostrophes are not supported in config of scripts");
-#endif
- if (*value == '\'')
- printf("'\\''");
- else
- putchar(*value);
- value++;
- }
- printf("'\n");
}
}
# Tests for configuration parser
-Run: obj/lib/shell/config -C/dev/null -S 'sec1{int1=23; long1=1234567812345678; long2=4321; str1="s1"; str2="s2"}' 'sec1 {#int1; ##long1; -str1; str2; #int2=123; ##long2=1234}; sec2{str3}'
+Run: obj/lib/shell/config -C/dev/null -S 'sec1{int1=23; long1=1234567812345678; long2=4321; str1="s1"; str2="s2"}' 'sec1 {#int1; ##long1; -:str1; :str2; #int2=123; ##long2=1234; #int3=0x10; #int4; $dbl1=001.100; $dbl2}; sec2{:str3}'
Out: CF_sec1_int1='23'
CF_sec1_long1='1234567812345678'
CF_sec1_str2='s2'
CF_sec1_int2='123'
CF_sec1_long2='4321'
+ CF_sec1_int3='16'
+ CF_sec1_int4='0'
+ CF_sec1_dbl1='1.1'
+ CF_sec1_dbl2='0'
CF_sec2_str3=''
-Run: obj/lib/shell/config -C/dev/null -S 'sec1{list1 1 a1 b1; list1:clear; list1 2 a2 b2 3 a3 b3}' 'sec1 {@list1 {#int1; str1; -str2}}'
-Out: CF_sec1_list1_int1[0]='2'
- CF_sec1_list1_str1[0]='a2'
- CF_sec1_list1_int1[1]='3'
- CF_sec1_list1_str1[1]='a3'
+Run: obj/lib/shell/config -C/dev/null -S 'sec1{list1 1 a1 b1; list1:clear; list1 2 a2 b2 3 a3 b3}' 'sec1 {@list1 {#int1; :str1; -:str2}}'
+Out: CF_sec1_list1_int1[1]='2'
+ CF_sec1_list1_str1[1]='a2'
+ CF_sec1_list1_int1[2]='3'
+ CF_sec1_list1_str1[2]='a3'
-Run: obj/lib/shell/config -C/dev/null -S 'sec1{list1 {str1=1; list2=a b c}; list1 {str1=2; list2=d e}}' 'sec1 {@list1 {str1; @list2{str2}}}'
-Out: CF_sec1_list1_str1[0]='1'
- CF_sec1_list1_list2_str2[0]='a'
- CF_sec1_list1_list2_str2[1]='b'
- CF_sec1_list1_list2_str2[2]='c'
- CF_sec1_list1_str1[1]='2'
- CF_sec1_list1_list2_str2[3]='d'
- CF_sec1_list1_list2_str2[4]='e'
+Run: obj/lib/shell/config -C/dev/null -S 'sec1{ar1 a b c d; ar1 a b c; ar2 1 2; ar3 1.1}' 'sec1 {:ar1[]; #ar2[2]; $ar3[-2]}'
+Out: CF_sec1_ar1[1]='a'
+ CF_sec1_ar1[2]='b'
+ CF_sec1_ar1[3]='c'
+ CF_sec1_ar2[1]='1'
+ CF_sec1_ar2[2]='2'
+ CF_sec1_ar3[1]='1.1'
-Run: obj/lib/shell/config -C/dev/null 'sec{str=a'\''b"c'\''d"\\e'\''f"g}'
+Run: obj/lib/shell/config -C/dev/null -S 'sec1{list1 {str1=1; list2=a b c}; list1 {str1=2; list2=d e}}' 'sec1 {@list1 {:str1; @list2{:str2}}}'
+Out: CF_sec1_list1_str1[1]='1'
+ CF_sec1_list1_list2_str2[1]='a'
+ CF_sec1_list1_list2_str2[2]='b'
+ CF_sec1_list1_list2_str2[3]='c'
+ CF_sec1_list1_str1[2]='2'
+ CF_sec1_list1_list2_str2[4]='d'
+ CF_sec1_list1_list2_str2[5]='e'
+
+Run: obj/lib/shell/config -C/dev/null 'sec{:str=a'\''b"c'\''d"\\e'\''f"g}'
Out: CF_sec_str='ab"cd\e'\''fg'