/*
 * Options manipulations for rootapps
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <locale.h>
#include <string.h>
#include <strings.h>
#include "options.h"
#include "dockapp.h"

#define LINEMAXLEN 512


static char *Package;
static char *Program;
static char *Version;
static char *Build;
static char *Authors;


void *xmalloc(size_t taille)
{
        void *ret = malloc(taille);
        if (ret == NULL)
        {
                perror("malloc() ");
                exit( -1);
        }
        else
                return ret;
}


char *xstrdup(const char *chaine)
{
        char *ret = strdup(chaine);
        if (ret == NULL)
        {
                perror("strdup() ");
                exit( -1);
        }
        else
                return ret;
}


int my_system(const char *command, const char *options)
{
        int pid;
        extern char **environ;

        if (command == 0) return 1;
        pid = fork ();
        if (pid == -1) return -1;
        if (pid == 0)
        {
                pid = fork ();
                if (pid == 0)
                {
                        char *cmd;
                        char *argv[4];
                        int size = strlen(command) + (options ? strlen(options) : 0) + 3;
                        cmd = xmalloc(size);
                        snprintf(cmd, size - 1, "%s %s", command, options ? options : "");
                        argv[0] = "sh";
                        argv[1] = "-c";
                        argv[2] = cmd;
                        argv[3] = 0;
                        execve("/bin/sh", argv, environ);
                        free(cmd);
                        exit(0);
                }
                exit(0);
        }
        return 0;
}


char *robust_home()
{
        if (getenv("HOME"))
                return getenv("HOME");
        else if (getenv("USER") && getpwnam(getenv("USER")))
                return getpwnam(getenv("USER"))->pw_dir;
        else if (getenv("LOGNAME") && getpwnam(getenv("LOGNAME")))
                return getpwnam(getenv("LOGNAME"))->pw_dir;
        else if ((getuid() != -1) && getpwuid(getuid()))
                return getpwuid(getuid())->pw_dir;
        else
                return "/";
}


int get_bool(const char *value)
{
        if (strcasecmp(value, "1") == 0) return True;
        if (strcasecmp(value, "0") == 0) return False;
        if (strcasecmp(value, "true") == 0) return True;
        if (strcasecmp(value, "false") == 0) return False;
        if (strcasecmp(value, "yes") == 0) return True;
        if (strcasecmp(value, "no") == 0) return False;
        if (strcasecmp(value, "on") == 0) return True;
        if (strcasecmp(value, "off") == 0) return False;
        fprintf(stderr, "Error converting \"%s\" to boolean value.\n", value);
        return False;
}


void strlst_add(StrLst **list, const char *value)
{
        StrLst *lst = *list;
        int ok = True;

        if (! value) return ;
        if (! lst)
        {
                lst = xmalloc(sizeof(StrLst));
                *list = lst;
        }
        else
        {
                if (strcmp(value, lst->entry) == 0) ok = False;
                while ((lst->next) && ok)
                {
                        lst = lst->next;
                        if (strcmp(value, lst->entry) == 0) ok = False;
                }
                if (! ok) return ;
                lst->next = xmalloc(sizeof(StrLst));
                lst = lst->next;
        }
        lst->entry = xstrdup(value);
        lst->time = 0;
        lst->next = NULL;
}


void free_strlst(StrLst **list)
{
        StrLst *lst = *list, *next;
        while (lst)
        {
                next = lst->next;
                FREE(lst->entry);
                free(lst);
                lst = next;
        }
        *list = NULL;
}


int strlst_nb(StrLst *list)
{
        StrLst *lst = list;
        int n = 0;

        while (lst)
        {
                n++;
                lst = lst->next;
        }

        return n;
}


int set_cfg_value(Option *opt, const char *value, int force)
{
        int res = True;

        if (opt->set && (opt->type != C_STRLST) && (! force)) return True;
        if (((! value) || (value[0] == 0) ) && (opt->type != C_BOOL)) return False;
        switch (opt->type)
        {
                case C_CHAR:
                        *((char *) opt->var) = value[0];
                        break;
                case C_INT:
                        *((int *) opt->var) = atoi(value);
                        break;
                case C_BOOL:
                        if (value && (value[0] != 0))
                                *((int *) opt->var) = get_bool(value);
                        else
                                *((int *) opt->var) = !(*((int *) opt->var));
                        break;
                case C_FLOAT:
                        *((float *) opt->var) = atof(value);
                        break;
                case C_LONG:
                        *((long *) opt->var) = atol(value);
                        break;
                case C_DOUBLE:
                        *((double *) opt->var) = strtod(value, NULL);
                        break;
                case C_STRING:
                        if (*((char **) opt->var) == NULL)
                                *((char **) opt->var) = xstrdup(value);
                        break;
                case C_STRLST:
                        strlst_add(((StrLst **) opt->var), value);
                        break;
                default:
                        res = False;
        }
        if (res) opt->set = True;
        return res;
}


/****** CONFIG LOADING ******/

