/* ------------------------------------------------------------------------- */
/*   "errors" : Warnings, errors and fatal errors                            */
/*              (with error throwback code for RISC OS machines)             */
/*                                                                           */
/*   Part of Inform 6    copyright (c) Graham Nelson 1993, 1994, 1995, 1996  */
/*                                                                           */
/* ------------------------------------------------------------------------- */

#include "header.h"

static char error_message_buff[256];

/* ------------------------------------------------------------------------- */
/*   Error preamble printing.                                                */
/* ------------------------------------------------------------------------- */

ErrorPosition ErrorReport;             /*  Maintained by "lexer.c"           */

static void print_preamble(void)
{
    /*  Only really prints the preamble to an error or warning message:

        e.g.  "jigsaw.apollo", line 24:

        The format is controllable (from an ICL switch) since this assists
        the working of some development environments.                        */

    int j, with_extension_flag = FALSE; char *p;

    j = ErrorReport.file_number;
    if (j <= 0) p = ErrorReport.source;
    else p = InputFiles[j-1].filename;

    switch(error_format)
    {
        case 0:  /* RISC OS error message format */

            if (!(ErrorReport.main_flag)) printf("\"%s\", ", p);
            printf("line %d: ", ErrorReport.line_number);
            break;

        case 1:  /* Microsoft error message format */

            for (j=0; p[j]!=0; j++)
            {   if (p[j] == FN_SEP) with_extension_flag = TRUE;
                if (p[j] == '.') with_extension_flag = FALSE;
            }
            printf("%s", p);
            if (with_extension_flag) printf("%s", Source_Extension);
            printf("(%d): ", ErrorReport.line_number);
            break;

        case 2:  /* Macintosh Programmer's Workshop error message format */

            printf("File \"%s\"; Line %d\t# ", p, ErrorReport.line_number);
            break;
    }
}

/* ------------------------------------------------------------------------- */
/*   Fatal errors.                                                           */
/* ------------------------------------------------------------------------- */

extern void fatalerror(char *s)
{   print_preamble();

    printf("Fatal error: %s\n",s);
#ifdef ARC_THROWBACK
    throwback(0, s);
    throwback_end();
#endif
#ifdef MAC_FACE
    free_arrays();
    if (store_the_text)
        my_free(&all_text,"transcription text");
    longjmp(g_fallback, 1);
#endif
    exit(1);
}

extern void fatalerror_named(char *m, char *fn)
{
    sprintf(error_message_buff, "%s \"%s\"", m, fn);
    fatalerror(error_message_buff);
}

extern void memory_out_error(int32 size, int32 howmany, char *name)
{   if (howmany == 1)
        sprintf(error_message_buff,
            "Run out of memory allocating %d bytes for %s", size, name);
    else
        sprintf(error_message_buff,
            "Run out of memory allocating array of %dx%d bytes for %s",
                howmany, size, name);
    fatalerror(error_message_buff);
}

extern void memoryerror(char *s, int32 size)
{
    sprintf(error_message_buff,
        "The memory setting %s (which is %ld at present) has been \
exceeded.  Try running Inform again with $%s=<some-larger-number> on the \
command line.",s,(long int) size,s);
    fatalerror(error_message_buff);
}

/* ------------------------------------------------------------------------- */
/*   Warnings and survivable errors.                                         */
/* ------------------------------------------------------------------------- */

static int errors[MAX_ERRORS];

int no_errors, no_warnings;

char *forerrors_buff;
int  forerrors_pointer;

static void message(int style, char *s)
{   if (hash_printed_since_newline) printf("\n");
    hash_printed_since_newline = FALSE;
    print_preamble();
    printf("%s: %s\n", (style==1)?"Error":"Warning", s);
#ifdef ARC_THROWBACK
    throwback(style, s);
#endif
    if ((!concise_switch) && (forerrors_pointer > 0))
    {   forerrors_buff[forerrors_pointer] = 0;
        sprintf(forerrors_buff+68,"  ...etc");
        printf("> %s\n",forerrors_buff);
    }
}

extern void error(char *s)
{   if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
    /* for (i=0; i<no_errors; i++)
        if (errors[i] == no_syntax_lines) return; */
    errors[no_errors++] = no_syntax_lines;
    message(1,s);
}

