
/* PDC Compiler - A Freely Distributable C Compiler for the Amiga
 *                Based upon prior work by Matthew Brandt and Jeff Lydiatt.
 *
 * PDC Compiler release 3.3 Copyright (C) 1989 Paul Petersen and Lionel Hummel.
 * PDC Software Distribution (C) 1989 Lionel Hummel and Paul Petersen.
 *
 * This code is freely redistributable upon the conditions that this 
 * notice remains intact and that modified versions of this file not be 
 * distributed as part of the PDC Software Distribution without the express
 * consent of the copyright holders.
 */

/*
 * Cmain.c
 * 
 * This is the main entry point to the compiler.  It manages compile-time
 * options and the individual steps that are performed during a compilation.
 */

#include    <stdio.h>
#include    "C.h"
#include    "Expr.h"
#include    "Gen.h"
#include    "Cglbdec.h"
#include    "Version.h"

/*
 * There are one or two HARMLESS jokes in this beta version of the compiler.
 * Do not worry: there is no effect on code generation or on overall
 * performance.  To the easily affronted: remove this line and please forgive
 * me.  It is very late and my linear thinking mode is shot. (LDH)
 */

#if 0
#define JOKE    1
#endif

char            infile[40], listfile[40], outfile[40], prefile[40];
char           *progname;
int             mainflag;
extern TABLE    tagtable;
extern int      total_errors;
extern int      inclnum;
extern int      fatal;
extern char    *incldir[];
extern char     prepbuffer[];
extern char    *itoa();
extern char    *litlate();
extern char    *xalloc();
extern void     read_precomp(), dump_precomp(), fmt_precomp();

void  usage(), makename(), summary(), closefiles();

void
add_option( tbl, cmd )
    TABLE   *tbl;
    char    *cmd;
{
    char    *ptr;
    SYM     *sp;

    ++global_flag;

    sp = (SYM *) xalloc(sizeof(SYM));
    sp->storage_class = sc_define;
    sp->storage_type = sc_define;
    sp->value.s = NULL;
    sp->tp = NULL;

    for (ptr = cmd; *ptr; ++ptr) 
        if (*ptr == '=')
            break;
    
    if (*ptr == '=') 
        *ptr++ = '\0';

    sp->name = litlate(cmd);

    if (*ptr)
        sp->value.s = litlate( ptr );

    if (tbl->head == NULL) 
        tbl->head = tbl->tail = sp;
    else {
        tbl->tail->next = sp;
        while (tbl->tail->next != NULL)
            tbl->tail = tbl->tail->next;
    }

    --global_flag;
}

main(argc, argv)
    int             argc;
    char          **argv;
{
    extern int      optind;
    extern int      opterr;
    extern char    *optarg;
    extern char     optsign;
    int             used_stdin;
    int             i, c;

    opterr = 1;
    used_stdin = FALSE;
    progname = argv[0];

#ifdef _unix_
    open_stdio();
#endif

    while ((c = getopt(argc, argv, "ABGLNQRabglnqrd:D:F:f:I:o:P:u:U:?")) != EOF)
        switch (c) {
        case 'a':
        case 'A':
            Options.Annote = !Options.Annote;
            break;
        case 'b':
        case 'B':
            Options.Builtin = !Options.Builtin;
            break;
        case 'd':
        case 'D':   /* Define a preprocessor Symbol */
            strcpy(prepbuffer, optarg);
            add_option( &cmd_defsyms, prepbuffer );
            break;
        case 'u':
        case 'U':   /* Undefine a preprocessor Symbol */    
            strcpy(prepbuffer, optarg);
            add_option( &cmd_undefsyms, prepbuffer );
            break;
        case 'g':
        case 'G':
            Options.Debug = !Options.Debug;
            break;
        case 'I':   /* Preprocessor include directory */
            ++global_flag;
            strcpy(prepbuffer, optarg);
            for (i = 0; prepbuffer[i]; i++)
                c = prepbuffer[i];
            if (c != ':' && c != '/')
                strcat(prepbuffer, "/");
            incldir[inclnum++] = litlate(prepbuffer);
            --global_flag;
            break;
        case 'l':
        case 'L':
            Options.List = !Options.List;
            break;
        case 'n':
        case 'N':
            Options.Optimize = !Options.Optimize;
            break;
        case 'o':
        case 'O':
            strcpy(outfile, optarg);
            break;
        case 'p':
        case 'P':
            Options.PreComp = 1;
            if (strcmp(optarg, "0") != 0) {
                Options.PreComp = 2;
                strcpy(prefile, optarg);
            }
            break;
        case 'q':
        case 'Q':
            Options.Quiet = !Options.Quiet;
            break;
        case 'r':
        case 'R':
            Options.MulDiv32 = !Options.MulDiv32;
            break;
        case 'f':
        case 'F':
            i = atoi(optarg);
            if (i >= 4 || i >= 6)
                Options.Frame = i;
            break;
        case '?':
            usage();
            break;
        }

    argc -= optind;
    argv += optind;

    for (i = 0; i < argc; i++) {
        if (strcmp(argv[i], "-") == 0) {
            if (used_stdin) {
                fputs(progname, stderr);
                fputs(": stdin used more than once.\n", stderr);
            }
            used_stdin = TRUE;
        }
#if unix
        else if (access(argv[i], 4) == -1) {
            fputs(progname, stderr);
            fputs(": cannot access ", stderr);
            fputs(argv[i], stderr);
            fputs(": ", stderr);
            perror("");
            exit(1);
        }
#endif
    }

    for (i = 0; i < argc; i++) {
        if (openfiles(argv[i])) {
            lineno = 0;
            initsym();

            if (Options.PreComp == 2)
                read_precomp(prefile);

            install_defines();

            getch();
            getsym();
            compile();
            getline(1);

            if (!fatal && Options.PreComp == 1) {
                strcpy(prefile, infile);
                makename(prefile, ".pre");
                dump_precomp(prefile);
                strcpy(prefile, infile);
                makename(prefile, ".prc");
                fmt_precomp(prefile);
            }

            summary();
            release_global();
            closefiles();
        }
    }

#ifdef _unix_
    close_stdio();
#endif
    exit(fatal || (total_errors != 0));
}

