| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #include "parser.h" |
| #include "list.h" |
| #include "log.h" |
| |
| #define RAW(x...) log_write(6, x) |
| |
| void DUMP(void) |
| { |
| #if 0 |
| struct service *svc; |
| struct action *act; |
| struct command *cmd; |
| struct listnode *node; |
| struct listnode *node2; |
| struct socketinfo *si; |
| int n; |
| |
| list_for_each(node, &service_list) { |
| svc = node_to_item(node, struct service, slist); |
| RAW("service %s\n", svc->name); |
| RAW(" class '%s'\n", svc->classname); |
| RAW(" exec"); |
| for (n = 0; n < svc->nargs; n++) { |
| RAW(" '%s'", svc->args[n]); |
| } |
| RAW("\n"); |
| for (si = svc->sockets; si; si = si->next) { |
| RAW(" socket %s %s 0%o\n", si->name, si->type, si->perm); |
| } |
| } |
| |
| list_for_each(node, &action_list) { |
| act = node_to_item(node, struct action, alist); |
| RAW("on %s\n", act->name); |
| list_for_each(node2, &act->commands) { |
| cmd = node_to_item(node2, struct command, clist); |
| RAW(" %p", cmd->func); |
| for (n = 0; n < cmd->nargs; n++) { |
| RAW(" %s", cmd->args[n]); |
| } |
| RAW("\n"); |
| } |
| RAW("\n"); |
| } |
| #endif |
| } |
| |
| void parse_error(struct parse_state *state, const char *fmt, ...) |
| { |
| va_list ap; |
| char buf[128]; |
| int off; |
| |
| snprintf(buf, 128, "%s: %d: ", state->filename, state->line); |
| buf[127] = 0; |
| off = strlen(buf); |
| |
| va_start(ap, fmt); |
| vsnprintf(buf + off, 128 - off, fmt, ap); |
| va_end(ap); |
| buf[127] = 0; |
| ERROR("%s", buf); |
| } |
| |
| int next_token(struct parse_state *state) |
| { |
| char *x = state->ptr; |
| char *s; |
| |
| if (state->nexttoken) { |
| int t = state->nexttoken; |
| state->nexttoken = 0; |
| return t; |
| } |
| |
| for (;;) { |
| switch (*x) { |
| case 0: |
| state->ptr = x; |
| return T_EOF; |
| case '\n': |
| state->line++; |
| x++; |
| state->ptr = x; |
| return T_NEWLINE; |
| case ' ': |
| case '\t': |
| case '\r': |
| x++; |
| continue; |
| case '#': |
| while (*x && (*x != '\n')) x++; |
| state->line++; |
| state->ptr = x; |
| return T_NEWLINE; |
| default: |
| goto text; |
| } |
| } |
| |
| textdone: |
| state->ptr = x; |
| *s = 0; |
| return T_TEXT; |
| text: |
| state->text = s = x; |
| textresume: |
| for (;;) { |
| switch (*x) { |
| case 0: |
| goto textdone; |
| case ' ': |
| case '\t': |
| case '\r': |
| x++; |
| goto textdone; |
| case '\n': |
| state->nexttoken = T_NEWLINE; |
| x++; |
| goto textdone; |
| case '"': |
| x++; |
| for (;;) { |
| switch (*x) { |
| case 0: |
| /* unterminated quoted thing */ |
| state->ptr = x; |
| return T_EOF; |
| case '"': |
| x++; |
| goto textresume; |
| default: |
| *s++ = *x++; |
| } |
| } |
| break; |
| case '\\': |
| x++; |
| switch (*x) { |
| case 0: |
| goto textdone; |
| case 'n': |
| *s++ = '\n'; |
| break; |
| case 'r': |
| *s++ = '\r'; |
| break; |
| case 't': |
| *s++ = '\t'; |
| break; |
| case '\\': |
| *s++ = '\\'; |
| break; |
| case '\r': |
| /* \ <cr> <lf> -> line continuation */ |
| if (x[1] != '\n') { |
| x++; |
| continue; |
| } |
| case '\n': |
| /* \ <lf> -> line continuation */ |
| state->line++; |
| x++; |
| /* eat any extra whitespace */ |
| while((*x == ' ') || (*x == '\t')) x++; |
| continue; |
| default: |
| /* unknown escape -- just copy */ |
| *s++ = *x++; |
| } |
| continue; |
| default: |
| *s++ = *x++; |
| } |
| } |
| return T_EOF; |
| } |