/*
 * options.c
 * Copyright (C) 2002 Florin Malita <mali@go.ro>
 *
 * This file is part of LUFS, a free userspace filesystem implementation.
 * See http://lufs.sourceforge.net/ for updates.
 *
 * LUFS 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 2 of the License, or
 * (at your option) any later version.
 *
 * LUFS 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fs.h"

#include "list.h"

struct option {
    char *key;
    char *value;
    struct list_head list;
};

struct domain {
    char *name;
    struct list_head properties;
    struct list_head list;
};


static void
trim(char *buf){
    int b,e;
    
    if(!buf[0])
	return;

    for(b = 0; (buf[b] == ' ') || (buf[b] == '\t'); b++);
    for(e = strlen(buf) - 1; (e >= 0) && ((buf[e] == ' ') || (buf[e] == '\t')); e--);
    if(e < 0)
	e = strlen(buf) - 1;

    buf[e + 1] = 0;
    
    if(b)
	strcpy(buf, &buf[b]);

}

static struct domain*
find_domain(struct list_head *conf, char *name){
    struct list_head *p;
    struct domain *cls;

    list_for_each(p, conf){
	cls = list_entry(p, struct domain, list);
	if(!strcmp(name, cls->name)){
	    TRACE("domain found");
	    return cls;
	}
    }

    return NULL;
}

int
lu_opt_loadcfg(struct list_head *conf, char *file){
    struct domain *class;
    struct option *prop;
    FILE *f;
    static char buf[1024];
    char *i, *j;
    char *cls, *key, *val;

    TRACE("loading config from %s", file);

    if(!(f = fopen(file, "r"))){
	WARN("could not open file for reading!");
	return -1;
    }

    while(fgets(buf, 1024, f)){
	
	buf[strlen(buf) - 1] = 0;

	if((i = strchr(buf, '#')))
	    *i = 0;
	
	if((i = strchr(buf, '='))){
	    if((j = strstr(buf, "::"))){
		cls = buf;
		key = j + 2;
		val = i + 1;
		
		*i = 0;
		*j = 0;

		trim(cls);
		trim(key);
		trim(val);

		TRACE("class: #%s#", cls);
		TRACE("key: #%s#", key);
		TRACE("val: #%s#", val);

		if(!(class = find_domain(conf, cls))){
		    TRACE("class not found, creating...");

		    if(!(class = malloc(sizeof(struct domain)))){
			WARN("out of mem!");
			break;
		    }

		    memset(class, 0, sizeof(struct domain));

		    if(!(class->name = malloc(strlen(cls) + 1))){
			WARN("out of mem!");
			free(class);
			break;
		    }

		    strcpy(class->name, cls);
		    INIT_LIST_HEAD(&class->properties);

		    list_add(&class->list, conf);
		}

		if(!(prop = malloc(sizeof(struct option)))){
		    WARN("out of mem!");
		    break;
		}

		if(!(prop->key = malloc(strlen(key) + 1))){
		    WARN("out of mem!");
		    free(prop);
		    break;
		}

		if(!(prop->value = malloc(strlen(val) + 1))){
		    WARN("out of mem!");
		    free(prop->key);
		    free(prop);
		    break;
		}

		strcpy(prop->key, key);
		strcpy(prop->value, val);
		
		list_add(&prop->list, &class->properties);
	    }
	}
	
    }


    fclose(f);

    return 0;
}


const char*
lu_opt_getchar(struct list_head *conf, char *cls, char *key){
    struct domain *class;
    struct option *prop;
    struct list_head *p;

    TRACE("retrieving %s::%s", cls, key);
    
    if(!(class = find_domain(conf, cls)))
	return NULL;

    list_for_each(p, &class->properties){
	prop = list_entry(p, struct option, list);

	if(!strcmp(key, prop->key)){
	    TRACE("key found");
	    return prop->value;
	}
    }

    TRACE("key not found");

    return NULL;
}

int
lu_opt_getint(struct list_head *conf, char *domain, char *key, long int *result, int base){
    char *end;
    const char *val;
    long int res;

    if(!(val = lu_opt_getchar(conf, domain, key)))
	return -1;
    
    res = strtol(val, &end, base);
    
    if(*end)
	return -1;
    
    *result = res;
    return 0;
}

int
lu_opt_parse(struct list_head *conf, char *domain, char *opts){
    struct domain *class;
    struct option *prop;
    char *p, *sep;

    if(!(class = find_domain(conf, domain))){
	TRACE("domain not found, creating...");
	
	if(!(class = malloc(sizeof(struct domain)))){
	    WARN("out of mem!");
	    return -1;
	}
	
	memset(class, 0, sizeof(struct domain));
	
	if(!(class->name = malloc(strlen(domain) + 1))){
	    WARN("out of mem!");
	    free(class);
	    return -1;
	}
	
	strcpy(class->name, domain);
	INIT_LIST_HEAD(&class->properties);
	
	list_add(&class->list, conf);
    }
    
    for(p = strtok(opts, ","); p; p = strtok(NULL, ",")){
	if(!strstr(p, "password"))
	    TRACE("option: %s", p);

	if(!(prop = malloc(sizeof(struct option)))){
	    WARN("out of mem!");
	    return -1;
	}

	if((sep = strchr(p, '=')))
	    *sep = 0;

	if(!(prop->key = malloc(strlen(p) + 1))){
	    WARN("out of mem!");
	    free(prop);
	    return -1;
	}
	strcpy(prop->key, p);

	if(sep){
	    TRACE("option with parameter");

	    if(!(prop->value = malloc(strlen(sep + 1) + 1))){
		WARN("out of mem!");
		free(prop->key);
		free(prop);
		return -1;
	    }
	    strcpy(prop->value, sep + 1);
	    
	    if(strstr(p, "password")){
		TRACE("hiding password...");
		memset(sep + 1, ' ', strlen(sep + 1));
	    }
	}else{
	    TRACE("flag");

	    if(!(prop->value = malloc(2))){
		WARN("out of mem!");
		free(prop->key);
		free(prop);
		return -1;
	    }
	    strcpy(prop->value, "");
	    
	}

	list_add(&prop->list, &class->properties);

    }

    return 0;
}


