view main.c @ 19:f9792a9ec704

macro expansion.
author David A. Holland
date Mon, 20 Dec 2010 03:55:19 -0500
parents c08a947d8f30
children 76c114899f63
line wrap: on
line source

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>

#include "version.h"
#include "config.h"
#include "utils.h"
#include "array.h"
#include "mode.h"
#include "place.h"
#include "files.h"
#include "directive.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 {
	struct place where;
	struct place where2;
	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 struct place *p, const char *macro,
		      const struct place *p2, const char *expansion)
{
	struct commandline_macro *cm;

	cm = domalloc(sizeof(*cm));
	cm->where = *p;
	cm->where2 = *p2;
	cm->macro = macro;
	cm->expansion = expansion;
}

static
void
commandline_def(const struct place *p, char *str)
{
	struct place p2;
	char *val;

	val = strchr(str, '=');
	if (val != NULL) {
		*val = '\0';
		val++;
	}

	if (val) {
		p2 = *p;
		p2.column += strlen(str);
	} else {
		place_setbuiltin(&p2, 1);
	}
	commandline_macro_add(p, str, &p2, val ? val : "1");
}

static
void
commandline_undef(const struct place *p, char *str)
{
	commandline_macro_add(p, str, p, 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_plain(&cm->where, cm->macro,
					   &cm->where2, cm->expansion);
		} else {
			macro_undef(cm->macro);
		}
		free(cm);
	}
	array_setsize(&commandline_macros, 0);
}

static
void
apply_builtin_macro(unsigned num, const char *name, const char *val)
{
	struct place p;

	place_setbuiltin(&p, num);
	macro_define_plain(&p, name, &p, val);
}

static
void
apply_builtin_macros(void)
{
	unsigned n = 1;

#ifdef CONFIG_OS
	apply_builtin_macro(n++, CONFIG_OS, "1");
#endif
#ifdef CONFIG_OS_2
	apply_builtin_macro(n++, CONFIG_OS_2, "1");
#endif

#ifdef CONFIG_CPU
	apply_builtin_macro(n++, CONFIG_CPU, "1");
#endif
#ifdef CONFIG_CPU_2
	apply_builtin_macro(n++, CONFIG_CPU_2, "1");
#endif

#ifdef CONFIG_SIZE
	apply_builtin_macro(n++, CONFIG_SIZE, "1");
#endif
#ifdef CONFIG_BINFMT
	apply_builtin_macro(n++, CONFIG_BINFMT, "1");
#endif

#ifdef CONFIG_COMPILER
	apply_builtin_macro(n++, CONFIG_COMPILER, VERSION_MAJOR);
	apply_builtin_macro(n++, CONFIG_COMPILER_MINOR, VERSION_MINOR);
	apply_builtin_macro(n++, "__VERSION__", VERSION_LONG);
#endif
}

////////////////////////////////////////////////////////////
// extra included files

struct commandline_file {
	struct place where;
	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(const struct place *p, char *name, bool suppress_output)
{
	struct commandline_file *cf;

	cf = domalloc(sizeof(*cf));
	cf->where = *p;
	cf->name = name;
	cf->suppress_output = suppress_output;
	array_add(&commandline_files, cf, NULL);
}

static
void
commandline_addfile_output(const struct place *p, char *name)
{
	commandline_addfile(p, name, false);
}

static
void
commandline_addfile_nooutput(const struct place *p, char *name)
{
	commandline_addfile(p, 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;
			file_readquote(&cf->where, cf->name);
			mode.do_output = save;
		} else {
			file_readquote(&cf->where, cf->name);
		}
		free(cf);
	}
	array_setsize(&commandline_files, 0);
}

////////////////////////////////////////////////////////////
// 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(const struct place *p, char *dir)
{
	(void)p;
	sysroot = dir;
}

static
void
commandline_addincpath(struct stringarray *arr, char *s)
{
	stringarray_add(arr, s, NULL);
}

static
void
commandline_addincpath_quote(const struct place *p, char *dir)
{
	(void)p;
	commandline_addincpath(&incpath_quote, dir);
}

static
void
commandline_addincpath_user(const struct place *p, char *dir)
{
	(void)p;
	commandline_addincpath(&incpath_user, dir);
}

static
void
commandline_addincpath_system(const struct place *p, char *dir)
{
	(void)p;
	commandline_addincpath(&incpath_system, dir);
}

static
void
commandline_addincpath_late(const struct place *p, char *dir)
{
	(void)p;
	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(const struct place *p, char *prefix)
{
	(void)p;
	commandline_prefix = prefix;
}

static
void
commandline_addincpath_user_withprefix(const struct place *p, char *dir)
{
	char *s;

	if (commandline_prefix == NULL) {
		warnx("-iprefix needed");
		die();
	}
	s = dostrdup3(commandline_prefix, "/", dir);
	freestringlater(s);
	commandline_addincpath_user(p, s);
}

static
void
commandline_addincpath_late_withprefix(const struct place *p, char *dir)
{
	char *s;

	if (commandline_prefix == NULL) {
		warnx("-iprefix needed");
		die();
	}
	s = dostrdup3(commandline_prefix, "/", dir);
	freestringlater(s);
	commandline_addincpath_late(p, s);
}

static
void
commandline_setstd(const struct place *p, char *std)
{
	(void)p;

	if (!strcmp(std, "krc")) {
		return;
	}
	warnx("Standard %s not supported by this preprocessor", std);
	die();
}

static
void
commandline_setlang(const struct place *p, char *lang)
{
	(void)p;

	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(const struct place *p, char *str)
{
	(void)p;
	/* XXX */
	(void)str;
	warnx("-iremap not supported");
	die();
}

static
void
commandline_tabstop(const struct place *p, char *s)
{
	char *t;
	unsigned long val;

	(void)p;

	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(const struct place *p, char *str)
{
	(void)p;
	mode.depend_target = str;
	mode.depend_quote_target = false;
}

static
void
commandline_setdependtarget_quoted(const struct place *p, char *str)
{
	(void)p;
	mode.depend_target = str;
	mode.depend_quote_target = true;
}

static
void
commandline_setdependoutput(const struct place *p, char *str)
{
	(void)p;
	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)(const struct place *, char *);
};

struct arg_option {
	const char *string;
	void (*func)(const struct place *, 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(const struct place *p, 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(p, opt);
			return true;
		}
		if (r < 0) {
			break;
		}
	}
	return false;
}

static
bool
check_arg_option(const char *opt, const struct place *argplace, 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(argplace, 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();

	place_init();
	files_init();
	directive_init();
	macros_init();
}

static
void
cleanup(void)
{
	unsigned i, num;

	macros_cleanup();
	directive_cleanup();
	files_cleanup();
	place_cleanup();

	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;
	struct place cmdplace;
	int i;

	init();

	for (i=1; i<argc; i++) {
		if (argv[i][0] != '-') {
			break;
		}
		place_setcommandline(&cmdplace, i, 1);
		if (check_flag_option(argv[i]+1)) {
			continue;
		}
		if (check_act_option(argv[i]+1)) {
			continue;
		}
		if (check_prefix_option(&cmdplace, argv[i]+1)) {
			continue;
		}
		place_setcommandline(&cmdplace, i+1, 1);
		if (check_arg_option(argv[i]+1, &cmdplace, 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();
	place_setnowhere(&cmdplace);
	file_readabsolute(&cmdplace, inputfile);

	cleanup();
	if (complain_failed()) {
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}