/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# include <ctype.h>
# ifdef HAVE_GETOPT_H
#  include <getopt.h>
# endif
# include "cdefs.h"
# include "util.h"
# include <begemot.h>

RCSID("$Id: mktext.c 476 2001-07-09 08:09:10Z hbb $")

/*
 * Special codes:
 *	200	- nop
 *	201	- \r\n
 *	202	- \r\n\r\n
 *	210..227- spaces
 *
 * 100 ored -> EOS
 */

typedef struct word_t word_t;
struct word_t {
	char	*word;
	word_t	*next;
	u_int	code;
};

typedef struct string_t string_t;
struct string_t {
	string_t *next;
	char	*name;
	u_int	len;
	u_char	*str;
	u_int	off;
};

word_t	*words, **wlast = &words;
string_t *strings, **slast = &strings;

u_int	next_off = 2;
u_int	next_code;
string_t *cstr;
char *page;

char	*outfile, *deffile;
FILE	*outfp, *deffp;

void pre(char *);
char *outspace(char *);
char *outnl(char *);
char *outword(char *);
void wordlist(void);
void stringlist(void);
void ptrlist(void);
void add_code(u_char);
char *mgetline(void);
void parse_label(char *);
void parse_string(char *);
void defs(void);
void usage(void) DEAD_FUNC;

static char usgtxt[] =
"Usage: mktext [-h] [-d file] [-o file] file page-name\n"
"Options:\n"
" -d file	write definitions into file\n"
" -o file	write output into file\n"
" -h		print this info\n";

int
main(int argc, char *argv[])
{
	char *line;
	int opt;

	set_argv0(argv[0]);
	while((opt = getopt(argc, argv, "ho:d:")) != EOF)
		switch(opt) {

		  case 'h':
			usage();

		  case 'o':
			outfile = optarg;
			break;

		  case 'd':
			deffile = optarg;
			break;
		}
	argc -= optind;
	argv += optind;

	if(argc != 2)
		usage();

	if(outfile && (outfp = fopen(outfile, "w")) == NULL)
		panic("%s: %s", outfile, strerror(errno));
	if(deffile && (deffp = fopen(deffile, "w")) == NULL)
		panic("%s: %s", deffile, strerror(errno));

	if(freopen(argv[0], "r", stdin) == NULL)
		panic("%s: %s", argv[0], strerror(errno));

	page = argv[1];

	if(outfp) {
		fprintf(outfp, "\t.include \"defs.s11\"\n");
		fprintf(outfp, "\n");
		fprintf(outfp, ". = PAGE0\n");
		fprintf(outfp, "\n");
		fprintf(outfp, "\t;\n");
		fprintf(outfp, "\t; pointer to string table\n");
		fprintf(outfp, "\t;\n");
		fprintf(outfp, "\t.word\tT0\n");
		fprintf(outfp, "\n");
	}
	while((line = mgetline()) != NULL) {
		parse_label(line);
		free(line);
		if((line = mgetline()) == NULL)
			panic("no string after label '%s'", cstr->name);
		parse_string(line);
		free(line);
	}

	if(outfp) {
		stringlist();
		fprintf(outfp, "\t.IF LT 377-(.-PAGE0)\n");
		fprintf(outfp, "\t.ERROR\n");
		fprintf(outfp, "\t.ENDC\n");
		wordlist();
	}
	if(deffp) {
		defs();
	}

	if(outfile)
		fclose(outfp);
	if(deffile)
		fclose(deffp);
	return 0;
}

void
usage()
{
	fprintf(stderr, usgtxt);
	exit(1);
}


char *
mgetline()
{
	char *line, *p;

	while((line = readline(stdin)) != NULL) {
		line[strlen(line)-1] = 0;
		p = line;
		while(*p && isspace((u_char)*p))
			p++;
		if(*p != 0 && *p != '#')
			return line;
		free(line);
	}
	return NULL;
}

void
parse_label(char *line)
{
	char *end, *p;

	if(*line++ != ':')
		panic("missing ':' in label");
	p = line;
	while(isalnum((u_char)*p) || *p == '.')
		p++;
	end = p;
	while(isspace((u_char)*p))
		p++;
	if(*p != 0 || end == line)
		panic("bad label");

	cstr = xalloc(sizeof(string_t));
	cstr->len = 0;
	cstr->str = NULL;
	cstr->next = NULL;
	cstr->off = next_off;
	cstr->name = xalloc(end - line + 1);
	strncpy(cstr->name, line, end - line);
	cstr->name[end - line] = 0;
	*slast = cstr;
	slast = &cstr->next;
}

