diff main.c @ 4:ee9a66b87c70

Initial version of toplevel and options handling.
author David A. Holland
date Sun, 19 Dec 2010 17:52:59 -0500
parents
children 7c489c73d62b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.c	Sun Dec 19 17:52:59 2010 -0500
@@ -0,0 +1,914 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+
+#include "inlinedefs.h" // XXX
+#include "version.h"
+#include "config.h"
+#include "utils.h"
+#include "array.h"
+#include "mode.h"
+#include "files.h"
+#include "macro.h"
+
+struct mode mode = {
+	.werror = false,
+
+	.input_allow_dollars = false,
+	.input_tabstop = 8,
+
+	.do_stdinc = true,
+	.do_stddef = true,
+
+	.do_output = true,
+	.output_linenumbers = true,
+	.output_retain_comments = false,
+	.output_file = NULL,
+
+	.do_depend = false,
+	.depend_report_system = false,
+	.depend_assume_generated = false,
+	.depend_issue_fakerules = false,
+	.depend_quote_target = true,
+	.depend_target = NULL,
+	.depend_file = NULL,
+
+	.do_macrolist = false,
+	.macrolist_include_stddef = false,
+	.macrolist_include_expansions = false,
+
+	.do_trace = false,
+	.trace_namesonly = false,
+	.trace_indented = false,
+};
+
+struct warns warns = {
+	.endiflabels = true,
+	.nestcomment = false,
+	.undef = false,
+	.unused = false,
+};
+
+////////////////////////////////////////////////////////////
+// commandline macros
+
+struct commandline_macro {
+	const char *macro;
+	const char *expansion;
+};
+
+static struct array commandline_macros;
+
+static
+void
+commandline_macros_init(void)
+{
+	array_init(&commandline_macros);
+}
+
+static
+void
+commandline_macros_cleanup(void)
+{
+	array_cleanup(&commandline_macros);
+}
+
+static
+void
+commandline_macro_add(const char *macro, const char *expansion)
+{
+	struct commandline_macro *cm;
+
+	cm = domalloc(sizeof(*cm));
+	cm->macro = macro;
+	cm->expansion = expansion;
+}
+
+static
+void
+commandline_def(char *str)
+{
+	char *val;
+
+	val = strchr(str, '=');
+	if (val != NULL) {
+		*val = '\0';
+		val++;
+	}
+	commandline_macro_add(str, val ? val : "1");
+}
+
+static
+void
+commandline_undef(char *str)
+{
+	commandline_macro_add(str, NULL);
+}
+
+static
+void
+apply_commandline_macros(void)
+{
+	struct commandline_macro *cm;
+	unsigned i, num;
+
+	num = array_num(&commandline_macros);
+	for (i=0; i<num; i++) {
+		cm = array_get(&commandline_macros, i);
+		if (cm->expansion != NULL) {
+			macro_define(NULL, cm->macro, cm->expansion);
+		} else {
+			macro_undef(cm->macro);
+		}
+		free(cm);
+	}
+	array_setsize(&commandline_macros, 0);
+}
+
+static
+void
+apply_builtin_macro(const char *name, const char *val)
+{
+	/* XXX distinguish builtin-place and commandline-place and nowhere */
+	macro_define(NULL, name, val);
+}
+
+static
+void
+apply_builtin_macros(void)
+{
+#ifdef CONFIG_OS
+	apply_builtin_macro(CONFIG_OS, "1");
+#endif
+#ifdef CONFIG_OS_2
+	apply_builtin_macro(CONFIG_OS_2, "1");
+#endif
+
+#ifdef CONFIG_CPU
+	apply_builtin_macro(CONFIG_CPU, "1");
+#endif
+#ifdef CONFIG_CPU_2
+	apply_builtin_macro(CONFIG_CPU_2, "1");
+#endif
+
+#ifdef CONFIG_SIZE
+	apply_builtin_macro(CONFIG_SIZE, "1");
+#endif
+#ifdef CONFIG_BINFMT
+	apply_builtin_macro(CONFIG_BINFMT, "1");
+#endif
+
+#ifdef CONFIG_COMPILER
+	apply_builtin_macro(CONFIG_COMPILER, VERSION_MAJOR);
+	apply_builtin_macro(CONFIG_COMPILER_MINOR, VERSION_MINOR);
+	apply_builtin_macro("__VERSION__", VERSION_LONG);
+#endif
+}
+
+////////////////////////////////////////////////////////////
+// extra included files
+
+struct commandline_file {
+	char *name;
+	bool suppress_output;
+};
+
+static struct array commandline_files;
+
+static
+void
+commandline_files_init(void)
+{
+	array_init(&commandline_files);
+}
+
+static
+void
+commandline_files_cleanup(void)
+{
+	array_cleanup(&commandline_files);
+}
+
+static
+void
+commandline_addfile(char *name, bool suppress_output)
+{
+	struct commandline_file *cf;
+
+	cf = domalloc(sizeof(*cf));
+	cf->name = name;
+	cf->suppress_output = suppress_output;
+	array_add(&commandline_files, cf, NULL);
+}
+
+static
+void
+commandline_addfile_output(char *name)
+{
+	commandline_addfile(name, false);
+}
+
+static
+void
+commandline_addfile_nooutput(char *name)
+{
+	commandline_addfile(name, true);
+}
+
+static
+void
+read_commandline_files(void)
+{
+	struct commandline_file *cf;
+	unsigned i, num;
+	bool save = false;
+
+	num = array_num(&commandline_files);
+	for (i=0; i<num; i++) {
+		cf = array_get(&commandline_files, i);
+		if (cf->suppress_output) {
+			save = mode.do_output;
+			mode.do_output = false;
+			files_read(NULL, cf->name);
+			mode.do_output = save;
+		} else {
+			files_read(NULL, cf->name);
+		}
+	}
+}
+
+////////////////////////////////////////////////////////////
+// include path accumulation
+
+static struct stringarray incpath_quote;
+static struct stringarray incpath_user;
+static struct stringarray incpath_system;
+static struct stringarray incpath_late;
+static const char *sysroot;
+
+static
+void
+incpath_init(void)
+{
+	stringarray_init(&incpath_quote);
+	stringarray_init(&incpath_user);
+	stringarray_init(&incpath_system);
+	stringarray_init(&incpath_late);
+}
+
+static
+void
+incpath_cleanup(void)
+{
+	stringarray_cleanup(&incpath_quote);
+	stringarray_cleanup(&incpath_user);
+	stringarray_cleanup(&incpath_system);
+	stringarray_cleanup(&incpath_late);
+}
+
+static
+void
+commandline_isysroot(char *dir)
+{
+	sysroot = dir;
+}
+
+static
+void
+commandline_addincpath(struct stringarray *arr, char *s)
+{
+	stringarray_add(arr, s, NULL);
+}
+
+static
+void
+commandline_addincpath_quote(char *dir)
+{
+	commandline_addincpath(&incpath_quote, dir);
+}
+
+static
+void
+commandline_addincpath_user(char *dir)
+{
+	commandline_addincpath(&incpath_user, dir);
+}
+
+static
+void
+commandline_addincpath_system(char *dir)
+{
+	commandline_addincpath(&incpath_system, dir);
+}
+
+static
+void
+commandline_addincpath_late(char *dir)
+{
+	commandline_addincpath(&incpath_late, dir);
+}
+
+static
+void
+loadincludepath(void)
+{
+	unsigned i, num;
+	const char *dir;
+	char *t;
+
+	num = stringarray_num(&incpath_quote);
+	for (i=0; i<num; i++) {
+		dir = stringarray_get(&incpath_quote, i);
+		files_addquotepath(dir, false);
+	}
+	files_addquotepath(".", false);
+
+	num = stringarray_num(&incpath_user);
+	for (i=0; i<num; i++) {
+		dir = stringarray_get(&incpath_user, i);
+		files_addquotepath(dir, false);
+		files_addbracketpath(dir, false);
+	}
+
+	if (mode.do_stdinc) {
+		if (sysroot != NULL) {
+			t = dostrdup3(sysroot, "/", CONFIG_LOCALINCLUDE);
+			freestringlater(t);
+			dir = t;
+		} else {
+			dir = CONFIG_LOCALINCLUDE;
+		}
+		files_addquotepath(dir, true);
+		files_addbracketpath(dir, true);
+
+		if (sysroot != NULL) {
+			t = dostrdup3(sysroot, "/", CONFIG_SYSTEMINCLUDE);
+			freestringlater(t);
+			dir = t;
+		} else {
+			dir = CONFIG_SYSTEMINCLUDE;
+		}
+		files_addquotepath(dir, true);
+		files_addbracketpath(dir, true);
+	}
+
+	num = stringarray_num(&incpath_system);
+	for (i=0; i<num; i++) {
+		dir = stringarray_get(&incpath_system, i);
+		files_addquotepath(dir, true);
+		files_addbracketpath(dir, true);
+	}
+
+	num = stringarray_num(&incpath_late);
+	for (i=0; i<num; i++) {
+		dir = stringarray_get(&incpath_late, i);
+		files_addquotepath(dir, false);
+		files_addbracketpath(dir, false);
+	}
+}
+
+////////////////////////////////////////////////////////////
+// silly commandline stuff
+
+static const char *commandline_prefix;
+
+static
+void
+commandline_setprefix(char *prefix)
+{
+	commandline_prefix = prefix;
+}
+
+static
+void
+commandline_addincpath_user_withprefix(char *dir)
+{
+	char *s;
+
+	if (commandline_prefix == NULL) {
+		warnx("-iprefix needed");
+		die();
+	}
+	s = dostrdup3(commandline_prefix, "/", dir);
+	freestringlater(s);
+	commandline_addincpath_user(s);
+}
+
+static
+void
+commandline_addincpath_late_withprefix(char *dir)
+{
+	char *s;
+
+	if (commandline_prefix == NULL) {
+		warnx("-iprefix needed");
+		die();
+	}
+	s = dostrdup3(commandline_prefix, "/", dir);
+	freestringlater(s);
+	commandline_addincpath_late(s);
+}
+
+static
+void
+commandline_setstd(char *std)
+{
+	if (!strcmp(std, "krc")) {
+		return;
+	}
+	warnx("Standard %s not supported by this preprocessor", std);
+	die();
+}
+
+static
+void
+commandline_setlang(char *lang)
+{
+	if (!strcmp(lang, "c") || !strcmp(lang, "assembler-with-cpp")) {
+		return;
+	}
+	warnx("Language %s not supported by this preprocessor", lang);
+	die();
+}
+
+////////////////////////////////////////////////////////////
+// complex modes
+
+static
+void
+commandline_iremap(char *str)
+{
+	/* XXX */
+	warnx("-iremap not supported");
+	die();
+}
+
+static
+void
+commandline_tabstop(char *s)
+{
+	char *t;
+	unsigned long val;
+
+	t = strchr(s, '=');
+	if (t == NULL) {
+		/* should not happen */
+		warnx("Invalid tabstop");
+		die();
+	}
+	t++;
+	errno = 0;
+	val = strtoul(t, &t, 10);
+	if (errno || *t != '\0') {
+		warnx("Invalid tabstop");
+		die();
+	}
+	if (val > 64) {
+		warnx("Preposterously large tabstop");
+		die();
+	}
+	mode.input_tabstop = val;
+}
+
+/*
+ * macrolist
+ */
+
+static
+void
+commandline_dD(void)
+{
+	mode.do_macrolist = true;
+	mode.macrolist_include_stddef = false;
+	mode.macrolist_include_expansions = true;
+}
+
+static
+void
+commandline_dM(void)
+{
+	mode.do_macrolist = true;
+	mode.macrolist_include_stddef = true;
+	mode.macrolist_include_expansions = true;
+	mode.do_output = false;
+}
+
+static
+void
+commandline_dN(void)
+{
+	mode.do_macrolist = true;
+	mode.macrolist_include_stddef = false;
+	mode.macrolist_include_expansions = false;
+}
+
+/*
+ * include trace
+ */
+
+static
+void
+commandline_dI(void)
+{
+	mode.do_trace = true;
+	mode.trace_namesonly = false;
+	mode.trace_indented = false;
+}
+
+static
+void
+commandline_H(void)
+{
+	mode.do_trace = true;
+	mode.trace_namesonly = true;
+	mode.trace_indented = true;
+}
+
+/*
+ * depend
+ */
+
+static
+void
+commandline_setdependtarget(char *str)
+{
+	mode.depend_target = str;
+	mode.depend_quote_target = false;
+}
+
+static
+void
+commandline_setdependtarget_quoted(char *str)
+{
+	mode.depend_target = str;
+	mode.depend_quote_target = true;
+}
+
+static
+void
+commandline_setdependoutput(char *str)
+{
+	mode.depend_file = str;
+}
+
+static
+void
+commandline_M(void)
+{
+	mode.do_depend = true;
+	mode.depend_report_system = true;
+	mode.do_output = false;
+}
+
+static
+void
+commandline_MM(void)
+{
+	mode.do_depend = true;
+	mode.depend_report_system = false;
+	mode.do_output = false;
+}
+
+static
+void
+commandline_MD(void)
+{
+	mode.do_depend = true;
+	mode.depend_report_system = true;
+}
+
+static
+void
+commandline_MMD(void)
+{
+	mode.do_depend = true;
+	mode.depend_report_system = false;
+}
+
+static
+void
+commandline_wall(void)
+{
+	warns.nestcomment = true;
+	warns.undef = true;
+	warns.unused = true;
+}
+
+static
+void
+commandline_wnoall(void)
+{
+	warns.nestcomment = false;
+	warns.undef = false;
+	warns.unused = false;
+}
+
+static
+void
+commandline_wnone(void)
+{
+	warns.nestcomment = false;
+	warns.endiflabels = false;
+	warns.undef = false;
+	warns.unused = false;
+}
+
+////////////////////////////////////////////////////////////
+// options
+
+struct flag_option {
+	const char *string;
+	bool *flag;
+	bool setto;
+};
+
+struct act_option {
+	const char *string;
+	void (*func)(void);
+};
+
+struct prefix_option {
+	const char *string;
+	void (*func)(char *);
+};
+
+struct arg_option {
+	const char *string;
+	void (*func)(char *);
+};
+
+static const struct flag_option flag_options[] = {
+	{ "C",                          &mode.output_retain_comments,  true },
+	{ "CC",                         &mode.output_retain_comments,  true },
+	{ "fdollars-in-identifiers",    &mode.input_allow_dollars,     true },
+	{ "fno-dollars-in-identifiers", &mode.input_allow_dollars,     false },
+	{ "MG",                         &mode.depend_assume_generated, true },
+	{ "MP",                         &mode.depend_issue_fakerules,  true },
+	{ "nostdinc",                   &mode.do_stdinc,               false },
+	{ "P",                          &mode.output_linenumbers,      false },
+	{ "undef",                      &mode.do_stddef,               false },
+	{ "Wcomment",                   &warns.nestcomment,    true },
+	{ "Wendif-labels",              &warns.endiflabels,    true },
+	{ "Werror",                     &mode.werror,          true },
+	{ "Wno-comment",                &warns.nestcomment,    false },
+	{ "Wno-endif-labels",           &warns.endiflabels,    false },
+	{ "Wno-error",                  &mode.werror,          false },
+	{ "Wno-undef",                  &warns.undef,          false },
+	{ "Wno-unused-macros",          &warns.unused,         false },
+	{ "Wundef",                     &warns.undef,          true },
+	{ "Wunused-macros",             &warns.unused,         true },
+};
+static const unsigned num_flag_options = HOWMANY(flag_options);
+
+static const struct act_option act_options[] = {
+	{ "dD",        commandline_dD },
+	{ "dI",        commandline_dI },
+	{ "dM",        commandline_dM },
+	{ "dN",        commandline_dN },
+	{ "H",         commandline_H },
+	{ "M",         commandline_M },
+	{ "MD",        commandline_MD },
+	{ "MM",        commandline_MM },
+	{ "MMD",       commandline_MMD },
+	{ "Wall",      commandline_wall },
+	{ "Wno-all",   commandline_wnoall },
+	{ "w",         commandline_wnone },
+};
+static const unsigned num_act_options = HOWMANY(act_options);
+
+static const struct prefix_option prefix_options[] = {
+	{ "D",         commandline_def },
+	{ "ftabstop=", commandline_tabstop },
+	{ "I",         commandline_addincpath_user },
+	{ "std=",      commandline_setstd },
+	{ "U",         commandline_undef },
+};
+static const unsigned num_prefix_options = HOWMANY(prefix_options);
+
+static const struct arg_option arg_options[] = {
+	{ "idirafter",   commandline_addincpath_late },
+	{ "imacros",     commandline_addfile_nooutput },
+	{ "include",     commandline_addfile_output },
+	{ "iprefix",     commandline_setprefix },
+	{ "iquote",      commandline_addincpath_quote },
+	{ "iremap",      commandline_iremap },
+	{ "isysroot",    commandline_isysroot },
+	{ "isystem",     commandline_addincpath_system },
+	{ "iwithprefix", commandline_addincpath_late_withprefix },
+	{ "iwithprefixbefore", commandline_addincpath_user_withprefix },
+	{ "MF",          commandline_setdependoutput },
+	{ "MT",          commandline_setdependtarget },
+	{ "MQ",          commandline_setdependtarget_quoted },
+	{ "x",           commandline_setlang },
+};
+static const unsigned num_arg_options = HOWMANY(arg_options);
+
+static
+bool
+check_flag_option(const char *opt)
+{
+	unsigned i;
+	int r;
+
+	for (i=0; i<num_flag_options; i++) {
+		r = strcmp(opt, flag_options[i].string);
+		if (r == 0) {
+			*flag_options[i].flag = flag_options[i].setto;
+			return true;
+		}
+		if (r < 0) {
+			break;
+		}
+	}
+	return false;
+}
+
+static
+bool
+check_act_option(const char *opt)
+{
+	unsigned i;
+	int r;
+
+	for (i=0; i<num_act_options; i++) {
+		r = strcmp(opt, act_options[i].string);
+		if (r == 0) {
+			act_options[i].func();
+			return true;
+		}
+		if (r < 0) {
+			break;
+		}
+	}
+	return false;
+}
+
+static
+bool
+check_prefix_option(char *opt)
+{
+	unsigned i;
+	int r;
+
+	for (i=0; i<num_prefix_options; i++) {
+		r = strncmp(opt, prefix_options[i].string,
+			    strlen(prefix_options[i].string));
+		if (r == 0) {
+			prefix_options[i].func(opt);
+			return true;
+		}
+		if (r < 0) {
+			break;
+		}
+	}
+	return false;
+}
+
+static
+bool
+check_arg_option(const char *opt, char *arg)
+{
+	unsigned i;
+	int r;
+
+	for (i=0; i<num_arg_options; i++) {
+		r = strcmp(opt, arg_options[i].string);
+		if (r == 0) {
+			if (arg == NULL) {
+				warnx("Option -%s requires an argument", opt);
+				die();
+			}
+			arg_options[i].func(arg);
+			return true;
+		}
+		if (r < 0) {
+			break;
+		}
+	}
+	return false;
+}
+
+static
+void
+usage(void)
+{
+	fprintf(stderr, "Usage: %s [options] [infile [outfile]]\n",
+		getprogname());
+	fprintf(stderr, "Common options:\n");
+	fprintf(stderr, "   -C               Retain comments\n");
+	fprintf(stderr, "   -Dmacro[=def]    Predefine macro\n");
+	fprintf(stderr, "   -Idir            Add to include path\n");
+	fprintf(stderr, "   -M               Issue depend info\n");
+	fprintf(stderr, "   -MD              Issue depend info and output\n");
+	fprintf(stderr, "   -MM              -M w/o system headers\n");
+	fprintf(stderr, "   -MMD             -MD w/o system headers\n");
+	fprintf(stderr, "   -nostdinc        Drop default include path\n");
+	fprintf(stderr, "   -Umacro          Undefine macro\n");
+	fprintf(stderr, "   -undef           Undefine everything\n");
+	fprintf(stderr, "   -Wall            Enable all warnings\n");
+	fprintf(stderr, "   -Werror          Make warnings into errors\n");
+	fprintf(stderr, "   -w               Disable all warnings\n");
+	die();
+}
+
+////////////////////////////////////////////////////////////
+// exit and cleanup
+
+static struct stringarray freestrings;
+
+static
+void
+init(void)
+{
+	stringarray_init(&freestrings);
+	incpath_init();
+	commandline_macros_init();
+	commandline_files_init();
+}
+
+static
+void
+cleanup(void)
+{
+	unsigned i, num;
+
+	commandline_files_cleanup();
+	commandline_macros_cleanup();
+	incpath_cleanup();
+
+	num = stringarray_num(&freestrings);
+	for (i=0; i<num; i++) {
+		free(stringarray_get(&freestrings, i));
+	}
+	stringarray_setsize(&freestrings, 0);
+	stringarray_cleanup(&freestrings);
+}
+
+void
+die(void)
+{
+	cleanup();
+	exit(EXIT_FAILURE);
+}
+
+void
+freestringlater(char *s)
+{
+	stringarray_add(&freestrings, s, NULL);
+}
+
+////////////////////////////////////////////////////////////
+// main
+
+int
+main(int argc, char *argv[])
+{
+	const char *inputfile = NULL;
+	const char *outputfile = NULL;
+	int i;
+
+	init();
+
+	for (i=1; i<argc; i++) {
+		if (argv[i][0] != '-') {
+			break;
+		}
+		if (check_flag_option(argv[i]+1)) {
+			continue;
+		}
+		if (check_act_option(argv[i]+1)) {
+			continue;
+		}
+		if (check_prefix_option(argv[i]+1)) {
+			continue;
+		}
+		if (check_arg_option(argv[i]+1, argv[i+1])) {
+			i++;
+			continue;
+		}
+		usage();
+	}
+	if (i < argc) {
+		inputfile = argv[i++];
+	}
+	if (i < argc) {
+		outputfile = argv[i++];
+	}
+	if (i < argc) {
+		usage();
+	}
+
+	mode.output_file = outputfile;
+
+	loadincludepath();
+	apply_builtin_macros();
+	apply_commandline_macros();
+	read_commandline_files();
+	files_read(NULL, inputfile);
+
+	cleanup();
+	return EXIT_SUCCESS;
+}