#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include "unicode.h"
#include "util.h"

void parser_fatal(struct parser *parser, const char *err) {
	fprintf(stderr, "Error at %d:%d: %s\n",
			parser->line, parser->col, err);
	fclose(parser->input);
	fclose(parser->output);
	exit(1);
}

uint32_t parser_getch(struct parser *parser) {
	if (parser->qhead) {
		return parser->queue[--parser->qhead];
	}
	if (parser->str) {
		uint32_t ch = utf8_decode(&parser->str);
		if (!ch || ch == UTF8_INVALID) {
			parser->str = NULL;
			return UTF8_INVALID;
		}
		return ch;
	}
	uint32_t ch = utf8_fgetch(parser->input);
	if (ch == '\n') {
		parser->col = 0;
		++parser->line;
	} else {
		++parser->col;
	}
	return ch;
}

void parser_pushch(struct parser *parser, uint32_t ch) {
	if (ch != UTF8_INVALID) {
		parser->queue[parser->qhead++] = ch;
	}
}

void parser_pushstr(struct parser *parser, const char *str) {
	parser->str = str;
}

int roff_macro(struct parser *p, char *cmd, ...) {
	FILE *f = p->output;
	int l = fprintf(f, ".%s", cmd);
	va_list ap;
	va_start(ap, cmd);
	const char *arg;
	while ((arg = va_arg(ap, const char *))) {
		fputc(' ', f);
		fputc('"', f);
		while (*arg) {
			uint32_t ch = utf8_decode(&arg);
			if (ch == '"') {
				fputc('\\', f);
				++l;
			}
			l += utf8_fputch(f, ch);
		}
		fputc('"', f);
		l += 3;
	}
	va_end(ap);
	fputc('\n', f);
	return l + 1;
}

void *xcalloc(size_t n, size_t s) {
	void *p = calloc(n, s);
	if (!p) {
		fputs("Out of memory\n", stderr);
		abort();
	}
	return p;
}

void *xrealloc(void *p, size_t s) {
	void *ret = realloc(p, s);
	if (!ret) {
		fputs("Out of memory\n", stderr);
		abort();
	}
	return ret;
}
