/* $Id: cfgtool.c 1535 2006-01-29 12:35:19Z mipsator $ */

/*
 * Copyright (c) 2004-2005 Damien Couderc
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *    - Neither the name of the copyright holder(s) nor the names of its
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */


#include <stdlib.h>
#include <errno.h>

#include "compat/pmk_stdbool.h"
#include "compat/pmk_stdio.h"
#include "compat/pmk_string.h"
#include "cfgtool.h"
#include "common.h"
#include "parse.h"
#include "premake.h"

/*#define CFGT_DEBUG	1*/

/* config tools data file keyword */
prskw	kw_pmkcfgtool[] = {
	{"ADD_CFGTOOL", CFGT_TOK_ADDCT, PRS_KW_CELL,	PRS_TOK_NULL,	NULL}
};
int	nbkwct = sizeof(kw_pmkcfgtool) / sizeof(prskw);

/**********************
 * cfgtcell_destroy() *
 ***********************************************************************
 DESCR
	free cfgtcell structure

 IN
	pcc :	configtool cell

 OUT
	NONE
 ***********************************************************************/

void cfgtcell_destroy(cfgtcell *pcc) {
#ifdef CFGT_DEBUG
debugf("free cfgtcell '%s'", pcc->name);
#endif
	free(pcc->name);
	free(pcc->binary);
	if (pcc->version != NULL)
		free(pcc->version);
	if (pcc->module != NULL)
		free(pcc->module);
	if (pcc->cflags != NULL)
		free(pcc->cflags);
	if (pcc->libs != NULL)
		free(pcc->libs);
	free(pcc);
}


/*******************
 * cfgtdata_init() *
 ***********************************************************************
 DESCR
	initialize cfgtdata structure

 IN
	NONE

 OUT
	config data structure
 ***********************************************************************/

cfgtdata *cfgtdata_init(void) {
	cfgtdata	*pcd;

	/* initialise configt tool data structure */
	pcd = (cfgtdata *) malloc(sizeof(cfgtdata));
	if (pcd == NULL) {
#ifdef CFGT_DEBUG
		debugf("cannot initialize config tool data");
#endif
		return(NULL);
	}

	/* initialize config tool hash table */
	pcd->by_mod = hash_init_adv(CFGTOOL_HT_SIZE, NULL,
			(void (*)(void *)) cfgtcell_destroy, NULL);
	if (pcd->by_mod == NULL) {
		free(pcd);
#ifdef CFGT_DEBUG
		debugf("cannot initialize config tool hash table");
#endif
		return(NULL);
	}

	/* initialize config tool hash table */
	pcd->by_bin = hash_init(CFGTOOL_HT_SIZE);
	if (pcd->by_bin == NULL) {
		hash_destroy(pcd->by_mod);
		free(pcd);
#ifdef CFGT_DEBUG
		debugf("cannot initialize config tool hash table");
#endif
		return(NULL);
	}

	return(pcd);
}


/**********************
 * cfgtdata_destroy() *
 ***********************************************************************
 DESCR
	free cfgtdata structure

 IN
	pcd :	config data structure

 OUT
 ***********************************************************************/

void cfgtdata_destroy(cfgtdata *pcd) {
#ifdef CFGT_DEBUG
debugf("destroying module hash table.");
#endif
	if (pcd->by_mod != NULL) {
		hash_destroy(pcd->by_mod);
#ifdef CFGT_DEBUG
debugf("destroyed module hash table.");
	} else {
debugf("WARNING : by_mod doesn't exists !!!");
#endif
	}

#ifdef CFGT_DEBUG
debugf("destroying binary links hash table.");
#endif
	if (pcd->by_mod != NULL) {
		hash_destroy(pcd->by_bin);
#ifdef CFGT_DEBUG
debugf("destroyed binary links hash table.");
	} else {
debugf("WARNING : by_bin doesn't exists !!!");
#endif
	}
}


/*****************
 * add_cfgtool() *
 ***********************************************************************
 DESCR
	add a new config tool cell

 IN
	pcd :	config tool data structure
	pht :	parsed data

 OUT
	boolean
 ***********************************************************************/

