/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */

/*
 *  DIRECT.C
 *
 */

#include "defs.h"
#ifdef _DCC
#include <lib/misc.h>	/*  get _parseargs1() & 2   */
#endif

Prototype short IfEnabled;
Prototype short IfIndex;

Prototype void InitDirective(void);
Prototype long HandleDirective(ubyte *, int, int);
Prototype void do_if(ubyte *, int, long *);
Prototype void do_ifndef(ubyte *, int, long *);
Prototype void do_ifdef(ubyte *, int, long *);
Prototype void do_else(ubyte *, int, long *);
Prototype void do_elif(ubyte *, int, long *);
Prototype void do_endif(ubyte *, int, long *);
Prototype void do_pragma(ubyte *, int, long *);
Prototype void do_error(ubyte *, int, long *);
Prototype void do_line(ubyte *, int, long *);
Prototype void do_null(ubyte *, int, long *);
Prototype void do_passthru(ubyte *, int, long *);

short IfEnabled = 1;
short IfIndex;

static char IfAry[MAX_IF_LEVEL];
static char InElse[MAX_IF_LEVEL];
static char AutoEndif[MAX_IF_LEVEL];	/*  for #elif	*/

typedef struct Direct {
    short   Len;
    short   IfFlag;
    void    (*Func)(ubyte *, int, long *);
    char    *Name;
} Direct;

Direct DirAry[] = {
    {	6,  0,	do_define,  "define"    },
    {	5,  1,	do_ifdef,   "ifdef"     },
    {	6,  1,	do_ifndef,  "ifndef"    },
    {	2,  1,	do_if,	    "if"        },
    {	4,  1,	do_else,    "else"      },
    {	4,  1,	do_elif,    "elif"      },
    {	5,  1,	do_endif,   "endif"     },
    {	7,  0,	do_include, "include"   },
    {	6,  0,	do_pragma,  "pragma"    },
    {	5,  0,	do_error,   "error"     },
    {	5,  0,	do_undef,   "undef"     },
    {	4,  0,	do_line,    "line"      },
    {	1, -1,	do_passthru,"#"         },
    {	0,  0,	do_null,    ""          },
    {	-1 }
};

void
InitDirective()
{
    IfEnabled = 1;
    IfAry[0] = 1;
    InElse[0] = 0;
}

long
HandleDirective(base, i, max)
ubyte *base;
int i;
int max;
{
    long b = i;
    long e;
    long idx;
    Direct *dir;

    /*
     *	Determine extent of directive and strip comments.  Comments
     *	are not subject to newline termination.  Strip out newlines as
     *	they can interfere with line numbering.
     */

    {
	long n = i;
	long x = i;

	while (n < max && base[n] != '\n') {
	    if (base[n] == '\'') {
		long n2 = SkipSingleSpec(base, n + 1, max);
		movmem(base + n, base + x, n2 - n);
		x += n2 - n;
		n = n2;
		continue;
	    }
	    if (base[n] == '\"') {
		long n2 = SkipString(base, n + 1, max);
		movmem(base + n, base + x, n2 - n);
		x += n2 - n;
		n = n2;
		continue;
	    }
	    if (base[n] == '/') {
		if (base[n+1] == '*') {
		    n = SkipComment(base, n + 2, max);
		    base[x++] = ' ';
		    continue;
		}
		if (base[n+1] == '/' && SlashSlashOpt) {
		    n = SkipCommentLine(base, n + 2, max);
		    base[x++] = ' ';
		    continue;
		}
	    }
	    if (base[n] == '\\' && base[n+1] == '\n') {
		++PushBase->LineNo;
		putc('\n', Fo);
		n += 2;
		continue;
	    }
	    base[x++] = base[n++];
	}
	while (x > i && (base[x-1] == ' ' || base[x-1] == '\t'))
	    --x;
	max = x;
	++PushBase->LineNo;
	e = n;
    }

    /*
     *	Skip white space, find the directive.  Allow ## directives through
     *	with no modifications (but #if/#endif will work around them).  If
     *  stripping an include, ignore the directive (simply spit it out)
     */

    while (i < max && (base[i] == ' ' || base[i] == 9))     /*  skip ws */
	++i;
    b = i;
    while (i < max && SymbolChar[base[i]])		    /*	directive */
	++i;

    for (dir = DirAry; dir->Len >= 0; ++dir) {
	if (dir->Len == i - b && strncmp(dir->Name, base + b, i - b) == 0)
	    break;
    }

    while (i < max && (base[i] == ' ' || base[i] == 9))     /*  more ws */
	++i;

    b = i;						    /*	BASE	*/

    if (dir->Len < 0) {
	if (IfEnabled)
	    cerror(EERROR_UNKNOWN_DIRECTIVE);
	putc('\n', Fo);
	return(e+1);
    }
    if (dir->IfFlag <= 0 && IfEnabled == 0) {
	putc('\n', Fo);
	return(e+1);
    }
    if (dir->IfFlag >= 0)
	putc('\n', Fo);

    /*
     *	note that max is different from idx.  The stuff between max and idx
     *	is junk.
     */

    idx = e + 1;

    (*dir->Func)(base + b, max - b, &idx);

    return(idx);
}

