/*
 * dnsutl - utilities to make DNS easier to configure
 * Copyright (C) 2001, 2003, 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/>.
 */

#include <ac/ctype.h>
#include <ac/string.h>

#include <error.h>
#include <output.h>
#include <srrf.h>
#include <srrf/address.h>
#include <srrf/dhcp.h>
#include <srrf/origin.h>
#include <srrf/private.h>
#include <symtab.h>


static void
check_list_of_one_or_more_machines(srrf_t *rp)
{
    string_ty       *tmp;
    size_t          j;

    if (rp->arg.nstrings < 1)
        srrf_lex_error("at least one machine must be named");
    for (j = 0; j < rp->arg.nstrings; ++j)
    {
        tmp = rp->arg.string[j];
        rp->arg.string[j] = srrf_relative_to_absolute(tmp);
        str_free(tmp);
    }
}


/* ----------  filename  -------------------------------------------*/


static void
filename_check_arguments(srrf_t *rp)
{
    if (rp->arg.string[0]->str_text[0] != '/')
        srrf_lex_error("the boot file must be an absolute path");
}


static void
filename_print(srrf_t *rp, void *p)
{
    (void)p;
    if (rp->arg.nstrings == 1)
    {
        output_printf
        (
            "        filename = \"%s\";\n",
            rp->arg.string[0]->str_text
        );
    }
}


/* ----------  hardware ethernet ----------------------------------*/


static void
hardware_ethernet_check_arguments(srrf_t * rp)
{
    check_list_of_one_or_more_machines(rp);
    srrf_lex_error
    (
        "you may not specify \"dhcp hardware-ethernet\" explicitly, "
        "it is generated from the \"ether a\" fields"
    );
}


static int
hex(int c)
{
    static char     digit[] = "0123456789ABCDEFabcdef";
    static int      value[] =
    {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
        10, 11, 12, 13, 14, 15,
        10, 11, 12, 13, 14, 15,
    };
    char            *cp;

    cp = strchr(digit, c);
    if (!cp)
        return 0;       /* should not happen */
    return value[cp - digit];
}


static string_ty *
is_legal_ether_address(string_ty *s)
{
    size_t          nbytes;
    unsigned char   buffer[6 + 1];      /* must be more than 6 */
    const char      *cp;

    cp = s->str_text;
    nbytes = 0;
    while (nbytes < SIZEOF(buffer))
    {
        if (!isxdigit(cp[0]))
            return 0;
        if (isxdigit(cp[1]))
        {
            buffer[nbytes++] = (hex(cp[0]) << 4) | hex(cp[1]);
            cp += 2;
        }
        else
            buffer[nbytes++] = hex(*cp++);
        if (*cp != ':')
            break;
        ++cp;
    }
    if (*cp != 0 || nbytes != 6)
        return 0;
    return
        str_format
        (
            "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
            buffer[0],
            buffer[1],
            buffer[2],
            buffer[3],
            buffer[4],
            buffer[5]
        );
}


static void
hardware_ethernet_print(srrf_t *rp, void *arg)
{
    dhcp_aux_ty     *a;
    string_ty       *s;
    srrf_t          *rp2;

    a = arg;
    s = rp->arg.string[0];
    rp2 = symtab_query(a->ether_a_stp, s);
    if (!rp2)
    {
        output_error_locn
        (
            rp->file_name->str_text,
            rp->line_number,
            "host \"%s\" has no Ethernet address",
            s->str_text
        );
        return;
    }
    s = is_legal_ether_address(rp2->arg.string[0]);
    if (!s)
    {
        output_error_locn
        (
            rp2->file_name->str_text,
            rp2->line_number,
            "the string \"%s\" is not a legal Ethernet address",
            rp2->arg.string[0]->str_text
        );
        return;
    }
    output_printf("        hardware ethernet %s;\n", s->str_text);
    str_free(s);
}

/* ----------  fixed-address  -------------------------------------*/

static void
fixed_address_check_arguments(srrf_t *rp)
{
    check_list_of_one_or_more_machines(rp);
    srrf_lex_error
    (
        "you may not specify \"fixed-address\" explicitly, "
        "it is generated from the \"in a\" fields"
    );
}


static void
fixed_address_print(srrf_t *rp, void *arg)
{
    dhcp_aux_ty     *a;
    srrf_t          *rp2;

    a = arg;
    rp2 = symtab_query(a->in_a_stp, rp->arg.string[0]);
    if (!rp2)
    {
        output_error_locn
        (
            rp->file_name->str_text,
            rp->line_number,
            "host \"%s\" has no IP address",
            rp->arg.string[0]->str_text
        );
        return;
    }
    output_printf("        fixed-address %s;\n", rp2->arg.string[0]->str_text);
}


/* ----------------------------------------------------------------*/


static srrf_type_ty type[] =
{
    {
        "filename",
        1,
        filename_check_arguments,
        0,  /* local_test */
        0,  /* print */
        0,  /* abs_to_rel */
        filename_print,     /* aux1 */
        0,
    },
    {
        "hardware-ethernet",
        1,
        hardware_ethernet_check_arguments,
        0,  /* local_test */
        0,  /* print */
        0,  /* abs_to_rel */
        hardware_ethernet_print,    /* aux1 */
        0,
    },
    {
        "fixed-address",
        1,
        fixed_address_check_arguments,
        0,  /* local_test */
        0,  /* print */
        0,  /* abs_to_rel */
        fixed_address_print,        /* aux1 */
        0,
    },
};


/*
 * This symbol describes the class.
 * It should be the only symbol exported from this file.
 */
srrf_class_ty   srrf_class_dhcp =
{
    "dhcp",
    type,
    SIZEOF(type)
};


syntax highlighted by Code2HTML, v. 0.9.1