/* $VER: pasm errors.c V1.3d (08.10.00)
 *
 * This file is part of pasm, a portable PowerPC assembler.
 * Copyright (c) 1997-2000  Frank Wille
 *
 * pasm is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-2000 by Volker Barthelmann.
 * pasm may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v1.3d  (08.10.00) jbecher
 *        New error message for multiple-char options.
 *        StormGCC special (define STORMSPECIAL) error handling for
 *        normal and internal errors.
 * v1.1a  (20.06.98) phx
 *        Warning 58, if rA=rD for an update instruction.
 * v1.1   (19.06.98) phx
 *        Error 46 changed into a warning (wrong base register for
 *        accessing a relocatable symbol).
 * v0.7   (02.01.98) phx
 *        Deleted "EHF" in warning 52.
 * v0.6   (30.10.97) phx
 *        New error messages.
 * v0.5   (12.10.97) phx
 *        New error messages.
 * v0.4   (05.07.97) phx
 *        Program returns EXIT_FAILURE if an error occurs.
 *        New error messages. Error 33 is a warning now.
 * v0.2   (25.03.97) phx
 *        Writes ELF object for 32-bit PowerPC big-endian. Either absolute
 *        or ELF output format may be selected. ELF is default for all
 *        currently supported platforms. PPCasm supports nine different
 *        relocation types (there are much more...).
 *        Compiles and works also under NetBSD/amiga (68k).
 *        Changed function declaration to 'new style' in all sources
 *        (to avoid problems with '...' for example).
 *        Warnings can be disabled.
 * v0.1   (11.03.97) phx
 *        First test version with all PowerPC instructions and most
 *        important directives. Only raw, absolute output.
 * v0.0   (14.02.97) phx
 *        File created. Project started.
 */


#define ERRORS_C
#include "ppcasm.h"
#ifdef STORMSPECIAL
#include "stormerrhandling.h"
#endif


/* error flags */
#define EF_NONE 0
#define EF_WARNING 1
#define EF_ERROR 2
#define EF_FATAL 3
#define EF_TYPEMASK 0xf

#define EF_NOLINE 0x10


void error(int,...);
void ierror(char *,...);



static struct {
  char *txt;
  int flags;
} errors[] = {
  "No error",EF_NONE|EF_NOLINE,
  "Out of memory",EF_FATAL|EF_NOLINE,                               /* 01 */
  "Missing source file name",EF_FATAL|EF_NOLINE,
  "Missing output file name",EF_FATAL|EF_NOLINE,
  "Multiple source file names detected",EF_FATAL|EF_NOLINE,
  "File \"%s\" has a read error",EF_FATAL|EF_NOLINE,                /* 05 */
  "Can't open \"%s\"",EF_FATAL|EF_NOLINE,
  "Symbol \"%s\" defined twice",EF_ERROR,
  "Macro \"%s\" defined twice",EF_ERROR,
  "Reference to undefined macro parameter \\%d",EF_ERROR,
  "Unknown opcode \"%s\"",EF_ERROR,                                 /* 10 */
  "Too many macro parameters",EF_ERROR,
  "Colon expected",EF_ERROR,
  "Syntax error",EF_ERROR,
  "String constant expected",EF_ERROR,
  "Illegal section attribute \"%c\"",EF_ERROR,                      /* 15 */
  "Section attributes don't match",EF_ERROR,
  "Missing argument",EF_ERROR,
  "Extra characters on line",EF_ERROR,
  "Undefined symbol",EF_ERROR,
  "Double unary operator",EF_ERROR,                                 /* 20 */
  "Missing closing parenthesis",EF_ERROR,
  "Illegal operation for a reloc expression",EF_ERROR,
  "Symbols reside in different sections",EF_ERROR,
  "Constant integer expression required",EF_ERROR,
  "Unable to create output file \"%s\"",EF_FATAL|EF_NOLINE,         /* 25 */
  "Error while writing to \"%s\"",EF_FATAL|EF_NOLINE,
  "Instruction has too few operands",EF_ERROR,
  "Can't assign external symbol",EF_ERROR,
  "Can't assign reloc half word",EF_ERROR,
  "Immediate operand doesn't fit into %d bits",EF_WARNING,          /* 30 */
  "Register operand out of range",EF_ERROR,
  "Reloc symbol required",EF_ERROR,
  "Branch destination is not in current section",EF_WARNING,
  "Branch operand out of range",EF_ERROR,
  "CR-specifier out of range",EF_ERROR,                             /* 35 */
  "Operand field \"%s\" out of range",EF_ERROR,
  "\"%s\" is a 64-bit instruction",EF_WARNING,
  "\"%s\" is an optional instruction",EF_WARNING,
  "\"%s\" is a supervisor-level instruction",EF_WARNING,
  "\"(rA)\" expected, after index expression",EF_ERROR,             /* 40 */
  "Expression must be dividable by four",EF_ERROR,
  "Invalid register field mask",EF_ERROR,
  "Maximum nesting depth for conditional assembly exceeded",EF_ERROR,
  "%s without matching .if",EF_ERROR,
  "\"fail\" directive encountered",EF_FATAL,                        /* 45 */
  "Wrong base register for base relative reloc symbol access",EF_WARNING,
  "Section \"%s\" was never defined",EF_ERROR,
  "Option -%c: argument expected",EF_FATAL|EF_NOLINE,
  "Unknown output format (%d)",EF_FATAL|EF_NOLINE,
  "\"%s\" is not a section base address",EF_ERROR,                  /* 50 */
  "Maximum of %d include paths reached. Path \"%s\" was ignored",
    EF_ERROR|EF_NOLINE,
  "Section \"%s\" with type #%d is not supported and was "
    "changed to HUNK_DATA.",EF_WARNING|EF_NOLINE,
  "Unsupported relocation type %s at offset %d in section \"%s\"",
    EF_ERROR|EF_NOLINE,
  "Option -%c: integer expected",EF_FATAL|EF_NOLINE,
  "Absolute output doesn't allow external references. Symbol "      /* 55 */
   "referenced was: %s",EF_ERROR|EF_NOLINE,
  "Option -%c: symbol name expected",EF_FATAL|EF_NOLINE,
  "Unknown assembler mode -m%s",EF_WARNING|EF_NOLINE,
  "r%d used as source and as destination register in an update "
   "instruction",EF_WARNING,
   /* jbecher: Options of type "-option argument" needs argument */
  "Option %s: argument expected",EF_FATAL|EF_NOLINE,
};


