/*
 *      dnsutl - utilities to make DNS easier to configure
 *      Copyright (C) 1999, 2006, 2007 Peter Miller
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program. If not, see
 *      <http://www.gnu.org/licenses/>.
 *
 * functions to lexically analyze files containing lines which consist
 * of space separated fields
 */

#include <ac/ctype.h>
#include <ac/stdarg.h>
#include <ac/stdio.h>

#include <map.h>
#include <error.h>
#include <mem.h>
#include <mprintf.h>
#include <strlist.h>


static const char *fn;
static FILE     *fp;
static int      linum;
static int      incriment_line_number;
static int      non_printing_whine;
map_token_ty    map_token;
char            *map_value;
static int      error_count;


void
map_open(const char *filename)
{
    if (filename)
    {
        fn = filename;
        fp = fopen(fn, "r");
        if (!fp)
            nfatal("open \"%s\"", fn);
    }
    else
    {
        fn = "(stdin)";
        fp = stdin;
    }
    linum = 1;
}


void
map_close(void)
{
    if (error_count)
    {
        fatal
        (
            "%s: found %d fatal error%s",
            fn,
            error_count,
            (error_count == 1 ? "" : "s")
        );
    }
    if (fp != stdin)
        fclose(fp);
    fn = 0;
    fp = 0;
    linum = 0;
    error_count = 0;
    incriment_line_number = 0;
}


static int
map_getc(void)
{
    int             c;

    for (;;)
    {
        c = getc(fp);
        switch (c)
        {
        case EOF:
            if (ferror(fp))
                nfatal("read \"%s\"", fn);
            break;

        case '\\':
            c = getc(fp);
            if (c == '\n')
            {
                ++incriment_line_number;
                continue;
            }
            if (c != EOF)
                ungetc(c, fp);
            c = '\\';
            break;
        }
        return c;
    }
}


static void
map_ungetc(int c)
{
    if (c != EOF)
        ungetc(c, fp);
}


void
map_lex(void)
{
    int             c;
    int             bufpos;
    static int      bufmax;
    static char     *buffer;

    map_value = 0;
    if (incriment_line_number)
    {
        linum += incriment_line_number;
        incriment_line_number = 0;
    }
    for (;;)
    {
        c = map_getc();
        switch (c)
        {
        case EOF:
            if (ferror(fp))
                nfatal("read \"%s\"", fn);
            map_token = map_token_eof;
            return;

        case '\n':
            ++incriment_line_number;
            non_printing_whine = 0;
            map_token = map_token_eoln;
            return;

        case ' ':
        case '\t':
            break;

        default:
            bufpos = 0;
            non_printing_whine = 0;
            for (;;)
            {
                if (!isprint(c) && !non_printing_whine)
                    map_error("line contains non printing character");
                if (bufpos >= bufmax)
                {
                    bufmax += 100;
                    buffer = mem_change_size(buffer, bufmax);
                }
                buffer[bufpos++] = c;

                c = map_getc();
                if (c == '\n' || c == ' ' || c == '\t' || c == EOF)
                {
                    map_ungetc(c);
                    break;
                }
            }
            if (bufpos >= bufmax)
            {
                bufmax += 100;
                buffer = mem_change_size(buffer, bufmax);
            }
            buffer[bufpos] = 0;
            map_value = buffer;
            map_token = map_token_string;
            return;

        case '#':
            /*
             * throw comments away
             */
            for (;;)
            {
                c = map_getc();
                if (c == '\n' || c == EOF)
                {
                    map_ungetc(c);
                    break;
                }
            }
            break;
        }
    }
}


void
map_error(const char *s, ...)
{
    va_list         ap;
    char            *msg;

    va_start(ap, s);
    msg = vmprintf(s, ap);
    va_end(ap);
    msg = mem_copy_string(msg);
    error("%s: %d: %s", fn, linum, msg);
    mem_free(msg);
    ++error_count;
    if (error_count >= 20)
        fatal("%s: too many fatal errors, aborting", fn);
}


void
map_warning(const char *s, ...)
{
    va_list         ap;
    char            *msg;

    va_start(ap, s);
    msg = vmprintf(s, ap);
    va_end(ap);
    msg = mem_copy_string(msg);
    error("%s: %d: warning: %s", fn, linum, msg);
    mem_free(msg);
}


int
map_read(strlist_ty *slp)
{
    strlist_zero(slp);
    for (;;)
    {
        map_lex();
        switch (map_token)
        {
        case map_token_eof:
            return 0;

        case map_token_eoln:
            /* ignore blank lines */
            break;

        case map_token_string:
            for (;;)
            {
                string_ty      *s;

                s = str_from_c(map_value);
                strlist_append(slp, s);
                str_free(s);
                map_lex();
                if (map_token != map_token_string)
                    break;
            }
            if (map_token == map_token_eoln)
                return 1;
            if (map_token == map_token_eof)
                return 1;
            /* fall through... */

        default:
            map_error("syntax error");
            for (;;)
            {
                map_lex();
                if (map_token == map_token_eoln)
                    break;
                if (map_token == map_token_eof)
                    break;
            }
            break;
        }
    }
}


syntax highlighted by Code2HTML, v. 0.9.1