/*
 * 4m v2.15 (configure.c)
 * (C) Copyright 1990 by Carrick Sean Casey
 * (C) Copyright 1993 by Scott Chasin and Michaeljon Miller
 *
 * 3-24-93 See file COPYING for copyright information.
 *
 * Configuration file parsing
 */

#include "4m.h"
#include "externs.h"
#include <stdio.h>

#ifndef NO_STDLIB_H
# include <stdlib.h>
#endif

#include <ctype.h>

#ifdef HAS_STRING
#include <string.h>
#endif

#ifdef HAS_STRINGS
#include <strings.h>
#endif

typedef struct {
    char *line;
    int type;
    int *number;
    char **string;
    NAMLIST **list;
}   rctable;

static int beepvalue;

static rctable configs[] =
{
    {"server", CHAR, (int *) 0, &gv.default_host, NULL},
    {"port", INT, &gv.default_port, NULL, NULL},
    {"nickname", CHAR, (int *) 0, &gv.nick, NULL},
    {"group", CHAR, (int *) 0, &gv.group, NULL},
    {"logfile", CHAR, (int *) 0, &gv.logfile, NULL},
    {"pagesize", INT, &gv.pagesize, NULL, NULL},
    {"bufferlines", INT, &gv.bufferlines, NULL, NULL},
    {"beeps", INT, &beepvalue, NULL, NULL},
    {"verifyquit", VALUE, &gv.verifyquit, NULL, NULL},
    {"shell", CHAR, (int *) 0, &gv.shell, NULL},
    {"alias", LIST, (int *) 0, NULL, &cmdalias},
    {"notify", LIST, (int *) 0, NULL, &notify},
    {"restrict", VALUE, &gv.restricted, NULL, NULL},
    {"echoback", VALUE, &echoback, NULL, NULL},
    {"execute", LIST, (int *) 0, NULL, &execute},
    {"history", INT, &gv.history, NULL, NULL},
    {"statusbar", CHAR, (int *) 0, &gv.statusbar, NULL},
    {"editorbeep", VALUE, &gv.editorbeep, NULL, NULL},
    {"dumb", VALUE, &dumb, NULL, NULL},
    {"openmsg", CHAR, (int *) 0, &gv.o_msgformat, NULL},
    {"privmsg", CHAR, (int *) 0, &gv.p_msgformat, NULL},
    {"securemsg", CHAR, (int *) 0, &gv.s_msgformat, NULL},
    {NULL, 0, (int *) 0, NULL, NULL}
};

char words[16][257], *args;
long number;
int yes, len, numwords;
char first, line[257];

/*
 * c o n f i g u r e
 *
 * Read and interpret the static file.
 *
 */



extern int printf ();
extern void nlinit ();
extern char *strchr ();
void parse ();
void convert ();
extern char *strdup ();
extern void nlput ();
int value ();
extern int fclose ();
char upper ();
int oneof ();

void configure()
{
    FILE *fp;

    int homerc = FALSE;
    char temp[128], filename[128], *home;
    rctable *items;

    if ((cmdalias = (NAMLIST *) malloc((unsigned) sizeof(NAMLIST))) == NULL)
        printf("4m: Cannot initialize alias array.\n");

    nlinit(cmdalias, 10);

    if ((notify = (NAMLIST *) malloc((unsigned) sizeof(NAMLIST))) == NULL)
        printf("4m: Cannot initialize notification array.\n");

    nlinit(notify, 15);

    if ((execute = (NAMLIST *) malloc((unsigned) sizeof(NAMLIST))) == NULL)
        printf("4m: Cannot initialize execution array.\n");

    nlinit(execute, 15);

    if ((home = getenv("HOME")) == NULL) {
        printf("[Error] HOME environment variable not defined.\n");
        exit(-1);
    }
    if (!strlen(config_file)) {
        sprintf(filename, "%s/.4mrc", home);
        homerc = TRUE;
    } else
        strcpy(filename, config_file);

    if ((fp = fopen(filename, "r")) == NULL) {
        if (homerc)
            return;

        printf("4m: Could not open %s.\n", filename);
        printf("4m: To run 4m without a .4mrc, use the -h and -p options.\n");
        exit(-1);
    }

    while (fgets(temp, 80, fp)) {

    /*
     * Remove trailing spaces, a frequent source of configuration
     * difficulties
     */

        args = (char *) strchr(temp, 0);

        while (args > temp && *args <= ' ')
            args--;
        *(++args) = 0;

        parse(temp);

    /*
     * Comments start with # or any whitespace. Somehow some of these
     * should be treated as continuation lines..
     */

        if (!first || first == '#' || first == ' ' ||
            first == ';' || first == '\t')
            continue;

        convert(words[0], ':', 0);
        args = (char *) strchr(line, ':');

        if (args) {
            args++;
            while (*args == ' ' || *args == '\t')
                args++;
        } else
            continue;

        for (items = configs; items->line; items++) {
            if (!strcasecmp(items->line, words[0])) {
                switch (items->type) {
                    case CHAR:
                        *items->string = strdup(args);
                        goto NEXT_KEY;

                    case INT:
                        *items->number = atoi(args);
                        goto NEXT_KEY;

                    case LIST:
                        nlput(*items->list, args);
                        goto NEXT_KEY;

                    case VALUE:
                        *items->number = value(args);
                        goto NEXT_KEY;
                }
            }
        }

NEXT_KEY: /* argh */ ;
    }

    fclose(fp);

    return;
}


