#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <memory.h>
#include <ctype.h>
#include <unistd.h>

#include "dpconf.h"

char conf_file[] = "/etc/dp.conf";

static var_val *dp_pathtab;
static unsigned int n_dp_paths;

extern var_val dp_pathtab_init[];

/*
 * Table lookup for path variables.
 */
var_val *
find_var(var)
char *var;
{
    var_val *vv;
    if (!var)
	return (var_val *)0;
    for (vv = dp_pathtab ; vv->var ; vv++)
	if (strcmp(vv->var, var) == 0)
	    return vv;
    return (var_val *)0;
}

var_val *
new_var(var)
char *var;
{
    var_val *vv = dp_pathtab + n_dp_paths;
    dp_pathtab = (var_val *)realloc((char *)dp_pathtab,
				    (++n_dp_paths + 1) * sizeof(*dp_pathtab));
    (void)memset((char *)(dp_pathtab + n_dp_paths), 0, sizeof (*dp_pathtab));
    vv->var = malloc((unsigned)strlen(var)+1);
    (void)strcpy(vv->var, var);
    return vv;
}

/*
 * Table lookup for path variables.
 */
char *
map_var(var)
char *var;
{
    var_val *vv;
    return (vv = find_var(var)) ? vv->val : (char *)0;
}


init_pathconf()
{
    var_val *cv, *vv;
    FILE *cf;
    extern void yyerror();
    
    /*
     * Start with defaults.
     */
    for (n_dp_paths = 0, vv = dp_pathtab_init ; vv->var ; vv++)
	n_dp_paths++;
    dp_pathtab = (var_val *)malloc((n_dp_paths + 1) * sizeof(*dp_pathtab));
    (void)memcpy((char *)dp_pathtab,
		 (char *)dp_pathtab_init,
		 (int)(n_dp_paths + 1) * sizeof(*dp_pathtab));

    /*
     * Read in config file, if present.
     */
    if (cf = fopen(conf_file, "r")) {
	while (cv = readconf(conf_file, cf))
	    for ( ; cv->var ; cv++) {
		if (!(vv = find_var(cv->var)))
		    vv = new_var(cv->var);
		vv->val = malloc((unsigned)strlen(cv->val)+1);
		(void)strcpy(vv->val, cv->val);
	    }
	(void)fclose(cf);
    }

    /*
     * Resolve any variable paths..
     */
    for (vv = dp_pathtab ; vv->var ; vv++)
	if (strchr(vv->val, '$'))
	    vv->val = expand_path(vv->val);
}


#define	istok(c)	((isalnum(c)) || (c) == '_')

/*
 * Returns the next character after the variable name.
 * Deposits the value of the variable in val.
 */
char *
expand_var(p0, val)
char *p0, *val;
{
    char *p = p0,
	 ec,
	 *v,
	 var_name[128],
	 *vn = var_name;

    *val = '\0';

    switch (*++p) {
     case '{':	ec = '}';  p++;	break;
     case '(':	ec = ')';  p++;	break;
     default:	ec = '\0';	break;
    }
    if (ec) {
	while (*p && *p != ec)
	  *vn++ = *p++;
	if (*p)
	    p++;
    }
    else
	while (*p && istok(*p))
	  *vn++ = *p++;
    *vn = '\0';
    if ((!ec || *(p-1) == ec) && (v = map_var(var_name)))
	(void)strcpy(val, v);
    else {
	(void)strncpy(val, p0, p-p0);
	val[p-p0] = '\0';
    }
    return p;
}

char *
expand_path(op)
char *op;
{
    char *np0, *np, *p;
    unsigned l;
    char varbuf[128];

    if (!dp_pathtab)
	init_pathconf();

    for (p = op, l = 0 ; *p ;)
	if (*p == '$') {
	    p = expand_var(p, varbuf);
	    l += strlen(varbuf);
	}
	else {
	    l++;
	    p++;
	}

    np0 = malloc(l + 1);

    for (p = op, np = np0 ; *p ; ) {
	if (*p == '$') {
	    p = expand_var(p, varbuf);
	    (void)strcpy(np, varbuf);
	    np += strlen(np);
	}
	else
	    *np++ = *p++;
    }

    *np = '\0';

    return np0;
}

char *
expand_dir_file(dir, file)
char *dir, *file;
{
    char *tmp,
	 *ep;
    if (*file == '/')
	return expand_path(file);
    else {
	tmp = malloc((unsigned)strlen(dir) + 1 + (unsigned)strlen(file) + 1);
	(void)sprintf(tmp, "%s/%s", dir, file);
	ep = expand_path(tmp);
	(void)free(tmp);
	return ep;
    }
}

char *
expand_dirs_file(dirs, file)
char *dirs, *file;
{
    char *dir;
    char *d, *de, *p;

    for ( d = dirs ; ; d = de ) {
	de = strchr(d, ':');
	if (!de)
	    de =  d + strlen(d);

	dir = malloc((unsigned)(de - d + 1));
	(void)strncpy(dir, d, de-d);
	dir[de-d] = '\0';

	p = expand_dir_file(dir, file);

	/*
	 * If the file exists in this directory,
	 * or we are at the last path on our list,
	 * return this path.
	 */
	if (!*de || (access(p, F_OK) == 0)) {
	    (void)free(dir);
	    return p;
	}

	/*
	 * More to try..
	 */
	(void)free(dir);
	(void)free(p);
	if (*de)
	    de++;
    }
}