void
parse_string(char *line)
{
	char *p;

	pre(line);
	p = line;

	while(p != NULL && *p != 0) {
		if(*p == ' ')
			p = outspace(p);
		else if(*p == '\n')
			p = outnl(p);
		else
			p = outword(p);
	}
	if(p != NULL)
		add_code(0300);
	next_off += cstr->len;
}


char *
outspace(char *p)
{
	char *last = p;
	u_int n;

	while(*p && *p == ' ')
		p++;
	if(p > last + 1) {
		n = p - last - 1;
		while(n > 0) {
			if(n > 020) {
				add_code(0217);
				n -= 020;
			} else {
				add_code(0210+n-1);
				n = 0;
			}
		}
	}

	return p;
}

char *
outnl(char *p)
{
	if(p[1] == 0) {
		add_code(0301);
		return NULL;
	}
	if(p[1] == '\n') {
		if(p[2] == 0) {
			add_code(0302);
			return NULL;
		}
		add_code(0202);
		return p + 2;
	}
	add_code(0201);
	return p + 1;
}

char *
outword(char *p)
{
	char *last = p;
	word_t *w;

	while(*p != 0 && *p != ' ' && *p != '\n')
		p++;
	if(p == last)
		return p;
	for(w = words; w != NULL; w = w->next) {
		if(strlen(w->word) == (u_int)(p - last) &&
		   strncmp(w->word, last, p - last) == 0) {
			add_code(w->code);
			return p;
		}
	}
	w = xalloc(sizeof(word_t));
	if((w->code = next_code++) == 0200)
		panic("too many words");
	w->word = xalloc(p - last + 1);
	strncpy(w->word, last, p - last);
	w->word[p - last] = 0;

	w->next = NULL;
	*wlast = w;
	wlast = &w->next;

	add_code(w->code);

	return p;
}

void
add_code(u_char code)
{
	cstr->str = xrealloc(cstr->str, cstr->len + 1);
	cstr->str[cstr->len] = code;
	cstr->len++;
}

void
stringlist()
{
	string_t *s;
	u_int i;

	fprintf(outfp, "\t;\n");
	fprintf(outfp, "\t; string table\n");
	fprintf(outfp, "\t;\n");
	for(s = strings; s != NULL; s = s->next) {
		fprintf(outfp, "\t.byte\t");
		for(i = 0; i < s->len; i++)
			fprintf(outfp, "%o%s", s->str[i], (i == s->len-1)?"\n":", ");
	}
	fprintf(outfp, "\n");
}

void
defs()
{
	string_t *s;

	for(s = strings; s != NULL; s = s->next)
		fprintf(deffp, "S.%-14s= (256. * %s) + %o\n",
			s->name, page, s->off);
}

void
wordlist()
{
	word_t *w;
	char *p;

	fprintf(outfp, "\t;\n");
	fprintf(outfp, "\t; word table\n");
	fprintf(outfp, "\t;\n");
	for(w = words; w != NULL; w = w->next) {
		fprintf(outfp, "T%o:\t.asciz\t\"", w->code);
		p = w->word;
		while(*p) {
			if(iscntrl((u_char)*p))
				fprintf(outfp, "\"<%o>\"", (u_char)*p);
			else
				fprintf(outfp, "%c", *p);
			p++;
		}
		fprintf(outfp, "\"\n");
	}
	fprintf(outfp, "\t.byte\t0\n");
	fprintf(outfp, "\t.even\n");
}

void
pre(char *p)
{
	char *to = p;

	while(*p != 0) {
		if(*p == '\\') {
			switch(p[1]) {
			  case 'b':
				*to++ = '\b';
				break;
			  case 'n':
				*to++ = '\n';
				break;
			  case 't':
				*to++ = '\t';
				break;
			  default:
				*to++ = p[1];
				break;
			}
			p += 2;
		} else
			*to++ = *p++;
	}
	*to = 0;
}
