/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/tools/tcc/options.c,v 1.23 2005/10/22 16:50:14 stefanf Exp $
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "cstring.h"
#include "fmm.h"
#include "msgcat.h"
#include "tenapp.h"
#include "filename.h"
#include "list.h"
#include "archive.h"
#include "environ.h"
#include "flags.h"
#include "main.h"
#include "options.h"
#include "startup.h"
#include "suffix.h"
#include "utility.h"
/*
* THE LIST OF ALL INPUT FILES
*
* All input files found by process_options are added to this list.
*/
filename *input_files = null;
/*
* THE MAIN OPTION MAP
*
* This table gives all the command-line options. The first entry is
* the 'match in' and roughly corresponds to command line input. The
* second entry is the 'match out' and provides instructions to the
* command line interpreter. The third entry is a description. The
* the final entry is a relative ranking, with lower numbers being
* higher priority. Thus, no matter what order the command line
* options are given, the lower ranked option will always be
* evaluated first. (Option rankings can be disabled with the
* -no_shuffle option.)
*/
optmap main_optmap [] = {
/* Common options */
{"^-$", "I?$0", "specifies an input file" , 1000},
{"-o+$", "SFN$1", "specifies output file name", 100},
{"-D+$=$", "D#define$s$1$s$2$n|AOC$0", "defines a macro", 101},
{"-D+$", "D#define$s$1$s1$n|AOC$0", "defines a macro to be 1", 102},
{"-F?", "H$1", "halts the compilation at the given stage", 103},
{"-I+$", "AUI$0|AOC$0|AUP$0", "specifies an include file directory", 104},
{"-L+$", "1ON|Io$0|AUL$0", "specifies a system library directory", 105},
{"-N+$:+$", "AUI$0|AOC-I$2",
"specifies an include file directory (with mode)", 106},
{"-O$", "1OP|AOC$0", "switches optimisation level", 107},
{"-P?*", "K$1", "causes intermediate files to be preserved", 108},
{"-S", "Hs", "halts compilation after creating .s files", 109},
{"-T+$", "CAT:$1|AOC-D$1", "specifies a command-line token", 110},
{"-U+$", "D#undef$s$1$n|AOC$0", "undefines a macro", 111},
{"-W?,+$,+*", "AQ$1$2", "passed an option to a compilation tool", 112},
{"-W?:+$", "AQ$1$2", "passed an option to a compilation tool", 113},
{"-X:+$,+*", "CAP:$1", "specifies a compilation option", 114},
{"-X$", "EX$1", "specifies a compilation mode", 115},
{"-Y+$", "E$1", "reads an environment", 12},
{"-copyright", "1CR", "outputs copyright notice", 117},
{"-c", "Ho", "halts compilation after creating .o files", 118},
{"-d", "Hd", "halts compilation after creating .d files", 119},
{"-dry", "1VB|1DR", "causes a dry run", 120},
{"-e+$", "AUe$0", "specifies a producer end-up file", 121},
{"-f+$", "AUf$0", "specifies a producer start-up file", 122},
{"-g", "1DG", "produces debugging information (default format)", 123},
{"-g1", "1DG", "produces debugging information (old format)", 124},
{"-g2", "2DG", "produces debugging information (new format)", 125},
{"-g3", "3DG", "produces debugging information (new format)", 126},
{"-k", "Hk|HK", "halts compilation after creating .k and .K files", 128},
{"-l+$", "1ON|Io$0|AUl$0", "specifies a system library", 129},
{"-not_ansi", "ASs-fnot_ansi", "allows some non-ANSI C features", 130},
{"-nepc", "1NE|ASs-fnepc", "switches off extra portability checks", 131},
{"-sym", "1CS|SDO-d=", "enables symbol table dump linking", 132},
{"-sym:$", "1CS|SDO-d$1=", "enables symbol table dump linking with flags", 133},
{"-v", "1VB", "specifies verbose mode", 50},
{"-vb", "1TC", "specifies fairly verbose mode", 51},
{"-vt", "1TT", "verbose information about tool chain invocation", 51},
{"-ve", "1TE", "verbose information about tool chain environment", 51},
{"-no_shuffle", "1ES", "turns off shuffle ranking of cmd line args", -1},
{"-y+$=$", "E?$1=$2", "sets an env directory variable", 1},
/* Options not allowed in checker */
{"-J+$", "AUJ-L$1", "specifies a TDF library directory", 20},
{"-M", "1MC", "causes all .j files to be merged", 133},
{"-MA", "1MC|1MP", "as -M, but with hiding",134},
{"-ch", "1CH", "invokes checker mode", 135},
{"-disp", "1PP", "causes all .j files to be pretty printed", 136},
{"-disp_t", "2PP", "causes all .t files to be pretty printed", 137},
{"-i", "Hj", "halts compilation after creating .j files", 138},
{"-im", "1CS", "enables intermodular checks", 139},
{"-j+$", "AUj-l$1", "specifies a TDF library", 30},
{"-prod", "1AR", "causes a TDF archive to be produced", 141},
/* Less common options */
{"-A+-", "AOC$0", "unasserts all built-in predicates", 142},
{"-A+$", "D#pragma$saccept$sdirective$sassert$n|D#assert$s$1$n|AOC$0",
"asserts a predicate", 143},
{"-B+$", "1ON|Io$0", "passed to the system linker", 144},
{"-C", "AOC$0", "preserves comments when preprocessing", 30},
{"-E", "1PR", "causes C source files to be preprocessed", 145},
{"-P", "1PR|Ki|KI", "halts compilation after creating .i and .I files", 145},
{"-E?:+$", "LE$1$2", "specifies an executable to be used", 146},
{"-H", "1PI", "causes included files to be printed", 147},
{"-S?,+$,+*", "I$1$2", "specifies input files", 15},
{"-S?:+$", "I$1$2", "specifies input files", 15},
{"-V", "EVersions", "causes all tools to print their version numbers", 21},
{"-cc", "1CC", "forces the system compiler to be used", 23},
{"-cc_only", "2CC", "forces only the system compiler to be used", 22},
{"-do?+$", "SN$1$2", "sets a default output file name", 148},
{"-dump", "CDS", "dumps the current status", 200},
{"-ignore_case", "1LC", "ignores case in file names", 12},
{"-im0", "2CS", "disables intermodular checks", 148},
{"-info", "1SA", "causes API information to be printed", 201},
{"-keep_errors", "1KE", "causes files to be preserved on errors",13},
{"-make_up_names", "1MN", "makes up names for intermediate files", 149},
{"-message+$", "@X$1", "causes %s to print a message", 24},
{"-p", "1PF", "causes profiling information to be produced", 31},
{"-q", "0VB", "specifies quiet mode", 1},
{"-quiet", "0VB", "equivalent to -q", 1},
{"-query", "Q", "causes a list of options to be printed", 2},
{"-s?:+$", "SS$1$2|1SO", "specifies a suffix override", 25},
{"-show_errors", "1SE", "causes error producing commands to be shown", 3},
{"-show_env", "CSE", "causes the environment path to be printed", 202},
{"-special+$", "CSP:$1", "allows various internal options", 4},
{"-startup+$", "@D$1$n", "specifies a start-up option", 5},
{"-target+$", "AOC-target|AOC$1", "provided for cc compatibility", 150},
{"-temp+$", "STD$1", "specifies the temporary directory", 1},
{"-tidy", "1TU", "causes %s to tidy up as it goes along", 152},
{"-time", "1TI|1VB", "causes all commands to be timed", 153},
{"-verbose", "1VB", "equivalent to -v", 1},
{"-version", "CPV", "causes %s to print its version number", 1},
{"-w", "0WA", "suppresses %s warnings", 1},
{"-work+$", "SWD$1", "specifies the work directory",1},
/* Options not allowed in checker */
{"-G", "EGoption", "provided for cc compatibility", 154},
{"-K+$,+*", "EK-$1", "provided for cc compatibility", 155},
{"-Z$", "EZ-$1", "provided for cc compatibility", 156},
{"-b", "LSc", "suppresses -lc in system linking", 157},
{"-dn", "AOl$0", "passed to the system linker", 158},
{"-dy", "AOl$0", "passed to the system linker", 159},
{"-h+$", "AOl$0", "passed to the system linker", 160},
{"-no_startup_options", "0ST", "suppresses start-up options", 161},
{"-s", "1SR", "passed to the system linker", 162},
{"-u+$", "AOl$0", "passed to the system linker", 163},
{"-wsl", "Ewsl", "causes string literals to be made writable", 164},
{"-z+$", "AOl$0", "passed to the system linker", 165},
{"-#", "1VB", "equivalent to -v", 1},
{"-##", "1VB", "equivalent to -v",1 },
{"-###", "1VB|1DR", "equivalent to -dry", 1},
/* Default options */
{"--+$,+*", "$1", "communicates directly with the option interpreter", 0},
{"$", "XUnknown option,$s$0|AXO$0", "unknown option", 0},
/* End marker */
{ null, null, null, 0 }
};
/*
* THE ENVIRONMENT OPTION MAP
*
* This table gives all the environment options. It needs to be kept
* in line with Table 4.
*/
optmap environ_optmap [] = {
/* Options */
{"\\+FLAG $", "=$1", null, 0},
{"\\+FLAG_TDFC $", "AOc$1", null, 0},
{"\\+FLAG_TDFCPP $", "AOp$1", null, 0},
{"\\+FLAG_TCPPLUS $", "AOx$1", null, 0},
{"\\+FLAG_TCPPLUSPP $", "AOg$1", null, 0},
{"\\+FLAG_TOT_CAT $", "", null, 0},
{"\\+FLAG_TLD $", "AOL$1", null, 0},
{"\\+FLAG_TRANS $", "AOt$1", null, 0},
{"\\+FLAG_AS $", "AOa$1", null, 0},
{"\\+FLAG_DYN_LINK $", "AOD$1", null, 0},
{"\\+FLAG_LD $", "AOl$1", null, 0},
{"\\+FLAG_DISP $", "AOd$1", null, 0},
{"\\+FLAG_TNC $", "AOn$1", null, 0},
{"\\+FLAG_PL_TDF $", "AOP$1", null, 0},
{"\\+FLAG_AS1 $", "AOA$1", null, 0},
{"\\+FLAG_TDFOPT $", "", null, 0},
{"\\+FLAG_SPEC_LINK $", "AOs$1", null, 0},
{"\\+FLAG_CPP_SPEC_LINK $", "AOS$1", null, 0},
{"\\+FLAG_DUMP_ANAL $", "AOe$1", null, 0},
{"\\+FLAG_DUMP_LINK $", "AOu$1", null, 0},
{"\\+FLAG_CC $", "AOC$1", null, 0},
{"\\+FLAG_INSTALL $", "AOI$1", null, 0},
/* Additional filename suffixes */
{"\\+SUFFIX_CPP $", "SSC$1|1SO", null, 0},
/* Executables */
{"?AS $", "$1Ea$2", null, 0},
{"?AS1 $", "$1EA$2", null, 0},
{"?BUILD_ARCH $", "$1BB$2", null, 0},
{"?CAT $", "$1BC$2", null, 0},
{"?CC $", "$1EC$2", null, 0},
{"?CPP_SPEC_LINK $", "$1ES$2", null, 0},
{"?DISP $", "$1Ed$2", null, 0},
{"?DUMP_ANAL $", "$1Ee$2", null, 0},
{"?DUMP_LINK $", "$1Eu$2", null, 0},
{"?DYN_LINK $", "$1ED$2", null, 0},
{"?LD $", "$1El$2", null, 0},
{"?MKDIR $", "$1BD$2", null, 0},
{"?MOVE $", "$1BM$2", null, 0},
{"?PL_TDF $", "$1EP$2", null, 0},
{"?REMOVE $", "$1BR$2", null, 0},
{"?SPEC_LINK $", "$1Es$2", null, 0},
{"?SPLIT_ARCH $", "$1BS$2", null, 0},
{"?TCPPLUS $", "$1Ex$2", null, 0},
{"?TCPPLUSPP $", "$1Eg$2", null, 0},
{"?TDFC $", "$1Ec$2", null, 0},
{"?TDFCPP $", "$1Ep$2", null, 0},
{"?TDFOPT $", "", null, 0},
{"?TLD $", "$1EL$2", null, 0},
{"?TNC $", "$1En$2", null, 0},
{"?TOUCH $", "$1BT$2", null, 0},
{"?TRANS $", "$1Et$2", null, 0},
/*
* Set special env file variables.
* These must be kept in sync with Table 5 in utility.c.
*/
{"$TENDRA_MACHDIR $", "CTV:0$2", null, 0},
{"$TENDRA_BINDIR $", "CTV:1$2", null, 0},
{"$TENDRA_ENVDIR $", "CTV:2$2", null, 0},
{"$TENDRA_LIBDIR $", "CTV:3$2", null, 0},
{"$TENDRA_INCLDIR $", "CTV:4$2", null, 0},
{"$TENDRA_STARTUPDIR $", "CTV:5$2", null, 0},
{"$TENDRA_TMPDIR $", "CTV:6$2", null, 0},
{"$TENDRA_BASEDIR $", "CTV:7$2", null, 0},
/* Flags */
{"?API $", "", null, 0},
{"?API_NAME $", "", null, 0},
{"?INCL $", "$1SI$2", null, 0},
{"?INCL_CPP $", "$1Si$2", null, 0},
{"?STARTUP_DIR $", "$1Sd$2", null, 0},
{"?STARTUP $", "$1Ss$2", null, 0},
{"?STARTUP_CPP_DIR $", "$1SD$2", null, 0},
{"?STARTUP_CPP $", "$1SS$2", null, 0},
{"?PORTABILITY $", "$1SP$2", null, 0},
{"?LINK $", "$1SJ$2", null, 0},
{"?LIB $", "$1Sj$2", null, 0},
{"?CRT0 $", "$1S0$2", null, 0},
{"?CRT1 $", "$1S1$2", null, 0},
{"?CRTN $", "$1S2$2", null, 0},
{"?CRTP_N $", "$1S3$2", null, 0},
{"?CRTI $", "$1S4$2", null, 0},
{"?SYS_LINK $", "$1SL$2", null, 0},
{"?SYS_LIB $", "$1Sl$2", null, 0},
{"?SYS_LIBC $", "$1Sc$2", null, 0},
{"?LINK_ENTRY $", "$1Se$2", null, 0},
/* Startup and endup lines */
{"\\+COMP_OPTION $", "@CAP:$1", null, 0},
{"\\+LINE_START $", "@D$1$n", null, 0},
{"\\+LINE_END $", "@F$1$n", null, 0},
/* Miscellaneous */
{"\\+INFO $", "@SAI$1", null, 0},
{">INFO $", "@SAI$SAI@plus@$1", null, 0},
{"<INFO $", "@SAI$1@plus@$SAI", null, 0},
{"\\+ENVDIR $", "SED$1|CFE", null, 0},
{"\\?ENVDIR $", "?:ED$1", null, 0},
{"\\+MACHINE $", "SMN$1|CSM", null, 0},
{"\\?MACHINE $", "?:MN$1", null, 0},
{"\\+SUFFIX $", "STD$1", null, 0}, /* this MUST be wrong !!! */
{"\\+TEMP $", "STD$1", null, 0},
{"\\?TEMP $", "?:TD$1", null, 0},
{"\\+VERSION $", "SVF$1", null, 0},
{"\\?VERSION $", "?:VF$1", null, 0},
/* Errors */
{"\\+E$ $", "X+E$soptions$sno$slonger$ssupported", null, 0},
{"$ $", "XUnknown$senvironmental$svariable,$s$1", null, 0},
{"$", "XIllegal$senvironmental$soption,$s$0", null, 0},
/* End marker */
{ null, null, null, 0 }
};
/*
* OPTION INTERPRETER DEBUGGING FLAG
*
* This flag indicates whether option interpreter debugging information
* should be printed.
*/
static boolean debug_options = 0;
/*
* ROUTINE IMPLEMENTING THE -SPECIAL OPTION
*
* This routine enables certain internal options depending on the
* value of the argument.
*/
static void
special_option(char *arg)
{
boolean b = 1;
char *s = arg;
if (strneq (s, "no_", 3)) {
b = 0;
s += 3;
}
if (streq (s, "cpp")) {
allow_cpp = b;
} else if (streq (s, "tnc") && !checker) {
allow_notation = b;
} else if (streq (s, "pl_tdf") && !checker) {
allow_pl_tdf = b;
} else if (streq (s, "c_spec") || streq (s, "cpp_spec")) {
allow_specs = b;
} else if (streq (s, "dump") || streq (s, "cpp_dump")) {
allow_specs = b;
if (dump_opts == null)
dump_opts = "-d=";
} else {
MSG_unknown_special_option (s);
}
return;
}
/*
* CONVERT A TWO LETTER CODE INTO A BOOLEAN
*
* This routine takes a two letter code, s, and returns a pointer to
* the corresponding boolean variable.
*/
static boolean *
lookup_bool(char *s)
{
char a = s [0];
char b = 0;
if (a) b = s [1];
switch (a) {
case 'A' : {
if (b == 'A') return (&use_alpha_assembler);
if (b == 'R') return (&make_archive);
if (b == 'S') return (&use_assembler);
break;
}
case 'C' : {
if (b == 'C') return (&use_system_cc);
if (b == 'H') return (&checker);
if (b == 'R') return (©right);
if (b == 'S') return (&allow_specs);
break;
}
case 'D' : {
if (b == 'B') return (&debug_options);
if (b == 'G') return (&flag_diag);
if (b == 'L') return (&use_dynlink);
if (b == 'R') return (&dry_run);
break;
}
case 'E': {
if (b == 'S') return &no_shuffle;
break;
}
case 'H' : {
if (b == 'L') return (&use_hp_linker);
break;
}
case 'K' : {
if (b == 'E') return (&flag_keep_err);
break;
}
case 'L' : {
if (b == 'C') return (&case_insensitive);
if (b == 'S') return (&link_specs);
break;
}
case 'M' : {
if (b == 'A') return (&use_mips_assembler);
if (b == 'C') return (&make_complex);
if (b == 'N') return (&make_up_names);
if (b == 'P') return (&flag_merge_all);
break;
}
case 'N' : {
if (b == 'E') return (&flag_nepc);
break;
}
case 'O' : {
if (b == 'N') return (&option_next);
if (b == 'P') return (&flag_optim);
break;
}
case 'P' : {
if (b == 'F') return (&flag_prof);
if (b == 'I') return (&flag_incl);
if (b == 'L') return (&allow_pl_tdf);
if (b == 'P') return (&make_pretty);
if (b == 'R') return (&make_preproc);
break;
}
case 'S' : {
if (b == 'A') return (&show_api);
if (b == 'C') return (&use_sparc_cc);
if (b == 'E') return (&show_errors);
if (b == 'O') return (&suffix_overrides);
if (b == 'R') return (&flag_strip);
if (b == 'T') return (&flag_startup);
break;
}
case 'T' : {
if (b == 'C') return (&taciturn);
if (b == 'T') return (&tool_chain);
if (b == 'E') return (&tool_chain_environ);
if (b == 'I') return (&time_commands);
if (b == 'N') return (&allow_notation);
if (b == 'S') return (&make_tspec);
if (b == 'U') return (&tidy_up);
break;
}
case 'V' : {
if (b == 'B') return (&verbose);
break;
}
case 'W' : {
if (b == 'A') return (&warnings);
break;
}
}
MSG_unknown_boolean_identifier (a, b);
return (null);
}
/*
* CONVERT A TWO LETTER CODE INTO A LIST
*
* This routine takes a two letter code, s, and returns a pointer to
* the corresponding list variable. This routine needs to be kept in
* step with Table 4.
*/
static list **
lookup_list(char *s)
{
char a = s [0];
char b = 0;
if (a)
b = s [1];
switch (a) {
case 'B' : {
switch (b) {
case 'B' : return (&exec_build_arch);
case 'C' : return (&exec_cat);
case 'D' : return (&exec_mkdir);
case 'M' : return (&exec_move);
case 'R' : return (&exec_remove);
case 'S' : return (&exec_split_arch);
case 'T' : return (&exec_touch);
}
break;
}
case 'E' : {
switch (b) {
case PRODUCE_ID : return (&exec_produce);
case PREPROC_ID : return (&exec_preproc);
case CPP_PRODUCE_ID : return (&exec_cpp_produce);
case CPP_PREPROC_ID : return (&exec_cpp_preproc);
case TDF_LINK_ID : return (&exec_tdf_link);
case TRANSLATE_ID : return (&exec_translate);
case ASSEMBLE_ID : return (&exec_assemble);
case LINK_ID : return (&exec_link);
case PRETTY_ID : return (&exec_pretty);
case NOTATION_ID : return (&exec_notation);
case PL_TDF_ID : return (&exec_pl_tdf);
case ASSEMBLE_MIPS_ID : return (&exec_assemble_mips);
case SPEC_LINK_ID : return (&exec_spec_link);
case CPP_SPEC_LINK_ID : return (&exec_cpp_spec_link);
case CC_ID : return (&exec_cc);
case DYNLINK_ID : return (&exec_dynlink);
case DUMP_ANAL_ID : return (&exec_dump_anal);
case DUMP_LINK_ID : return (&exec_dump_link);
}
MSG_unknown_compilation_stage (b);
return (null);
}
case 'Q' : {
if (checker) {
switch (b) {
case PRODUCE_ID : return (&opt_produce);
case CPP_PRODUCE_ID : return (&opt_cpp_produce);
case SPEC_LINK_ID : return (&opt_spec_link);
case CPP_SPEC_LINK_ID : return (&opt_cpp_spec_link);
case ARCHIVER_ID : return (&opt_joiner);
case CC_ID : return (&opt_cc);
}
MSG_unknown_compilation_stage (b);
return (null);
}
goto case_O;
}
case 'O' :
case_O :{
switch (b) {
case PRODUCE_ID : return (&opt_produce);
case PREPROC_ID : return (&opt_preproc);
case CPP_PRODUCE_ID : return (&opt_cpp_produce);
case CPP_PREPROC_ID : return (&opt_cpp_preproc);
case TDF_LINK_ID : return (&opt_tdf_link);
case TRANSLATE_ID : return (&opt_translate);
case ASSEMBLE_ID : return (&opt_assemble);
case DYNLINK_ID : return (&opt_dynlink);
case LINK_ID : return (&opt_link);
case PRETTY_ID : return (&opt_pretty);
case NOTATION_ID : return (&opt_notation);
case PL_TDF_ID : return (&opt_pl_tdf);
case ASSEMBLE_MIPS_ID : return (&opt_assemble_mips);
case SPEC_LINK_ID : return (&opt_spec_link);
case CPP_SPEC_LINK_ID : return (&opt_cpp_spec_link);
case INSTALL_ID : return (&opt_archive);
case ARCHIVER_ID : return (&opt_joiner);
case CC_ID : return (&opt_cc);
case DUMP_ANAL_ID : return (&opt_dump_anal);
case DUMP_LINK_ID : return (&opt_dump_link);
}
MSG_unknown_compilation_stage (b);
return (null);
}
case 'S' : {
switch (b) {
case 'I' : return (&std_prod_incldirs);
case 'P' : return (&std_prod_portfile);
case 'd' : return (&std_prod_startdirs);
case 's' : return (&std_prod_startup);
case 'i' : return (&std_cpp_prod_incldirs);
case 'D' : return (&std_cpp_prod_startdirs);
case 'S' : return (&std_cpp_prod_startup);
case 'J' : return (&std_tdf_link_libdirs);
case 'j' : return (&std_tdf_link_libs);
case '0' : return (&std_link_crt0);
case '1' : return (&std_link_crt1);
case '2' : return (&std_link_crtn);
case '3' : return (&std_link_crtp_n);
case '4' : return (&std_link_crti);
case 'L' : return (&std_link_libdirs);
case 'l' : return (&std_link_libs);
case 'c' : return (&std_link_c_libs);
case 'e' : return (&std_link_entry);
}
break;
}
case 'U' : {
switch (b) {
case 'I' : return (&usr_prod_incldirs);
case 's' : return (&usr_prod_startup);
case 'e' : return (&usr_prod_eoptions);
case 'f' : return (&usr_prod_foptions);
case 'P' : return (&usr_pl_tdf_incldirs);
case 'J' : return (&usr_tdf_link_libdirs);
case 'j' : return (&usr_tdf_link_libs);
case 'L' : return (&usr_link_libdirs);
case 'l' : return (&usr_link_libs);
}
break;
}
case 'X' : {
switch (b) {
case 'O' : return (&opt_unknown);
}
break;
}
}
MSG_unknown_list_identifier (a, b);
return (null);
}
/*
* CONVERT A TWO LETTER CODE INTO A STRING
*
* This routine takes a two letter code, s, and returns a pointer to
* the corresponding string variable.
*/
static char **
lookup_string(char *s)
{
char a = s [0];
char b = 0;
if (a)
b = s [1];
if (a == 'N') {
switch (b) {
case INDEP_TDF_KEY : return (&name_j_file);
case C_SPEC_KEY : return (&name_k_file);
case CPP_SPEC_KEY : return (&name_K_file);
case STARTUP_FILE_KEY : return (&name_h_file);
case PRETTY_TDF_KEY : return (&name_p_file);
}
MSG_unknown_output_file_specifier (b);
return (null);
}
if (a == 'S') {
int t;
t = find_type (b, 0);
return (suffixes + t);
}
if (a == 'A' && b == 'I') return (&api_info);
if (a == 'A' && b == 'O') return (&api_output);
if (a == 'D' && b == 'O') return (&dump_opts);
if (a == 'E' && b == 'D') return (&environ_dir);
if (a == 'F' && b == 'N') return (&final_name);
if (a == 'M' && b == 'N') return (&machine_name);
if (a == 'P' && b == 'N') return (char **)(&progname);
if (a == 'T' && b == 'D') return (&temporary_dir);
if (a == 'V' && b == 'F') return (&version_flag);
if (a == 'W' && b == 'D') return (&workdir);
MSG_unknown_string_identifier (a, b);
return (null);
}
/*
* DUMMY WRAPPER PROCEDURES
*
* These routine(s) are used to call procedures without an argument.
*/
static void
find_envpath_aux(char *dummy)
{
UNUSED (dummy);
find_envpath ();
}
/*
* CONVERT A TWO LETTER CODE INTO A PROCEDURE
*
* This routine takes a two letter code, s, and returns a pointer to
* the corresponding procedure.
*/
typedef void (*proc)(char *);
static proc
lookup_proc(char *s)
{
char a = s [0];
char b = 0;
if (a) b = s [1];
if (a == 'A' && b == 'P') return (add_pragma);
if (a == 'A' && b == 'T') return (add_token);
if (a == 'F' && b == 'E') return (find_envpath_aux);
if (a == 'P' && b == 'V') return (print_version);
if (a == 'S' && b == 'E') return (show_envpath);
if (a == 'S' && b == 'M') return (set_machine);
if (a == 'S' && b == 'P') return (special_option);
if (a == 'T' && b == 'V') return (set_tendra_var_env);
MSG_unknown_procedure_identifier (a, b);
return (null);
}
/*
* RETURN VALUES FOR MATCH_OPTION
*
* These values are returned by match_option. MATCH_OK means that the
* option matches, MATCH_MORE means that it may match with an additional
* option, MATCH_FAILED means it does not match, and the other values
* indicate errors of some kind.
*/
#define MATCH_OK 0
#define MATCH_MORE 1
#define MATCH_FAILED 2
#define MATCH_IN_ERR 3
#define MATCH_OUT_ERR 4
#define MATCH_OPT_ERR 5
/*
* MATCH AN OPTION
*/
static int
match_option(char *in, char *out, char *opt, args_out *res)
{
char *p = in;
char *q = opt;
int i, a, v = 1;
int go = 1, loop = 1, loopv = -1;
struct {
char *txt;
int len;
} var [max_var];
/* Process input */
while (*p && go) {
if (*p == '$') {
char c = p [1];
if (c) {
int wraps = 0;
if (p [2] == '+' && p [3] == '*') {
/* List of strings with breaks : $c+* */
wraps = 1;
p++;
}
if (p [2] == '*') {
/* List of strings : $c* */
loop = 0;
loopv = v;
if (p [3])
return (MATCH_IN_ERR);
while (*q) {
int l = 0;
var [v].txt = q;
while (*q && *q != c) {
l++;
q++;
}
var [v].len = l;
if (*q) {
/* Found c */
q++;
if (*q == 0 && wraps)
return (MATCH_MORE);
}
if (++v >= max_var)
return (MATCH_OPT_ERR);
loop++;
}
go = 0;
} else {
/* Terminated string : $c */
int l = 0;
var [v].txt = q;
while (*q != c) {
if (*q == 0)
return (MATCH_FAILED);
l++;
q++;
}
var [v].len = l;
if (++v >= max_var)
return (MATCH_OPT_ERR);
}
} else {
/* Simple string : $ */
int l = (int) strlen (q);
var [v].txt = q;
var [v].len = l;
if (++v >= max_var)
return (MATCH_OPT_ERR);
go = 0;
}
} else if (*p == '?') {
if (p [1] == '*') {
/* List of characters : ?* */
if (p [2])
return (MATCH_IN_ERR);
loop = 0;
loopv = v;
while (*q) {
var [v].txt = q;
var [v].len = 1;
if (++v >= max_var)
return (MATCH_OPT_ERR);
q++;
loop++;
}
if (loop == 0)
return (MATCH_FAILED);
go = 0;
} else {
/* Simple character : ? */
if (*q == 0)
return (MATCH_FAILED);
var [v].txt = q;
var [v].len = 1;
if (++v >= max_var)
return (MATCH_OPT_ERR);
q++;
}
} else if (*p == '+') {
/* Potential break : + */
if (*q == 0)
return (MATCH_MORE);
} else if (*p == '^') {
/* Negated letter */
p++;
if (*p == 0)
return (MATCH_IN_ERR);
if (*p == *q)
return (MATCH_FAILED);
q++;
} else if (*p == '\\') {
/* Escaped letter : \c */
p++;
if (*p == 0)
return (MATCH_IN_ERR);
if (*p != *q)
return (MATCH_FAILED);
q++;
} else {
/* Letter : c */
if (*p != *q)
return (MATCH_FAILED);
q++;
}
p++;
}
/* Check end of option */
if (go && *q)
return (MATCH_FAILED);
/* The first variable is the whole option */
var [0].txt = opt;
var [0].len = (int) strlen (opt);
/* Print output */
a = 0;
for (i = 0; i < loop; i++) {
int count = 0;
char buff [MAX_LINE];
q = buff;
for (p = out; *p && count < MAX_LINE; p++, count++) {
if (*p == '$') {
/* Variable */
int n;
char c = *(++p);
if (c == 's') {
/* $s expands to a space */
*(q++) = ' ';
} else if (c == 'n') {
/* $n expands to a newline */
*(q++) = '\n';
} else if (c >= '0' && c <= '9') {
n = (c - '0');
if (n == loopv) n += i;
if (n < v) {
int l = var [n].len;
IGNORE strncpy (q, var [n].txt, (size_t) l);
q += l;
}
} else if (c == 'B') {
boolean *b = lookup_bool (p + 1);
if (b == null)
return (MATCH_OUT_ERR);
IGNORE sprintf (q, "%d", (int) *b);
while (*q) q++;
p += 2;
} else if (c == 'L') {
list *pt;
list **sp = lookup_list (p + 1);
if (sp == null)
return (MATCH_OUT_ERR);
for (pt = *sp; pt; pt = pt->next) {
int l = (int) strlen (pt->item);
IGNORE strncpy (q, pt->item, (size_t) l);
q += l;
*(q++) = ' ';
}
p += 2;
} else if (c == 'S') {
int l;
char **sp = lookup_string (p + 1);
if (sp == null)
return (MATCH_OUT_ERR);
if (*sp) {
l = (int) strlen (*sp);
IGNORE strncpy (q, *sp, (size_t) l);
q += l;
}
p += 2;
} else {
return (MATCH_OUT_ERR);
}
} else if (*p == '|') {
/* Multiple output */
*q = 0;
res->argv [a] = string_copy (buff);
if (++a >= max_var) return (MATCH_OPT_ERR);
q = buff;
} else if (*p == '\\') {
/* Escaped output character */
if (*(++p) == 0) return (MATCH_OUT_ERR);
*(q++) = *p;
} else {
/* Simple output character */
*(q++) = *p;
}
}
*q = 0;
res->argv [a] = string_copy (buff);
if (++a >= max_var) return (MATCH_OPT_ERR);
}
res->argc = a;
return (MATCH_OK);
}
/*
* INTERPRET AN OPTION COMMAND
*/
static void
interpret_cmd(char *cmd)
{
char c = *cmd;
/* Debugging */
if (debug_options)
MSG_interpreting (cmd);
/* Deal with at-hack */
if (c == '@') {
char *p = string_copy (cmd + 1), *q;
for (q = p; *q; q++) {
if (*q == '@')
*q = ' ';
}
cmd = p;
c = *p;
}
/* Deal with empty strings */
if (c == 0)
return;
/* Digits set values */
if (c >= '0' && c <= '9') {
boolean *b = lookup_bool (cmd + 1);
if (b == null)
return;
*b = (boolean) (c - '0');
return;
}
/* Translations */
if (c == '>') c = 'A';
if (c == '<') c = 'B';
if (c == '+') c = 'L';
/* Deal with list query */
if (c == '?') {
if (cmd [1] == ':') {
char **sp = lookup_string (cmd + 2);
if (sp == null)
return;
comment (1, "%s=\"%s\"\n", cmd + 4, *sp);
} else {
list *p;
list **sp = lookup_list (cmd + 1);
if (sp == null)
return;
comment (1, "%s=\"", cmd + 3);
for (p = *sp; p != null; p = p->next) {
comment (1, "%s", p->item);
if (p->next) comment (1, " ");
}
comment (1, "\"\n");
}
return;
}
/* Deal with equivalences */
if (c == '=') {
list *p = make_list (cmd + 1);
process_options (p, main_optmap, 1);
free_list (p);
return;
}
/* Deal with primitives */
switch (c) {
case 'A' : {
/* Change list */
list **sp = lookup_list (cmd + 1);
if (sp == null)
return;
*sp = add_list (*sp, make_list (cmd + 3));
return;
}
case 'B' : {
/* Change list */
list **sp = lookup_list (cmd + 1);
if (sp == null)
return;
*sp = add_list (make_list (cmd + 3), *sp);
return;
}
case 'L' : {
/* Change list */
list **sp = lookup_list (cmd + 1);
if (sp == null)
return;
free_list (*sp);
*sp = make_list (cmd + 3);
return;
}
case 'C' : {
/* Call */
char *arg = NULL;
proc p = lookup_proc (cmd + 1);
if (p == null)
return;
if (cmd [3] == ':') {
arg = cmd + 4;
}
(*p) (arg);
return;
}
case 'D' : {
/* Startup options */
add_to_startup (cmd + 1);
return;
}
case 'E' : {
/* Environment */
if (cmd [1] == '?') {
#if FS_STAT
struct stat sb;
#endif
char *var = cmd + 2;
char *val = strchr (var, '=');
if (val == NULL)
return;
*val++ = '\0';
/* additional error checking for those platforms supporting stat */
#if FS_STAT
if (stat (val, &sb) == -1) {
MSG_interpret_cmd (val);
}
#endif
if (!set_tendra_var (var, val))
MSG_ignoring_non_standard_env_assignment (var, val);
} else {
read_env (cmd + 1);
}
return;
}
case 'F' : {
/* Endup options */
add_to_endup (cmd + 1);
return;
}
case 'H' : {
/* Halt */
char stage = cmd [1];
set_stage (find_type (stage, 0), STOP_STAGE);
return;
}
case 'I' : {
/* Input file */
int t;
filename *f;
char stage = cmd [1];
char *name = cmd + 2;
if (stage == '?') {
t = UNKNOWN_TYPE;
} else {
t = find_type (stage, 0);
}
f = find_filename (name, t);
input_files = add_filename (input_files, f);
return;
}
case 'K' : {
/* Keep */
static int k = KEEP_STAGE;
char stage = cmd [1];
if (stage == '-') {
k = DONT_KEEP_STAGE;
} else {
set_stage (find_type (stage, 0), k);
k = KEEP_STAGE;
}
return;
}
case 'Q' : {
/* Query */
char *s;
optmap *t = main_optmap;
MSG_list_of_recognised_options ();
while (s = t->in, s != null) {
if (*s == '-') {
char d;
comment (0, " ");
while (d = *(s++), d != 0) {
switch (d) {
case '$' : {
IGNORE fputs ("<string>", stderr);
break;
}
case '?' : {
IGNORE fputs ("<letter>", stderr);
break;
}
case '*' : {
IGNORE fputs ("...", stderr);
break;
}
case '+' : {
IGNORE fputc (' ', stderr);
break;
}
case '\\' : {
IGNORE fputc (*(s++), stderr);
break;
}
default : {
IGNORE fputc (d, stderr);
break;
}
}
}
s = t->explain;
if (s == null)
s = "not documented";
comment (0, " : ");
comment (0, s, progname);
comment (0, ".\n");
}
t++;
}
return;
}
case 'S' : {
/* String */
char **s = lookup_string (cmd + 1);
if (s == null) return;
*s = cmd + 3;
return;
}
case 'V' : {
if (cmd [1] == 'B') {
boolean *b = lookup_bool (cmd + 2);
if (b == null) return;
comment (1, "%c%c = %d\n", cmd [2], cmd [3], *b);
return;
} else if (cmd [1] == 'L') {
list **sp = lookup_list (cmd + 2), *pt;
if (sp == null) return;
comment (1, "%c%c =", cmd [2], cmd [3]);
for (pt = *sp; pt != null; pt = pt->next) {
if (pt->item) {
comment (1, " %s", pt->item);
} else {
comment (1, " (null)");
}
}
comment (1, "\n");
return;
} else if (cmd [1] == 'S') {
char **s = lookup_string (cmd + 2);
if (s == null) return;
if (*s) {
comment (1, "%c%c = %s\n", cmd [2], cmd [3], *s);
} else {
comment (1, "%c%c = (null)\n", cmd [2], cmd [3]);
}
return;
}
break;
}
case 'X' : {
/* Error */
MSG_X_error (cmd + 1);
return;
}
}
MSG_syntax_error (cmd);
return;
}
/*
* PROCESS A LIST OF OPTIONS
*
* This routine processes a list of options, opt, using the options
* from table tab.
*/
void
process_options(list *opt, optmap *tab, int fast)
{
optmap *t;
list *p = opt;
list *accum = null;
char *arg = null;
int status = MATCH_OK;
int a;
/* Scan through the options */
while (p != null) {
if (status == MATCH_MORE) {
arg = string_concat (arg, p->item);
} else {
arg = p->item;
}
status = MATCH_FAILED;
for (t = tab; t->in != null; t++) {
args_out res;
status = match_option (t->in, t->out, arg, &res);
switch (status) {
case MATCH_OK : {
/* Complete option - interpret result */
for (a = 0; a < res.argc; a++) {
if (no_shuffle != 0 || fast == 1) {
interpret_cmd (res.argv[a]);
}
else {
ordered_node* dn = xalloc(sizeof(*dn));
dn->rank = t->rank;
dn->cmd = res.argv[a];
accum = insert_inorder(dn, accum);
}
}
goto end_search;
}
case MATCH_MORE : {
/* Incomplete option - move on to next option */
goto end_search;
}
case MATCH_FAILED : {
/* Try again */
break;
}
case MATCH_IN_ERR : {
/* Error in optmap input */
MSG_illegal_input (t->in);
status = MATCH_FAILED;
break;
}
case MATCH_OUT_ERR : {
/* Error in optmap output */
MSG_illegal_option (t->out);
status = MATCH_FAILED;
break;
}
case MATCH_OPT_ERR : {
/* Ran out of space for result */
MSG_too_many_components (arg);
status = MATCH_FAILED;
break;
}
}
}
MSG_cant_interpret (arg);
end_search :
p = p->next;
}
/* Check for incomplete options */
if (status == MATCH_MORE) {
MSG_option_is_incomplete (arg);
}
/* if the no_shuffle flag is unset, we have order cmds to run */
if (no_shuffle == 0 && fast == 0) {
while (accum) {
ordered_node* dn;
dn = accum->item;
interpret_cmd (dn->cmd);
accum = accum->next;
}
}
return;
}
syntax highlighted by Code2HTML, v. 0.9.1