/* * SCCS: @(#)config.c 1.12 (98/12/11) * * UniSoft Ltd., London, England * * (C) Copyright 1996 X/Open Company Limited * * All rights reserved. No part of this source code may be reproduced, * stored in a retrieval system, or transmitted, in any form or by any * means, electronic, mechanical, photocopying, recording or otherwise, * except as stated in the end-user licence agreement, without the prior * permission of the copyright owners. * A copy of the end-user licence agreement is contained in the file * Licence which accompanies this distribution. * * X/Open and the 'X' symbol are trademarks of X/Open Company Limited in * the UK and other countries. */ #ifdef HAVE_CONFIG_H #include #endif /************************************************************************ SCCS: @(#)config.c 1.12 98/12/11 TETware release 3.3 NAME: config.c PRODUCT: TETware AUTHOR: Andrew Dingwall, UniSoft Ltd. DATE CREATED: August 1996 DESCRIPTION: functions which deal with configuration variables MODIFICATIONS: Geoff Clare, UniSoft Ltd., August 1996 Missing . Andrew Dingwall, UniSoft Ltd., June 1997. added get_runtime_tsroot() to return the runtime value of TET_TSROOT - required to support the correct operation of TET_RUN when a remote system's test suite root directory resides on a read-only file system Andrew Dingwall, UniSoft Ltd., June 1998 Don't try to do a config variable exchange with a remote system when TET_TSROOT is undefined or when remote config file is inaccessible. Give TET_REMnnn variables in the master configurations priority over generic ones when performing a configuration variable exchange. Andrew Dingwall, UniSoft Ltd., July 1998 Added support for configuration variable expansion. Andrew Dingwall, UniSoft Ltd., December 1998 In TETware-Lite, write the local system's per-system configurations to the temporary files instead of the master configurations. ************************************************************************/ #include #include #include #include #include #include #include # include #include "dtmac.h" #include "dtmsg.h" #include "ptab.h" #include "error.h" #include "globals.h" #include "ltoa.h" #include "servlib.h" #include "dtetlib.h" #include "tet3_config.h" #include "systab.h" #include "tcc.h" #include "dtcc.h" /* default config file names on each system */ static char *defcfname[] = { "tetbuild.cfg", "tetexec.cfg", "tetclean.cfg", #ifndef TET_LITE /* -START-LITE-CUT- */ "tetdist.cfg" #endif /* !TET_LITE */ /* -END-LITE-CUT- */ }; /* the default config file name for a particular mode */ #define DEFCFNAME(mode) (defcfname[TC_CONF_MODE(mode)]) /* config file names - these get filled in by initcfg() */ static char *cfname[sizeof defcfname / sizeof defcfname[0]]; /* the actual config file name for a particular mode */ #define CFNAME(mode) (cfname[TC_CONF_MODE(mode)]) /* ** the master configuration lists ** ** there are master lists for: build mode, execute mode, clean mode, ** the so-called distributed variables and variables specified with -v ** on the command-line ** ** for each list there is a pointer, a length and the number of entries in ** the list ** ** the per-system lists are maintained in the systab entry for each system */ static struct cflist mcflist[TC_NCONF_MODES + 1]; static struct cflist vopts; /* the master config list for a particular mode */ #define MCFLIST(mode) (mcflist[TC_CONF_MODE(mode)]) /* config error counter */ static int conferrors; #ifdef TET_LITE /* -LITE-CUT-LINE- */ /* ** file names for the temporary master config files which are passed ** to test cases via the TET_CONFIG communication variable */ static char *tcfname[sizeof defcfname / sizeof defcfname[0]]; #define Ntcfname (sizeof tcfname / sizeof tcfname[0]) #define TCFNAME(mode) (tcfname[TC_CONF_MODE(mode)]) #else /* -START-LITE-CUT- */ /* ** file names for the temporary master exec, distrib and command-line ** config files which are passed to XRESD in order to support tet_remexec() */ static char *ecfname, *dcfname, *ccfname; #endif /* TET_LITE */ /* -END-LITE-CUT- */ /* ** names of defined distributed variables ** ** environment variables with these names on the master system are added ** to the distributed configuration ** ** note that the number and order of these variables must ** correspond to the initialisation code in initdvar() */ struct dvar { char *dv_name; /* variable name */ int dv_needed; /* variable is needed on each system */ int dv_pathvar; /* variable is a path */ int dv_len; /* strlen(mv_name) */ char *dv_value; /* value on master system */ }; static struct dvar dvar[] = { { "TET_ROOT", 1, 1 }, { "TET_EXEC_ROOT", 1, 1 }, { "TET_SUITE_ROOT", 0, 1 }, { "TET_TSNAME", 0, 0 }, { "TET_TSROOT", 1, 1 }, { "TET_EXECUTE", 0, 1 }, { "TET_TMP_DIR", 0, 1 }, { "TET_RUN", 0, 1 } }; #define Ndvar (sizeof dvar / sizeof dvar[0]) /* ** names of variables that may appear in the master distributed configuration ** but should not be copied to the per-system distributed configurations */ static struct dvar mdvar[] = { { "TET_LOCALHOST", 0, 1 }, { "TET_XTI_MODE", 0, 1 }, { "TET_XTI_TPI", 0, 1 } }; #define Nmdvar (sizeof mdvar / sizeof mdvar[0]) /* ** a simple stack structure - ** used to detect configuration variable substitution loops */ struct cfstack { struct cfstack *cs_last; /* pointer to the previous element */ char *cs_name; /* variable name */ }; /* static function declarations */ static void addvopts PROTOLIST((struct cflist *)); static int cfix2 PROTOLIST((int)); static int cflag2bool PROTOLIST((char *, char *)); static void checkbvar PROTOLIST((struct cflist *, int, int)); static void compat_fix PROTOLIST((void)); static void confgiveup PROTOLIST((void)); static void config_variable_dollar PROTOLIST((void)); static void cvd2 PROTOLIST((int)); static void cvd3 PROTOLIST((struct cflist *, int, int)); static void cvd4 PROTOLIST((char *)); static void config_variable_expand PROTOLIST((struct cflist *, int, int)); static char *cp2value PROTOLIST((char **, int, int, struct cfstack *)); static void cve2 PROTOLIST((char **, int, int, struct cfstack *)); static void cve3 PROTOLIST((char **, char *, char *, int, int, int *, struct cfstack *)); static char *cve3_getvalue PROTOLIST((char *, char *, int, int, struct cfstack *)); static char *cve3_dist PROTOLIST((char *, char *, int, int, struct cfstack *)); static char *cve3_opmode PROTOLIST((char *, char *, int, int, int, struct cfstack *)); static void cve_error PROTOLIST((char *, int, int, char *)); static int docff2 PROTOLIST((char *, struct cflist *, char *)); static char *docffile PROTOLIST((struct cflist *, char *)); static void docfl2 PROTOLIST((struct systab *, int)); static void docfloc PROTOLIST((struct systab *)); static char **findcfg PROTOLIST((char *, struct cflist *)); static char **finddcfg PROTOLIST((char *, int)); static void fix_tet_api_compliant PROTOLIST((int)); static void fix_tet_pass_tc_name PROTOLIST((int)); static void initdvar PROTOLIST((void)); static void initmdvar PROTOLIST((void)); static int is_dist_var PROTOLIST((char *)); static int is_mdist_var PROTOLIST((char *)); static void proccfl2 PROTOLIST((char *, struct cflist *)); static void proccfline PROTOLIST((char *, struct cflist *, int, char *)); static void readmconf PROTOLIST((char *, struct cflist *)); static void reportcfg PROTOLIST((struct cflist *, int, int)); #ifndef TET_LITE /* -START-LITE-CUT- */ static void docfr2 PROTOLIST((struct systab *, struct cflist *, int)); static void docfrem PROTOLIST((struct systab *)); static void reportdcfg PROTOLIST((void)); #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /************************************************************************ * * * configuration file processing functions * * * ************************************************************************/ /* ** initcfg() - perform initial configuration ** ** fopt is the -f command-line option ** gopt is the -g command-line option ** xopt is the -x command-line option ** cwd is tcc's initial working directory */ void initcfg(fopt, gopt, xopt, cwd) char *fopt, *gopt, *xopt, *cwd; { static char tet_version[] = #ifdef TET_LITE /* -LITE-CUT-LINE- */ "TET_VERSION=3.3-lite"; #else /* -START-LITE-CUT- */ "TET_VERSION=3.3"; #endif /* -END-LITE-CUT- */ char fname[MAXPATH]; TRACE5(tet_Ttcc, 2, "initcfg(): fopt = \"%s\", gopt = \"%s\", xopt = \"%s\", cwd = \"%s\"", fopt ? fopt : "", gopt ? gopt : "", xopt ? xopt : "", cwd); /* read in the configuration variables for each of the chosen modes of operation */ if (tcc_modes & TCC_BUILD) { if (gopt && *gopt) fullpath(cwd, gopt, fname, sizeof fname, 0); else fullpath(tet_tsroot, DEFCFNAME(CONF_BUILD), fname, sizeof fname, 0); readmconf(fname, &MCFLIST(CONF_BUILD)); CFNAME(CONF_BUILD) = rstrstore(fname); TRACE2(tet_Ttcc, 1, "build config file = %s", CFNAME(CONF_BUILD)); } if (tcc_modes & TCC_EXEC) { if (xopt && *xopt) fullpath(cwd, xopt, fname, sizeof fname, 0); else if (tet_execute && *tet_execute) { fullpath(tet_execute, DEFCFNAME(CONF_EXEC), fname, sizeof fname, 0); if (tet_eaccess(fname, 0) < 0) fullpath(tet_tsroot, DEFCFNAME(CONF_EXEC), fname, sizeof fname, 0); } else fullpath(tet_tsroot, DEFCFNAME(CONF_EXEC), fname, sizeof fname, 0); readmconf(fname, &MCFLIST(CONF_EXEC)); CFNAME(CONF_EXEC) = rstrstore(fname); TRACE2(tet_Ttcc, 1, "exec config file = %s", CFNAME(CONF_EXEC)); } if (tcc_modes & TCC_CLEAN) { if (fopt && *fopt) fullpath(cwd, fopt, fname, sizeof fname, 0); else fullpath(tet_tsroot, DEFCFNAME(CONF_CLEAN), fname, sizeof fname, 0); readmconf(fname, &MCFLIST(CONF_CLEAN)); CFNAME(CONF_CLEAN) = rstrstore(fname); TRACE2(tet_Ttcc, 1, "clean config file = %s", CFNAME(CONF_CLEAN)); } if (conferrors) confgiveup(); /* ** add in the command-line variables ** supply default TET_API_COMPLIANT and TET_PASS_TC_NAME ** add in TET_VERSION */ if (tcc_modes & TCC_BUILD) { addvopts(&MCFLIST(CONF_BUILD)); fix_tet_api_compliant(CONF_BUILD); fix_tet_pass_tc_name(CONF_BUILD); proccfl2(tet_version, &MCFLIST(CONF_BUILD)); } if (tcc_modes & TCC_EXEC) { addvopts(&MCFLIST(CONF_EXEC)); fix_tet_api_compliant(CONF_EXEC); fix_tet_pass_tc_name(CONF_EXEC); proccfl2(tet_version, &MCFLIST(CONF_EXEC)); } if (tcc_modes & TCC_CLEAN) { addvopts(&MCFLIST(CONF_CLEAN)); fix_tet_api_compliant(CONF_CLEAN); fix_tet_pass_tc_name(CONF_CLEAN); proccfl2(tet_version, &MCFLIST(CONF_CLEAN)); } /* fix up tet_compat */ compat_fix(); if (conferrors) confgiveup(); TRACE1(tet_Ttcc, 2, "initcfg() RETURN"); } /* ** readmconf() - read in a set of master configuration variables ** from a configuration file */ static void readmconf(fname, lp) char *fname; struct cflist *lp; { FILE *fp; char buf[MAXPATH * 2]; register char *p; int lcount = 0; /* open the config file */ if ((fp = fopen(fname, "r")) == (FILE *) 0) { error(errno, "can't open", fname); conferrors++; return; } /* process each line in turn - ** ignore blank lines and comments ** trim trailing spaces */ while (fgets(buf, sizeof buf, fp) != (char *) 0) { lcount++; for (p = buf; *p; p++) if (*p == '\n' || *p == '#') { *p = '\0'; break; } while (--p >= buf) if (isspace(*p)) *p = '\0'; else break; if (p >= buf) proccfline(buf, lp, lcount, fname); } (void) fclose(fp); } /* ** proccfline() - process a single configuration line */ static void proccfline(line, lp, lineno, fname) char *line, *fname; struct cflist *lp; int lineno; { static char fmt[] = "bad format config variable assignment at line %d in file"; char msg[sizeof fmt + LNUMSZ]; /* check the format of the config line */ if (!tet_equindex(line) || !tet_remvar(line, -1)) { (void) sprintf(msg, fmt, lineno); error(0, msg, fname); conferrors++; return; } /* perform common variable assignment processing */ proccfl2(line, lp); } /* ** proccfl2() - common config line processing */ static void proccfl2(line, lp) char *line; register struct cflist *lp; { register char **cp; /* add a new value or update an existing one */ if ((cp = findcfg(line, lp)) == (char **) 0) { RBUFCHK((char **) &lp->cf_conf, &lp->cf_lconf, (int) ((lp->cf_nconf + 1) * sizeof *lp->cf_conf)); *(lp->cf_conf + lp->cf_nconf++) = rstrstore(line);; } else { ASSERT(*cp); if (strcmp(line, *cp)) { TRACE2(tet_Tbuf, 6, "proccfl2(): free config line = %s", tet_i2x(*cp)); free(*cp); *cp = rstrstore(line); } } } /* ** addvopts() - add the command-line variables (except distributed ** variables) to a configuration variable list ** ** this function should not be called for the master distributed ** configuration list */ static void addvopts(lp) struct cflist *lp; { register char **cp; TRACE1(tet_Ttcc, 6, "addvopts()"); ASSERT(lp != &MCFLIST(CONF_DIST)); for (cp = vopts.cf_conf; cp < vopts.cf_conf + vopts.cf_nconf; cp++) if (!is_mdist_var(*cp)) proccfl2(*cp, lp); TRACE1(tet_Ttcc, 6, "addvopts() RETURN"); } /* ** fix_tet_api_compliant() - provide a default value for TET_API_COMPLIANT */ static void fix_tet_api_compliant(mode) int mode; { static char name[] = "TET_API_COMPLIANT"; char line[sizeof name + 6]; TRACE2(tet_Ttcc, 6, "fix_tet_api_compliant(%s)", prcfmode(mode)); ASSERT(CONF_MODE_OK(mode, mcflist)); /* return now if TET_API_COMPLIANT is already defined */ if (getmcfg(name, mode)) return; /* here to add a default value of !TET_OUTPUT_CAPTURE */ (void) sprintf(line, "%s=%s", name, getmcflag("TET_OUTPUT_CAPTURE", mode) ? "False" : "True"); proccfl2(line, &MCFLIST(mode)); } /* ** fix_tet_pass_tc_name() - provide a default value for TET_PASS_TC_NAME */ static void fix_tet_pass_tc_name(mode) int mode; { static char name[] = "TET_PASS_TC_NAME"; char line[sizeof name + 6]; TRACE2(tet_Ttcc, 6, "fix_pass_tc_name(%s)", prcfmode(mode)); ASSERT(CONF_MODE_OK(mode, mcflist)); /* return now if TET_PASS_TC_NAME is already defined */ if (getmcfg(name, mode)) return; /* here to add a default value of TET_OUTPUT_CAPTURE */ (void) sprintf(line, "%s=%s", name, getmcflag("TET_OUTPUT_CAPTURE", mode) ? "True" : "False"); proccfl2(line, &MCFLIST(mode)); } /* ** compat_fix() - fix up the value of tet_compat ** ** the value of TET_COMPAT is extracted from the master config list ** for each of the selected modes of operation */ static void compat_fix() { int compat; int conflict; TRACE1(tet_Ttcc, 6, "compat_fix()"); /* ** get the value of TET_COMPAT from each of the master ** configurations and check for conflicts */ tet_compat = cfix2(CONF_BUILD); conflict = 0; if ((compat = cfix2(CONF_EXEC)) != 0) { if (tet_compat && compat != tet_compat) conflict++; tet_compat = compat; } if ((compat = cfix2(CONF_CLEAN)) != 0) { if (tet_compat && compat != tet_compat) conflict++; tet_compat = compat; } /* report a conflict if one has been detected */ if (conflict) { error(0, "conflicting values for TET_COMPAT", "have been specified"); conferrors++; } #ifndef NOTRACE if (tet_compat) TRACE2(tet_Tscen, 1, "running in %sTET compatibility mode", tet_compat == COMPAT_ETET ? "E" : "D"); else TRACE1(tet_Tscen, 1, "no compatibility mode specified"); #endif } /* ** cfix2() - extend the compat_fix() processing for a particular ** master configuration list ** ** return COMPAT_DTET or COMPAT_ETET if TET_COMPAT has been so ** defined, or 0 if TET_COMPAT has not been defined with a valid value */ static int cfix2(mode) int mode; { static char compatname[] = "TET_COMPAT"; register char *p; if ((p = getmcfg(compatname, mode)) != (char *) 0) switch (*p) { case 'D': case 'd': return(COMPAT_DTET); case 'E': case 'e': return(COMPAT_ETET); default: error(0, compatname, "variable has ambiguous value"); conferrors++; break; } return(0); } /* ** distcfg() - process the distributed configuration */ void distcfg() { struct dvar *dvp; int sysid, sysmax; char *p; struct systab *sp; char buf[MAXPATH + 40]; static char fmt1[] = "%s distributed configuration variable for system %d"; static char fmt2[] = "%s distributed configuration variable not defined for system"; #ifndef TET_LITE /* -START-LITE-CUT- */ char **cp; struct cflist *lp; char fname[MAXPATH]; #endif /* !TET_LITE */ /* -END-LITE-CUT- */ TRACE1(tet_Ttcc, 2, "distcfg(): process distributed configuration"); sysmax = symax(); ASSERT_LITE(sysmax == 0); initdvar(); /* ** if we are processing test cases on the local system, ** copy the (non-empty) defined environment variables to the ** distributed configuration for the local system ** and mark the list as set up */ if ((sp = syfind(0)) != (struct systab *) 0) { for (dvp = dvar; dvp < dvar + Ndvar; dvp++) { if (!dvp->dv_value || !*dvp->dv_value) continue; (void) sprintf(buf, "%s=%.*s",dvp->dv_name, (int) sizeof buf - dvp->dv_len - 1, dvp->dv_value); proccfl2(buf, &CFLIST(sp, CONF_DIST)); } SET_CFSETUP(sp, CONF_DIST); } #ifndef TET_LITE /* -START-LITE-CUT- */ /* ** if we need the distributed config file, read it in; then add in ** distributed variables with nnn > 0 from the tcc command line set */ if (sysmax > 0 || ts_needdist()) { fullpath(tet_tsroot, DEFCFNAME(CONF_DIST), fname, sizeof fname, 0); readmconf(fname, &MCFLIST(CONF_DIST)); CFNAME(CONF_DIST) = rstrstore(fname); TRACE2(tet_Ttcc, 1, "distributed config file = %s", CFNAME(CONF_DIST)); for (cp = vopts.cf_conf; cp < vopts.cf_conf + vopts.cf_nconf; cp++) if (is_mdist_var(*cp) && tet_remvar_sysid(*cp) != 0) proccfl2(*cp, &MCFLIST(CONF_DIST)); } /* ** copy the defined distributed variables to the appropriate ** per-system distributed configuration(s) ** ** if a defined distributed variable has a TET_REMnnn_ prefix: ** if nnn is one of the sysids mentioned in the chosen scenario, ** then the unadorned variable assignment is copied to the ** distributed configuration for that system ** otherwise: ** the variable assignment is copied to the distributed ** configuration for each of the sysids mentioned in the ** chosen scenario */ lp = &MCFLIST(CONF_DIST); for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) { if (!is_dist_var(*cp)) continue; if ((sysid = tet_remvar_sysid(*cp)) > 0) { p = tet_remvar(*cp, -1); ASSERT(p && p != *cp); if ((sp = syfind(sysid)) != (struct systab *) 0) proccfl2(p, &CFLIST(sp, CONF_DIST)); continue; } ASSERT(sysid == -1); for (sysid = 1; sysid <= sysmax; sysid++) if ((sp = syfind(sysid)) != (struct systab *) 0) proccfl2(*cp, &CFLIST(sp, CONF_DIST)); } /* mark the destination lists as being set up */ for (sysid = 1; sysid <= sysmax; sysid++) if ((sp = syfind(sysid)) != (struct systab *) 0) SET_CFSETUP(sp, CONF_DIST); /* ** perform variable expansion on the distributed configurations ** for remote systems */ config_variable_expand(&MCFLIST(CONF_DIST), -1, CONF_DIST); for (sysid = 1; sysid <= sysmax; sysid++) if ((sp = syfind(sysid)) != (struct systab *) 0) config_variable_expand(&CFLIST(sp, CONF_DIST), sysid, CONF_DIST); #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* ** ensure that we have all the dist config variables that we need ** and that they refer to full path names */ for (sysid = 0; sysid <= sysmax; sysid++) { if (!syfind(sysid)) continue; for (dvp = dvar; dvp < dvar + Ndvar; dvp++) { p = getdcfg(dvp->dv_name, sysid); if (p && *p) { if (dvp->dv_pathvar) { if (sysid == 0) ASSERT(isabspathloc(p)); else if (!isabspathrem(p)) { (void) sprintf(buf, fmt1, dvp->dv_name, sysid); error(0, buf, "is not an " "absolute path name"); conferrors++; } } } else if (dvp->dv_needed) { (void) sprintf(buf, fmt2, dvp->dv_name); error(0, buf, tet_i2a(sysid)); conferrors++; } } } if (conferrors) confgiveup(); TRACE1(tet_Ttcc, 2, "distcfg() RETURN"); } /* ** procvopt() - process a -v command-line option ** ** return 0 if successful or -1 on error */ int procvopt(line) char *line; { /* check the format of the config line */ if (!tet_equindex(line) || !tet_remvar(line, -1)) { error(0, "-v option is badly formatted:", line); return(-1); } /* perform common variable assignment processing */ proccfl2(line, &vopts); return(0); } /************************************************************************ * * * configuration fixup and reporting functions * * * ************************************************************************/ /* ** doconfig() - fix up all the configurations and write them to the ** journal */ void doconfig() { register struct systab *sp; struct cflist *blp, *elp, *clp; #ifndef TET_LITE /* -START-LITE-CUT- */ register int sysid, sysmax; struct cflist tmp; struct cflist *lp; char **cp; static char fmt[] = "TET_REM%03d_%.*s"; char buf[MAXPATH + sizeof fmt]; #endif /* !TET_LITE */ /* -END-LITE-CUT- */ TRACE1(tet_Ttcc, 2, "doconfig(): fix up all the configurations and write them to the journal"); /* ** if we are processing on the local system, fix up the local configs ** and arrange to report the local configs to the journal; ** otherwise, arrange to report the master configs to the journal */ if ((sp = syfind(0)) != (struct systab *) 0) { docfloc(sp); blp = &CFLIST(sp, CONF_BUILD); elp = &CFLIST(sp, CONF_EXEC); clp = &CFLIST(sp, CONF_CLEAN); } else { blp = &MCFLIST(CONF_BUILD); elp = &MCFLIST(CONF_EXEC); clp = &MCFLIST(CONF_CLEAN); } #ifndef TET_LITE /* -START-LITE-CUT- */ /* fix up the remote configs */ sysmax = symax(); for (sysid = 1; sysid <= sysmax; sysid++) if ((sp = syfind(sysid)) != (struct systab *) 0) docfrem(sp); #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* give up if any of the above failed */ if (conferrors) confgiveup(); /* ** now that all the config variable expansion is done, ** condense each occurrence of $$ to a single $ */ config_variable_dollar(); /* report the local/master configs to the journal */ if (tcc_modes & TCC_BUILD) reportcfg(blp, 0, CONF_BUILD); if (tcc_modes & TCC_EXEC) reportcfg(elp, 0, CONF_EXEC); if (tcc_modes & TCC_CLEAN) reportcfg(clp, 0, CONF_CLEAN); #ifdef TET_LITE /* -LITE-CUT-LINE- */ /* ** write out the temporary config files for the local system which are ** passed to test cases via the TET_CONFIG communication variable */ if ( (tcc_modes & TCC_BUILD) && (TCFNAME(CONF_BUILD) = docffile(blp, "build")) == (char *) 0 ) conferrors++; if ( (tcc_modes & TCC_EXEC) && (TCFNAME(CONF_EXEC) = docffile(elp, "exec")) == (char *) 0 ) conferrors++; if ( (tcc_modes & TCC_CLEAN) && (TCFNAME(CONF_CLEAN) = docffile(clp, "clean")) == (char *) 0 ) conferrors++; #else /* -START-LITE-CUT- */ /* report the dist config */ if (sysmax > 0 || ts_needdist()) reportdcfg(); /* report the remote configs */ for (sysid = 1; sysid <= sysmax; sysid++) { if ((sp = syfind(sysid)) == (struct systab *) 0) continue; if (tcc_modes & TCC_BUILD) reportcfg(&CFLIST(sp, CONF_BUILD), sysid, CONF_BUILD); if (tcc_modes & TCC_EXEC) reportcfg(&CFLIST(sp, CONF_EXEC), sysid, CONF_EXEC); if (tcc_modes & TCC_CLEAN) reportcfg(&CFLIST(sp, CONF_CLEAN), sysid, CONF_CLEAN); } /* ** write the master exec, distributed and command-line config ** variables to temporary files for use with tet_remexec() */ if ((ecfname = docffile(&MCFLIST(CONF_EXEC), "master exec")) == (char *) 0) conferrors++; #if 0 if ((dcfname = docffile(&MCFLIST(CONF_DIST), "distributed")) == (char *) 0) conferrors++; #endif tmp.cf_conf = (char **) 0; tmp.cf_lconf = 0; tmp.cf_nconf = 0; for (sysid = 0; sysid <= sysmax; sysid++) { if ((sp = syfind(sysid)) == (struct systab *) 0) continue; lp = &CFLIST(sp, CONF_DIST); for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) { if (!*cp) continue; (void) sprintf(buf, fmt, sysid % 1000, (int) (sizeof buf - sizeof fmt), *cp); RBUFCHK((char **) &tmp.cf_conf, &tmp.cf_lconf, (int) ((tmp.cf_nconf + 1) * sizeof *tmp.cf_conf)); *(tmp.cf_conf + tmp.cf_nconf++) = rstrstore(buf);; } } if ((dcfname = docffile(&tmp, "distributed")) == (char *) 0) conferrors++; for (cp = tmp.cf_conf; cp < tmp.cf_conf + tmp.cf_nconf; cp++) { TRACE2(tet_Tbuf, 6, "free tmp dist config var = %s", tet_i2x(*cp)); free(*cp); } TRACE2(tet_Tbuf, 6, "free tmp dist config var list = %s", tet_i2x(tmp.cf_conf)); free((char *) tmp.cf_conf); if ((ccfname = docffile(&vopts, "command-line")) == (char *) 0) conferrors++; /* give up if any of the above failed */ if (conferrors) confgiveup(); /* then send the file names to XRESD */ if (tet_xdcfname(ecfname, dcfname, ccfname) < 0) { error(tet_xderrno, "can't send config file names to XRESD", (char *) 0); conferrors++; } #endif /* TET_LITE */ /* -END-LITE-CUT- */ /* give up if any of the above failed */ if (conferrors) confgiveup(); } /* ** docfloc() - fix up the local configs for each of the selected ** modes of operation */ static void docfloc(sp) struct systab *sp; { TRACE1(tet_Ttcc, 2, "docfloc(): fix up all the local configurations"); if (tcc_modes & TCC_BUILD) docfl2(sp, CONF_BUILD); if (tcc_modes & TCC_EXEC) docfl2(sp, CONF_EXEC); if (tcc_modes & TCC_CLEAN) docfl2(sp, CONF_CLEAN); } /* ** docfl2() - extend the docfloc() processing for a particular mode */ static void docfl2(sp, mode) struct systab *sp; int mode; { struct cflist *from, *to; register char **cp; register char *p; TRACE2(tet_Ttcc, 3, "docfl2(): fix up the local %s configuration", prcfmode(mode)); /* ** determine the source (master config list) ** and destination (per-system config list for the local system) */ ASSERT(CONF_MODE_OK(mode, sp->sy_cflist)); from = &MCFLIST(mode); to = &CFLIST(sp, mode); /* ** do two passes down the source list; ** ** in the first pass we add/update all the non TET_REMnnn_ ** variables into the destination list ** ** in the second pass we add/update all the TET_REMnnn_ ** variables for the local system into the destination list without ** the TET_REMnnn_ prefix */ /* first pass */ for (cp = from->cf_conf; cp < from->cf_conf + from->cf_nconf; cp++) if (tet_remvar(*cp, -1) == *cp) proccfl2(*cp, to); /* second pass */ for (cp = from->cf_conf; cp < from->cf_conf + from->cf_nconf; cp++) if ((p = tet_remvar(*cp, 0)) != *cp) { ASSERT(p); proccfl2(p, to); } /* mark the destination list as being set up */ SET_CFSETUP(sp, mode); /* perform variable expansion if so required */ if (getcflag("TET_EXPAND_CONF_VARS", sp->sy_sysid, mode)) config_variable_expand(to, sp->sy_sysid, mode); /* finally, check all of the boolean variables */ checkbvar(to, 0, mode); } #ifndef TET_LITE /* -START-LITE-CUT- */ /* ** docfrem() - fix up the remote configs for each of the selected ** modes of operation */ static void docfrem(sp) struct systab *sp; { struct cflist tmp; TRACE2(tet_Ttcc, 2, "docfrem() - fix up all the configurations on system %s", tet_i2a(sp->sy_sysid)); ASSERT(sp->sy_sysid > 0); /* initialise the scratchpad */ tmp.cf_conf = (char **) 0; tmp.cf_lconf = 0; tmp.cf_nconf = 0; /* fix up the configs for each mode */ if (tcc_modes & TCC_BUILD) docfr2(sp, &tmp, CONF_BUILD); if (tcc_modes & TCC_EXEC) docfr2(sp, &tmp, CONF_EXEC); if (tcc_modes & TCC_CLEAN) docfr2(sp, &tmp, CONF_CLEAN); /* free the scratchpad area */ if (tmp.cf_conf) { TRACE2(tet_Tbuf, 6, "free tmp config list = %s", tet_i2x(tmp.cf_conf)); free((char *) tmp.cf_conf); } } /* ** docfr2() - extend the docfrem() processing for a particular mode */ static void docfr2(sp, tp, mode) struct systab *sp; struct cflist *tp; int mode; { char fname[MAXPATH]; char *texec, *tsroot; register char **cp; struct cflist *from, *to; static char fmt1[] = "can't access %s mode configuration file %.*s on system"; static char fmt2[] = "tet_tcxconfig() failed when performing %s mode configuration variable exchange with system"; char msg[sizeof fmt1 + MAXPATH + 6]; TRACE3(tet_Ttcc, 3, "docfr2(): fix up the %s configuration on system %s", prcfmode(mode), tet_i2a(sp->sy_sysid)); /* ** determine the source (master config list) ** and destination (per-system config list for this system) */ ASSERT(CONF_MODE_OK(mode, sp->sy_cflist)); from = &MCFLIST(mode); to = &CFLIST(sp, mode); /* determine the config file name on the remote system */ if ((tsroot = getdcfg("TET_TSROOT", sp->sy_sysid)) == (char *) 0) { error(0, "TET_TSROOT not defined for system", tet_i2a(sp->sy_sysid)); conferrors++; return; } ASSERT(CONF_MODE_OK(mode, defcfname)); if (mode == CONF_EXEC && (texec = getdcfg("TET_EXECUTE", sp->sy_sysid)) != (char *) 0) { fullpath(texec, DEFCFNAME(mode), fname, sizeof fname, 1); if (tet_tcaccess(sp->sy_sysid, fname, 0) < 0) fullpath(tsroot, DEFCFNAME(mode), fname, sizeof fname, 1); } else fullpath(tsroot, DEFCFNAME(mode), fname, sizeof fname, 1); /* make sure that the remote config file is accessible */ if (tcc_access(sp->sy_sysid, fname, 04) < 0) { (void) sprintf(msg, fmt1, prcfmode(mode), MAXPATH, fname); error(errno ? errno : tet_tcerrno, msg, tet_i2a(sp->sy_sysid)); conferrors++; return; } /* ** build a list of configuration variables to send to the remote ** system which consists of all the generic variables plus all ** the TET_REMnnn variables for that system; ** the generic variables go first so as to give TET_REMnnn variables ** priority over them */ tp->cf_nconf = 0; for (cp = from->cf_conf; cp < from->cf_conf + from->cf_nconf; cp++) { if (tet_remvar(*cp, -1) != *cp) continue; RBUFCHK((char **) &tp->cf_conf, &tp->cf_lconf, (int) ((tp->cf_nconf + 1) * sizeof *tp->cf_conf)); *(tp->cf_conf + tp->cf_nconf++) = *cp; } for (cp = from->cf_conf; cp < from->cf_conf + from->cf_nconf; cp++) { if (tet_remvar(*cp, sp->sy_sysid) == *cp) continue; RBUFCHK((char **) &tp->cf_conf, &tp->cf_lconf, (int) ((tp->cf_nconf + 1) * sizeof *tp->cf_conf)); *(tp->cf_conf + tp->cf_nconf++) = *cp; } /* perform a config variable exchange with the remote system */ if (tet_tcxconfig(sp->sy_sysid, fname, tp, &vopts, to) < 0) { (void) sprintf(msg, fmt2, prcfmode(mode)); error(tet_tcerrno, msg, tet_i2a(sp->sy_sysid)); conferrors++; } /* mark the destination list as being set up */ SET_CFSETUP(sp, mode); /* perform variable expansion if so required */ if (getcflag("TET_EXPAND_CONF_VARS", sp->sy_sysid, mode)) config_variable_expand(to, sp->sy_sysid, mode); /* finally, check all of the boolean variables */ checkbvar(to, sp->sy_sysid, mode); } #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* ** checkbvar() - check that all the boolean variables are ** either "True" or "False" in a particular configuration */ static void checkbvar(lp, sysid, mode) struct cflist *lp; int sysid; int mode; { /* list of boolean configuration variables */ static struct bvar { char *bv_name; int bv_len; } bvar[] = { { "TET_EXEC_IN_PLACE" }, { "TET_EXEC_LOCK" }, { "TET_OUTPUT_CAPTURE" }, { "TET_API_COMPLIANT" }, { "TET_PASS_TC_NAME" }, { "TET_TRANSFER_SAVE_FILES" }, { "TET_EXPAND_CONF_VARS" } }; #define Nbvar (sizeof bvar / sizeof bvar[0]) static char fmt[] = "bad value for boolean variable %s in %s configuration on system"; char msg[sizeof fmt + 40]; register char *p1, *p2; register char **cp; register struct bvar *vp; TRACE3(tet_Ttcc, 2, "checkbvar(): check the boolean variables in the %s configuration for system %s", prcfmode(mode), tet_i2a(sysid)); /* initialise the name lengths first time through */ if (!bvar[0].bv_len) for (vp = bvar; vp < bvar + Nbvar; vp++) vp->bv_len = strlen(vp->bv_name); /* ** search the list for boolean variables and check that each one is ** either "true" or "false" (actually we only check for 't' and 'f') */ for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) { if ((p1 = tet_remvar(*cp, -1)) == (char *) 0 || (p2 = tet_equindex(p1)) == (char *) 0) continue; for (vp = bvar; vp < bvar + Nbvar; vp++) { if (vp->bv_len != p2 - p1 || strncmp(p1, vp->bv_name, vp->bv_len)) continue; switch (*++p2) { case 'T': case 'F': case 't': case 'f': break; default: (void) sprintf(msg, fmt, vp->bv_name, prcfmode(mode)); error(0, msg, tet_i2a(sysid)); conferrors++; break; } break; } } } /* ** reportcfg() - report a per-mode configuration to the journal */ static void reportcfg(lp, sysid, mode) struct cflist *lp; int sysid, mode; { char **cp; TRACE3(tet_Ttcc, 3, "reportcfg(): report %s mode configuration for system %s", prcfmode(mode), tet_i2a(sysid)); ASSERT_LITE(sysid == 0); ASSERT(mode != CONF_DIST); /* emit the config start message */ if (sysid == 0) { ASSERT(CONF_MODE_OK(mode, cfname)); jnl_mcfg_start(CFNAME(mode), mode); } #ifndef TET_LITE /* -START-LITE-CUT- */ else jnl_scfg_start(sysid, mode); #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* emit the config lines */ for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) jnl_cfg(*cp); /* emit the config end message */ jnl_cfg_end(); } #ifndef TET_LITE /* -START-LITE-CUT- */ /* ** reportdcfg() - report the distributed configuration to the journal */ static void reportdcfg() { struct systab *sp; struct cflist *lp; char **cp; int sysid, sysmax; char *buf = (char *) 0; int buflen = 0; TRACE1(tet_Ttcc, 3, "reportdcfg(): report the distributed configuration"); /* emit the config start message */ jnl_mcfg_start(CFNAME(CONF_DIST), CONF_DIST); /* ** first, report all the defined distributed configuration ** variables in sysid order */ for (sysid = 0, sysmax = symax(); sysid <= sysmax; sysid++) { if ((sp = syfind(sysid)) == (struct systab *) 0) continue; lp = &CFLIST(sp, CONF_DIST); for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) { RBUFCHK(&buf, &buflen, (int) strlen(*cp) + 12); (void) sprintf(buf, "TET_REM%03d_%s", sysid % 1000, *cp); jnl_cfg(buf); } } if (buf) { TRACE2(tet_Tbuf, 6, "reportdcfg(): free buf = %s", tet_i2x(buf)); free(buf); buf = (char *) 0; buflen = 0; } /* ** then, report the other variables (if any) that are in ** the master distributed configuration */ lp = &MCFLIST(CONF_DIST); for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) if (!is_dist_var(*cp)) jnl_cfg(*cp); /* emit the config end message */ jnl_cfg_end(); } #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* ** docffile() - write out a configuration to a temporary file ** and return a pointer to the file name which is stored ** in malloc'd memory ** ** return (char *) 0 on error */ static char *docffile(lp, type) struct cflist *lp; char *type; { char *fname; /* get a temporary file name */ if ((fname = tet_mktfname("tcc")) == (char *) 0) return(fname); /* write out the variables to the file */ if (docff2(fname, lp, type) < 0) { TRACE2(tet_Tbuf, 6, "free tmp config file name = %s", tet_i2x(fname)); free(fname); fname = (char *) 0; } return(fname); } /* ** docff2() - extend the docffile() processing ** ** return 0 if successful or -1 on error */ static int docff2(fname, lp, type) register struct cflist *lp; char *fname, *type; { register char **cp; FILE *fp; /* open the file */ if ((fp = fopen(fname, "w")) == (FILE *) 0) { error(errno, "can't open", fname); return(-1); } /* write out the variables */ (void) fprintf(fp, "# %s configuration variables\n\n", type); for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) if (fprintf(fp, "%s\n", *cp) < 0) { error(errno, "write error on", fname); (void) fclose(fp); return(-1); } /* close the file */ if (fclose(fp) < 0) { error(errno, "close error on", fname); return(-1); } /* all OK so return success */ return(0); } /************************************************************************ * * * configuration variable expansion * * * ************************************************************************/ /* ** config_variable_expand() - expand variables in the specified ** configuration ** ** if a value contains a ${variable}, then the variable's value is ** interpolated ** ** a $$ in the value is left in place for now - ** later it will be condensed to a single $ ** ** lp points to the configuration list to process ** if the list is for a per-system configuration, sysid is the ** system number; ** otherwise, if the list is for the distributed configuration, ** sysid should be -1 */ static void config_variable_expand(lp, sysid, mode) struct cflist *lp; int sysid, mode; { char **cp; #ifndef NOTRACE if (mode == CONF_DIST && sysid < 0) TRACE1(tet_Ttcc, 3, "config_variable_expand(): expand master distributed configuration variables"); else TRACE3(tet_Ttcc, 3, "config_variable_expand(): expand %s mode configuration variables for system %s", prcfmode(mode), tet_i2a(sysid)); TRACE1(tet_Ttcc, 10, "configuration list before expansion:"); if (tet_Ttcc > 0) for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) TRACE4(tet_Ttcc, 10, "\tcp = %s, *cp = %s -> \"%s\"", tet_i2x(cp), tet_i2x(*cp), *cp ? *cp : ""); #endif ASSERT_LITE(sysid == 0 || mode == CONF_DIST); /* process each configuration variable assignment in turn */ for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) cve2(cp, sysid, mode, (struct cfstack *) 0); #ifndef NOTRACE TRACE1(tet_Ttcc, 10, "configuration list after expansion:"); if (tet_Ttcc > 0) for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) TRACE4(tet_Ttcc, 10, "\tcp = %s, *cp = %s -> \"%s\"", tet_i2x(cp), tet_i2x(*cp), *cp ? *cp : ""); TRACE1(tet_Ttcc, 3, "config_variable_expand() RETURN"); #endif } /* ** cve2() - extend the config_variable_expand() processing ** for a particular variable assignment ** ** this function can be called recursively - ** the stack at *stp is used to detect a configuration variable ** substitution loop */ #ifdef NOTRACE # define TRACE_ENTER # define TRACE_RETURN #else # define TRACE_ENTER \ level++; \ TRACE6(tet_Ttcc, 8, "cve2() ENTER at level %s, cp = %s, sysid = %s, mode = %s, assignment = \"%s\"", \ tet_i2a(level), tet_i2x(cp), tet_i2a(sysid), \ prcfmode(mode), *cp) # define TRACE_RETURN \ TRACE2(tet_Ttcc, 8, "cve2() RETURN from level %s", tet_i2a(level)); \ --level; \ return #endif static void cve2(cp, sysid, mode, stp) char **cp; int sysid, mode; struct cfstack *stp; { char *p, *s; int syntax_errors; static char fmt[] = "ignoring useless %.50s"; char msg[sizeof fmt + 50]; #ifndef NOTRACE static int level; #endif TRACE_ENTER; /* ** if this variable is in a distributed configuration: ** ** if it is a master distributed configuration variable: ** ignore defined distributed variables */ if (mode == CONF_DIST) { if (sysid < 0) { if (is_dist_var(*cp)) { TRACE1(tet_Ttcc, 8, "cve2(): ignoring a defined distributed variable in the master distributed configuration"); TRACE_RETURN; } if (tet_remvar_sysid(*cp) >= 0) { p = tet_equindex(*cp); ASSERT(*p == '='); *p = '\0'; (void) sprintf(msg, fmt, *cp); *p = '='; error(0, msg, "variable assignment in the distributed configuration"); TRACE_RETURN; } } else ASSERT(tet_remvar_sysid(*cp) == -1); } /* ** loop until all the ${variable}s have been expanded ** but leave all the $$s in place for now */ syntax_errors = 0; for (;;) { p = tet_equindex(*cp); ASSERT(p); for (p += 1; *p; p++) { if (*p == '$') { if (*(p + 1) == '$') p++; else break; } } if (!*p) break; s = rstrstore(*cp); p = tet_equindex(s); ASSERT(p); *p++ = '\0'; cve3(cp, s, p, sysid, mode, &syntax_errors, stp); TRACE2(tet_Tbuf, 6, "cve2(): free config line = %s", tet_i2x(s)); free(s); } TRACE_RETURN; } /* ** cve3() - extend the config_variable_expand() processing some more ** when the variable assignment at *cp is known to contain ** a ${variable} ** ** name and value should point to null-terminated COPIES of the original ** assignment ** these copies can get modified below here */ static void cve3(cp, name, value, sysid, mode, errp, stp) char **cp, *name, *value; int sysid, mode, *errp; struct cfstack *stp; { static char badsyntax[] = "bad variable substitution syntax"; char *p, *head, *tail, *fullvar; int buflen, len; TRACE2(tet_Ttcc, 8, "cve3(): expand variable assignment \"%s\"", *cp); /* ** split the value into three null-terminated parts: ** head ** variable name (fullvar) ** tail ** ** again, a $$ is left in place for now */ head = value; for (p = value; *p; p++) { if (*p == '$') { if (*(p + 1) == '$') p++; else break; } } ASSERT(*p == '$'); *p++ = '\0'; if (*p == '{') { fullvar = tail = ++p; for (; *p; p++) if (*p == '}') { *p = '\0'; tail = p + 1; break; } else if (*p == '$') { fullvar = (char *) 0; tail = p; break; } } else { fullvar = (char *) 0; tail = p; } /* ** here: ** fullvar points to the name of the variable to be interpolated ** or is (char *) 0 to indicate a syntax error ** head points to the part of the value before the variable name ** tail points to the part of the value after the variable name ** ** each of these strings are null-terminated */ /* interpolate the value and return */ if (!fullvar || !*fullvar) { if ((*errp)++ == 0) cve_error(name, mode, sysid, badsyntax); p = (char *) 0; } else p = cve3_getvalue(name, fullvar, sysid, mode, stp); len = (int) (strlen(name) + strlen(head) + strlen(tail)) + 2; if (p) len += (int) strlen(p); buflen = (int) strlen(*cp) + 1; RBUFCHK(cp, &buflen, len); (void) sprintf(*cp, "%s=%s%s%s", name, head, p ? p : "", tail); TRACE2(tet_Ttcc, 8, "cve3(): assignment after expansion: \"%s\"", *cp); } /* ** cve3_getvalue() - extend the cve3 processing ** ** return pointer to the value to be interpolated, ** or (char *) 0 on error */ static char *cve3_getvalue(name, fullvar, sysid, mode, stp1) char *name, *fullvar; int sysid, mode; struct cfstack *stp1; { static char subloop[] = "variable substitution loop"; static char fmt[] = "can't find a value to substitute for ${%.40s}"; char msg[sizeof fmt + 40]; struct cfstack *stp2; char *p, *var; int mmm; /* ** if this is a recursive call, stp1 points to a stack containing the ** names of all the variables currently in the process of ** being interpolated ** ** check for a variable substitution loop by looking back up the ** stack to see if we are already trying to interpolate the ** current variable name */ for (stp2 = stp1; stp2; stp2 = stp2->cs_last) if (!strcmp(name, stp2->cs_name)) { cve_error(name, mode, sysid, subloop); return((char *) 0); } /* ** set var to the short (unadorned) name of the variable whose value ** is to be looked up */ if ((mmm = tet_remvar_sysid(fullvar)) >= 0) { var = tet_remvar(fullvar, mmm); ASSERT(fullvar != var); } else { ASSERT(mmm == -1); var = fullvar; } /* look up the value of the variable that is to be interpolated */ if (mode == CONF_DIST) p = cve3_dist(name, var, mmm, sysid, stp1); else p = cve3_opmode(name, var, mmm, sysid, mode, stp1); if (!p) { (void) sprintf(msg, fmt, fullvar); cve_error(name, mode, sysid, msg); } return(p); } /* ** cve3_dist() - extend the config_variable_expand() processing for a ** distributed configuration variable assignment ** ** return a pointer to the value to be interpolated, ** or (char *) 0 if no value can be found ** ** variable assignment can take one of these forms: ** ** FOO=head${TET_REMmmm_BAR}tail ** FOO=head${BAR}tail ** ** TET_REMnnn_FOO variables don't appear in a per-system distributed ** configuration ** TET_REMnnn_FOO variables in the master distributed configuration ** get filtered out in cve2() above ** ** the following strategy is used: ** ** if ${fullvar} is TET_REMmmm_BAR: ** look for BAR in the distributed configuration for system mmm; ** if not found: ** look for BAR in the distributed configuration for system sysid; ** if not found: ** report an error ** otherwise: ** (${fullvar} is BAR) ** look for BAR in the distributed configuration for system sysid; ** if not found: ** report an error */ static char *cve3_dist(name, var, mmm, sysid, stp) char *name, *var; int mmm, sysid; struct cfstack *stp; { struct cfstack stack; char **cp; int n; /* ** make cp point to the address of the configuration variable ** assignment whose value we want to interpolate ** ** most of the work is done by finddcfg() which looks for ** var in the per-system distributed configuration first, then ** drops back to looking in the master distributed configuration */ if ((n = (mmm >= 0) ? mmm : sysid) >= 0) cp = finddcfg(var, n); else cp = findcfg(var, &MCFLIST(CONF_DIST)); /* ** perform recursive varible expansion on the chosen variable ** and return its value */ stack.cs_name = name; stack.cs_last = stp; return(cp2value(cp, n, CONF_DIST, &stack)); } /* ** cve3_opmode() - extend the config_variable_expand() processing for a ** build, exec or clean mode configuration variable assignment ** ** return a pointer to the value to be interpolated, ** or (char *) 0 if no value can be found ** ** variable assignment can take one of these forms: ** ** FOO=head${TET_REMmmm_BAR}tail ** FOO=head${BAR}tail ** ** (there are no TET_REMnnn_ variables in a per-system configuration) ** ** the following strategy is used: ** ** if ${fullvar} is one of the known distributed variables: ** if ${fullvar} is TET_REMmmm_BAR: ** get the value of BAR for system mmm from the ** distributed configuration; ** if not defined: ** report an error ** otherwise: ** (${fullvar} is BAR) ** get the value of BAR for system sysid from the ** distributed configuration; ** if not defined: ** report an error ** otherwise: ** (${fullvar} is not a distributed variable) ** if ${fullvar} is TET_REMmmm_BAR: ** look for BAR in the configuration for system mmm; ** if not found: ** report an error ** otherwise: ** (${fullvar} is BAR) ** look for BAR in the configuration for system sysid; ** if not found: ** report an error */ static char *cve3_opmode(name, var, mmm, sysid, mode, stp) char *name, *var; int mmm, sysid, mode; struct cfstack *stp; { struct systab *sp; struct cfstack stack; int n; n = (mmm >= 0) ? mmm : sysid; /* ** if var is one of the defined distributed configuration variables, ** return its value ** ** we don't need to perform recursive variable expansion on a ** distributed configuration variable because all such variables ** have already been fully expanded */ if (is_dist_var(var)) return(cp2value(finddcfg(var, n), n, CONF_DIST, (struct cfstack *) 0)); /* ** not a defined distributed configuration variable, so look for ** the variable in the configuration for the current mode ** ** perform recursive variable expansion on it before returning ** its value */ if ((sp = syfind(n)) == (struct systab *) 0) return((char *) 0); ASSERT(CONF_MODE_OK(mode, sp->sy_cflist)); stack.cs_name = name; stack.cs_last = stp; return(cp2value(findcfg(var, &CFLIST(sp, mode)), n, mode, &stack)); } /* ** cp2value() - return pointer to the value part of a ** configuration variable assignment whose address is ** pointed to by cp */ static char *cp2value(cp, sysid, mode, stp) char **cp; int sysid, mode; struct cfstack *stp; { char *p; /* return now if we have been passed a NULL pointer */ if (cp == (char **) 0) return((char *) 0); /* ** if required, perform recursive configuration variable expansion on ** the value that we are about to return */ if (stp) cve2(cp, sysid, mode, stp); /* locate the value and return it */ p = tet_equindex(*cp); ASSERT(p); return(p + 1); } /* ** cve_error() - common error reporting function for ** configuration variable expansion errors */ static void cve_error(name, mode, sysid, text) char *name, *text; int mode, sysid; { static const char fmt1[] = "%.80s in %.40s assignment in the %s"; static const char fmt2[] = "%.14s for system %d"; static const char conf[] = "configuration"; char msg1[sizeof fmt1 + 80 + 40 + 14 + LNUMSZ]; char msg2[sizeof fmt2 + 14 + LNUMSZ]; char *p; if (mode == CONF_DIST) { p = "Distributed"; (void) sprintf(msg2, conf); } else { p = prcfmode(mode); if (sysid < 0) { (void) sprintf(msg2, "%s %s", p, conf); p = "master"; } else (void) sprintf(msg2, fmt2, conf, sysid); } (void) sprintf(msg1, fmt1, text, name, p); error(0, msg1, msg2); conferrors++; } /* ** config_variable_dollar() - condense each occurrence of $$ to $ ** in variable assignments */ static void config_variable_dollar() { if (tcc_modes & TCC_BUILD) cvd2(CONF_BUILD); if (tcc_modes & TCC_EXEC) cvd2(CONF_EXEC); if (tcc_modes & TCC_CLEAN) cvd2(CONF_CLEAN); cvd2(CONF_DIST); } /* ** cvd2() - extend the config_variable_dollar() processing for ** a particular mode of operation */ static void cvd2(mode) int mode; { struct systab *sp; int sysid, sysmax; #ifndef TET_LITE /* -START-LITE-CUT- */ if (mode == CONF_DIST) cvd3(&MCFLIST(CONF_DIST), -1, CONF_DIST); #endif /* !TET_LITE */ /* -END-LITE-CUT- */ /* ** for each system in the chosen scenario: ** process the list if: ** mode is DIST and sysid > 0, or ** variable expansion is enabled */ for (sysid = 0, sysmax = symax(); sysid <= sysmax; sysid++) if ( (mode != CONF_DIST || sysid > 0) && (sp = syfind(sysid)) != (struct systab *) 0 && ( mode == CONF_DIST || getcflag("TET_EXPAND_CONF_VARS", sysid, mode) ) ) cvd3(&CFLIST(sp, mode), sysid, mode); } /* ** cvd3() - extend the config_variable_dollar() processing for ** a particular configuration list */ static void cvd3(lp, sysid, mode) struct cflist *lp; int sysid, mode; { char **cp; /* ** process each variable assigment in the list ** ** entries in build, exec and clean configurations are ** always processed ** ** entries in a per-system distributed configurations are ** always processed ** (these lists contain only defined distributed variables) ** ** in the master distributed configuration, only entries that are ** not defined distributed variables are processed */ for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) if (mode != CONF_DIST || sysid >= 0 || !is_dist_var(*cp)) cvd4(*cp); } /* ** cvd4() - extend the config_variable_dollar() processing ** for an individual configuration variable assignment */ static void cvd4(s) char *s; { char *p1, *p2; p1 = tet_equindex(s); ASSERT(p1); /* replace each $$ with a $ */ while (*++p1) if (*p1 == '$') { p2 = p1 + 1; ASSERT(*p2 == '$'); do { *p2 = *(p2 + 1); } while (*++p2); } } /* ** is_dist_var() - see if name is a defined distributed variable ** which may appear in a per-system distributed configuration ** ** return 1 if name is FOO or TET_REMnnn_FOO, where FOO is a ** defined distributed variable ** ** otherwise, return 0 ** ** name may be delimited either by '=' or '\0' */ static int is_dist_var(name) char *name; { struct dvar *dvp; char *p, *shortname; int len; initdvar(); shortname = tet_remvar(name, -1); ASSERT(shortname); if ((p = tet_equindex(shortname)) == (char *) 0) len = (int) strlen(shortname); else len = (int) (p - shortname); for (dvp = dvar; dvp < dvar + Ndvar; dvp++) if (len == dvp->dv_len && !strncmp(shortname, dvp->dv_name, len)) return(1); return(0); } /* ** is_mdist_var() - see if name is a variable which may appear in ** the master distributed configuration ** ** return 1 if name is FOO or TET_REMnnn_FOO, where FOO is a ** distributed variable ** ** otherwise, return 0 ** ** name may be delimited either by '=' or '\0' */ static int is_mdist_var(name) char *name; { struct dvar *dvp; char *p, *shortname; int len; initmdvar(); shortname = tet_remvar(name, -1); ASSERT(shortname); if ((p = tet_equindex(shortname)) == (char *) 0) len = (int) strlen(shortname); else len = (int) (p - shortname); for (dvp = mdvar; dvp < mdvar + Nmdvar; dvp++) if (len == dvp->dv_len && !strncmp(shortname, dvp->dv_name, len)) return(1); return(is_dist_var(shortname)); } /************************************************************************ * * * configuration variable lookup functions * * * ************************************************************************/ /* ** getmcfg() - return pointer to value of config variable "name" ** in the master configuration for the specified mode ** ** return (char *) 0 if no entry appears for name */ char *getmcfg(name, mode) char *name; int mode; { register char **cp, *p; mode = tcc2cfmode(mode); ASSERT(CONF_MODE_OK(mode, mcflist)); if ((cp = findcfg(name, &MCFLIST(mode))) == (char **) 0) p = (char *) 0; else { p = tet_equindex(*cp); ASSERT(p); p++; } TRACE4(tet_Ttcc, 10, "getmcfg(\"%s\", %s) returns %s", name, prcfmode(mode), p ? p : "NULL"); return(p); } /* ** getcfg() - return pointer to value of config variable "name" ** in the per-system configuration for the specified mode ** ** return (char *) 0 if no entry appears for name */ char *getcfg(name, sysid, mode) char *name; int sysid, mode; { register char **cp, *p; register struct systab *sp; ASSERT_LITE(sysid == 0); /* get the systab entry for this system */ sp = syfind(sysid); /* ** if there is no systab entry for this system: ** if this is the local system, return the master config value; ** otherwise bail out on a programming error */ if (sp == (struct systab *) 0 && sysid == 0) return(getmcfg(name, mode)); ASSERT(sp); /* convert mode if necessary and check it */ mode = tcc2cfmode(mode); ASSERT(CONF_MODE_OK(mode, sp->sy_cflist)); /* look up the variable in the per-system configuration */ if ((cp = findcfg(name, &CFLIST(sp, mode))) == (char **) 0) p = (char *) 0; else { p = tet_equindex(*cp); ASSERT(p); p++; } TRACE5(tet_Ttcc, 10, "getcfg(\"%s\", %s, %s) returns %s", name, tet_i2a(sysid), prcfmode(mode), p ? p : "NULL"); return(p); } /* ** getdcfg() - return pointer to value of config variable "name" ** in the per-system distributed configuration for sysid ** ** return (char *) 0 if no entry appears for name ** ** this function must not be called before the per-system distributed ** configurations have been set up */ char *getdcfg(name, sysid) char *name; int sysid; { char *p, **cp; if ((cp = finddcfg(name, sysid)) == (char **) 0) p = (char *) 0; else { p = tet_equindex(*cp); ASSERT(p); p++; } TRACE4(tet_Ttcc, 10, "getdcfg(\"%s\", %s) returns %s", name, tet_i2a(sysid), p ? p : "NULL"); return(p); } /* ** putdcfg() - add/update a distributed configuration variable ** for a particular system ** ** this function must not be called before the per-system ** distributed configurations have been set up */ void putdcfg(name, sysid, value) char *name, *value; int sysid; { struct cflist *lp; struct systab *sp; char buf[MAXPATH + 40]; ASSERT_LITE(sysid == 0); sp = syfind(sysid); ASSERT(sp); ASSERT(IS_CFSETUP(sp, CONF_DIST)); lp = &CFLIST(sp, CONF_DIST); (void) sprintf(buf, "%s=%.*s", name, (int) sizeof buf - (int) strlen(name) - 2, value); proccfl2(buf, lp); } /* ** getmcflag() - get the value of a configuration variable from the ** specified master configuration list whose value can be ** True or False ** ** return 1 if the variable is set and its value is True, 0 otherwise ** ** it is assumed that all flags default to False if unset ** (currently this is the case) */ int getmcflag(name, mode) char *name; int mode; { register char *p; if ((p = getmcfg(name, mode)) == (char *) 0) return(0); return(cflag2bool(name, p)); } /* ** getcflag() - get the value of a configuration variable from the ** per-system configuration for the specified mode ** whose value can be True or False ** ** return 1 if the variable is set and its value is True, 0 otherwise ** ** it is assumed that all flags default to False if unset ** (currently this is the case) */ int getcflag(name, sysid, mode) char *name; int sysid, mode; { register char *p; ASSERT_LITE(sysid == 0); if ((p = getcfg(name, sysid, mode)) == (char *) 0) return(0); return(cflag2bool(name, p)); } /* ** findcfg() - find a config list entry in a configuration list ** for the specified name and return a pointer to the ** entry's address ** ** name may be delimited either by '=' or '\0' ** ** return (char **) 0 if no match can be found */ static char **findcfg(name, lp) char *name; register struct cflist *lp; { register int len; register char **cp, *p; /* make len equal to the number of bytes in the name */ if ((p = tet_equindex(name)) == (char *) 0) len = strlen(name); else len = p - name; /* ** search the list for an entry whose initial part matches the ** specified name */ for (cp = lp->cf_conf; cp < lp->cf_conf + lp->cf_nconf; cp++) if (*cp && !strncmp(*cp, name, len) && *(*cp + len) == '=') return(cp); return((char **) 0); } /* ** finddcfg() - find a config variable assignment for name on sysid ** in the distributed configuration and return a pointer ** to the entry's address ** ** name may be delimited either by '=' or '\0' ** ** return (char **) 0 if no match can be found ** ** this function must not be called before the per-system distributed ** configurations have been set up */ static char **finddcfg(name, sysid) char *name; int sysid; { struct systab *sp; char **cp; ASSERT(sysid >= 0); ASSERT_LITE(sysid == 0); ASSERT(tet_remvar_sysid(name) == -1); /* ** first look for a defined distributed variable in the per-system ** distributed configuration */ if ( is_dist_var(name) && (sp = syfind(sysid)) != (struct systab *) 0 ) { ASSERT(IS_CFSETUP(sp, CONF_DIST)); if ((cp = findcfg(name, &CFLIST(sp, CONF_DIST))) != (char **) 0) return(cp); } /* ** not found ** if sysid > 0, look for the variable in ** the master distributed configuration ** ** we don't look for system 0 variables in the master config - ** this is because distributed variables for system 0 are are derived ** from environment variables and we don't want an undadorned variable ** in tetdist.cfg supplying a default value when the corresponding ** environment variable has not been specified on the local system */ return(sysid > 0 ? findcfg(name, &MCFLIST(CONF_DIST)) : (char **) 0); } /************************************************************************ * * * miscellaneous functions * * * ************************************************************************/ /* ** tcc2cfmode() - convert a mode of operation to a config mode ** ** returns mode if mode is not a valid tcc mode of operation */ int tcc2cfmode(mode) int mode; { switch (mode) { case TCC_BUILD: return(CONF_BUILD); case TCC_EXEC: return(CONF_EXEC); case TCC_CLEAN: return(CONF_CLEAN); case CONF_BUILD: case CONF_EXEC: case CONF_CLEAN: case CONF_DIST: return(mode); default: /* this "can't happen" */ fatal(0, "unexpected mode", tet_i2o(mode)); /* NOTREACHED */ return(mode); } } /* ** prcfmode() - return printable representation of a configuration ** mode */ char *prcfmode(mode) int mode; { static char text[] = "conf-mode "; static char msg[sizeof text + LNUMSZ]; switch (mode) { case CONF_BUILD: return("BUILD"); case CONF_EXEC: return("EXEC"); case CONF_CLEAN: return("CLEAN"); case CONF_DIST: return("DIST"); default: (void) sprintf(msg, "%s%d", text, mode); return(msg); } } /* ** cflag2bool() - return the boolean value of a string which should ** either be "True" or "False" */ static int cflag2bool(name, val) char *name, *val; { switch (*val) { case 'T': case 't': return(1); case 'F': case 'f': break; default: error(0, name, "variable has ambiguous value - False assumed"); break; } return(0); } #ifdef TET_LITE /* -LITE-CUT-LINE- */ /* ** tet_config_putenv() - set up the value of the TET_CONFIG environment ** variable which corresponds to the current mode of operation ** ** return 0 if successful or -1 on error */ int tet_config_putenv(opmode) int opmode; { static int currmode = -1; static char *var; static int lvar; static char envname[] = "TET_CONFIG"; int cfmode; cfmode = tcc2cfmode(opmode); ASSERT(CONF_MODE_OK(cfmode, cfname)); /* return now if the current value has already been set */ if (cfmode == currmode) return(0); /* ** here to change the setting of TET_CONFIG in the environment ** ** ensure that the environment string is big enough, ** then format the variable */ ASSERT(TCFNAME(cfmode)); if (BUFCHK(&var, &lvar, (int) (sizeof envname + strlen(TCFNAME(cfmode)) + 1)) < 0) return(-1); (void) sprintf(var, "%s=%s", envname, TCFNAME(cfmode)); /* put the variable in the environment */ if (tet_putenv(var) < 0) return(-1); /* all ok so remember the current mode and return success */ currmode = cfmode; return(0); } #endif /* TET_LITE */ /* -LITE-CUT-LINE- */ /* ** confgiveup() - exit after finding configuration errors */ static void confgiveup() { (void) fprintf(stderr, "%s: giving up after %d configuration error%s\n", tet_progname, conferrors, conferrors == 1 ? "" : "s"); tcc_exit(1); } /* ** config_cleanup() - remove temporary config files before exit */ void config_cleanup() { #ifdef TET_LITE /* -LITE-CUT-LINE- */ char **fname; for (fname = tcfname; fname < &tcfname[Ntcfname]; fname++) if (*fname) (void) UNLINK(*fname); #else /* -START-LITE-CUT- */ if (ecfname) (void) UNLINK(ecfname); if (dcfname) (void) UNLINK(dcfname); if (ccfname) (void) UNLINK(ccfname); #endif /* TET_LITE */ /* -END-LITE-CUT- */ } /* ** get_runtime_tsroot() - return the name of the test suite root ** directory to be used at runtime on a particular system ** ** this function should not be called before distcfg() has set ** up the distributed configurations for each system ** ** the value returned by this function may not be useful before any ** required runtime copy operations have been performed ** (see the calls to rtlcopy() and rtrcopy() in tcc.c) */ char *get_runtime_tsroot(sysid) int sysid; { char *retval, *rtdir, *tsroot; static char rtsroot[MAXPATH]; /* ** if there is no TET_RUN, just return the system's TET_TSROOT; ** otherwise we must construct the name of the tsroot directory ** below TET_RUN and return that */ tsroot = getdcfg("TET_TSROOT", sysid); ASSERT(tsroot); if ((rtdir = getdcfg("TET_RUN", sysid)) == (char *) 0) retval = tsroot; else { fullpath(rtdir, tet_basename(tsroot), rtsroot, sizeof rtsroot, sysid == 0 ? 0 : 1); retval = rtsroot; } TRACE3(tet_Ttcc, 10, "get_runtime_tsroot(%s) returns %s", tet_i2a(sysid), retval); return(retval); } /* ** initdvar() - initialise elements of the dvar array */ static void initdvar() { register struct dvar *dvp = dvar; static int been_here; if (been_here) return; else been_here = 1; /* ** set up dvar first time through - ** the number and order of these assignments must ** correspond to the variables in the dvar array */ (dvp++)->dv_value = tet_root; (dvp++)->dv_value = tet_exec_root; (dvp++)->dv_value = tet_suite_root; (dvp++)->dv_value = tet_tsname; (dvp++)->dv_value = tet_tsroot; (dvp++)->dv_value = tet_execute; (dvp++)->dv_value = tet_tmp_dir; (dvp++)->dv_value = tet_run; for (dvp = dvar; dvp < dvar + Ndvar; dvp++) dvp->dv_len = strlen(dvp->dv_name); } /* ** initmdvar() - initialise elements of the mdvar array */ static void initmdvar() { register struct dvar *dvp = mdvar; static int been_here; if (been_here) return; else been_here = 1; /* set up mdvar first time through - */ for (dvp = mdvar; dvp < mdvar + Nmdvar; dvp++) dvp->dv_len = strlen(dvp->dv_name); }