void Usage(Option *opttab, int help)
{
        int i;

        printf("%s version %s - %s %s\n",
                        Package ? Package : "undefined",
                        Version ? Version : "0.0",
                        Authors ? Authors : "nobody",
                        Build   ? Build   : "");
        if (help)
        {
                printf("Usage: %s ", Program);
                for (i = 0 ; opttab[i].key ; i++)
                        printf("[-%c%s%s]%c", opttab[i].key,
                               opttab[i].arg ? " " : "",
                               opttab[i].arg ? opttab[i].arg : "",
                               opttab[i + 1].key ? ' ' : '\n');
                printf(" Options :\n");
                for (i = 0 ; opttab[i].key ; i++)
                        printf("   -%c %s%s\t : %s\n", opttab[i].key,
                               opttab[i].arg ? opttab[i].arg : "",
                               opttab[i].arg ? (strlen(opttab[i].arg) < 2 ? "\t" : "") : "\t",
                                               opttab[i].help);
                printf("\n");
        }
        exit(0);
}


void parse_argv(Option *opttab, int argc, char *argv[])
{
        int opt = 1, i = 0;

        while (opt < argc)
        {
                if (argv[opt][0] == '-')
                {
                        int found = False;
                        for (i = 0 ; opttab[i].key ; i++)
                        {
                                if (opttab[i].key == argv[opt][1])
                                {
                                        switch (opttab[i].type)
                                        {
                                                case O_HELP:
                                                        Usage(opttab, True);
                                                        break;
                                                case O_VERS:
                                                        Usage(opttab, False);
                                                        break;
                                                default:
                                                        {
                                                                char *val = NULL;
                                                                if (opttab[i].type != C_BOOL) val = argv[++opt];
                                                                if (!set_cfg_value(&opttab[i], val, False))
                                                                        Usage(opttab, True);
                                                        }
                                        }
                                        found = True;
                                        break;
                                }
                        }
                        if (! found)
                        {
                                fprintf(stderr, "%s: unknown option [%s]\n", Program, argv[opt]);
                        }
                }
                else
                {
                        fprintf(stderr, "%s: unknown option [%s]\n", Program, argv[opt]);
                }
                opt++;
        }
}


void load_config(Option *opttab, const char *filename, int force)
{
        char line[LINEMAXLEN + 1];
        char *value;
        FILE *file;
        int i;

        if (! filename) return ;
        if ((file = fopen(filename, "r")) == NULL) return ;
        while (! feof (file))
        {
                memset(line, 0, LINEMAXLEN + 1);
                fgets(line, LINEMAXLEN, file);
                if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = 0;
                if ((line[0] == '#') || (line[0] == 0)) continue;
                value = strchr(line, '=') + 1;
                while ((value[0] == ' ') && (value[0] != 0)) value++;
                if (value[0] == 0) continue;
                for (i = 0 ; opttab[i].type ; i++)
                {
                        int min;
                        if (!opttab[i].entry) continue;
                        min = MIN(strlen(line), strlen(opttab[i].entry));
                        if (strlen(line) <= strlen (opttab[i].entry)) continue;
                        if (strncmp (line, opttab[i].entry, min) == 0)
                        {
                                set_cfg_value(&opttab[i], value, force);
                        }
                }
        }
        fclose(file);
}


void free_vars(Option *opttab)
{
        int i;

        for (i = 0 ; opttab[i].type ; i++)
        {
                switch (opttab[i].type)
                {
                        case C_STRING :
                                FREE(*((char **) opttab[i].var));
                                break;
                        case C_STRLST :
                                free_strlst((StrLst **) opttab[i].var);
                                break;
                        default:
                                break;
                }
                opttab[i].set = False;
        }
}


void init_app(char *package, char *program, char *version, char *build, char *authors)
{
        Package = package;
        Program = program;
        Version = version;
        Build   = build;
        Authors = authors;
}

