/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 */

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"
#include "version.h"

#include <ctype.h>

#ifdef SIGNAL
#include <signal.h>
#ifndef _NSIG
#ifdef NSIG
#define _NSIG	NSIG
#endif /* NSIG */
#endif /* _NSIG */
#endif /* SIGNAL */

#if defined(__STDC__) || defined(__cplusplus)
#define P_(s) s
#else
#define P_(s) ()
#endif

#ifdef _NSIG
static	void	exception	P_((int));
#endif /* _NSIG */

#undef P_

/*
**	All the parameter options supported are defined by
**	entries in the following structure.
*/
typedef enum {
     none,
     invert,	/* set flag - inverted by 'no' prefix */
     uniq,	/* set flag if present */
     numeric,	/* numeric value */
     string,	/* string value */
     chip	/* chip/assembler selection */
} OPTTYPE;

static struct {
    char*	text;	/* Text for option */
    OPTTYPE	type;	/* type of option */
    void*	value;	/* Address of flag to set */
    void*	value2;
} *optptr, opts[] = {
#ifdef DEBUGOPT
    {"g",	    uniq,	&debug_option,		NULL},
#endif /*DEBUGOPT*/
    {"O",	    uniq,	&optimize_option,	NULL},
    {"v",	    uniq,	&verbose_option,	NULL},
#ifdef DEBUG
    {"debug",	    numeric,	&internal_option,	NULL},
#endif /* DEBUG */
    {"error",	    numeric,	&error_option,		NULL},
    {"warn",	    numeric,	&warn_option,		NULL},
    {"maxerr",	    numeric,	&max_error_count,	NULL},
#ifdef MC680X0
    {"target",	    numeric,	&target_option,		NULL},
#endif /* MC680X0 */
    {"prefix",	    string,	&external_prefix,	NULL},
    {"align",	    invert,	&align_option,		NULL},
#ifdef ASM
    {"asm",	    invert,	&asm_option,		NULL},
#endif /* ASM */
    {"code",   	    invert,	&code_option,		NULL},
#ifdef EXTERNAL
#ifdef TMS320C30
    {"delayed",	    numeric,	&opt_delayed_branches,	NULL},
#endif /* TMS320C30 */
    {"extern",      invert,	&extern_option,		NULL},
#endif /* EXTERNAL */
#ifdef FLOAT_CHECK
    {"fcheck",	    invert,	&fcheck_option,		NULL},
#endif /* FLOAT_CHECK */
#ifdef FORMAT_CHECK
    {"format",      invert,	&format_option,		NULL},
#endif /* FORMAT_CHECK */
    {"fpu",         invert,	&fpu_option,		NULL},
    {"fpureturn",   invert,	&fpu_return_option,	NULL},
#ifdef INTEL_386
    {"fpret386",    invert,	&fpret386_option,	NULL},
#endif /* INTEL_386 */
#ifdef MC680X0
    {"frame",       numeric,	&frame_option,		NULL},
#endif /* MC680X0 */
#ifdef ICODE
    {"icode",       uniq,	&icode_option,		NULL},
#endif /* ICODE */
#ifdef MC680X0
    {"large",	    invert,	&large_option,		NULL},
#endif /* MC680X0 */
    {"lattice",     invert,	&lattice_option,	NULL},
#ifdef LIST
    {"list",        invert,	&list_option,		NULL},
#endif /* LIST */
    {"longdouble",  invert,	&longdouble_option,	NULL},
    {"obsolete",    invert,	&obsolete_option,	NULL},
    {"opt",         invert,	&opt_option,		NULL},
#ifdef PACKENUM
    {"packenum",    invert,	&packenum_option,	NULL},
#endif /* PACKENUM */
    {"peep",        invert,	&peep_option,		NULL},
#ifdef PROBES
    {"probe",	    invert,	&probe_option,		NULL},
#endif /* PROBES */
    {"reg",         invert,  	&reg_option,		NULL},
    {"revbit",      invert,	&bitfield_option,	NULL},
    {"short",       invert,	&short_option,		NULL},
    {"separate",    invert,	&IandD_option,		NULL},
    {"small",	    invert,	&small_option,		NULL},
#ifdef STACK_CHECK
    {"stackcheck",  invert,	&stackcheck_option,	NULL},
#endif /* STACK_CHECK */
    {"stackopt",    numeric,	&stackopt_option,	NULL},
#ifdef TOPSPEED
    {"topspeed",    invert,	&topspeed_option,	NULL},
#endif /* TOPSPEED */
#ifdef TRACE
    {"trace",       invert,	&trace_option,		NULL},
#endif /* TRACE */
    {"trad",        invert,	&trad_option,		NULL},
#ifdef TRANSLATE
    {"trans",       invert,	&trans_option,		NULL},
#endif /* TRANSLATE */
    {"uchar",       invert,	&uchar_option,		NULL},

#ifdef MULTIPLE_ASSEMBLERS
#ifdef MULTIPLE_PROCESSORS
#define	MC86_FUNCS	&mc86_funcs
#define	MC386_FUNCS	&mc386_funcs
#define	MC68K_FUNCS	&mc68k_funcs
#define	MCARM_FUNCS	&mcarm_funcs
#define	MCC30_FUNCS	&mcc30_funcs
#else
#define	MC86_FUNCS	0
#define	MC386_FUNCS	0
#define	MC68K_FUNCS	0
#define	MCARM_FUNCS	0
#define	MCC30_FUNCS	0
#endif /* MULTIPLE_PROCESSORS */

#ifdef INTEL_86
#ifdef TARGET_MASM
    {"masm86",      chip,	&masm386_funcs,		MC86_FUNCS},
#endif /* TARGET_MASM */
#ifdef TARGET_BAS
    {"bas86",      chip,	&bas386_funcs,		MC86_FUNCS},
#endif /* TARGET_BAS */
#ifdef TARGET_GAS
    {"gas86",      chip,	&gas386_funcs,		MC86_FUNCS},
#endif /* TARGET_GAS */
#ifdef TARGET_SYSV
    {"sysv86",     chip,	&sysv386_funcs,		MC86_FUNCS},
#endif /* TARGET_SYSV */
#endif /* INTEL_86 */

#ifdef INTEL_386
#ifdef TARGET_MASM
    {"masm386",      chip,	&masm386_funcs,		MC386_FUNCS},
#endif /* TARGET_MASM */
#ifdef TARGET_BAS
    {"bas386",      chip,	&bas386_funcs,		MC386_FUNCS},
#endif /* TARGET_BAS */
#ifdef TARGET_GAS
    {"gas386",      chip,	&gas386_funcs,		MC386_FUNCS},
#endif /* TARGET_GAS */
#ifdef TARGET_SYSV
    {"sysv386",     chip,	&sysv386_funcs,		MC386_FUNCS},
#endif /* TARGET_SYSV */
#endif /* INTEL_386 */

#ifdef MC680X0
#ifdef TARGET_ACK
    {"ack68k",      chip,	&ack68k_funcs,		MC68K_FUNCS},
#endif /* TARGET_ACK */
#ifdef TARGET_CPM
    {"cpm68k",      chip,	&cpm68k_funcs,		MC68K_FUNCS},
#endif /* TARGET_CPM */
#ifdef TARGET_GAS
    {"gas68k",      chip,	&gas68k_funcs,		MC68K_FUNCS},
#endif /* TARGET_GAS */
#ifdef TARGET_QMAC
    {"qmc68k",      chip,	&qmac68k_funcs,		MC68K_FUNCS},
#endif /* TARGET_QMAC */
#endif /* MC680X0 */

#ifdef ARM
#ifdef TARGET_OBJ
    {"objarm",	    chip,	&armobj_funcs,		MCARM_FUNCS},
#endif /* TARGET_OBJ */
#endif /* ARM */

#ifdef TMS320C30
#ifdef TARGET_ROSSIN
    {"rosc30",	    chip,	&rosc30_funcs,		MCC30_FUNCS},
#endif /* TARGET_ROSSIN */
#endif /* TMS320C30 */
#endif /* MULTIPLE_ASSEMBLERS */
    {NULL,	    none,	NULL,			NULL}
};