void
do_if(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    short undef;
    long v;

    if (IfIndex == MAX_IF_LEVEL)
	cerror(EFATAL_MAX_IFS, MAX_IF_LEVEL);
    if (IfEnabled == 0) {
	++IfIndex;
	IfAry[IfIndex] = 0;
	InElse[IfIndex] = 0;
	AutoEndif[IfIndex] = 0;
	return;
    }
    v = ParseIfExp(buf, &undef, max, 1);
    if (undef) {
	/* REMOVED, ANSI COMPAT
	cerror(EERROR, "#if : undefined symbol");
	v = 1;
	*/
    }
    if (v == 0)
	IfEnabled = 0;
    ++IfIndex;
    IfAry[IfIndex] = IfEnabled;
    InElse[IfIndex] = 0;
    AutoEndif[IfIndex] = 0;
}

void
do_ifndef(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    short undef;

    ParseIfExp(buf, &undef, max, 0);
    if (undef)
	do_if("1", 1, NULL);
    else
	do_if("0", 1, NULL);
}

void
do_ifdef(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    short undef;

    ParseIfExp(buf, &undef, max, 0);
    if (undef)
	do_if("0", 1, NULL);
    else
	do_if("1", 1, NULL);
}

void
do_else(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    if (InElse[IfIndex]) {
	cerror(EERROR_MULTIPLE_ELSE_FOR_IF);
	return;
    }
    InElse[IfIndex] = 1;
    if (IfIndex && IfAry[IfIndex-1] == 0)
	return;
    IfAry[IfIndex] = 1 - IfAry[IfIndex];
    IfEnabled = IfAry[IfIndex];
}

void
do_elif(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    do_else(buf, max, NULL);
    AutoEndif[IfIndex] = 1;
    do_if(buf, max, NULL);
}

void
do_endif(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    if (IfIndex == 0) {
	cerror(EERROR_ENDIF_WITHOUT_IF);
	return;
    }
    do {
	--IfIndex;
	IfEnabled = IfAry[IfIndex];
    } while (IfIndex && AutoEndif[IfIndex]);
}

/*
 *  #pragma
 *
 *  DCCOPTS - processed by DCPP and passed on
 *  <other> - passed on
 */

void
do_pragma(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    if (strncmp(buf, "DCCOPTS", 7) == 0) {
#ifdef _DCC
	/*
	 *  Parse #pragma DCCOPTS lines.  Use DICE's argument parser
	 *  for main().  I am following the standard argc/argv opts
	 *  here with a dummy av[0] and av[n] = NULL to give ParseOpts()
	 *  the same format as main() gives it.  This is really not
	 *  necessary but what the hell.
	 */

	char *copy;
	char **av;
	short ac;

	movmem(buf, copy = zalloc(max + 1), max);

	if (ac = _parseargs1(copy + 7, max - 7)) {
	    av = zalloc((ac + 2) * 4);
	    _parseargs2(copy + 7, av+1, ac);
	    ++ac;
	    ParseOpts(ac, av, 0);
	}
#endif
    }

    /*
     *	Pragma's are always passed on
     */

    Dump("#pragma ", 0, 8);
    Dump(buf, 0, max);
    Dump("\n", 0, 1);
}

void
do_null(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused	*/
{

}

void
do_error(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    cerror(EERROR_ERROR_DIRECTIVE, max, buf);
}

/*
 *  # line <lineno> "filename"
 */

void
do_line(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    short len = 0;
    Include *pb = PushBase;

    pb->LineNo = atoi(buf);
    while (max && *buf != '\"') {
	--max;
	++buf;
    }
    if (*buf == '\"') {
	++buf;
	--max;
	while (len < max && buf[len] != '\"')
	    ++len;
	if (strlen(pb->FileName) != len || strncmp(pb->FileName, buf, len) != 0) {
	    pb->FileName = malloc(len + 1);
	    pb->FileName[len] = 0;
	    strncpy(pb->FileName, buf, len);
	}
    }
    fprintf(Fo, "# %ld \"%s\" %ld\n", pb->LineNo, pb->FileName, pb->Level);
}

void
do_passthru(buf, max, pu)
ubyte *buf;
int max;
long *pu;   /*	unused */
{
    Dump("##", 0, 2);
    Dump(buf, 0, max);
    Dump("\n", 0, 1);
}

