/* $Id: func.c 1955 2007-02-28 17:07:04Z coudercd $ */
/*
* Copyright (c) 2003-2006 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 "compat/pmk_stdio.h"
#include "compat/pmk_string.h"
#include "compat/pmk_unistd.h"
#include "autoconf.h"
#include "cfgtool.h"
#include "codebuild.h"
#include "common.h"
#include "detect.h"
#include "func.h"
#include "functool.h"
#include "hash.h"
#include "pkgconfig.h"
#include "premake.h"
#include "tags.h"
/*#define SHLIB_DEBUG 1*/
/***************
* parser data *
***********************************************************************/
/* common required options */
kw_t req_name[] = {
{KW_OPT_NAME, PO_STRING}
};
/* CHECK_BIN options */
kw_t opt_chkbin[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_VARIABLE, PO_STRING}
};
kwopt_t kw_chkbin = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chkbin,
sizeof(opt_chkbin) / sizeof(kw_t)
};
/* CHECK_HEADER options */
kw_t opt_chkhdr[] = {
{KW_OPT_CFLAGS, PO_STRING},
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_SUBHDR, PO_LIST},
{KW_OPT_FUNCTION, PO_STRING | PO_LIST},
{KW_OPT_LANG, PO_STRING},
{KW_OPT_MACRO, PO_LIST},
{KW_OPT_REQUIRED, PO_BOOL}
};
kwopt_t kw_chkhdr = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chkhdr,
sizeof(opt_chkhdr) / sizeof(kw_t)
};
/* CHECK_LIB options */
kw_t opt_chklib[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_FUNCTION, PO_STRING | PO_LIST},
{KW_OPT_LANG, PO_STRING},
{KW_OPT_LIBS, PO_STRING},
{KW_OPT_MACRO, PO_LIST},
{KW_OPT_REQUIRED, PO_BOOL}
};
kwopt_t kw_chklib = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chklib,
sizeof(opt_chklib) / sizeof(kw_t)
};
/* CHECK_CONFIG options */
kw_t opt_chkcfg[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_CFLAGS, PO_STRING},
{KW_OPT_LIBS, PO_STRING},
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_VARIABLE, PO_STRING},
{KW_OPT_VERSION, PO_STRING}
};
kwopt_t kw_chkcfg = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chkcfg,
sizeof(opt_chkcfg) / sizeof(kw_t)
};
/* CHECK_PKG_CONFIG options */
kw_t opt_chkpc[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_CFLAGS, PO_STRING},
{KW_OPT_LIBS, PO_STRING},
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_VERSION, PO_STRING}
};
kwopt_t kw_chkpc = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chkpc,
sizeof(opt_chkpc) / sizeof(kw_t)
};
/* CHECK_TYPE options */
kw_t opt_chktyp[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_HEADER, PO_STRING},
{KW_OPT_LANG, PO_STRING},
{KW_OPT_MEMBER, PO_STRING},
{KW_OPT_REQUIRED, PO_BOOL}
};
kwopt_t kw_chktyp = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chktyp,
sizeof(opt_chktyp) / sizeof(kw_t)
};
/* CHECK_VARIABLE options */
kw_t opt_chkvar[] = {
{KW_OPT_DEFS, PO_LIST},
{KW_OPT_DEPEND, PO_LIST},
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_VALUE, PO_STRING}
};
kwopt_t kw_chkvar = {
req_name,
sizeof(req_name) / sizeof(kw_t),
opt_chkvar,
sizeof(opt_chkvar) / sizeof(kw_t)
};
/* BUILD_LIB_NAME options */
kw_t req_bldlib[] = {
{KW_OPT_NAME, PO_STRING}
};
kw_t opt_bldlib[] = {
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_MAJOR, PO_STRING},
{KW_OPT_MINOR, PO_STRING},
{KW_SL_STATIC, PO_STRING},
{KW_SL_SHARED, PO_STRING},
{KW_SL_VERSION, PO_BOOL}
};
kwopt_t kw_bldlib = {
req_bldlib,
sizeof(req_bldlib) / sizeof(kw_t),
opt_bldlib,
sizeof(opt_bldlib) / sizeof(kw_t)
};
/* BUILD_SHLIB_NAME options */
kw_t req_bldshl[] = {
{KW_OPT_NAME, PO_STRING}
};
kw_t opt_bldshl[] = {
{KW_OPT_REQUIRED, PO_BOOL},
{KW_OPT_MAJOR, PO_STRING},
{KW_OPT_MINOR, PO_STRING},
{KW_SL_VERS_FULL, PO_STRING},
{KW_SL_VERS_NONE, PO_STRING}
};
kwopt_t kw_bldshl = {
req_bldshl,
sizeof(req_bldshl) / sizeof(kw_t),
opt_bldshl,
sizeof(opt_bldshl) / sizeof(kw_t)
};
/* keyword list */
prskw kw_pmkfile[] = {
{"DEFINE", PMK_TOK_DEFINE, PRS_KW_NODE, PMK_TOK_SETVAR, NULL},
{"SETTINGS", PMK_TOK_SETNGS, PRS_KW_NODE, PMK_TOK_SETPRM, NULL},
{"IF", PMK_TOK_IFCOND, PRS_KW_NODE, PRS_TOK_NULL, NULL},
{"ELSE", PMK_TOK_ELCOND, PRS_KW_NODE, PRS_TOK_NULL, NULL},
{"SWITCHES", PMK_TOK_SWITCH, PRS_KW_CELL, PRS_TOK_NULL, NULL},
{"CHECK_BINARY", PMK_TOK_CHKBIN, PRS_KW_CELL, PRS_TOK_NULL, &kw_chkbin},
{"CHECK_HEADER", PMK_TOK_CHKINC, PRS_KW_CELL, PRS_TOK_NULL, &kw_chkhdr},
{"CHECK_LIB", PMK_TOK_CHKLIB, PRS_KW_CELL, PRS_TOK_NULL, &kw_chklib},
{"CHECK_CONFIG", PMK_TOK_CHKCFG, PRS_KW_CELL, PRS_TOK_NULL, &kw_chkcfg},
{"CHECK_PKG_CONFIG", PMK_TOK_CHKPKG, PRS_KW_CELL, PRS_TOK_NULL, &kw_chkpc},
{"CHECK_TYPE", PMK_TOK_CHKTYP, PRS_KW_CELL, PRS_TOK_NULL, &kw_chktyp},
{"CHECK_VARIABLE", PMK_TOK_CHKVAR, PRS_KW_CELL, PRS_TOK_NULL, &kw_chkvar},
{"BUILD_LIB_NAME", PMK_TOK_BLDLIB, PRS_KW_CELL, PRS_TOK_NULL, &kw_bldlib},
{"BUILD_SHLIB_NAME", PMK_TOK_BLDSLN, PRS_KW_CELL, PRS_TOK_NULL, &kw_bldshl}
};
size_t nbkwpf = sizeof(kw_pmkfile) / sizeof(prskw);
/******************
* misc functions *
***********************************************************************/
/*****************
* func_wrapper()*
***********************************************************************
DESCR
wrapper from token to functions
IN
pcell : cell structure
pgd : global data
OUT
boolean
***********************************************************************/
bool func_wrapper(prscell *pcell, pmkdata *pgd) {
bool rval;
pmkcmd cmd;
cmd.token = pcell->token;
cmd.label = pcell->label;
switch (cmd.token) {
case PMK_TOK_DEFINE :
rval = pmk_define(&cmd, pcell->data, pgd);
break;
case PMK_TOK_SWITCH :
rval = pmk_switches(&cmd, pcell->data, pgd);
break;
case PMK_TOK_SETNGS :
rval = pmk_settings(&cmd, pcell->data, pgd);
break;
case PMK_TOK_IFCOND :
rval = pmk_ifcond(&cmd, pcell->data, pgd);
break;
case PMK_TOK_ELCOND :
rval = pmk_elcond(&cmd, pcell->data, pgd);
break;
case PMK_TOK_SETVAR :
rval = pmk_set_variable(&cmd, pcell->data, pgd);
break;
case PMK_TOK_SETPRM :
rval = pmk_set_parameter(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKBIN :
rval = pmk_check_binary(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKINC :
rval = pmk_check_header(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKLIB :
rval = pmk_check_lib(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKCFG :
rval = pmk_check_config(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKPKG :
rval = pmk_check_pkg_config(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKTYP :
rval = pmk_check_type(&cmd, pcell->data, pgd);
break;
case PMK_TOK_CHKVAR :
rval = pmk_check_variable(&cmd, pcell->data, pgd);
break;
case PMK_TOK_BLDLIB :
rval = pmk_build_lib_name(&cmd, pcell->data, pgd);
break;
case PMK_TOK_BLDSLN :
rval = pmk_build_shlib_name(&cmd, pcell->data, pgd);
break;
default :
errorf("unknown token %d", cmd.token);
rval = false;
}
return(rval);
}
/******************
* process_node() *
***********************************************************************
DESCR
processing of given node
IN
pnode : node structure
pgd : global data
OUT
boolean
***********************************************************************/
bool process_node(prsnode *pnode, pmkdata *pgd) {
prscell *pcell;
/* init pcell with the first cell of pdata */
pcell = pnode->first;
while (pcell != NULL) {
if (func_wrapper(pcell, pgd) == false)
return(false);
pcell = pcell->next;
}
return(true);
}
/*****************
* node functions
*
* IN
* cmd : command structure
* pnode : node structure
* pgd : global data
*
* OUT
* boolean
***********************************************************************/
/****************
* pmk_define() *
***********************************************************************
DESCR
define variables
***********************************************************************/
bool pmk_define(pmkcmd *cmd, prsnode *pnode, pmkdata *pgd) {
pmk_log("\n* Parsing define\n");
return(process_node(pnode, pgd));
}
/******************
* pmk_settings() *
***********************************************************************
DESCR
pmk settings
***********************************************************************/
bool pmk_settings(pmkcmd *cmd, prsnode *pnode, pmkdata *pgd) {
pmk_log("\n* Parsing settings\n");
return(process_node(pnode, pgd));
}
/****************
* pmk_ifcond() *
***********************************************************************
DESCR
if condition
***********************************************************************/
bool pmk_ifcond(pmkcmd *cmd, prsnode *pnode, pmkdata *pgd) {
if (label_check(pgd->labl, cmd->label) == true) {
pmk_log("\n< Begin of 'IF' condition [%s]\n", cmd->label);
process_node(pnode, pgd);
pmk_log("\n> End of 'IF' condition [%s]\n", cmd->label);
} else {
pmk_log("\n- Skipping 'IF' condition [%s]\n", cmd->label);
}
return(true);
}
/****************
* pmk_elcond() *
***********************************************************************
DESCR
else condition
***********************************************************************/
bool pmk_elcond(pmkcmd *cmd, prsnode *pnode, pmkdata *pgd) {
if (label_check(pgd->labl, cmd->label) == false) {
pmk_log("\n< Begin of 'ELSE' condition [%s]\n", cmd->label);
process_node(pnode, pgd);
pmk_log("\n> End of 'ELSE' condition [%s]\n", cmd->label);
} else {
pmk_log("\n- Skipping 'ELSE' condition [%s]\n", cmd->label);
}
return(true);
}
/*****************
* cell functions
*
* IN
* cmd : command structure
* ht : command options
* pgd : global data
*
* OUT
* boolean
***********************************************************************/
/******************
* pmk_switches() *
***********************************************************************
DESCR
switches
***********************************************************************/
bool pmk_switches(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
char *value;
hkeys *phk;
pmkobj *po;
unsigned int i,
n = 0;
pmk_log("\n* Parsing switches\n");
phk = hash_keys(ht);
for(i = 0 ; i < phk->nkey ; i++) {
if (hash_get(pgd->labl, phk->keys[i]) == NULL) {
po = hash_get(ht, phk->keys[i]);
switch(po_get_type(po)) {
case PO_BOOL :
value = bool_to_str(po_get_bool(po));
break;
case PO_STRING :
value = po_get_str(po);
break;
default :
errorf("bad type for switch '%s'.", phk->keys[i]);
return(false);
break;
}
if (hash_update_dup(pgd->labl, phk->keys[i], value) != HASH_ADD_FAIL) {
pmk_log("\tAdded '%s' switch.\n", phk->keys[i]);
n++;
} else {
errorf(HASH_ERR_UPDT_ARG, phk->keys[i]);
return(false);
}
} else {
pmk_log("\tSkipped '%s' switch (overriden).\n", phk->keys[i]);
}
}
pmk_log("\tTotal %d switch(es) added.\n", n);
hash_free_hkeys(phk);
return(true);
}
/**********************
* pmk_check_binary() *
***********************************************************************
DESCR
check binary
***********************************************************************/
bool pmk_check_binary(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
rslt;
char *filename,
*varname,
*vtmp,
*bpath,
binpath[MAXPATHLEN];
pmk_log("\n* Checking binary [%s]\n", cmd->label);
required = require_check(ht);
filename = po_get_str(hash_get(ht, KW_OPT_NAME));
if (filename == NULL) {
errorf("NAME not assigned in label '%s'", cmd->label);
return(false);
}
record_def_adv(pgd->htab, TAG_TYPE_BIN, filename, NULL, NULL, NULL);
/* check if a variable name is given */
varname = po_get_str(hash_get(ht, KW_OPT_VARIABLE));
if (varname == NULL) {
vtmp = conv_to_tag(filename);
if (vtmp == NULL) {
errorf("failed to generate definition name for "
"'%s' in label '%s'", filename, cmd->label);
return(false);
}
/* if not then use default naming scheme */
varname = strdup(vtmp);
if (varname == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
}
bpath = hash_get(pgd->htab, PMKCONF_PATH_BIN);
if (bpath == NULL) {
errorf("%s not available.", PMKCONF_PATH_BIN);
return(false);
}
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, filename, NULL));
}
/* try to locate binary */
pmk_log("\tFound binary '%s' : ", filename);
if (get_file_path(filename, bpath, binpath, sizeof(binpath)) == false) {
pmk_log("no.\n");
rslt = process_required(pgd, cmd, required, filename, NULL);
if (rslt == true) {
/* set path as empty for the key given by varname */
if (hash_update_dup(pgd->htab, varname, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, varname);
return(false);
}
}
return(rslt);
} else {
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, filename, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_BIN, filename, NULL, NULL, binpath);
/* recording path of config tool under the key given by varname */
if (hash_update_dup(pgd->htab, varname, binpath) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, varname);
return(false);
}
label_set(pgd->labl, cmd->label, true);
return(true);
}
}
/**********************
* pmk_check_header() *
***********************************************************************
DESCR
check header file
***********************************************************************/
bool pmk_check_header(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
rslt = true,
rbis,
rval;
char inc_path[MAXPATHLEN] = "",
*lang,
*pstr,
*target = NULL;
code_bld_t scb;
dynary *funcs,
*macros,
*shdrs,
*defs;
int i,
n;
pmk_log("\n* Checking header [%s]\n", cmd->label);
code_bld_init(&scb, pgd->buildlog);
required = require_check(ht);
/* get include filename */
pstr = po_get_str(hash_get(ht, KW_OPT_NAME));
if (pstr == NULL) {
errorf("%s not assigned in label '%s'", KW_OPT_NAME, cmd->label);
return(false);
}
scb.header = pstr;
record_def_data(pgd->htab, scb.header, NULL);
record_def_adv(pgd->htab, TAG_TYPE_HDR, scb.header, NULL, NULL, NULL);
/* any macro(s) to check ? */
macros = po_get_list(hash_get(ht, KW_OPT_MACRO));
if (macros != NULL) {
for (i = 0 ; i < (int) da_usize(macros) ; i++) {
pstr = da_idx(macros, i);
record_def_data(pgd->htab, pstr, NULL);
record_def_adv(pgd->htab, TAG_TYPE_HDR_MCR, scb.header, pstr, NULL, NULL);
}
}
/* optional header sub dependencies */
shdrs = po_get_list(hash_get(ht, KW_OPT_SUBHDR));
/* check if a function or more must be searched */
obsolete_string_to_list(ht, KW_OPT_FUNCTION);
funcs = po_get_list(hash_get(ht, KW_OPT_FUNCTION));
if (funcs != NULL) {
n = da_usize(funcs);
for (i = 0 ; i < n ; i++) {
pstr = da_idx(funcs, i);
record_def_data(pgd->htab, pstr, NULL);
record_def_adv(pgd->htab, TAG_TYPE_HDR_PRC, scb.header, pstr, NULL, NULL);
}
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get language */
lang = get_lang_str(ht, pgd);
if (lang == NULL) {
errorf("Failed, language has not been set.");
return(false);
}
/* init build structure */
if (set_language(&scb, lang) == false) {
pmk_log("\tSkipped, unknown language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get the appropriate compiler */
pstr = set_compiler(&scb, pgd->htab);
if (pstr == NULL) {
pmk_log("\tSkipped, cannot get compiler path for language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("\tUse %s language with %s compiler.\n", lang, pstr);
/* check for alternative variable for CFLAGS */
pstr = po_get_str(hash_get(ht, PMKVAL_ENV_CFLAGS));
if (pstr != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, pstr) == NULL) {
if (hash_update_dup(pgd->htab, pstr, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, pstr);
return(false);
}
}
scb.alt_cflags = pstr;
}
pmk_log("\tStore compiler flags in '%s'.\n", get_cflags_label(&scb));
/* use each element of INC_PATH with -I */
pstr = (char *) hash_get(pgd->htab, PMKCONF_PATH_INC);
if (pstr == NULL) {
errorf("%s not available.", PMKCONF_PATH_INC);
return(false);
}
if (get_file_dir_path(scb.header, pstr, inc_path, sizeof(inc_path)) == true) {
pstr = strdup(inc_path);
if (pstr == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
strlcpy(inc_path, "-I", sizeof(inc_path)); /* no check */
if (strlcat_b(inc_path, pstr, sizeof(inc_path)) == false) {
errorf("failed to add '%s' in include path.", inc_path);
return(false);
}
} else {
/* include not found, init inc_path with "" */
if (strlcpy_b(inc_path, "", sizeof(inc_path)) == false) {
errorf("failed to initialise include path.");
return(false);
}
}
set_cflags(&scb, inc_path);
/*
check header file
*/
pmk_log("\tFound header '%s' : ", scb.header);
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rslt = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* if result is false and dependency headers are provided */
if ((rslt == false) && (shdrs != NULL)) {
/* output result of previous test */
pmk_log("no.\n");
/*
check header file with sub headers
*/
pmk_log("\tFound header '%s' using dependency headers : ", scb.header);
scb.subhdrs = shdrs;
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rslt = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* XXX TODO set sub header flag ? */
}
/* process result */
if (rslt == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, scb.header, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_HDR, scb.header, NULL, NULL, DEFINE_DEFAULT);
/*
check macros in header file
*/
if (macros != NULL) {
while ((target = da_shift(macros)) && (target != NULL)) {
/* init rbis */
rbis = false;
/* if header is okay */
pmk_log("\tFound macro '%s' : ", target);
scb.define = target;
/* fill test file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rbis = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rbis == true) {
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, target, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_HDR_MCR, scb.header, target, NULL, DEFINE_DEFAULT);
} else {
pmk_log("no.\n");
rslt = false;
}
}
/* reset define */
scb.define = NULL;
}
/*
check functions in header file
*/
if (funcs != NULL) {
while ((target = da_shift(funcs)) && (target != NULL)) {
/* test each function */
pmk_log("\tFound function '%s' : ", target);
scb.procedure = target;
/* fill test file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rbis = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rbis == true) {
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, target, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_HDR_PRC, scb.header, target, NULL, DEFINE_DEFAULT);
} else {
pmk_log("no.\n");
rslt = false;
}
}
}
if (rslt == true) {
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
/* put result in CFLAGS, CXXFLAGS or alternative variable */
if (single_append(pgd->htab, get_cflags_label(&scb), strdup(inc_path)) == false) {
errorf("failed to append '%s' in '%s'.", inc_path, get_cflags_label(&scb));
return(false);
}
rval = true;
} else {
rval = process_required(pgd, cmd, required, NULL, NULL);
}
return(rval);
}
/*******************
* pmk_check_lib() *
***********************************************************************
DESCR
check library
***********************************************************************/
bool pmk_check_lib(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
rbis,
rslt = true,
rval;
char lib_buf[TMP_BUF_LEN] = "",
lib_path[TMP_BUF_LEN] = "",
*lang,
pattern[TMP_BUF_LEN] = "",
*pstr,
*target = NULL;
code_bld_t scb;
dynary *da,
*funcs,
*defs;
int i,
n;
pmk_log("\n* Checking library [%s]\n", cmd->label);
code_bld_init(&scb, pgd->buildlog);
required = require_check(ht);
pstr = po_get_str(hash_get(ht, KW_OPT_NAME));
if (pstr == NULL) {
errorf("NAME not assigned in label '%s'.", cmd->label);
return(false);
}
scb.library = pstr;
record_def_adv(pgd->htab, TAG_TYPE_LIB, scb.library, NULL, NULL, NULL);
/* check if a function or more must be searched */
obsolete_string_to_list(ht, KW_OPT_FUNCTION);
funcs = po_get_list(hash_get(ht, KW_OPT_FUNCTION));
if (funcs != NULL) {
n = da_usize(funcs);
for (i = 0 ; i < n ; i++) {
pstr = da_idx(funcs, i);
record_def_data(pgd->htab, pstr, NULL);
record_def_adv(pgd->htab, TAG_TYPE_LIB_PRC, scb.library, pstr, NULL, NULL);
}
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get language */
lang = get_lang_str(ht, pgd);
if (lang == NULL) {
errorf("Failed, language has not been set.");
return(false);
}
/* init build structure */
if (set_language(&scb, lang) == false) {
pmk_log("\tSkipped, unknown language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get the appropriate compiler */
pstr = set_compiler(&scb, pgd->htab);
if (pstr == NULL) {
pmk_log("\tSkipped, cannot get compiler path for language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("\tUse %s language with %s compiler.\n", lang, pstr);
if (rslt == true) {
/* check for alternative variable for LIBS */
pstr = po_get_str(hash_get(ht, KW_OPT_LIBS));
if (pstr != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, pstr) == NULL) {
if (hash_update_dup(pgd->htab, pstr, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, pstr);
return(false);
}
}
scb.alt_libs = pstr;
}
pmk_log("\tStore library flags in '%s'.\n", get_libs_label(&scb));
/* get actual content of LIBS, no need to check as it is initialised */
set_cflags(&scb, hash_get(pgd->htab, PMKVAL_ENV_LIBS));
}
/* get the list of library path */
pstr = (char *) hash_get(pgd->htab, PMKCONF_PATH_LIB);
if (pstr == NULL) {
errorf("%s not available.", PMKCONF_PATH_LIB);
return(false);
}
/* convert to dynary */
da = str_to_dynary(pstr, PATH_STR_DELIMITER);
if (da == NULL) {
errorf("unable to convert '%s'",PMKCONF_PATH_LIB);
return(false);
}
/* generate library pattern */
snprintf(pattern, sizeof(pattern), "lib%s*.*", scb.library);
/* parse list of path to find the pattern */
if (find_pattern(da, pattern, lib_buf, sizeof(lib_buf)) == true) {
snprintf(lib_path, sizeof(lib_path), "-L%s", lib_buf);
set_ldflags(&scb, lib_path);
}
/* clean dynary now useless */
da_destroy(da);
/*
check library
*/
pmk_log("\tFound library '%s' : ", scb.library);
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rslt = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rslt == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, scb.library, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_LIB, scb.library, pstr, NULL, DEFINE_DEFAULT);
/*
check functions
*/
if (funcs != NULL) {
while ((target = da_shift(funcs)) && (target != NULL)) {
/* test each function */
pmk_log("\tFound function '%s' : ", target);
scb.procedure = target;
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rbis = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rbis == true) {
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, target, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_LIB_PRC, scb.library, target, NULL, DEFINE_DEFAULT);
} else {
pmk_log("no.\n");
rslt = false;
}
}
}
if (rslt == true) {
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
if (scb.ldflags != NULL) {
if (snprintf_b(lib_buf, sizeof(lib_buf), "%s -l%s", scb.ldflags, scb.library) == false) {
errorf("failed to build library path or name.");
return(false);
}
} else {
if (snprintf_b(lib_buf, sizeof(lib_buf), "-l%s", scb.library) == false) {
errorf("failed to build library name.");
return(false);
}
}
/* put result in LIBS or alternative variable */
if (single_append(pgd->htab, get_libs_label(&scb), strdup(lib_buf)) == false) {
errorf("failed to append '%s' in '%s'.", lib_buf, get_libs_label(&scb));
return(false);
}
rval = true;
} else {
rval = process_required(pgd, cmd, required, NULL, NULL);
}
return(rval);
}
/**********************
* pmk_check_config() *
***********************************************************************
DESCR
check with *-config utility
***********************************************************************/
bool pmk_check_config(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required = true,
rval = false;
char pipebuf[TMP_BUF_LEN],
cfgpath[MAXPATHLEN],
*bpath,
*cfgtool,
*cflags,
*lang,
*libs,
*libvers,
*modname,
*opt,
*varname,
*vtmp;
code_bld_t scb;
dynary *defs;
cfgtcell *pcc = NULL;
pmk_log("\n* Checking with config tool [%s]\n", cmd->label);
code_bld_init(&scb, pgd->buildlog);
required = require_check(ht);
cfgtool = po_get_str(hash_get(ht, KW_OPT_NAME));
if (cfgtool == NULL) {
errorf("NAME not assigned in label '%s'.", cmd->label);
return(false);
}
record_def_adv(pgd->htab, TAG_TYPE_CFGTOOL, cfgtool, NULL, NULL, NULL);
/* check dependencies */
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, cfgtool, NULL));
}
/* check if a module name is given */
modname = po_get_str(hash_get(ht, KW_OPT_MODULE));
/* check if a variable name is given */
varname = po_get_str(hash_get(ht, KW_OPT_VARIABLE));
if (varname == NULL) {
vtmp = conv_to_tag(cfgtool);
if (vtmp == NULL) {
errorf("VARIABLE not assigned in label '%s'.", cmd->label);
return(false);
}
/* if not then use default naming scheme */
varname = strdup(vtmp);
if (varname == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
}
bpath = hash_get(pgd->htab, PMKCONF_PATH_BIN);
if (bpath == NULL) {
errorf("%s not available.", PMKCONF_PATH_BIN);
return(false);
}
/* get language */
lang = get_lang_str(ht, pgd);
if (lang == NULL) {
errorf("Failed, language has not been set.");
return(false);
}
/* init build structure */
if (set_language(&scb, lang) == false) {
pmk_log("\tSkipped, unknown language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
/* check for alternative variable for CFLAGS */
cflags = po_get_str(hash_get(ht, PMKVAL_ENV_CFLAGS));
if (cflags != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, cflags) == NULL) {
if (hash_update_dup(pgd->htab, cflags, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, cflags);
return(false);
}
}
} else {
/* use default variable of the used language */
cflags = get_cflags_label(&scb);
}
/* check for alternative variable for LIBS */
libs = po_get_str(hash_get(ht, KW_OPT_LIBS));
if (libs != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, libs) == NULL) {
if (hash_update_dup(pgd->htab, libs, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, libs);
return(false);
}
}
} else {
/* use default library variable */
libs = PMKVAL_ENV_LIBS;
}
/* try to locate cfgtool */
pmk_log("\tFound config tool '%s' : ", cfgtool);
if (get_file_path(cfgtool, bpath, cfgpath, sizeof(cfgpath)) == false) {
pmk_log("no.\n");
rval = process_required(pgd, cmd, required, cfgtool, NULL);
if (rval == true) {
/* set path as empty for the key given by varname */
if (hash_update_dup(pgd->htab, varname, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, varname);
return(false);
}
}
return(rval);
} else {
pmk_log("yes.\n");
/* recording path of config tool under the key given by varname */
if (hash_update_dup(pgd->htab, varname, cfgpath) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, varname);
return(false);
}
}
/* check if config tool data is loaded */
if (check_cfgt_data(pgd) == false) {
errorf("unable to load config tool data.");
return(false);
}
/* check if specific tool option exists */
pmk_log("\tUse specific options : ");
pcc = cfgtcell_get_cell(pgd->cfgt, cfgtool);
if (pcc != NULL) {
pmk_log("yes\n");
} else {
pmk_log("no\n");
}
libvers = po_get_str(hash_get(ht, KW_OPT_VERSION));
/* if VERSION is provided then check it */
if (libvers != NULL) {
/* check if specific option exists */
if ((pcc != NULL) && (pcc->version != NULL)) {
opt = pcc->version;
} else {
opt = CFGTOOL_OPT_VERSION;
}
if (ct_get_version(cfgpath, opt, pipebuf, sizeof(pipebuf)) == false) {
errorf("cannot get version from '%s'.", cfgpath);
return(false);
} else {
pmk_log("\tFound version >= %s : ", libvers);
if (compare_version(libvers, pipebuf) < 0) {
/* version does not match */
pmk_log("no (%s).\n", pipebuf);
return(process_required(pgd, cmd, required, cfgtool, NULL));
} else {
pmk_log("yes (%s).\n", pipebuf);
}
}
}
/* record gathered data */
record_def_data(pgd->htab, cfgtool, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_CFGTOOL, cfgtool, NULL, NULL, DEFINE_DEFAULT);
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
/* check if specific option exists */
if ((pcc != NULL) && (pcc->cflags != NULL)) {
opt = pcc->cflags;
} else {
opt = CFGTOOL_OPT_CFLAGS;
}
if (ct_get_data(cfgpath, opt, modname, pipebuf, sizeof(pipebuf)) == false) {
errorf("cannot get CFLAGS.");
return(false);
} else {
/* put result in CFLAGS, CXXFLAGS or alternative variable */
if (single_append(pgd->htab, cflags, strdup(pipebuf)) == false) {
errorf("failed to append '%s' in '%s'.", pipebuf, cflags);
return(false);
} else {
pmk_log("\tStored compiler flags in '%s'.\n", cflags);
}
}
/* check if specific option exists */
if ((pcc != NULL) && (pcc->libs != NULL)) {
opt = pcc->libs;
} else {
opt = CFGTOOL_OPT_LIBS;
}
if (ct_get_data(cfgpath, opt, "", pipebuf, sizeof(pipebuf)) == false) {
errorf("cannot get LIBS.");
return(false);
} else {
/* put result in LIBS or alternative variable */
if (single_append(pgd->htab, libs, strdup(pipebuf)) == false) {
errorf("failed to append '%s' in '%s'.", pipebuf, libs);
return(false);
} else {
pmk_log("\tStored library flags in '%s'.\n", libs);
}
}
return(true);
}
/**************************
* pmk_check_pkg_config() *
***********************************************************************
DESCR
check pkg-config module using internal support
***********************************************************************/
bool pmk_check_pkg_config(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required = true;
char pc_cmd[MAXPATHLEN],
pc_buf[MAXPATHLEN],
*bpath,
*cflags,
*lang,
*libs,
*libvers,
*pc_path,
*pipebuf,
*pstr,
*target;
code_bld_t scb;
dynary *defs;
pkgcell *ppc;
pkgdata *ppd;
pmk_log("\n* Checking pkg-config module [%s]\n", cmd->label);
code_bld_init(&scb, pgd->buildlog);
required = require_check(ht);
target = po_get_str(hash_get(ht, KW_OPT_NAME));
if (target == NULL) {
errorf("NAME not assigned in label '%s'.", cmd->label);
return(false);
}
record_def_adv(pgd->htab, TAG_TYPE_PKGCFG, target, NULL, NULL, NULL);
/* check dependencies */
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, target, NULL));
}
/* try to get pkg-config lib path from pmk.conf */
pstr = hash_get(pgd->htab, PMKCONF_PC_PATH_LIB);
if (pstr == NULL) {
pmk_log("\tUnable to get %s from %s.\n",
PMKCONF_PC_PATH_LIB, PREMAKE_CONFIG_PATH);
return(false);
}
pc_path = process_string(pstr, pgd->htab);
if (pc_path == NULL) {
pmk_log("\tUnable to find pkg-config libdir.\n");
pmk_log("\tWARNING : pkg-config may not be installed\n");
pmk_log("\t or pmk.conf need to be updated.\n");
return(false);
}
/* get language */
lang = get_lang_str(ht, pgd);
if (lang == NULL) {
errorf("Failed, language has not been set.");
return(false);
}
/* init build structure */
if (set_language(&scb, lang) == false) {
pmk_log("\tSkipped, unknown language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
/* check for alternative variable for CFLAGS */
cflags = po_get_str(hash_get(ht, PMKVAL_ENV_CFLAGS));
if (cflags != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, cflags) == NULL) {
if (hash_update_dup(pgd->htab, cflags, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, cflags);
return(false);
}
}
} else {
/* use default variable of the used language */
cflags = get_cflags_label(&scb);
}
/* check for alternative variable for LIBS */
libs = po_get_str(hash_get(ht, KW_OPT_LIBS));
if (libs != NULL) {
/* init alternative variable */
if (hash_get(pgd->htab, libs) == NULL) {
if (hash_update_dup(pgd->htab, libs, "") == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, libs);
return(false);
}
}
} else {
/* use default library variable */
libs = PMKVAL_ENV_LIBS;
}
ppd = pkgdata_init();
if (ppd == NULL) {
errorf("cannot init pkgdata.");
return(false);
}
/* collect packages data */
if (pkg_collect(pc_path, ppd) == false) {
pkgdata_destroy(ppd);
errorf("cannot collect packages data");
return(false);
}
/* check if package exists */
pmk_log("\tFound package '%s' : ", target);
if (pkg_mod_exists(ppd, target) == false) {
pkgdata_destroy(ppd);
pmk_log("no.\n");
/* get binary path */
bpath = hash_get(pgd->htab, PMKCONF_PATH_BIN);
if (bpath == NULL) {
errorf("%s not available.", PMKCONF_PATH_BIN);
return(false);
}
/* check if config tool data is loaded */
if (check_cfgt_data(pgd) == false) {
errorf("unable to load config tool data.");
return(false);
}
/* set config tool filename */
if (cfgtcell_get_binary(pgd->cfgt, target, pc_cmd, sizeof(pc_cmd)) == false) {
if (snprintf_b(pc_cmd, sizeof(pc_cmd), "%s-config", target) == false) {
errorf("overflow in snprintf().");
return(false);
}
}
/* looking for it in the path */
if (get_file_path(pc_cmd, bpath, pc_buf, sizeof(pc_buf)) == true) {
/* use CHECK_CONFIG */
pmk_log("\tFound alternative '%s' tool.\n", pc_cmd);
/* override NAME to use divert on CHECK_CONFIG */
hash_update(ht, KW_OPT_NAME, po_mk_str(pc_cmd));
pmk_log("\tWARNING: rerouting to CHECK_CONFIG\n");
pmk_log("\tPlease consider using directly CHECK_CONFIG with '%s'\n", pc_cmd);
/* call pmk_check_config with the config tool */
return(pmk_check_config(cmd, ht, pgd));
}
return(process_required(pgd, cmd, required, target, NULL));
} else {
pmk_log("yes.\n");
}
libvers = po_get_str(hash_get(ht, KW_OPT_VERSION));
if (libvers != NULL) {
/* if VERSION is provided then check it */
ppc = pkg_cell_add(ppd, target);
if (ppc == NULL) {
pkgdata_destroy(ppd);
errorf("cannot get version.");
return(false);
} else {
pmk_log("\tFound version >= %s : ", libvers);
pipebuf = ppc->version;
if (compare_version(libvers, pipebuf) < 0) {
/* version does not match */
pmk_log("no (%s).\n", pipebuf);
pkgdata_destroy(ppd);
return(process_required(pgd, cmd, required, target, NULL));
} else {
pmk_log("yes (%s).\n", pipebuf);
}
}
}
/* gather data */
if (pkg_recurse(ppd, target) == false) {
pkgdata_destroy(ppd);
errorf("failed to gather packages data (pkg_recurse() function call)");
return(false);
}
/* record gathered data */
record_def_data(pgd->htab, target, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_PKGCFG, target, NULL, NULL, DEFINE_DEFAULT);
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
/* get cflags recursively */
pipebuf = pkg_get_cflags(ppd);
if (pipebuf == NULL) {
pkgdata_destroy(ppd);
errorf("cannot get CFLAGS.");
return(false);
} else {
/* put result in CFLAGS, CXXFLAGS or alternative variable */
if (single_append(pgd->htab, cflags, pipebuf) == false) {
pkgdata_destroy(ppd);
errorf("failed to append '%s' in '%s'.", pipebuf, cflags);
return(false);
} else {
pmk_log("\tStored compiler flags in '%s'.\n", cflags);
}
}
/* get libs recursively */
pipebuf = pkg_get_libs(ppd);
if (pipebuf == NULL) {
pkgdata_destroy(ppd);
errorf("cannot get LIBS.");
return(false);
} else {
/* put result in LIBS or alternative variable */
if (single_append(pgd->htab, libs, pipebuf) == false) {
pkgdata_destroy(ppd);
errorf("failed to append '%s' in '%s'.", pipebuf, libs);
return(false);
} else {
pmk_log("\tStored library flags in '%s'.\n", libs);
}
}
return(true);
}
/********************
* pmk_check_type() *
***********************************************************************
DESCR
check type
***********************************************************************/
bool pmk_check_type(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
rslt = true,
rval;
char inc_path[MAXPATHLEN] = "",
*type,
*member,
*lang,
*pstr;
code_bld_t scb;
dynary *defs;
pmk_log("\n* Checking type [%s]\n", cmd->label);
code_bld_init(&scb, pgd->buildlog);
required = require_check(ht);
/* get type name */
type = po_get_str(hash_get(ht, KW_OPT_NAME));
if (type == NULL) {
errorf("%s not assigned in label '%s'", KW_OPT_NAME, cmd->label);
return(false);
}
record_def_data(pgd->htab, type, NULL);
record_def_adv(pgd->htab, TAG_TYPE_TYPE, type, NULL, NULL, NULL);
/* check if an header must be used */
scb.header = po_get_str(hash_get(ht, KW_OPT_HEADER));
if (scb.header != NULL) {
record_def_data(pgd->htab, scb.header, NULL);
}
/* check if a structure member is given */
member = po_get_str(hash_get(ht, KW_OPT_MEMBER));
if (member != NULL) {
record_def_data(pgd->htab, member, NULL);
}
if (scb.header != NULL) {
record_def_adv(pgd->htab, TAG_TYPE_HDR_TYPE, scb.header, type, NULL, NULL);
if (member != NULL) {
record_def_adv(pgd->htab, TAG_TYPE_HDR_TYP_MBR, scb.header, type, member, NULL);
}
} else {
if (member != NULL) {
record_def_adv(pgd->htab, TAG_TYPE_TYP_MBR, type, member, NULL, NULL);
}
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get language */
lang = get_lang_str(ht, pgd);
if (lang == NULL) {
errorf("Failed, language has not been set.");
return(false);
}
/* init build structure */
if (set_language(&scb, lang) == false) {
pmk_log("\tSkipped, unknown language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
/* get the appropriate compiler */
pstr = set_compiler(&scb, pgd->htab);
if (pstr == NULL) {
pmk_log("\tSkipped, cannot get compiler path for language '%s'.\n", lang);
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("\tUse %s language with %s compiler.\n", lang, pstr);
/* check optional header */
if (scb.header != NULL) {
/* use each element of INC_PATH with -I */
pstr = (char *) hash_get(pgd->htab, PMKCONF_PATH_INC);
if (pstr == NULL) {
errorf("%s not available.", PMKCONF_PATH_INC);
return(false);
}
if (get_file_dir_path(scb.header, pstr, inc_path, sizeof(inc_path)) == true) {
pstr = strdup(inc_path);
if (pstr == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
strlcpy(inc_path, "-I", sizeof(inc_path)); /* no check */
if (strlcat_b(inc_path, pstr, sizeof(inc_path)) == false) {
errorf("failed to add '%s' in include path.", inc_path);
return(false);
}
} else {
/* include not found, init inc_path with "" */
if (strlcpy_b(inc_path, "", sizeof(inc_path)) == false) {
errorf("failed to initialise include path.");
return(false);
}
}
set_cflags(&scb, inc_path);
/*
check header file
*/
pmk_log("\tFound header '%s' : ", scb.header);
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rslt = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rslt == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, scb.header, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_HDR_TYPE, scb.header, type, NULL, DEFINE_DEFAULT);
}
scb.type = type;
pmk_log("\tFound type '%s' : ", scb.type);
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* launch build and get result */
rslt = object_builder(&scb);
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rslt == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, type, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_TYPE, type, NULL, NULL, DEFINE_DEFAULT);
/* check optional member */
if (member != NULL) {
scb.member = member;
pmk_log("\tFound member '%s' : ", scb.member);
/* build test source file */
if (code_builder(&scb) == false) {
errorf("cannot build test file.");
return(false);
}
/* build compiler command */
if (cmdline_builder(&scb, LINK_SRC) == false) {
errorf(ERR_MSG_CC_CMD);
return(false);
}
/* clean files */
cb_cleaner(&scb);
/* process result */
if (rslt == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
/* define for template */
record_def_data(pgd->htab, member, DEFINE_DEFAULT);
record_def_adv(pgd->htab, TAG_TYPE_HDR_TYPE, type, member, NULL, DEFINE_DEFAULT);
if (scb.header != NULL) {
record_def_adv(pgd->htab, TAG_TYPE_HDR_TYP_MBR, scb.header, type, member, DEFINE_DEFAULT);
}
}
if (rslt == true) {
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
rval = true;
} else {
rval = process_required(pgd, cmd, required, scb.type, NULL);
}
return(rval);
}
/************************
* pmk_check_variable() *
***********************************************************************
DESCR
check if variable exists and optionally it's value
***********************************************************************/
bool pmk_check_variable(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
rval = false;
char *var,
*value,
*varval;
dynary *defs;
pmk_log("\n* Checking variable [%s]\n", cmd->label);
required = require_check(ht);
var = po_get_str(hash_get(ht, KW_OPT_NAME));
if (var == NULL) {
errorf("NAME not assigned in label '%s'", cmd->label);
return(false);
}
/* look for additional defines */
defs = po_get_list(hash_get(ht, KW_OPT_DEFS));
if (process_def_list(pgd->htab, defs, false) == false) {
return(false);
}
if (depend_check(ht, pgd) == false) {
pmk_log("\t%s\n", pgd->errmsg);
return(process_required(pgd, cmd, required, var, NULL));
}
pmk_log("\tFound variable '%s' : ", var);
/* trying to get variable */
varval = hash_get(pgd->htab, var);
if (varval != NULL) {
pmk_log("yes.\n");
value = po_get_str(hash_get(ht, KW_OPT_VALUE));
if (value == NULL) {
label_set(pgd->labl, cmd->label, true);
/* process additional defines */
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
rval = true;
} else {
pmk_log("\tVariable match value '%s' : ", value);
if (strncmp(value, varval, sizeof(varval)) == 0) {
pmk_log("yes.\n");
label_set(pgd->labl, cmd->label, true);
if (process_def_list(pgd->htab, defs, true) == false) {
return(false);
}
rval = true;
} else {
pmk_log("no.\n");
rval = process_required(pgd, cmd, required, NULL, NULL);
if (rval == false) {
errorf("variable value does not match ('%s' != '%s').", value, varval);
}
}
}
} else {
pmk_log("no.\n");
rval = process_required(pgd, cmd, required, NULL, NULL);
if (rval == false) {
errorf("failed to find variable '%s'.", var);
}
}
return(rval);
}
/************************
* pmk_build_lib_name() *
***********************************************************************
DESCR
build names of a library
***********************************************************************/
bool pmk_build_lib_name(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
bool required,
versbool;
char *shvar = NULL,
*stvar = NULL,
*pstr,
*value;
pmkobj *po;
pmk_log("\n* Building library name\n");
required = require_check(ht);
pmk_log("\tShared library support : ");
if (pgd->sys_detect == false) {
pmk_log("no.\n");
return(process_required(pgd, cmd, required, NULL, NULL));
}
pmk_log("yes.\n");
pstr = hash_get(pgd->htab, SL_SYS_LABEL);
pmk_log("\tUsing support for '%s'.\n", pstr);
/* get name (REQUIRED) */
pstr = po_get_str(hash_get(ht, KW_OPT_NAME));
if (pstr == NULL) {
errorf("NAME not assigned in label '%s'", cmd->label);
return(false);
}
#ifdef SHLIB_DEBUG
debugf("pstr(name) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
hash_update(pgd->htab, LIB_KW_NAME, value);
/* get major version number */
pstr = po_get_str(hash_get(ht, KW_OPT_MAJOR));
if (pstr != NULL) {
#ifdef SHLIB_DEBUG
debugf("pstr(major) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
hash_update(pgd->htab, LIB_KW_MAJ, value); /* no dup */
}
/* get minor version number */
pstr = po_get_str(hash_get(ht, KW_OPT_MINOR));
if (pstr != NULL) {
#ifdef SHLIB_DEBUG
debugf("pstr(minor) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
hash_update(pgd->htab, LIB_KW_MIN, value); /* no dup */
}
/* get shared lib variable */
pstr = po_get_str(hash_get(ht, KW_SL_SHARED));
if (pstr != NULL) {
#ifdef SHLIB_DEBUG
debugf("pstr(SHARED) = '%s'", pstr);
#endif
shvar = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("shvar = '%s'", shvar);
#endif
}
/* get static lib variable */
pstr = po_get_str(hash_get(ht, KW_SL_STATIC));
if (pstr != NULL) {
#ifdef SHLIB_DEBUG
debugf("pstr(STATIC) = '%s'", pstr);
#endif
stvar = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("stvar = '%s'", stvar);
#endif
}
/* get versioning boolean */
po = hash_get(ht, KW_SL_VERSION);
if (po != NULL) {
versbool = po_get_bool(po);
} else {
/* no version by default */
versbool = false;
}
/* process shared lib name */
if (shvar != NULL) {
if (versbool == false) {
/* get libname without version */
pstr = hash_get(pgd->htab, LIB_KW_SH_NONE);
#ifdef SHLIB_DEBUG
debugf("pstr(LIB_KW_SH_NONE) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
} else {
pstr = hash_get(pgd->htab, LIB_KW_SH_VERS);
#ifdef SHLIB_DEBUG
debugf("pstr(LIB_KW_SH_VERS) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
}
/* no dup, no free */
if (hash_update(pgd->htab, shvar, value) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, shvar);
return(false);
}
pmk_log("\tSetting %s to '%s'\n", shvar, value);
#ifdef SHLIB_DEBUG
debugf("save in '%s'", shvar);
#endif
free(shvar);
}
/* process static lib name */
if (stvar != NULL) {
if (versbool == false) {
/* get libname without version */
pstr = hash_get(pgd->htab, LIB_KW_ST_NONE);
#ifdef SHLIB_DEBUG
debugf("pstr(LIB_KW_ST_NONE) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
} else {
pstr = hash_get(pgd->htab, LIB_KW_ST_VERS);
#ifdef SHLIB_DEBUG
debugf("pstr(LIB_KW_ST_VERS) = '%s'", pstr);
#endif
value = process_string(pstr, pgd->htab);
#ifdef SHLIB_DEBUG
debugf("value = '%s'", value);
#endif
}
/* no dup, no free */
if (hash_update(pgd->htab, stvar, value) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, stvar);
return(false);
}
pmk_log("\tSetting %s to '%s'\n", stvar, value);
#ifdef SHLIB_DEBUG
debugf("save in '%s'", stvar);
#endif
free(stvar);
}
return(true);
}
/**************************
* pmk_build_shlib_name() *
***********************************************************************
DESCR
build name of a shared library
***********************************************************************/
bool pmk_build_shlib_name(pmkcmd *cmd, htable *ht, pmkdata *pgd) {
pmk_log("\n* Building shared library name\n");
pmk_log("\tBUILD_SHLIB_NAME() has been obsoleted by BUILD_LIB_NAME().\n");
pmk_log("\tRead the man page for further information.\n");
return(false);
}
/********************
* option functions *
***********************************************************************
* IN
* cmd : command structure
* popt : option structure
* pgd : global data
*
* OUT
* boolean
***********************************************************************/
/***********************
* pmk_set_parameter() *
***********************************************************************
DESCR
set parameter
***********************************************************************/
bool pmk_set_parameter(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
/* gnu autoconf compatibility */
if (strncmp(popt->key, KW_SETNGS_ACCOMP, sizeof(popt->key)) == 0) {
return(pmk_setparam_accompat(cmd, popt, pgd));
}
/* global language */
if (strncmp(popt->key, KW_SETNGS_GLANG, sizeof(popt->key)) == 0) {
return(pmk_setparam_glang(cmd, popt, pgd));
}
/* set target files */
if (strncmp(popt->key, KW_SETNGS_TARGET, sizeof(popt->key)) == 0) {
return(pmk_setparam_target(cmd, popt, pgd));
}
/* compiler detection */
if (strncmp(popt->key, KW_SETNGS_CCDTCT, sizeof(popt->key)) == 0) {
return(pmk_setparam_detect(cmd, popt, pgd));
}
/* found unknown setting */
pmk_log("\tunknown '%s' setting.\n", popt->key);
return(false);
}
/***************************
* pmk_setparam_accompat() *
***********************************************************************
DESCR
set autoconf compatability parameter
***********************************************************************/
bool pmk_setparam_accompat(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
char *pstr;
pmk_log("\tSetting autoconf compatibility :\n");
/* XXX must check if valid
pstr = (char *) hash_get(pgd->htab, "SYSCONFDIR");
hash_update_dup(pgd->htab, "sysconfdir", pstr);
*/
/* if a file is given then it will be parsed later */
pstr = po_get_str(popt->value);
if (*pstr != CHAR_EOS) {
pgd->ac_file = strdup(pstr);
if (pgd->ac_file == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
pmk_log("\t\tSet file to '%s'.\n", pstr);
if (hash_update_dup(pgd->htab, AC_VAR_DEF, AC_VALUE_DEF) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, AC_VAR_DEF);
return(false);
}
pmk_log("\t\tSet '%s' value to '%s'.\n", AC_VAR_DEF, AC_VALUE_DEF);
}
/* compatibility tags */
if (ac_set_variables(pgd->htab) == false)
return(false); /* XXX error message ? */
pmk_log("\t\tSet specific variables.\n");
return(true);
}
/************************
* pmk_setparam_glang() *
***********************************************************************
DESCR
set global lang parameter
***********************************************************************/
bool pmk_setparam_glang(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
bool rval = false;
char *pstr;
pmk_log("\tSetting global language :\n");
/* set global language */
pstr = po_get_str(popt->value);
if (pstr != NULL) {
/* check if provided lang is supported */
if (verify_language(pstr) != LANG_UNKNOWN) {
pgd->lang = strdup(pstr);
if (pgd->lang == NULL) {
errorf(ERRMSG_MEM);
return(false);
}
pmk_log("\t\tSet to '%s'.\n", pstr);
rval = true;
} else {
errorf("unknown language.");
}
} else {
errorf("syntax error in LANG.");
}
return(rval);
}
/*************************
* pmk_setparam_target() *
***********************************************************************
DESCR
set target list parameter
***********************************************************************/
bool pmk_setparam_target(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
dynary *da;
int i = 0,
n;
pmk_log("\tCollecting targets :\n");
da = po_get_list(popt->value);
if (da == NULL) {
errorf("syntax error in TARGET.");
return(false);
}
n = da_usize(da);
for (i=0 ; i < n ; i++) {
pmk_log("\t\tAdded '%s'.\n", da_idx(da, i));
}
pgd->tlist = da;
pmk_log("\t\tTotal %d target(s) added.\n", n);
return(true);
}
/*************************
* pmk_setparam_detect() *
***********************************************************************
DESCR
detect a list of compilers
***********************************************************************/
bool pmk_setparam_detect(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
char *ccpath,
*lang,
*osname,
*pstr,
temp[64]; /* XXX size */
code_bld_t scb;
comp_parse_t *pcp;
compiler_t *pc;
dynary *da;
hkeys *phk;
int i = 0,
n;
pmk_log("\tDetecting compiler shared library support :\n");
da = po_get_list(popt->value);
if (da == NULL) {
errorf("syntax error in DETECT.");
return(false);
}
/* get os name */
osname = hash_get(pgd->htab, PMKCONF_OS_NAME);
/*
* parsing compiler data to build a database that will be used further
*/
pmk_log("\t\tGathering data for compiler detection.\n");
pcp = parse_comp_file(PMKCOMP_DATA, osname);
/* process each language provided in detection list */
n = da_usize(da);
for (i=0 ; i < n ; i++) {
lang = da_idx(da, i);
/* init code building structure */
code_bld_init(&scb, pgd->buildlog);
/* init build structure */
if (set_language(&scb, lang) == false) {
/*
* if language name is invalid then check
* for obsolete compiler name
*/
pstr = obsolete_get_lang_from_comp(lang); /* OBSOLETE */
if (pstr == NULL) {
errorf("Unknown language'%s'.\n", lang);
return(false);
}
pmk_log("\t\tWARNING: obsolete value for compiler detection,\n");
pmk_log("\t\t\tuse language label instead.\n");
lang = pstr;
if (set_language(&scb, lang) == false) {
errorf("Unknown language '%s'.\n", lang);
return(false);
}
}
/* get the appropriate compiler */
set_compiler(&scb, pgd->htab);
pstr = get_compiler_label(&scb);
ccpath = hash_get(pgd->htab, pstr);
if (ccpath == NULL) {
errorf("Cannot get compiler path ('%s').\n", pstr);
return(false);
}
pmk_log("\t\tDetecting %s compiler : ", lang);
/* try to identify the compiler */
pc = &(pgd->comp_data.data[scb.lang]);
if (comp_detect(ccpath, pgd->buildlog, pc, pcp, scb.pld->slcflags) == false) {
pmk_log("failed.\n");
return(false);
}
/* set the language type, so the compiler data are valid */
pc->lang = scb.lang; /* XXX set in detect proc ? */
pmk_log("%s (version %s).\n", pc->descr, pc->version);
/* set shared lib compiler flags */
pmk_log("\t\tSetting %s to '%s'\n", scb.pld->slcflags, pc->slcflags);
if (hash_update_dup(pgd->htab, scb.pld->slcflags, pc->slcflags) == HASH_ADD_FAIL) {
return(false);
}
/* set shared lib linker flags */
pmk_log("\t\tSetting %s to '%s'\n", scb.pld->slldflags, pc->slldflags);
if (hash_update_dup(pgd->htab, scb.pld->slldflags, pc->slldflags) == HASH_ADD_FAIL) {
return(false);
}
/* try to compile shared object */
pmk_log("\t\tChecking shared library support : ");
if (check_so_support(&scb, pc->slcflags, pc->slldflags) == true) {
pmk_log("ok.\n");
/* shared lib support */
snprintf(temp, sizeof(temp), "%s ", scb.pld->mk_bld_rule);
if (hash_append(pgd->htab, MK_VAR_SL_BUILD, strdup(temp), NULL) == HASH_ADD_FAIL) {
return(false);
}
snprintf(temp, sizeof(temp), "%s ", scb.pld->mk_cln_rule);
if (hash_append(pgd->htab, MK_VAR_SL_CLEAN, strdup(temp), NULL) == HASH_ADD_FAIL) {
return(false);
}
snprintf(temp, sizeof(temp), "%s ", scb.pld->mk_inst_rule);
if (hash_append(pgd->htab, MK_VAR_SL_INST, strdup(temp), NULL) == HASH_ADD_FAIL) {
return(false);
}
snprintf(temp, sizeof(temp), "%s ", scb.pld->mk_deinst_rule);
if (hash_append(pgd->htab, MK_VAR_SL_DEINST, strdup(temp), NULL) == HASH_ADD_FAIL) {
return(false);
}
} else {
pmk_log("failed.\n");
}
/* clean cdata */
cb_cleaner(&scb);
}
/* get system data */
pmk_log("\t\tGetting system data for %s : ", osname);
phk = hash_keys_sorted(pcp->sht);
if (phk == NULL) {
pmk_log("failed.\n");
} else {
for (i = 0 ; i < (int) phk->nkey ; i++) {
pstr = hash_get(pcp->sht, phk->keys[i]) ;
hash_update_dup(pgd->htab, phk->keys[i], pstr); /* XXX check */
}
pmk_log("ok.\n");
/* mark system as detected */
pgd->sys_detect = true;
}
/* clean now useless parsed data */
destroy_comp_parse(pcp);
return(true);
}
/**********************
* pmk_set_variable() *
***********************************************************************
DESCR
set variable
***********************************************************************/
bool pmk_set_variable(pmkcmd *cmd, prsopt *popt, pmkdata *pgd) {
char buffer[TMP_BUF_LEN],
*pstr,
*value,
*defname;
int hval;
/* XXX better way to do (specific hash for override)
if (hash_get(pgd->htab, popt->key) == NULL) {
*/
/* process value string */
pstr = po_get_str(popt->value);
if (pstr == NULL) {
errorf("bad value for '%s'.", popt->key);
return(false);
}
value = process_string(pstr, pgd->htab);
if (value != NULL) {
hval = hash_update(pgd->htab, popt->key, value); /* no need to strdup */
/* check return for message : defined or redefined */
switch (hval) {
case HASH_ADD_FAIL:
errorf(HASH_ERR_UPDT_ARG, popt->key);
return(false);
break;
case HASH_ADD_OKAY:
case HASH_ADD_COLL:
pmk_log("\tdefined");
break;
case HASH_ADD_UPDT:
pmk_log("\tredefined");
break;
}
/* remaining part of the message */
pmk_log(" '%s' variable.\n", popt->key);
/* store definition for autoconf compatibility */
defname = gen_basic_tag_def(popt->key);
if (defname == NULL) {
errorf("unable to build define name for '%s'.", popt->key);
return(false);
}
if (snprintf_b(buffer, sizeof(buffer),
"#define %s \"%s\"", popt->key,
value) == false) {
errorf("buffer overflow for define value of '%s'.", popt->key);
return(false);
}
if (hash_update_dup(pgd->htab, defname, buffer) == HASH_ADD_FAIL) {
errorf(HASH_ERR_UPDT_ARG, defname);
return(false);
}
} else {
pmk_log("\tFailed processing of '%s'.\n", popt->key);
return(false);
}
/*
} else {
pmk_log("\tSkipped '%s' define (overriden).\n", popt->key);
}
*/
return(true);
}
/* vim: set noexpandtab tabstop=4 softtabstop=4 shiftwidth=4: */
syntax highlighted by Code2HTML, v. 0.9.1