extern void warning_named(char *s1, char *s2)
{   int i;
    i=strlen(s1)+strlen(s2)+3;
    if (i>250) s2[247-strlen(s1)]=0;
    sprintf(error_message_buff,"%s \"%s\"",s1,s2);
    no_warnings++;
    message(2,error_message_buff);
}

extern void dbnu_warning(char *type, char *name, int32 report_line)
{   int i;
    ErrorPosition E = ErrorReport;
    if (report_line != -1)
    {   ErrorReport.file_number = report_line/0x10000;
        ErrorReport.line_number = report_line%0x10000;
        ErrorReport.main_flag = (ErrorReport.file_number == 1);
    }
    sprintf(error_message_buff, "%s \"%s\" declared but not used", type, name);
    no_warnings++;
    i = concise_switch; concise_switch = TRUE;
    message(2,error_message_buff);
    concise_switch = i;
    ErrorReport = E;
}

extern void warning(char *s1)
{   no_warnings++;
    message(2,s1);
}

extern void obsolete_warning(char *s1)
{   if (obsolete_switch || (is_systemfile()==1)) return;
    sprintf(error_message_buff, "Obsolete usage: %s",s1);
    no_warnings++;
    message(2,error_message_buff);
}

extern void error_named(char *s1, char *s2)
{   int i;
    i=strlen(s1)+strlen(s2)+3;
    if (i>250) s2[247-strlen(s1)]=0;
    sprintf(error_message_buff,"%s \"%s\"",s1,s2);
    error(error_message_buff);
}

extern void error_named_at(char *s1, char *s2, int32 report_line)
{   int i;
    ErrorPosition E = ErrorReport;
    if (report_line != -1)
    {   ErrorReport.file_number = report_line/0x10000;
        ErrorReport.line_number = report_line%0x10000;
        ErrorReport.main_flag = (ErrorReport.file_number == 1);
    }
    i=strlen(s1)+strlen(s2)+3;
    if (i>250) s2[247-strlen(s1)]=0;
    sprintf(error_message_buff,"%s \"%s\"",s1,s2);
    i = concise_switch; concise_switch = TRUE;
    error(error_message_buff);
    ErrorReport = E; concise_switch = i;
}

extern void no_such_label(char *lname)
{   error_named("No such label as",lname);
}

extern void ebf_error(char *s1, char *s2)
{   sprintf(error_message_buff, "Expected %s but found %s", s1, s2);
    error(error_message_buff);
}

/* ------------------------------------------------------------------------- */
/*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
/*   to provide error throwback under the DDE environment                    */
/* ------------------------------------------------------------------------- */

#ifdef ARC_THROWBACK

#define DDEUtils_ThrowbackStart 0x42587
#define DDEUtils_ThrowbackSend  0x42588
#define DDEUtils_ThrowbackEnd   0x42589

#include "kernel.h"

extern void throwback_start(void)
{    _kernel_swi_regs regs;
     if (throwback_switch)
         _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
}

extern void throwback_end(void)
{   _kernel_swi_regs regs;
    if (throwback_switch)
        _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
}

int throwback_started = FALSE;

extern void throwback(int severity, char * error)
{   _kernel_swi_regs regs;
    if (!throwback_started)
    {   throwback_started = TRUE;
        throwback_start();
    }
    if (throwback_switch)
    {   regs.r[0] = 1;
        if ((ErrorReport.file_number == -1)
            || (ErrorReport.file_number == 0))
            regs.r[2] = (int) (InputFiles[0].filename);
        else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
        regs.r[3] = ErrorReport.line_number;
        regs.r[4] = (2-severity);
        regs.r[5] = (int) error;
       _kernel_swi(DDEUtils_ThrowbackSend, &regs, &regs);
    }
}

#endif

/* ========================================================================= */
/*   Data structure management routines                                      */
/* ------------------------------------------------------------------------- */

extern void init_errors_vars(void)
{   forerrors_buff = NULL;
    no_errors = 0; no_warnings = 0;
}

extern void errors_begin_pass(void)
{   ErrorReport.line_number = 0;
    ErrorReport.file_number = -1;
    ErrorReport.source = "<no text read yet>";
    ErrorReport.main_flag = FALSE;
}

extern void errors_allocate_arrays(void)
{   forerrors_buff = my_malloc(512, "errors buffer");
}

extern void errors_free_arrays(void)
{   my_free(&forerrors_buff, "errors buffer");
}

/* ========================================================================= */
