enum ParamsType { pstring, pint, punsigned, pbool, pfloat, pdouble, pcolormap };
typedef struct {
char * key;
enum ParamsType type;
void * val;
int n;
} Params;
static bool atobool (char * s)
{
if (!strcmp (s, "true"))
return true;
if (!strcmp (s, "false"))
return false;
return atoi (s) != 0;
}
static bool args (Params * p, char * val)
{
static char * name[] = { "string", "int", "unsigned",
"bool", "float", "double", "colormap" };
switch (p->type) {
case pstring:
if (val[0] != '"') {
fprintf (stderr, "expecting a string for '%s' got '%s'\n", p->key, val);
return false;
}
if (val[strlen(val) - 1] != '"') {
fprintf (stderr, "unterminated quoted string '%s'\n", val);
return false;
}
val[strlen(val) - 1] = '\0';
char * s = &val[1];
int nc = 0; // number of non-blank characters
while (*s != '\0') {
if (!strchr (" \t\n\r", *s))
nc++;
s++;
}
*((char **)p->val) = nc > 0 ? &val[1] : NULL;
break;
case pcolormap:
if (!strcmp (val, "jet"))
*((Colormap *)p->val) = jet;
else if (!strcmp (val, "cool_warm"))
*((Colormap *)p->val) = cool_warm;
else if (!strcmp (val, "gray"))
*((Colormap *)p->val) = gray;
else if (!strcmp (val, "randomap"))
*((Colormap *)p->val) = randomap;
else {
fprintf (stderr, "unknown colormap '%s'\n", val);
return false;
}
break;
case pint: case punsigned: case pbool: case pdouble: case pfloat:
if (val[0] == '"') {
fprintf (stderr, "expecting a %s for '%s' got %s\n",
name[p->type], p->key, val);
return false;
}
if (!p->n) {
switch (p->type) {
case pint: *((int *)p->val) = atoi(val); break;
case punsigned: *((unsigned *)p->val) = atoi(val); break;
case pbool: *((bool *)p->val) = atobool(val); break;
case pfloat: *((float *)p->val) = atof(val); break;
case pdouble: *((double *)p->val) = atof(val); break;
default: assert (false);
}
}
else {
if (val[0] != '{') {
fprintf (stderr, "expecting an array for '%s' got %s\n", p->key, val);
return false;
}
val++;
int i = 0;
char c = ',';
while (i < p->n && c != '}') {
char * s = strchr (val, ',');
if (!s)
s = strchr (val, '}');
if (!s) {
fprintf (stderr, "expecting an array for '%s' got %s\n", p->key, val);
return false;
}
c = *s;
*s++ = '\0';
switch (p->type) {
case pint: ((int *)p->val)[i++] = atoi (val); break;
case punsigned: ((unsigned *)p->val)[i++] = atoi (val); break;
case pbool: ((bool *)p->val)[i++] = atobool (val); break;
case pfloat: ((float *)p->val)[i++] = atof (val); break;
case pdouble: ((double *)p->val)[i++] = atof (val); break;
default: assert (false);
}
val = s;
}
if (c != '}') {
fprintf (stderr, "expecting '}' for '%s' got %s\n", p->key, val);
return false;
}
}
break;
default:
assert (false);
}
return true;
}
static char * find_comma (char * s)
{
int par = 0;
while (*s != '\0') {
if (*s == ',' && par == 0) {
*s = '\0';
return s + 1;
}
if (*s == '{')
par++;
else if (*s == '}')
par--;
s++;
}
return NULL;
}
static char * mystrtok (char * str, const char * delim)
{
static char * s = NULL;
char * start = str ? str : s;
bool string = false;
s = start;
while (*s != '\0') {
if (*s == '"')
string = !string;
if (!string && strchr(delim, *s))
break;
s++;
}
if (*s != '\0')
*s++ = '\0';
return start;
}
int parse_params (Params * params)
{
char * s;
int i = 0, n = 0;
Params * p = params;
while (p->key) p++, n++;
if (!(s = mystrtok (NULL, ");")) || s[0] == '\n')
return false;
while (s && *s != '\0') {
char * next = find_comma (s), * key = s;
if ((s = strchr (key, '='))) {
s[0] = '\0', s++;
i = -1;
Params * p = params;
while (p->key && strcmp(p->key, key)) p++;
if (!p->key) {
fprintf (stderr, "unknown key '%s'\n", key);
return false;
}
if (!args (p, s))
return false;
}
else {
if (i < 0) {
fprintf (stderr, "anonymous value '%s' after keys\n", key);
return false;
}
if (i >= n) {
fprintf (stderr, "too many parameters: '%s' %d %d\n", key, i, n);
return false;
}
if (!args (¶ms[i], key))
return false;
i++;
}
s = next;
}
return true;
}