* 3. config_parser recognizes \n and \r as 'end' token, while commands_parser
* ignores them.
*
+ * 4. config_parser skips the current line on invalid inputs and follows the
+ * nearest <error> token.
+ *
*/
#include <stdio.h>
#include <stdlib.h>
* single array, since the number of entries we have to store is very small.
*
*/
-static void push_string(const char *identifier, char *str) {
+static void push_string(const char *identifier, const char *str) {
for (int c = 0; c < 10; c++) {
if (stack[c].identifier != NULL &&
strcmp(stack[c].identifier, identifier) != 0)
if (stack[c].identifier == NULL) {
/* Found a free slot, let’s store it here. */
stack[c].identifier = identifier;
- stack[c].val.str = str;
+ stack[c].val.str = sstrdup(str);
stack[c].type = STACK_STR;
} else {
/* Append the value. */
- sasprintf(&(stack[c].val.str), "%s,%s", stack[c].val.str, str);
+ char *prev = stack[c].val.str;
+ sasprintf(&(stack[c].val.str), "%s,%s", prev, str);
+ free(prev);
}
return;
}
static struct ConfigResult subcommand_output;
static struct ConfigResult command_output;
+/* A list which contains the states that lead to the current state, e.g.
+ * INITIAL, WORKSPACE_LAYOUT.
+ * When jumping back to INITIAL, statelist_idx will simply be set to 1
+ * (likewise for other states, e.g. MODE or BAR).
+ * This list is used to process the nearest error token. */
+static cmdp_state statelist[10] = { INITIAL };
+/* NB: statelist_idx points to where the next entry will be inserted */
+static int statelist_idx = 1;
+
#include "GENERATED_config_call.h"
static void next_state(const cmdp_token *token) {
+ cmdp_state _next_state = token->next_state;
+
//printf("token = name %s identifier %s\n", token->name, token->identifier);
//printf("next_state = %d\n", token->next_state);
if (token->next_state == __CALL) {
subcommand_output.json_gen = command_output.json_gen;
GENERATED_call(token->extra.call_identifier, &subcommand_output);
+ _next_state = subcommand_output.next_state;
clear_stack();
- return;
}
- state = token->next_state;
+ state = _next_state;
if (state == INITIAL) {
clear_stack();
}
+
+ /* See if we are jumping back to a state in which we were in previously
+ * (statelist contains INITIAL) and just move statelist_idx accordingly. */
+ for (int i = 0; i < statelist_idx; i++) {
+ if (statelist[i] != _next_state)
+ continue;
+ statelist_idx = i+1;
+ return;
+ }
+
+ /* Otherwise, the state is new and we add it to the list */
+ statelist[statelist_idx++] = _next_state;
}
/*
linecnt++;
}
state = INITIAL;
+ statelist_idx = 1;
/* A YAJL JSON generator used for formatting replies. */
#if YAJL_MAJOR >= 2
if (token->name[0] == '\'') {
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
if (token->identifier != NULL)
- push_string(token->identifier, sstrdup(token->name + 1));
+ push_string(token->identifier, token->name + 1);
walk += strlen(token->name) - 1;
next_state(token);
token_handled = true;
}
if (token->identifier)
push_string(token->identifier, str);
+ free(str);
/* If we are at the end of a quoted string, skip the ending
* double quote. */
if (*walk == '"')
tokenwalk += strlen(token->name + 1);
*tokenwalk++ = '\'';
} else {
+ /* Skip error tokens in error messages, they are used
+ * internally only and might confuse users. */
+ if (strcmp(token->name, "error") == 0)
+ continue;
/* Any other token is copied to the error message enclosed
* with angle brackets. */
*tokenwalk++ = '<';
ystr(position);
y(map_close);
+ /* Skip the rest of this line, but continue parsing. */
+ while ((walk - input) <= len && *walk != '\n')
+ walk++;
+
free(position);
free(errormessage);
clear_stack();
- break;
+
+ /* To figure out in which state to go (e.g. MODE or INITIAL),
+ * we find the nearest state which contains an <error> token
+ * and follow that one. */
+ bool error_token_found = false;
+ for (int i = statelist_idx-1; (i >= 0) && !error_token_found; i--) {
+ cmdp_token_ptr *errptr = &(tokens[statelist[i]]);
+ for (int j = 0; j < errptr->n; j++) {
+ if (strcmp(errptr->array[j].name, "error") != 0)
+ continue;
+ next_state(&(errptr->array[j]));
+ error_token_found = true;
+ break;
+ }
+ }
+
+ assert(error_token_found);
}
}