/*
**  Process a parameter option
**
**  For convenience with front-end CC programs, any of the
**  parameters can optionally be preceeded by 'Q'.
*/
void
options P1(char*, s)
{

    int value = 1;

    s++; 		/* forget '-' */
    if (*s == (char)'Q')
        s++; 		/* forget 'Q' */

    if (*s == (char)'?') {
	message (MSG_USEAGE,PROGNAME);
	for (optptr = opts ; optptr->text ; optptr++) {
	    eprintf ("-%s%s%s ",(optptr->type==invert ? "[no]" : ""),
				optptr->text,
				(optptr->type==numeric ? "=n" : 
				(optptr->type==string ? "=str" : "")));
	}
	message (MSG_DEFAULTS);
	for (optptr = opts ; optptr->text ; optptr++) {
	    switch (optptr->type) {
	      case invert:
		eprintf ("-%s%s ",*((int *)optptr->value) ? "" : "no",
				optptr->text);
		break;
	      case numeric:
		eprintf ("-%s=%d ",optptr->text,*((int *)optptr->value));
		break;
	      case string:
		eprintf ("-%s=%s ",optptr->text,*((char **)optptr->value));
		break;
	      default:
		break;
	    }
	}
	eprintf ("%s%s", newline, newline);
	exit(EXIT_FAILURE);
    }

    if (s[0] == (char)'n' && s[1] == (char)'o') {
	s += 2;
	value = 0;
    }
    /*
     * Search the table to try and find a match
     */
    for (optptr = opts ; optptr->text != NULL ; optptr++) {
	int x = (int)strlen(optptr->text);
	if (memcmp(optptr->text,s,(size_t)x))
	    continue;
	switch (optptr->type) {
	  case invert:			/* keyword optionally preceeded by 'no' */
	    if (s[x] != 0)
		break;
	    *((int *)optptr->value) = value;
#ifdef MC68000
	    volatile_found = !trad_option;
#endif /* MC68000 */
	    return;
	  case uniq:			/* keyword not preceeded by 'no' */
	    if (value==0 || s[x] != 0)
		break;
	    *((int *)optptr->value) = 1;
	    return;
	  case numeric:
	    if ((s[x] == (char)'=') && isdigit(((int)s[x+1])) ) {
		*((int *)optptr->value) = atoi(&s[x+1]);
            }
	    return;
	  case string:
	    if (s[x] == (char)'=') {
		*((char **)optptr->value) = &s[x+1];
	    }
	    return;
	  case chip:
#ifdef MULTIPLE_ASSEMBLERS
	    Funcs = (struct funcs *)optptr->value;
#endif /* MULTIPLE_ASSEMBLERS */
#ifdef MULTIPLE_PROCESSORS
	    GFuncs = (struct genfuncs *)optptr->value2;
#endif /* MULTIPLE_PROCESSORS */
	    return;
	  case none:
	    return;
	  default:
	    CANNOT_REACH_HERE();
	    break;
	} /* end of switch */
    } /* end of for loop */

    message(MSG_UNKNOWNOPT, --s);
}

