/***********************************************************************
 *
 *  avra - Assembler for the Atmel AVR microcontroller series
 *
 *  Copyright (C) 1998-2004 Jon Anders Haugum, Tobias Weber
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *
 *
 *  Authors of avra can be reached at:
 *     email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
 *     www: http://sourceforge.net/projects/avra
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "misc.h"
#include "args.h"
#include "avra.h"
#include "device.h"

enum
{
	DIRECTIVE_BYTE = 0,
	DIRECTIVE_CSEG,
	DIRECTIVE_CSEGSIZE,
	DIRECTIVE_DB,
	DIRECTIVE_DEF,
	DIRECTIVE_DEVICE,
	DIRECTIVE_DSEG,
	DIRECTIVE_DW,
	DIRECTIVE_ENDM,
	DIRECTIVE_ENDMACRO,
	DIRECTIVE_EQU,
	DIRECTIVE_ESEG,
	DIRECTIVE_EXIT,
	DIRECTIVE_INCLUDE,
	DIRECTIVE_LIST,
	DIRECTIVE_LISTMAC,
	DIRECTIVE_MACRO,
	DIRECTIVE_NOLIST,
	DIRECTIVE_ORG,
	DIRECTIVE_SET,
	DIRECTIVE_DEFINE,
	DIRECTIVE_UNDEF,
	DIRECTIVE_IFDEF,
	DIRECTIVE_IFNDEF,
	DIRECTIVE_IF,
	DIRECTIVE_ELSE,
	DIRECTIVE_ELIF,
	DIRECTIVE_ENDIF,
	DIRECTIVE_MESSAGE,
	DIRECTIVE_WARNING,
	DIRECTIVE_ERROR,
	DIRECTIVE_COUNT
};

char *directive_list[] =
{
	"BYTE",
	"CSEG",
	"CSEGSIZE",
	"DB",
	"DEF",
	"DEVICE",
	"DSEG",
	"DW",
	"ENDM",
	"ENDMACRO",
	"EQU",
	"ESEG",
	"EXIT",
	"INCLUDE",
	"LIST",
	"LISTMAC",
	"MACRO",
	"NOLIST",
	"ORG",
	"SET",
	"DEFINE",
	"UNDEF",
	"IFDEF",
	"IFNDEF",
	"IF",
	"ELSE",
	"ELIF",
	"ENDIF",
	"MESSAGE",
	"WARNING",
	"ERROR"
};


int parse_directive(struct prog_info *pi, int pass)
{
	int directive;
    int ok = True;
    int i;
	char *next, *data;
	struct file_info *fi_bak;
	struct label *label;
	struct def *def;
	struct data_list *includedir;

	next = get_next_token(pi->fi->scratch, TERM_SPACE);

	for(i = 0; pi->fi->scratch[i] != '\0'; i++) {
		pi->fi->scratch[i] = toupper(pi->fi->scratch[i]);
	}
	directive = get_directive_type(pi->fi->scratch + 1);
	if(directive == -1) {
		print_msg(pi, MSGTYPE_ERROR, "Unknown directive: %s", pi->fi->scratch);
		return(True);
	}
	switch(directive) {
		case DIRECTIVE_BYTE:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".BYTE needs a size operand");
				return(True);
			}
			if(pi->segment != SEGMENT_DATA)
				print_msg(pi, MSGTYPE_ERROR, ".BYTE directive can only be used in data segment (.DSEG)");
			get_next_token(next, TERM_END);
			if(!get_expr(pi, next, &i))
				return(False);
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "D:%06x    %s", pi->dseg_addr, pi->list_line);
				pi->list_line = NULL;
			}
			pi->dseg_addr += i;
			if(pass == PASS_1)
				pi->dseg_count += i;
			break;
		case DIRECTIVE_CSEG:
			pi->segment = SEGMENT_CODE;
			break;
		case DIRECTIVE_CSEGSIZE:
			break;
		case DIRECTIVE_DB:
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "          %s", pi->list_line);
				pi->list_line = NULL;
			}
			return(parse_db(pi, next, pass));
			break;
		case DIRECTIVE_DEF:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".DEF needs an operand");
				return(True);
			}
			data = get_next_token(next, TERM_EQUAL);
			if(!(data && (tolower(data[0]) == 'r') && isdigit(data[1]))) {
				print_msg(pi, MSGTYPE_ERROR, "%s needs a register (e.g. .def BZZZT = r16)", next);
				return(True);
			}
			i = atoi(&data[1]);
			if(i > 31)
				print_msg(pi, MSGTYPE_ERROR, "R%d is not a valid register", i);
			for(def = pi->first_def; def; def = def->next)
				if(!nocase_strcmp(def->name, next)) {
					def->reg = i;
					return(True);
				}
			def = malloc(sizeof(struct def));
			if(!def) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			def->next = NULL;
			if(pi->last_def)
				pi->last_def->next = def;
			else
				pi->first_def = def;
			pi->last_def = def;
			def->name = malloc(strlen(next) + 1);
			if(!def->name) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			strcpy(def->name, next);
			def->reg = i;
			break;
		case DIRECTIVE_DEVICE:
			if(pass == PASS_2)
				return(True);
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".DEVICE needs an operand");
				return(True);
			}
			get_next_token(next, TERM_END);
			pi->device = get_device(pi,next);
			if(!pi->device)
				print_msg(pi, MSGTYPE_ERROR, "Unknown device: %s", next);
			break;
		case DIRECTIVE_DSEG:
			pi->segment = SEGMENT_DATA;
			break;
		case DIRECTIVE_DW:
			if(pi->segment == SEGMENT_DATA) {
				print_msg(pi, MSGTYPE_ERROR, "Can't use .DW directive in data segment (.DSEG)");
				return(True);
			}
			while(next) {
				data = get_next_token(next, TERM_COMMA);
				if(pass == PASS_2) {
				  if(!get_expr(pi, next, &i))
				    return(False);
				  if((i < -32768) || (i > 65535))
				    print_msg(pi, MSGTYPE_WARNING, "Value %d is out of range (-32768 <= k <= 65535). Will be masked", i);
                }
				if(pi->segment == SEGMENT_EEPROM) {
					if(pass == PASS_2) {
						write_ee_byte(pi, pi->eseg_addr, (unsigned char)i);
						write_ee_byte(pi, pi->eseg_addr + 1, (unsigned char)(i >> 8));
					}
					pi->eseg_addr += 2;
					if(pass == PASS_1)
						pi->eseg_count += 2;
				}
				else {
					if((pass == PASS_2) && pi->hfi)
						write_prog_word(pi, pi->cseg_addr, i);
					pi->cseg_addr++;
					if(pass == PASS_1)
						pi->cseg_count++;
				}
				next = data;
			}
			break;
		case DIRECTIVE_ENDM:
		case DIRECTIVE_ENDMACRO:
			print_msg(pi, MSGTYPE_ERROR, "No .MACRO found before .ENDMACRO");
			break;
		case DIRECTIVE_EQU:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".EQU needs an operand");
				return(True);
			}
			data = get_next_token(next, TERM_EQUAL);
			if(!data) {
				print_msg(pi, MSGTYPE_ERROR, "%s needs an expression (e.g. .EQU BZZZT = 0x2a)", next);
				return(True);
			}
			get_next_token(data, TERM_END);
			if(!get_expr(pi, data, &i))
				return(False);
			for(label = pi->first_label; label; label = label->next) {
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next);
					return(True);
				}
			}
			for(label = pi->first_variable; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .SET variable", next);
					return(True);
				}
			for(label = pi->first_constant; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "Can't redefine constant %s, use .SET instead", next);
					return(True);
				}
			label = malloc(sizeof(struct label));
			if(!label) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			label->next = NULL;
			if(pi->last_constant)
				pi->last_constant->next = label;
			else
				pi->first_constant = label;
			pi->last_constant = label;
			label->name = malloc(strlen(next) + 1);
			if(!label->name) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			strcpy(label->name, next);
			label->value = i;
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "          %s", pi->list_line);
				pi->list_line = NULL;
			}
			break;
		case DIRECTIVE_ESEG:
			pi->segment = SEGMENT_EEPROM;
			break;
		case DIRECTIVE_EXIT:
			pi->fi->exit_file = True;
			break;
		case DIRECTIVE_INCLUDE:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, "Nothing to include");
				return(True);
			}
			next = term_string(pi, next);
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "          %s", pi->list_line);
				pi->list_line = NULL;
			}
			// Test if include is in local directory
			ok = test_include(next);
			data = NULL;
			if(!ok)
				for(includedir = GET_ARG(pi->args, ARG_INCLUDEDIR); includedir && !ok; includedir = includedir->next) {
					i = strlen(includedir->data);
					if(data)
						free(data);
					data = malloc(i + strlen(next) + 2);
					if(!data) {
						print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
						return(False);
					}
					strcpy(data, includedir->data);
					if((data[i - 1] != '\\') && (data[i - 1] != '/'))
						data[i++] = '/';
					strcpy(&data[i], next);
					ok = test_include(data);
				}
			if(ok) {
				fi_bak = pi->fi;
				ok = parse_file(pi, data ? data : next, pass);
				pi->fi = fi_bak;
			}
			else
				print_msg(pi, MSGTYPE_ERROR, "cannot find include file: %s", next);
			if(data)
				free(data);
			break;
		case DIRECTIVE_LIST:
			if(pass == PASS_2)
				if(pi->list_file)
                  pi->list_on = True;
			break;
		case DIRECTIVE_LISTMAC:
			if(pass == PASS_2)
				SET_ARG(pi->args, ARG_LISTMAC, True);
			break;
		case DIRECTIVE_MACRO:
			return(read_macro(pi, next, pass));
			break;
		case DIRECTIVE_NOLIST:
			if(pass == PASS_2)
				pi->list_on = False;
			break;
		case DIRECTIVE_ORG:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".ORG needs an operand");
				return(True);
			}
			get_next_token(next, TERM_END);
			if(!get_expr(pi, next, &i))
				return(False);
			switch(pi->segment) {
				case SEGMENT_CODE:
					pi->cseg_addr = i;
					break;
				case SEGMENT_DATA:
					pi->dseg_addr = i;
					break;
				case SEGMENT_EEPROM:
					pi->eseg_addr = i;
			}
			if(pi->fi->label)
				pi->fi->label->value = i;
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "          %s", pi->list_line);
				pi->list_line = NULL;
			}
			break;
		case DIRECTIVE_SET:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".SET needs an operand");
				return(True);
			}
			data = get_next_token(next, TERM_EQUAL);
			if(!data) {
				print_msg(pi, MSGTYPE_ERROR, "%s needs an expression (e.g. .SET BZZZT = 0x2a)", next);
				return(True);
			}
			get_next_token(data, TERM_END);
			if(!get_expr(pi, data, &i))
				return(False);
			for(label = pi->first_label; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next);
					return(True);
				}
			for(label = pi->first_constant; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .EQU constant", next);
					return(True);
				}
			for(label = pi->first_variable; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					label->value = i;
					return(True);
				}
			label = malloc(sizeof(struct label));
			if(!label) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			label->next = NULL;
			if(pi->last_variable)
				pi->last_variable->next = label;
			else
				pi->first_variable = label;
			pi->last_variable = label;
			label->name = malloc(strlen(next) + 1);
			if(!label->name) {
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
			}
			strcpy(label->name, next);
			label->value = i;
			break;
		case DIRECTIVE_DEFINE:
			if(!next) {
				print_msg(pi, MSGTYPE_ERROR, ".DEFINE needs an operand");
				return(True);
			}
			data = get_next_token(next, TERM_SPACE);
			if(data) {
				get_next_token(data, TERM_END);
				if(!get_expr(pi, data, &i))
				        return(False);
			}
			else
				i = 1;
			for(label = pi->first_label; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next);
					return(True);
				}
			for(label = pi->first_variable; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .SET variable", next);
					return(True);
				}
			for(label = pi->first_constant; label; label = label->next)
				if(!nocase_strcmp(label->name, next)) {
					print_msg(pi, MSGTYPE_ERROR, "Can't redefine constant %s, use .SET instead", next);
					return(True);
					}
			label = malloc(sizeof(struct label));
			if(!label)
				{
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
				}
			label->next = NULL;
			if(pi->last_constant)
				pi->last_constant->next = label;
			else
				pi->first_constant = label;
			pi->last_constant = label;
			label->name = malloc(strlen(next) + 1);
			if(!label->name)
				{
				print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
				return(False);
				}
			strcpy(label->name, next);
			label->value = i;
			if((pass == PASS_2) && pi->list_line && pi->list_on) {
				fprintf(pi->list_file, "          %s", pi->list_line);
				pi->list_line = NULL;
			}
			break;
		case DIRECTIVE_UNDEF: // TODO
			break;
		case DIRECTIVE_IFDEF:
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, ".IFDEF needs an operand");
				return(True);
				}
			get_next_token(next, TERM_END);
			if(get_symbol(pi, next, NULL))
				pi->conditional_depth++;
			else
			        {
				if(!spool_conditional(pi, False))
				        return(False);
				}
			break;
		case DIRECTIVE_IFNDEF:
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, ".IFNDEF needs an operand");
				return(True);
				}
			get_next_token(next, TERM_END);
			if(get_symbol(pi, next, NULL))
			        {
				if(!spool_conditional(pi, False))
				        return(False);
				}
			else
				pi->conditional_depth++;
			break;
		case DIRECTIVE_IF:
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, ".IF needs an expression");
				return(True);
				}
			get_next_token(next, TERM_END);
			if(!get_expr(pi, next, &i))
			        return(False);
			if(i)
				pi->conditional_depth++;
			else
			        {
				if(!spool_conditional(pi, False))
				        return(False);
				}
			break;
		case DIRECTIVE_ELSE:
		case DIRECTIVE_ELIF:
		        if(!spool_conditional(pi, True))
			        return(False);
			break;
		case DIRECTIVE_ENDIF:
		        if(pi->conditional_depth == 0)
			        print_msg(pi, MSGTYPE_ERROR, "Too many .ENDIF");
			else
			        pi->conditional_depth--;
			break;
		case DIRECTIVE_MESSAGE:
			if(pass == PASS_1)
				return(True);
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, "No message string supplied");
				return(True);
				}
			next = term_string(pi, next);
			print_msg(pi, MSGTYPE_MESSAGE, next);
			break;
		case DIRECTIVE_WARNING:
			if(pass == PASS_1)
				return(True);
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, "No warning string supplied");
				return(True);
				}
			next = term_string(pi, next);
			print_msg(pi, MSGTYPE_WARNING, next);
			break;
		case DIRECTIVE_ERROR:
			next = term_string(pi, next);
			print_msg(pi, MSGTYPE_ERROR, next);
            pi->error_count = pi->max_errors;
			if(pass == PASS_1)
				return(True);
			if(!next)
				{
				print_msg(pi, MSGTYPE_ERROR, "No error string supplied");
				return(True);
				}
			break;
	}
	return(ok);
}


int get_directive_type(char *directive) {
  int i;

  for(i = 0; i < DIRECTIVE_COUNT; i++) {
	if(!strcmp(directive, directive_list[i])) return(i);
  }
  return(-1);
}


char *term_string(struct prog_info *pi, char *string) {
  int i;

  if(string[0] != '\"') {
	print_msg(pi, MSGTYPE_ERROR, "String must be enclosed in \"-signs");
  }
  else {
	string++;
  }
  for(i = 0; (string[i] != '\"') && !((string[i] == 10) || (string[i] == 13) || (string[i] == '\0')); i++);
  if((string[i] == 10) || (string[i] == 13) || (string[i] == '\0')) {
	print_msg(pi, MSGTYPE_ERROR, "String is missing a closing \"-sign");
  }
  string[i] = '\0';
  return(string);
}


int parse_db(struct prog_info *pi, char *next, int pass) {
  int i;
  int count;
  char *data;
  char prev;

  if(pi->segment == SEGMENT_DATA) {
	print_msg(pi, MSGTYPE_ERROR, "Can't use .DB directive in data segment (.DSEG).");
	return(True);
  }
  count = 0;
  if(pass == PASS_2 && pi->list_on) {
    if(pi->segment == SEGMENT_EEPROM)
      fprintf(pi->list_file, "E:%06X ", pi->eseg_addr);
    if(pi->segment == SEGMENT_CODE)
      fprintf(pi->list_file, "C:%06X ", pi->cseg_addr);
  }
  while(next) {
	data = get_next_token(next, TERM_COMMA);
	if(next[0] == '\"') {
	  next = term_string(pi, next);
	  while(*next != '\0') {
		count++;
		write_db(pi, *next, &prev, count, pass);
        if(pass == PASS_2 && pi->list_on) fprintf(pi->list_file, "%02X", *next);
		next++;
	  }
	}
	else {
	  if(pass == PASS_2) {
		if(!get_expr(pi, next, &i))
		  return(False);
		if((i < -128) || (i > 255))
		  print_msg(pi, MSGTYPE_WARNING, "Value %d is out of range (-128 <= k <= 255). Will be masked", i);
          if(pi->list_on) fprintf(pi->list_file, "%02X", (char)i);
	  }
	  count++;
	  write_db(pi, (char)i, &prev, count, pass);
	}
	next = data;
  }
  if(pi->segment == SEGMENT_CODE) {
	if((count % 2) == 1) {
	  if(pass == PASS_2)  {
        if(pi->list_on) fprintf(pi->list_file, "00 ; zero byte added");
		write_prog_word(pi, pi->cseg_addr, prev & 0xFF);
		print_msg(pi, MSGTYPE_WARNING, "A .DB segment with an odd number of bytes is detected. A zero byte is added.");
	  }
	  pi->cseg_addr++;
	  if(pass == PASS_1) {
		pi->cseg_count++;
	  }
	}
  }
  if(pass == PASS_2 && pi->list_on) {
    fprintf(pi->list_file, "\n");
    pi->list_line = NULL;
  }
  return(True);
}


void write_db(struct prog_info *pi, char byte, char *prev, int count, int pass) {
  if(pi->segment == SEGMENT_EEPROM)	{
    if(pass == PASS_2) {
      write_ee_byte(pi, pi->eseg_addr, byte);
	}
	pi->eseg_addr++;
	if(pass == PASS_1) {
	  pi->eseg_count++;
	} 
  }
  else { /* pi->segment == SEGMENT_CODE */
    if((count % 2) == 0) {
	  if(pass == PASS_2) {
		write_prog_word(pi, pi->cseg_addr, (byte << 8) | (*prev & 0xff));
	  }
	  pi->cseg_addr++;
	  if(pass == PASS_1) {
		pi->cseg_count++;
	  }
	}
	else {
	  *prev = byte;
	}
  }
}