void
usage()
{
    fputs(VERSION, stderr);
    fputs( "\nProduced by Paul Petersen and Lionel Hummel.\n", stderr);
    fputs( "Based upon prior work by Matthew Brandt and Jeff Lydiatt.\n\n", stderr);
    fputs("Usage: ", stderr);
    fputs(progname, stderr);
    fputs(" [ -ABGILNQRP? ] [ -D/U symbol ] [ -F reg ] [ -o file ] [ -p 0|file ] [ file ... ]\n", stderr);
    fputs("\t-a\tAnnotate the assembler listing with source.\n", stderr);
    fputs("\t-b\tGenerate inline assembler for builtins.\n", stderr);
    fputs("\t-D\tDefine a preprocessor symbol.\n", stderr);
    fputs("\t-f\tUse address register [4|5|6] as the frame pointer.\n", stderr);
    fputs("\t-g\tGenerate debugging info.\n", stderr);
    fputs("\t-I\tDirectory for include files.\n", stderr);
    fputs("\t-l\tCreate a source listing file.\n", stderr);
    fputs("\t-n\tTurn off optimization.\n", stderr);
    fputs("\t-o\tSpecify name for output.\n", stderr);
    fputs("\t-p\tCreate or use precompiled header.\n", stderr);
    fputs("\t-q\tRun quietly.\n", stderr);
    fputs("\t-r\tUse library for integer math.\n", stderr);
    fputs("\t-U\tUndefine a preprocessor symbol.\n", stderr);
    fputs("\t-?\tThis help information.\n", stderr);

    exit(1);
}

void
formsection( buffer, name, ext )
    char    *buffer, *name, *ext;
{
    while (*name && *name != '.')
        *buffer++ = *name++;

    while (*ext)
        *buffer++ = *ext++;

    *buffer = '\0';
}

int
openfiles(s)
    char           *s;
{
    if (!Options.Quiet) {
        fputs(VERSION, stderr);
        fputs( "\nProduced by Paul Petersen and Lionel Hummel.\n", stderr);
        fputs( "Based upon prior work by Matthew Brandt and Jeff Lydiatt.\n\n", stderr);
    }

    if (strcmp(s, "-") == 0) {  /* Read from stdin */
        strcpy(infile, "stdin");
        if (outfile[0] == '\0')
            strcpy(outfile, "stdout");
        strcpy(listfile, "stdlist");

        input = stdin;
        output = stdout;
    }
    else {
        strcpy(infile, s);
        strcpy(listfile, s);
        makename(listfile, ".lis");

        if (outfile[0] == '\0') {
            strcpy(outfile, s);
            makename(outfile, ".s");
        }

        if ((input = fopen(infile, "r")) == 0) {
            fputs(" can't open ", stderr);
            fputs(infile, stderr);
            fputs("\n", stderr);
            fatal = TRUE;
            return 0;
        }

        if ((output = fopen(outfile, "w")) == NULL) {
            fputs(" can't open ", stderr);
            fputs(outfile, stderr);
            fputs("\n", stderr);
            fclose(input);
            fatal = TRUE;
            return 0;
        }
    }

    if (Options.List) {
        if ((list = fopen(listfile, "w")) == NULL) {
            fputs(" can't open ", stderr);
            fputs(listfile, stderr);
            fputs("\n", stderr);
            fclose(input);
            fclose(output);
            fatal = TRUE;
            return 0;
        }
    }

    curfile = infile;

    if (Options.Debug) {
        formsection( code_name, infile, "_code" );
        formsection( data_name, infile, "_data" );
        formsection( bss_name, infile, "_bss" );
    }

    return 1;
}

void
makename(s, e)
    char           *s, *e;
{
    char    *p = NULL;

    if (s != NULL && e != NULL) {
        while (*s) {
            if (*s == '.')
                p = s;
            ++s;
        }
        if (p == NULL)
            p = s;

        while (*p++ = *e++);
    }
}

void
summary()
{
    if (!Options.Quiet) {
#ifdef JOKE
        if (total_errors == 1) {
            fputs("\n *** error 65536 Illegal Number of Errors: 1\n", stderr);
            total_errors++;
        }
#endif
        fputs("\n -- ", stderr);
        if (total_errors == 0)
            fputs(" no errors found.", stderr);
        else {
            fputs(itoa(total_errors), stderr);
            if (total_errors == 1)
                fputs(" error found.", stderr);
            else
                fputs(" errors found.", stderr);
        }
    }

    if (Options.List)
        fputs("\f\n *** global scope symbol table ***\n\n", list);
    list_table(&gsyms, 0);
    if (Options.List)
        fputs("\n *** structures and unions ***\n\n", list);
    list_table(&tagtable, 0);
    fputs("\tEND\n", output);
}


void
closefiles()
{
    fclose(input);
    fclose(output);
    if (Options.List)
        fclose(list);
}
