replace config file parser with a more tolerant one that allows spaces around tokens

This commit is contained in:
marschap
2006-09-18 10:39:21 +00:00
parent 086b56a60d
commit 40cde70e7f
+195 -130
View File
@@ -594,20 +594,22 @@ static char get_next_char_f(FILE *f)
/* Parser states */ /* Parser states */
#define ST_INITIAL 0 #define ST_INITIAL 0
#define ST_IGNORE 257 #define ST_COMMENT 257
#define ST_SECTIONNAME 258 #define ST_SECTIONLABEL 258
#define ST_KEYNAME 259 #define ST_KEYNAME 259
#define ST_VALUE 260 #define ST_ASSIGNMENT 260
#define ST_QUOTEDVALUE 261 #define ST_VALUE 261
#define ST_QUOTEDVALUE_ESCCHAR 262 #define ST_QUOTEDVALUE 262
#define ST_INVALID_SECTIONNAME 263 #define ST_SECTIONLABEL_DONE 263
#define ST_INVALID_KEYNAME 264 #define ST_VALUE_DONE 264
#define ST_INVALID_VALUE 265 #define ST_INVALID_SECTIONLABEL 265
#define ST_INVALID_QUOTEDVALUE 266 #define ST_INVALID_KEYNAME 266
#define ST_INVALID_ASSIGNMENT 267
#define ST_INVALID_VALUE 268
#define ST_END 999 #define ST_END 999
/* Limits */ /* Limits */
#define MAXSECTIONNAMELENGTH 40 #define MAXSECTIONLABELLENGTH 40
#define MAXKEYNAMELENGTH 40 #define MAXKEYNAMELENGTH 40
#define MAXVALUELENGTH 200 #define MAXVALUELENGTH 200
@@ -616,7 +618,7 @@ static int process_config(section **current_section, char(*get_next_char)(), con
{ {
int state = ST_INITIAL; int state = ST_INITIAL;
char ch; char ch;
char sectionname[MAXSECTIONNAMELENGTH+1]; char sectionname[MAXSECTIONLABELLENGTH+1];
int sectionname_pos = 0; int sectionname_pos = 0;
char keyname[MAXKEYNAMELENGTH+1]; char keyname[MAXKEYNAMELENGTH+1];
int keyname_pos = 0; int keyname_pos = 0;
@@ -625,6 +627,7 @@ static int process_config(section **current_section, char(*get_next_char)(), con
int escape = 0; int escape = 0;
key *k; key *k;
int line_nr = 1; int line_nr = 1;
int error = 0;
while (state != ST_END) { while (state != ST_END) {
@@ -639,180 +642,206 @@ static int process_config(section **current_section, char(*get_next_char)(), con
switch (state) { switch (state) {
case ST_INITIAL: case ST_INITIAL:
switch (ch) { switch (ch) {
case '\n':
case '\r':
case '\t':
case ' ':
break;
case '#': case '#':
case ';': case ';':
case '=': /* comment start */
case ']': state = ST_COMMENT;
/* It's a comment or an error */ /* fall through */
state = ST_IGNORE;
break;
case '[':
/* It's a section name */
state = ST_SECTIONNAME;
sectionname[0] = '\0';
sectionname_pos = 0;
break;
case '\0':
break;
default:
/* It's a keyname */
state = ST_KEYNAME;
keyname[0] = ch;
keyname[1] = '\0';
keyname_pos = 1;
}
break;
case ST_IGNORE:
switch (ch) {
case '\n':
state = ST_INITIAL;
break;
}
break;
case ST_SECTIONNAME:
switch (ch) {
case '\0': case '\0':
case '\n': case '\n':
report(RPT_WARNING, "Section name incorrectly closed on line %d of %s: %s",
line_nr, source_descr, sectionname);
state = ST_INITIAL;
break;
case '\r': case '\r':
case '\t': case '\t':
case ' ': case ' ':
case '"': /* ignore spaces */
case '[':
report(RPT_WARNING, "Invalid characters in section name on line %d of %s: %s",
line_nr, source_descr, sectionname);
state = ST_INVALID_SECTIONNAME;
break; break;
case ']': case '[':
if (!(*current_section=find_section(sectionname))) { /* section name */
*current_section=add_section(sectionname); state = ST_SECTIONLABEL;
} sectionname_pos = 0;
state = ST_INITIAL; sectionname[sectionname_pos] = '\0';
break; break;
default: default:
if (sectionname_pos < MAXSECTIONNAMELENGTH) { /* key word */
state = ST_KEYNAME;
keyname_pos = 0;
keyname[keyname_pos++] = ch;
keyname[keyname_pos] = '\0';
}
break;
case ST_SECTIONLABEL:
/* section label: "["{non-space chars}+"]" */
switch (ch) {
case '\0':
case '\n':
/* premature end of label */
report(RPT_WARNING, "Unterminated section label on line %d of %s: %s",
line_nr, source_descr, sectionname);
error = 1;
state = ST_INITIAL; /* alrady at the end, no resync required */
break;
case ']':
/* label terminated: find/create section */
if (!(*current_section = find_section(sectionname))) {
*current_section = add_section(sectionname);
}
state = ST_SECTIONLABEL_DONE;
break;
// case '\r':
// case '\t':
// case ' ':
// /* no spaces allowed in section labels WHY? */
// report(RPT_WARNING, "Invalid character in section label on line %d of %s: %s",
// line_nr, source_descr, sectionname);
// error = 1;
// state = ST_INVALID_SECTIONLABEL; /* resync required */
// break;
default:
/* append char to section label */
if (sectionname_pos < MAXSECTIONLABELLENGTH) {
sectionname[sectionname_pos++] = ch; sectionname[sectionname_pos++] = ch;
sectionname[sectionname_pos] = '\0'; sectionname[sectionname_pos] = '\0';
} else { break;
}
report(RPT_WARNING, "Section name too long on line %d of %s: %s", report(RPT_WARNING, "Section name too long on line %d of %s: %s",
line_nr, source_descr, sectionname); line_nr, source_descr, sectionname);
state = ST_INVALID_SECTIONNAME; error = 1;
} state = ST_INVALID_SECTIONLABEL; /* resync required */
}
break;
case ST_INVALID_SECTIONNAME:
switch (ch) {
case '\n':
case ']':
state = ST_INITIAL;
break;
} }
break; break;
case ST_KEYNAME: case ST_KEYNAME:
/* key name: {non-space chars}+ */
switch (ch) { switch (ch) {
case '\0':
case '\n':
case '\r': case '\r':
case '\t': case '\t':
case ' ': case ' ':
/* ignore trailing spaces */
if (keyname_pos != 0)
state = ST_ASSIGNMENT;
break;
case '\0':
case '\n':
/* premature end of line */
report(RPT_WARNING, "Loose word found on line %d of %s: %s", report(RPT_WARNING, "Loose word found on line %d of %s: %s",
line_nr, source_descr, keyname); line_nr, source_descr, keyname);
state = ST_INITIAL; error = 1;
break; state = ST_INITIAL; /* already at the end; no resync required */
case '"':
case '[':
case ']':
report(RPT_WARNING, "Invalid characters in key name on line %d of %s: %s",
line_nr, source_descr, keyname);
state = ST_INVALID_KEYNAME;
break; break;
case '=': case '=':
/* end of key reached, "=" found, now we need a value */
state = ST_VALUE;
value[0] = '\0';
value_pos = 0;
break;
// case '"':
// case '[':
// case ']':
// /* character invalid in key names WHY ? */
// report(RPT_WARNING, "Invalid character in key name on line %d of %s: %s",
// line_nr, source_descr, keyname);
// error = 1;
// state = ST_INVALID_KEYNAME; /* resync required */
// break;
default:
/* append char to key name */
if (keyname_pos < MAXKEYNAMELENGTH) {
keyname[keyname_pos++] = ch;
keyname[keyname_pos] = '\0';
break;
}
report(RPT_WARNING, "Key name too long on line %d of %s: %s",
line_nr, source_descr, keyname);
error = 1;
state = ST_INVALID_KEYNAME; /* resync required */
}
break;
case ST_ASSIGNMENT:
/* assignement: "=" */
switch (ch) {
case '\t':
case ' ':
/* ignore leading spaces */
break;
case '=':
/* "=" found, now we need a value */
state = ST_VALUE; state = ST_VALUE;
value[0] = '\0'; value[0] = '\0';
value_pos = 0; value_pos = 0;
break; break;
default: default:
if (keyname_pos>=MAXKEYNAMELENGTH) { report(RPT_WARNING, "Assigment expected on line %d of %s: %s",
report(RPT_WARNING, "Key name too long on line %d of %s: %s",
line_nr, source_descr, keyname); line_nr, source_descr, keyname);
state = ST_INVALID_KEYNAME; error = 1;
} else { state = ST_INVALID_ASSIGNMENT;
keyname[keyname_pos++] = ch;
keyname[keyname_pos] = '\0';
}
}
break;
case ST_INVALID_KEYNAME:
switch (ch) {
case '\n':
state = ST_INITIAL;
} }
break; break;
case ST_VALUE: case ST_VALUE:
/* value: {non-space char}+ | "\""{any potentially-quoted char}+"\"" */
switch (ch) { switch (ch) {
case '[':
case ']':
case '#': case '#':
case ';': case ';':
/* allow comment if we already had a value */
/* WHY ONLY THEN ? 'xx=' can be seen as equivalent to 'xx=""' */
if (value_pos > 0) {
state = ST_COMMENT;
break;
}
/* fall through */
case '[':
case ']':
case '=': case '=':
report(RPT_WARNING, "Invalid characters in value on line %d of %s, at key: %s", /* illegal characters WHY? */
line_nr, source_descr, keyname); report(RPT_WARNING, "Invalid character '%c' in value on line %d of %s, at key: %s",
ch, line_nr, source_descr, keyname);
error = 1;
state = ST_INVALID_VALUE; state = ST_INVALID_VALUE;
break; break;
case '"': case '\t':
state = ST_QUOTEDVALUE; case ' ':
/* ignore leading spaces */
if (value_pos == 0)
break; break;
/* fall through */
case '\0': case '\0':
case '\n': case '\n':
case '\r': case '\r':
case '\t': /* value complete */
case ' ': if (!*current_section) {
/* Value complete ! */
if (! *current_section) {
report(RPT_WARNING, "Data outside sections on line %d of %s with key: %s", report(RPT_WARNING, "Data outside sections on line %d of %s with key: %s",
line_nr, source_descr, keyname); line_nr, source_descr, keyname);
error = 1;
} }
else { else {
/* Store the value */ /* Store the value*/
k = add_key(*current_section, keyname, value); k = add_key(*current_section, keyname, value);
} }
/* And be ready for next thing... */ /* And be ready for next thing...*/
state = ST_INITIAL; state = ((ch == ' ') || (ch == '\t')) ? ST_VALUE_DONE : ST_INITIAL;
break;
default:
if (value_pos<MAXVALUELENGTH) {
value[value_pos++] = ch;
value[value_pos] = '\0';
} else {
report(RPT_WARNING, "Value too long on line %d of %s, at key: %s",
line_nr, source_descr, keyname);
state = ST_INVALID_KEYNAME;
}
}
break;
case ST_INVALID_VALUE:
switch (ch) {
case '\n':
state = ST_INITIAL;
break; break;
case '"': case '"':
state = ST_INVALID_QUOTEDVALUE; /* quoted string */
state = ST_QUOTEDVALUE;
break;
default:
/* append char to value */
if (value_pos < MAXVALUELENGTH) {
value[value_pos++] = ch;
value[value_pos] = '\0';
break;
}
report(RPT_WARNING, "Value too long on line %d of %s, at key: %s",
line_nr, source_descr, keyname);
error = 1;
state = ST_INVALID_VALUE;
} }
break; break;
case ST_QUOTEDVALUE: case ST_QUOTEDVALUE:
/* a quoted part of a string */
switch (ch) { switch (ch) {
case '\0': case '\0':
case '\n': case '\n':
report(RPT_WARNING, "String is incorrectly terminated on line %d of %s: %s", report(RPT_WARNING, "Premature end of quoted string on line %d of %s: %s",
line_nr, source_descr, keyname); line_nr, source_descr, keyname);
error = 1;
state = ST_INITIAL; state = ST_INITIAL;
break; break;
case '\\': case '\\':
@@ -830,9 +859,13 @@ static int process_config(section **current_section, char(*get_next_char)(), con
default: default:
if (escape) { if (escape) {
switch (ch) { switch (ch) {
case 'a': ch = '\a'; break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break; case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break; case 'r': ch = '\r'; break;
case 't': ch = '\t'; break; case 't': ch = '\t'; break;
case 'v': ch = '\v'; break;
/* default: literal char (i.e. ignore '\') */ /* default: literal char (i.e. ignore '\') */
} }
escape = 0; escape = 0;
@@ -841,21 +874,53 @@ static int process_config(section **current_section, char(*get_next_char)(), con
value[value_pos] = '\0'; value[value_pos] = '\0';
} }
break; break;
case ST_INVALID_QUOTEDVALUE: case ST_SECTIONLABEL_DONE:
case ST_VALUE_DONE:
switch (ch) { switch (ch) {
case ';':
case '#':
state = ST_COMMENT;
break;
case '\0':
case '\n': case '\n':
state = ST_INITIAL; state = ST_INITIAL;
break; break;
case '"': case '\t':
case ' ':
break;
default:
/* illegal characters */
report(RPT_WARNING, "Invalid character '%c' on line %d of %s",
ch, line_nr, source_descr);
error = 1;
state = ST_INVALID_VALUE; state = ST_INVALID_VALUE;
} }
break; case ST_INVALID_SECTIONLABEL:
/* invalid section label: resync up to end of label/next line */
if (ch == ']')
state = ST_INITIAL;
/* fall through */
case ST_INVALID_ASSIGNMENT:
case ST_INVALID_KEYNAME:
case ST_INVALID_VALUE:
case ST_COMMENT:
/* comment or error: ignore anything up to the next line */
if (ch == '\n')
state = ST_INITIAL;
} }
if (ch == '\0') { if (ch == '\0') {
if ((!error) && (state != ST_INITIAL) && (state != ST_COMMENT) &&
(state != ST_SECTIONLABEL_DONE) && (state != ST_VALUE_DONE)) {
report(RPT_WARNING, "Premature end of configuration on line %d of %s: %d",
line_nr, source_descr, state);
error = 1;
}
state = ST_END; state = ST_END;
} }
} }
return 0; return (error) ? -1 : 0;
} }