int spool_conditional(struct prog_info *pi, int only_endif) {
	int current_depth = 0, do_next;

	if(pi->macro_line) {
      while((pi->macro_line = pi->macro_line->next)) {
		pi->macro_call->line_index++;
		if(check_conditional(pi, pi->macro_line->line, &current_depth,  &do_next, only_endif)) {
		  if(!do_next)
		  return(True);
		}
		else
		  return(False);
	  }
	  print_msg(pi, MSGTYPE_ERROR, "Found no closing .ENDIF in macro");
	}
	else {
	  while(fgets(pi->fi->buff, LINEBUFFER_LENGTH, pi->fi->fp)) {
	  pi->fi->line_number++;
	    if(check_conditional(pi, pi->fi->buff, &current_depth,  &do_next, only_endif)) {
		  if(!do_next)
		  return(True);
		}
		else
		  return(False);
	  }
	  if(feof(pi->fi->fp)) {
		print_msg(pi, MSGTYPE_ERROR, "Found no closing .ENDIF");
		return(True);
      }
	  else {
		perror(pi->fi->include_file->name);
		return(False);
	  }
	}
	return(True);
}


int check_conditional(struct prog_info *pi, char *pbuff, int *current_depth, int *do_next, int only_endif)
{
	int i = 0;
	char *next;
	char linebuff[LINEBUFFER_LENGTH];

    strcpy(linebuff, pbuff); /* avoid cutting of the end of .elif line */

	*do_next = False;
	while(IS_HOR_SPACE(linebuff[i]) && !IS_END(linebuff[i])) i++;
	if(linebuff[i] == '.') {
	  i++;
	  if(!nocase_strncmp(&linebuff[i], "if", 2))
	    (*current_depth)++;
	  else if(!nocase_strncmp(&linebuff[i], "endif", 5)) {
        if(*current_depth == 0)
          return(True);
		(*current_depth)--;
	  }
	  else if(!only_endif && (*current_depth == 0)) {
		if(!nocase_strncmp(&linebuff[i], "else", 4)) {
		  pi->conditional_depth++;
		      return(True);
		}
		else if(!nocase_strncmp(&linebuff[i], "elif", 4)) {
		  next = get_next_token(&linebuff[i], TERM_SPACE);
		  if(!next) {
		    print_msg(pi, MSGTYPE_ERROR, ".ELIF needs an operand");
			return(True);
		  }
		  get_next_token(next, TERM_END);
		  if(!get_expr(pi, next, &i))
		    return(False);
		  if(i)
		    pi->conditional_depth++;
		  else {
			if(!spool_conditional(pi, False))
			  return(False);
		  }
		  return(True);
	    }
	  }
	}
	*do_next = True;
	return(True);
}

int test_include(const char *filename)
{
  FILE *fp;
  fp = fopen(filename, "r");
  if(fp)
  {
    fclose(fp);
    return(True);
  }
  else
    return(False);
}

/* end of directiv.c */