void error(int errn,...)
/* prints errors and warnings */
{
  struct GlobalVars *gv = &gvars;
  va_list vl;
  char *errtype;
  int flags = errors[errn].flags;
  struct SourceThread *mst,*st = gv->cthread;

#ifdef STORMSPECIAL
  if (gv->storm_errport)
  {
    static char msg[2048];

    errportname = gv->storm_errport;
    errportsource = NULL;
    if (!(flags & EF_NOLINE))
    {
      if (st->macro)
      {
        mst = st;
        do
        {
          if (!(mst = mst->prev))
            ierror("There's no source text where macro \"%s\" is called from",st->csource->name);
        }
        while (mst->macro);
        errportfile = mst->csource->name;
        errportline = mst->line;
      }
      else
      {
        errportfile = st->csource->name;
        errportline = st->line;
      }
    }
    else
    {
      errportfile = NULL;
      errportline = -1;
    }
    switch (flags&EF_TYPEMASK)
    {
      case EF_WARNING:
        errportkind = ERRKIND_WARNING;
        break;
      case EF_ERROR:
        errportkind = ERRKIND_ERROR;
        break;
      case EF_FATAL:
        errportkind = ERRKIND_FATAL;
        break;
      default:
        errportkind = ERRKIND_MESSAGE;
        break;
    }
    va_start(vl,errn);
    vsprintf(msg,errors[errn].txt,vl);
    va_end(vl);
    stormMessage(msg);
    if ((flags&EF_TYPEMASK) == EF_FATAL)
      cleanup(gv);
  }
  else
#endif
  {

    if (((flags&EF_TYPEMASK) == EF_WARNING) && gv->dontwarn)
      return;
    switch(flags&EF_TYPEMASK) {
      case EF_WARNING:
        errtype = "Warning";
        break;
      case EF_ERROR:
        gv->returncode = EXIT_FAILURE;
        errtype = "Error";
        break;
      case EF_FATAL:
        gv->returncode = EXIT_FAILURE;
        errtype = "Fatal error";
        break;
      default:
        ierror("Illegal error type %d",flags&EF_TYPEMASK);
        gv->returncode = EXIT_FAILURE;
        errtype = "";
        break;
    }
    if (!(flags&EF_NOLINE))
      printf("\n%s\n",st->lineptr);  /* display error line */
  
    /* print error message */
    printf("%s %d: ",errtype,errn);
    va_start(vl,errn);
    vprintf(errors[errn].txt,vl);
    va_end(vl);

    if (!(flags&EF_NOLINE)) {
      if (st->macro) {
        mst = st;
        do {
          if (!(mst = mst->prev))
            ierror("There's no source text where macro \"%s\" is "
                   "called from",st->csource->name);
        }
        while (mst->macro);
        printf(" in line %d of macro \"%s\", called from line %d of "
               "file \"%s\"",(int)st->line,st->csource->name,
               (int)mst->line,mst->csource->name);
      }
      else
        printf(" in line %d of file \"%s\"",(int)st->line,st->csource->name);
    }
    printf(".\n");

    switch(flags&EF_TYPEMASK) {
      case EF_ERROR:
        /* check if maximum number of errors reached */
        if (++gv->errcnt >= gv->maxerrors) {
          gv->errcnt = 0;
          printf("Do you want to continue (y/n) ? ");
          fflush(stdin);
          if (toupper((unsigned char)getchar()) == 'N')
            cleanup(gv);
        }
        break;
      case EF_FATAL:
        printf("Aborting.\n");  /* fatal error aborts the assembler */
        cleanup(gv);
        break;
    }
  }
}


void ierror(char *errtxt,...)
/* display internal error and quit */
{
  struct GlobalVars *gv = &gvars;
  struct SourceThread *st = gv->cthread;
  va_list vl;

#ifdef STORMSPECIAL
  if (gv->storm_errport)
  {
    static char msg[2048];

    errportfile = st->csource->name;
    errportsource = NULL;
    errportline = st->line;
    errportkind = ERRKIND_FATAL;
    sprintf(msg,"INTERNAL ERROR: ");
    va_start(vl,errtxt);
    vsprintf(msg+16,errtxt,vl);
    va_end(vl);
    stormMessage(msg);
  }
  else
#endif
  {
    printf("\nINTERNAL ERROR: ");
    va_start(vl,errtxt);
    vprintf(errtxt,vl);
    va_end(vl);
    printf(" in line %d of \"%s\".\nAborting.\n",(int)st->line,
           st->csource->name);
  }
  cleanup(gv);
}