/*
 * p a r s e
 *
 * Perform rudimentary parsing of the line into individual words, and various
 * global variables:
 *
 * args - everything past first word
 * first - first character, capitalized
 * len - length of line
 * number - integer value of line, if any
 * numwords - total
 * numwords - number of words
 * words[] - each individual word in a separate string
 * yes - true if first is Y
 *
 */

void parse(string)
     char *string;
{
    char *s, *d;

    strcpy(line, string);

    first = upper(*line);
    yes = first == 'Y';

 /*
  * Absolute value. Cannot use abs() as that returns an integer, not a
  * long
  */

    if ((number = atol(line)) < 0L)
        number = (-number);

    len = strlen(line);

    for (s = line; *s == ' ' || *s == '\t'; s++);

 /*
  * args must point to first argument, which cannot be whitespace or comma
  */

    args = s;

    while (*args && *args != ' ' && *args != '\t')
        args++;

    while (*args == ' ' || *args == '\t')
        args++;

 /*
  * The entire program is allowed to be sloppy about the usage of words[]
  * array
  */

/*
  for (i = 0; i <= 16; i++)
    *words[i] = 0;

  for (i = 0; i <= 16; i++)
     words[i][0] = 0;
*/

    memset(words, 0, 16 * 257);

    numwords = 0;

    while (*s) {
        d = words[numwords];
        while (*s && *s != ' ' && *s != '\t')
            *d++ = *s++;
        while (*s == ' ' || *s == '\t')
            s++;
        *d = 0;
        if (++numwords >= 100)
            break;
    }
    numwords--;

    return;
}

/*
 * l o w e r / u p p e r
 *
 * Conversion to/from lower/uppercase. Most systems already have a way to do
 * this, but sometimes it will fail if the character does not need
 * conversion. To be safe..
 *
 */

char 
lower(c)
     char c;
{
    return (c >= 'A' && c <= 'Z') ? c + ' ' : c;
}

char 
upper(c)
     char c;
{
    return (c >= 'a' && c <= 'z') ? c - ' ' : c;
}

char *
tag(s)
     char *s;
{
    char *save = s;

    while (*s && *s != '\n' && *s != '\r')
        s++;
    *s = 0;

    return save;
}

/*
 * u n l i s t
 *
 * Handler for configuration type "LIST" in table.
 *
 * Process an item from the configuration file by splitting it into words in a
 * dynamically allocated string array.
 *
 */

char **
unlist(string)
     char *string;
{
    int i;
    char **list;

    parse(string);
    list = (char **) malloc((numwords + 2) * sizeof(char *));

    for (i = 0; i <= numwords; i++)
        list[i] = strdup(words[i]);

    list[i] = NULL;
    return list;
}

/*
 * c o n v e r t
 *
 * Change all occurrences of one character in a string to another.
 *
 */

void convert(s, old, new)
     char *s;
     char old;
     char new;
{
    while (*s) {
        if (*s == old)
            *s = new;
        s++;
    }
}

/*
 * v a l u e
 *
 * Return something reasonable for an argument when a flag is requested. Will
 * return an integer is given, or TRUE/FALSE when any one of a number of
 * keywords is passed.
 *
 */

int 
value(string)
     char *string;
{
    static char *good[] =
    {"", "+", "yes", "true", "on", "all", "any", NULL};

    static char *evil[] =
    {"no", "false", "off", "none", "never", NULL};

    if (oneof(string, good) >= 0)
        return TRUE;
    if (oneof(string, evil) >= 0)
        return FALSE;
    return atoi(string);
}

/*
 * o n e o f
 *
 * Is the given string a member of the list? If so, return its index number,
 * otherwise return something below zero in Negativeland.
 *
 */

int 
oneof(string, list)
     char *string;
     char **list;
{
    int num;

    for (num = 0; *list; list++, num++) {
        if (!strcasecmp(*list, string))
            return num;
    }
    return -1;
}
