#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "parser.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':
            x++;
            state->ptr = x;
            return T_NEWLINE;
        case ' ':
        case '\t':
        case '\r':
            x++;
            continue;
        case '#':
            while (*x && (*x != '\n')) x++;
            if (*x == '\n') {
                state->ptr = x+1;
                return T_NEWLINE;
            } else {
                state->ptr = x;
                return T_EOF;
            }
        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;
}