/*
 * error.c: Halibut error handling
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "halibut.h"

/*
 * Error flags
 */
#define PREFIX 0x0001		/* give `halibut:' prefix */
#define FILEPOS 0x0002		/* give file position prefix */

static void do_error(int code, va_list ap)
{
    char error[1024];
    char auxbuf[256];
    char *sp, *sp2;
    wchar_t *wsp;
    filepos fpos, fpos2;
    int flags;

    switch (code) {
    case err_nomemory:		/* no arguments */
	sprintf(error, "out of memory");
	flags = PREFIX;
	break;
    case err_optnoarg:
	sp = va_arg(ap, char *);
	sprintf(error, "option `-%.200s' requires an argument", sp);
	flags = PREFIX;
	break;
    case err_nosuchopt:
	sp = va_arg(ap, char *);
	sprintf(error, "unrecognised option `-%.200s'", sp);
	flags = PREFIX;
	break;
    case err_noinput:		/* no arguments */
	sprintf(error, "no input files");
	flags = PREFIX;
	break;
    case err_cantopen:
	sp = va_arg(ap, char *);
	sprintf(error, "unable to open input file `%.200s'", sp);
	flags = PREFIX;
	break;
    case err_nodata:		/* no arguments */
	sprintf(error, "no data in input files");
	flags = PREFIX;
	break;
    case err_brokencodepara:
	fpos = *va_arg(ap, filepos *);
	sprintf(error,
		"every line of a code paragraph should begin `\\c'");
	flags = FILEPOS;
	break;
    case err_kwunclosed:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `}' after paragraph keyword");
	flags = FILEPOS;
	break;
    case err_kwexpected:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected a paragraph keyword");
	flags = FILEPOS;
	break;
    case err_kwillegal:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected no paragraph keyword");
	flags = FILEPOS;
	break;
    case err_kwtoomany:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected only one paragraph keyword");
	flags = FILEPOS;
	break;
    case err_bodyillegal:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected no text after paragraph keyword");
	flags = FILEPOS;
	break;
    case err_badparatype:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "command `%.200s' unrecognised at start of"
		" paragraph", sp);
	flags = FILEPOS;
	break;
    case err_badmidcmd:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "command `%.200s' unexpected in mid-paragraph", sp);
	flags = FILEPOS;
	break;
    case err_unexbrace:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "brace character unexpected in mid-paragraph");
	flags = FILEPOS;
	break;
    case err_explbr:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `{' after command");
	flags = FILEPOS;
	break;
    case err_commenteof:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "end of file unexpected inside `\\#{...}' comment");
	flags = FILEPOS;
	break;
    case err_kwexprbr:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected `}' after cross-reference");
	flags = FILEPOS;
	break;
    case err_missingrbrace:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unclosed braces at end of paragraph");
	flags = FILEPOS;
	break;
    case err_nestedstyles:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unable to nest text styles");
	flags = FILEPOS;
	break;
    case err_nestedindex:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "unable to nest index markings");
	flags = FILEPOS;
	break;
    case err_nosuchkw:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "unable to resolve cross-reference to `%.200s'",
		sp);
	flags = FILEPOS;
	break;
    case err_multiBR:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "multiple `\\BR' entries given for `%.200s'", sp);
	flags = FILEPOS;
	break;
    case err_nosuchidxtag:
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "`\\IM' on unknown index tag `%.200s'", sp);
	flags = 0;
	/* FIXME: need to get a filepos to here somehow */
	break;
    case err_cantopenw:
	sp = va_arg(ap, char *);
	sprintf(error, "unable to open output file `%.200s'", sp);
	flags = PREFIX;
	break;
    case err_macroexists:
	fpos = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "macro `%.200s' already defined", sp);
	flags = FILEPOS;
	break;
    case err_sectjump:
	fpos = *va_arg(ap, filepos *);
	sprintf(error, "expected higher heading levels before this one");
	flags = FILEPOS;
	break;
    case err_winhelp_ctxclash:
	fpos = *va_arg(ap, filepos *);
	sp = va_arg(ap, char *);
	sp2 = va_arg(ap, char *);
	sprintf(error, "Windows Help context id `%.200s' clashes with "
		"previously defined `%.200s'", sp, sp2);
	flags = FILEPOS;
	break;
    case err_multikw:
	fpos = *va_arg(ap, filepos *);
	fpos2 = *va_arg(ap, filepos *);
	wsp = va_arg(ap, wchar_t *);
	sp = ustrtoa(wsp, auxbuf, sizeof(auxbuf));
	sprintf(error, "paragraph keyword `%.200s' already defined at ",
		sp);
	sprintf(error + strlen(error), "%s:%d", fpos2.filename,
		fpos2.line);
	flags = FILEPOS;
	break;
    case err_whatever:
	sp = va_arg(ap, char *);
	vsprintf(error, sp, ap);
	flags = PREFIX;
	break;
    }

    if (flags & PREFIX)
	fputs("halibut: ", stderr);
    if (flags & FILEPOS) {
	fprintf(stderr, "%s:%d:", fpos.filename, fpos.line);
	if (fpos.col > 0)
	    fprintf(stderr, "%d:", fpos.col);
	fputc(' ', stderr);
    }
    fputs(error, stderr);
    fputc('\n', stderr);
}

void fatal(int code, ...)
{
    va_list ap;
    va_start(ap, code);
    do_error(code, ap);
    va_end(ap);
    exit(EXIT_FAILURE);
}

void error(int code, ...)
{
    va_list ap;
    va_start(ap, code);
    do_error(code, ap);
    va_end(ap);
}