bool add_cfgtool(cfgtdata *pcd, htable *pht) {
	cfgtcell	*pcell;
	char		*pstr;

	pcell = (cfgtcell *) malloc(sizeof(cfgtcell));
	if (pcell == NULL)
		return(false);

	pstr = po_get_str(hash_get(pht, "NAME"));
	if (pstr == NULL) {
		free(pcell);
		return(false);
	} else {
		pcell->name = strdup(pstr);
		if (pcell->name == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	}

	pstr = po_get_str(hash_get(pht, "BINARY"));
	if (pstr == NULL) {
		free(pcell->name);
		free(pcell);
		return(false);
	} else {
		pcell->binary = strdup(pstr);
		if (pcell->binary == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	}

	pstr = po_get_str(hash_get(pht, "VERSION"));
	if (pstr != NULL) {
		pcell->version = strdup(pstr);
		if (pcell->version == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	} else {
		pcell->version = NULL;
	}

	pstr = po_get_str(hash_get(pht, "MODULE"));
	if (pstr != NULL) {
		pcell->module = strdup(pstr);
		if (pcell->module  == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	} else {
		pcell->module = NULL;
	}

	pstr = po_get_str(hash_get(pht, "CFLAGS"));
	if (pstr != NULL) {
		pcell->cflags = strdup(pstr);
		if (pcell->cflags == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	} else {
		pcell->cflags = NULL;
	}

	pstr = po_get_str(hash_get(pht, "LIBS"));
	if (pstr != NULL) {
		pcell->libs = strdup(pstr);
		if (pcell->libs == NULL) {
			free(pcell);
			errorf(ERRMSG_MEM);
			return(false);
		}
	} else {
		pcell->libs = NULL;
	}

	/* no need to strdup */
	if (hash_update(pcd->by_mod, pcell->name, pcell) == HASH_ADD_FAIL)
		return(false);
#ifdef CFGT_DEBUG
debugf("added cfgtcell '%s'", pcell->name);
#endif
	if (hash_update_dup(pcd->by_bin, pcell->binary, pcell->name) == HASH_ADD_FAIL)
		return(false);
#ifdef CFGT_DEBUG
debugf("added cfgtcell '%s'", pcell->binary);
#endif

	return(true);
}


/*********************
 * parse_cfgt_file() *
 ***********************************************************************
 DESCR
	parse data from PMKCFG_DATA file

 IN
	NONE

 OUT
	compiler data structure or NULL
 ***********************************************************************/

cfgtdata *parse_cfgt_file(void) {
	FILE		*fp;
	bool		 rval;
	cfgtdata	*pcd;
	prscell		*pcell;
	prsdata		*pdata;

	/* initialize parsing structure */
	pdata = prsdata_init();
	if (pdata == NULL) {
		errorf("cannot intialize prsdata.");
		return(NULL);
	}

	/* initialise configt tool data structure */
	pcd = cfgtdata_init();
	if (pcd == NULL) {
		prsdata_destroy(pdata);
		debugf("cannot initialize config tool data");
		return(NULL);
	}

	fp = fopen(PMKCFG_DATA, "r");
	if (fp == NULL) {
		hash_destroy(pcd->by_mod);
		hash_destroy(pcd->by_bin);
		free(pcd);
		prsdata_destroy(pdata);
		errorf("cannot open '%s' : %s.", PMKCFG_DATA, strerror(errno));
		return(NULL);
	}

	/* parse data file and fill prsdata strucutre */
	rval = parse_pmkfile(fp, pdata, kw_pmkcfgtool, nbkwct);
	fclose(fp);

	if (rval == true) {
		pcell = pdata->tree->first;
		while (pcell != NULL) {
			switch(pcell->token) {
				case CFGT_TOK_ADDCT :
					add_cfgtool(pcd, pcell->data);
					break;

				default :
					errorf("parsing of data file failed.");
					hash_destroy(pcd->by_mod);
					hash_destroy(pcd->by_bin);
					free(pcd);
					prsdata_destroy(pdata);
					return(NULL);
					break;
			}

			pcell = pcell->next;
		}
	} else {
		errorf("parsing of data file failed.");
		hash_destroy(pcd->by_mod);
		hash_destroy(pcd->by_bin);
		free(pcd);
		prsdata_destroy(pdata);
		return(NULL);
	}

	/* parsing data no longer needed */
	prsdata_destroy(pdata);

	return(pcd);
}


/*************************
 * cfgtcell_get_binary() *
 ***********************************************************************
 DESCR
	get binary name from a given module name

 IN
	pgd : global data structure
	mod : module name
	buf : buffer to store binary name
	sb : buffer size

 OUT
	boolean
 ***********************************************************************/

bool cfgtcell_get_binary(cfgtdata *pcd, char *mod, char *buf, size_t sb) {
	cfgtcell	*pcc;

	if (pcd == NULL) {
		/* not found */
		return(false);
	}

	pcc = hash_get(pcd->by_mod, mod);
	if (pcc == NULL) {
		/* not found */
		return(false);
	} else {
		/* found, copy */
		if (strlcpy_b(buf, pcc->binary, sb) == false)
			return(false);

		return(true);
	}
}


/***********************
 * cfgtcell_get_cell() *
 ***********************************************************************
 DESCR
	get cell relative to a given config tool filename

 IN
	pgd : global data structure
	binary : binary name

 OUT
	cfgtcell structure or NULL
 ***********************************************************************/

cfgtcell *cfgtcell_get_cell(cfgtdata *pcd, char *binary) {
	char		*mod;

	if (pcd == NULL) {
		/* not found */
		return(false);
	}

	mod = hash_get(pcd->by_bin, binary);
	if (mod == NULL) {
		/* not found */
		return(NULL);
	} else {
		/* found, copy */
		return(hash_get(pcd->by_mod, mod));
	}
}


/********************
 * ct_get_version() *
 ***********************************************************************
 DESCR
	use config tool to get the version

 IN
	ctpath : config tool path
	vstr : option string to get version
	buffer : storage buffer for the result
	sbuf : size of buffer

 OUT
	boolean
 ***********************************************************************/

bool ct_get_version(char *ctpath, char *vstr, char *buffer, size_t sbuf) {
	FILE	*rpipe;
	bool	 rval = false;
	char	 cfgcmd[MAXPATHLEN];


	if (snprintf_b(cfgcmd, sizeof(cfgcmd),
			CT_FORMAT_VERSION, ctpath, vstr) == false)
		return(false);

	rpipe = popen(cfgcmd, "r");
	if (rpipe != NULL) {
		/* get version line */
		rval = get_line(rpipe, buffer, sbuf);

		pclose(rpipe);
	}

	return(rval);
}


/*****************
 * ct_get_data() *
 ***********************************************************************
 DESCR
	use config tool to get data

 IN
	ctpath : config tool path
	vstr : option string to get version
	buffer : storage buffer for the result
	sbuf : size of buffer

 OUT
	boolean
 ***********************************************************************/

bool ct_get_data(char *ctpath, char *ostr, char *mod, char *buffer, size_t sbuf) {
	FILE	*rpipe;
	bool	 rval = false;
	char	 cfgcmd[MAXPATHLEN];

	if (mod == NULL) {
		/* no module */
		if (snprintf_b(cfgcmd, sizeof(cfgcmd), CT_FORMAT_DATA,
						ctpath, ostr) == false)
			return(false);
	} else {
		/* use module */
		if (snprintf_b(cfgcmd, sizeof(cfgcmd), CT_FORMAT_DATA_MOD,
						ctpath, ostr, mod) == false)
			return(false);
	}

	rpipe = popen(cfgcmd, "r");
	if (rpipe != NULL) {
		/* get version line */
		rval = get_line(rpipe, buffer, sbuf);

		pclose(rpipe);
	}

	return(rval);
}


syntax highlighted by Code2HTML, v. 0.9.1