root/libexec/tradcpp/main.c
/*-
 * Copyright (c) 2010 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by David A. Holland.
 *
 * 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, 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "bool.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_cheaplinenumbers = false,
        .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)
{
        unsigned i, num;
        struct commandline_macro *cm;

        num = array_num(&commandline_macros);
        for (i=0; i<num; i++) {
                cm = array_get(&commandline_macros, i);
                dofree(cm, sizeof(*cm));
        }
        array_setsize(&commandline_macros, 0);
        
        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;

        array_add(&commandline_macros, cm, NULL);
}

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

        if (*str == '\0') {
                complain(NULL, "-D: macro name expected");
                die();
        }

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

        if (val) {
                p2 = *p;
                place_addcolumns(&p2, 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)
{
        if (*str == '\0') {
                complain(NULL, "-U: macro name expected");
                die();
        }
        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);
                }
                dofree(cm, sizeof(*cm));
        }
        array_setsize(&commandline_macros, 0);
}

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

        place_setbuiltin(&p, num);
        macro_define_magic(&p, name);
}

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;

        apply_magic_macro(n++, "__FILE__");
        apply_magic_macro(n++, "__LINE__");

#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)
{
        unsigned i, num;
        struct commandline_file *cf;

        num = array_num(&commandline_files);
        for (i=0; i<num; i++) {
                cf = array_get(&commandline_files, i);
                if (cf != NULL) {
                        dofree(cf, sizeof(*cf));
                }
        }
        array_setsize(&commandline_files, 0);

        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);
                array_set(&commandline_files, i, NULL);
                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);
                }
                dofree(cf, sizeof(*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_setsize(&incpath_quote, 0);
        stringarray_setsize(&incpath_user, 0);
        stringarray_setsize(&incpath_system, 0);
        stringarray_setsize(&incpath_late, 0);

        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)
{
        if (*s == '\0') {
                complain(NULL, "Empty include directory");
                die();
        }
        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(NULL, 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) {
                complain(NULL, "-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) {
                complain(NULL, "-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;
        }
        complain(NULL, "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;
        }
        complain(NULL, "Language %s not supported by this preprocessor", lang);
        die();
}

////////////////////////////////////////////////////////////
// complex modes

DEAD static
void
commandline_iremap(const struct place *p, char *str)
{
        (void)p;
        /* XXX */
        (void)str;
        complain(NULL, "-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 */
                complain(NULL, "Invalid tabstop");
                die();
        }
        t++;
        errno = 0;
        val = strtoul(t, &t, 10);
        if (errno || *t != '\0') {
                complain(NULL, "Invalid tabstop");
                die();
        }
        if (val > 64) {
                complain(NULL, "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 ignore_option {
        const char *string;
};

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 ignore_option ignore_options[] = {
        { "m32" },
        { "traditional" },
};
static const unsigned num_ignore_options = HOWMANY(ignore_options);

static const struct flag_option flag_options[] = {
        { "C",                          &mode.output_retain_comments,  true },
        { "CC",                         &mode.output_retain_comments,  true },
        { "MG",                         &mode.depend_assume_generated, true },
        { "MP",                         &mode.depend_issue_fakerules,  true },
        { "P",                          &mode.output_linenumbers,      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 },
        { "fdollars-in-identifiers",    &mode.input_allow_dollars,     true },
        { "fno-dollars-in-identifiers", &mode.input_allow_dollars,     false },
        { "nostdinc",                   &mode.do_stdinc,               false },
        { "p",                          &mode.output_cheaplinenumbers, true },
        { "undef",                      &mode.do_stddef,               false },
};
static const unsigned num_flag_options = HOWMANY(flag_options);

static const struct act_option act_options[] = {
        { "H",         commandline_H },
        { "M",         commandline_M },
        { "MD",        commandline_MD },
        { "MM",        commandline_MM },
        { "MMD",       commandline_MMD },
        { "Wall",      commandline_wall },
        { "Wno-all",   commandline_wnoall },
        { "dD",        commandline_dD },
        { "dI",        commandline_dI },
        { "dM",        commandline_dM },
        { "dN",        commandline_dN },
        { "w",         commandline_wnone },
};
static const unsigned num_act_options = HOWMANY(act_options);

static const struct prefix_option prefix_options[] = {
        { "D",         commandline_def },
        { "I",         commandline_addincpath_user },
        { "U",         commandline_undef },
        { "ftabstop=", commandline_tabstop },
        { "std=",      commandline_setstd },
};
static const unsigned num_prefix_options = HOWMANY(prefix_options);

static const struct arg_option arg_options[] = {
        { "MF",          commandline_setdependoutput },
        { "MQ",          commandline_setdependtarget_quoted },
        { "MT",          commandline_setdependtarget },
        { "debuglog",    debuglog_open },
        { "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 },
        { "x",           commandline_setlang },
};
static const unsigned num_arg_options = HOWMANY(arg_options);

static
bool
check_ignore_option(const char *opt)
{
        unsigned i;
        int r;

        for (i=0; i<num_ignore_options; i++) {
                r = strcmp(opt, ignore_options[i].string);
                if (r == 0) {
                        return true;
                }
                if (r < 0) {
                        break;
                }
        }
        return false;
}

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, len;
        int r;

        for (i=0; i<num_prefix_options; i++) {
                len = strlen(prefix_options[i].string);
                r = strncmp(opt, prefix_options[i].string, len);
                if (r == 0) {
                        prefix_options[i].func(p, opt + len);
                        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) {
                                complain(NULL,
                                         "Option -%s requires an argument",
                                         opt);
                                die();
                        }
                        arg_options[i].func(argplace, arg);
                        return true;
                }
                if (r < 0) {
                        break;
                }
        }
        return false;
}

DEAD PF(2, 3) static
void
usage(const char *progname, const char *fmt, ...)
{
        va_list ap;

        fprintf(stderr, "%s: ", progname);
        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
        va_end(ap);
        fprintf(stderr, "\n");

        fprintf(stderr, "usage: %s [options] [infile [outfile]]\n", progname);
        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();
        debuglog_close();

        num = stringarray_num(&freestrings);
        for (i=0; i<num; i++) {
                dostrfree(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 *progname;
        const char *inputfile = NULL;
        const char *outputfile = NULL;
        struct place cmdplace;
        int i;

        progname = strrchr(argv[0], '/');
        progname = progname == NULL ? argv[0] : progname + 1;
        complain_init(progname);

        if (pledge("stdio rpath wpath cpath", NULL) == -1) {
                fprintf(stderr, "%s: pledge: %s", progname, strerror(errno));
                exit(1);
        }

        init();

        for (i=1; i<argc; i++) {
                if (argv[i][0] != '-' || argv[i][1] == 0) {
                        break;
                }
                place_setcommandline(&cmdplace, i, 1);
                if (check_ignore_option(argv[i]+1)) {
                        continue;
                }
                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(progname, "Invalid option %s", argv[i]);
        }
        if (i < argc) {
                inputfile = argv[i++];
                if (!strcmp(inputfile, "-")) {
                        inputfile = NULL;
                }
        }
        if (i < argc) {
                outputfile = argv[i++];
                if (!strcmp(outputfile, "-")) {
                        outputfile = NULL;
                }
        }
        if (i < argc) {
                usage(progname, "Extra non-option argument %s", argv[i]);
        }

        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;
}