/*
**	Parse the command line
*/
static void
commandline P2(int, argc, char**, argv)
{
    int		i;
    char **	pptr;
#ifdef ENVVAR
    char *	ptr;
#endif

#ifdef EPOC
    CommandLineParameters(&argc, &argv);
#else
    /* set up default files */
    input     = stdin;
    output    = stdout;
    errfile   = stderr;
#endif /* EPOC */

#ifdef ENVVAR
    /* Accept parameters from Environment */
    if ((ptr=getenv(ENVNAME)) != NULL) {
	ptr=strtok(ptr," \t");
	while (ptr != NULL) {
	    options (ptr);
	    ptr = strtok((char *)NULL, " \t");
	}
    }
#endif /* ENVAR */

    argc--;
    argv++;
    /* Accept parameters from the command line */
    for (i = 0, pptr = argv; i < argc;) {
	if (**pptr == (char)'-') {
	    options(*pptr);
	    argc--;
	    VOIDCAST memmove((void *)pptr, (void *)(pptr + 1), (size_t)(argc - i) * sizeof (*pptr));
	} else {
	    i++;
	    pptr++;
	}
    }
    openfiles(argc, argv);
#ifdef LIST
    listfile  = errfile;
#endif /* LIST */
#ifdef DEBUG
    debugfile = errfile;
#endif /* DEBUG */
}

#ifdef _NSIG
static void
exception P1(int, sig)
{
    message(MSG_SIGNAL, sig);
    exit(EXIT_FAILURE);
}
#endif /* _NSIG */

int
main P2(int, argc, char**, argv)
{
#ifdef _NSIG
    int		 i;

    for (i = 1; i < _NSIG; ++i)
	if (signal(i, SIG_IGN) != SIG_IGN)
	    VOIDCAST signal(i, exception);
#endif /* _NSIG */

    openerror();
    commandline(argc, argv);

    if (verbose_option)
	eprintf ("%s v%s (%s)%s",PROGNAME, VERSION, LAST_CHANGE_DATE, newline);

    /*
     * set and check some global options
     */
    if (!optimize_option)
	stackopt_option	= 0;
    if (stackopt_option)
	is_parameter	= 0;
    if (trad_option)
	obsolete_option	= 0;

#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
    {
	/* set up floating point constants used within the compiler */
	int j;
	j = 0;  ITOF(F_zero, j);
	j = 1;  ITOF(F_one, j);
	j = 2;  ITOF(F_two, j);
	j = 10; ITOF(F_ten, j);
	FASSIGN(F_half, F_one); FDIV(F_half, F_two);
    }
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */

    initialize_types();
    initsym();
    getsym();
#ifdef CPU_DEFINED
    g_initialize();
#endif /* CPU_DEFINED */
    translation_unit();
    rel_global();
#ifdef VERBOSE
    if (verbose_option) {
	message(MSG_TIMES, decl_time, parse_time, opt_time, gen_time, flush_time);
	message(MSG_ERRORCNT, total_errors);
    }
#endif
    return (total_errors > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
