#include <fido.h>
#include <ascii.h>

long stonum();

/*
char compiler_name[80] = "Pablo Kleinman (4:900/101)";	/* who compiled this */
char compiler_name[80] = "Tomas Gradin (2:200/108)";	/* who compiled this */
*/

char compiler_name[80] = "Fido Software (1:125/111)";	/* who compiled this */



/*
	Compiles a .LXT file, producing the .SLN file (SM+ and LM messages)
	and the .CLN file, containing the CM messages.


*/

struct _fido fido;
struct _lang_hdr lang_hdr;
_stack = 4000;

#define SIZE 1000		/* max. strings per table */
unsigned *ptr;			/* table of text offsets */
char *text;			/* the text blob */
unsigned size;			/* it's maximum size */
unsigned highest;		/* highest index, per table */
unsigned amt;			/* how much in there, per table */

unsigned totl_strings;		/* total number of strings */
unsigned totl_size;		/* total string size */

char fname[SS];			/* language filename */

int logfile;			/* log shit here */

main(argc,argv)
int argc;
char **argv;
{
char buff[256],*cp,*op;
char et;			/* element type */
unsigned en;			/* element number */
int in,out;			/* file handles */
char phase;			/* which pass: LM, SM, CM */
int i,n,f;
unsigned totl_strings,totl_size;

	printf("Language Compiler (11 Jun 90) for ");
	copr();

	printf("\r\nCompiled by: \"%s\"\r\n",compiler_name);
	printf("\r\nThis is a restricted use program. Not for distribution.\r\n\r\n");
	allmem();
	fastfile(2);

	size= sizmem();
	if (size < 20000) {
		printf("Not enough memory to run!\r\n");
		exit(1);
	}
	ptr= (unsigned *) getmem(size);		/* get working storage */
	size -= SIZE * 2; 			/* how much left */
	text= (char *) &ptr[SIZE];		/* rest is text space */

	if (--argc) 				/* get filename */
		cpyarg(fname,*++argv);		/* from command line */
	else {
		printf("You have to specify a language filename\r\n");
		exit(1);
	}

	n= open("fido.sys",0);				/* load system stuff */
	if (n == -1) {
		clprintf("The Fido system file \"FIDO.SYS\" is missing!\r\n");
		exit(1);
	}
	read(n,&fido,sizeof(fido));
	close(n);
	if ((fido.fido_version != FIDOVER) && (fido.file_version != FILEVER)) {
		clprintf("Wrong Fido/FidoNet version!\r\n");
		exit(1);
	}

/* Pre-process the filename so that we have a clean copy of the name portion
around for making the full filename. Basically, lop off any extention and
make upper case. */

	for (cp= fname; *cp; cp++) {			/* remove any extention */
		if (*cp == '.') {			/* from filename */
			*cp= NUL;
			break;
		}
	}
	stoupper(fname);				/* make all upper case */

	strcpy(buff,fname);
	strcat(buff,".LXT");				/* input file name */
	in= open(buff,0);
	if (in == -1) {
		printf("Can't find \"%s\" input file!\r\n",buff);
		exit(1);
	}
	strcpy(text,fname);
	strcat(text,".LOG");
	logfile= creat(text,2);				/* start logging */

	clprintf("Log File:               %s\r\n",text);
	clprintf("Input File:             %s\r\n",buff);
	clprintf("Maximum String Size:    %8,lu\r\n",0L + size);
	clprintf("Maximum Table Elements: %8,d\r\n",SIZE);

/* Now compile the output file. */

	newdelim(" ,\t");				/* the only delimiters */
	phase= 'S';					/* from the top */
	totl_strings= totl_size= 0;			/* zero tally counters */

	while (phase != 'X') {
		switch (phase) {
			case 'S': out= makeout(".SLN"); break;
			case 'C': out= makeout(".CLN"); break;
		}

		op= text;				/* set output pointer */
		amt= highest= 0;			/* table index */
		lseek(in,0L,0);				/* start from the beginning */
		for (i= 0; i < SIZE; i++) ptr[i]= 0;	/* clear the table */

/* Lines have the format:

XMnnn	"string"

Where X is the element type, and nnn is the element number. */


		while (getline(in,buff,sizeof(buff))) {	/* read a line */
			cp= skip_delim(buff);
			et= toupper(*cp);		/* get element type */
			en= atoi(&cp[2]);		/* element number */

			if ((et == phase) || (et == 'N')) { /* if correct phase */
				cp= next_arg(cp);	/* skip keyword */
				cpyscrarg(op,cp);	/* copy, strip quotes */
				scrmac(op,op);		/* expand controls */
				if (en >= SIZE) {
					clprintf("OOPS -- A numerator is out of range!\r\n");
					clprintf("I give up!\r\n");
					phase= 'X';
					break;
				}
				ptr[en]= op - text;	/* set it's place in the table */
				if (en > highest) highest= en;
				++totl_strings;		/* count total defined */

/* Generally strings are null terminated. There is a special case where
they end with \377, the marker for the ubiquituous CR/LF/NUL sequence; the
377 takes the place of the null. */

				i= strlen(op) + 1;	/* if \377 */

/*
BREAKS FIDO -- "strcpy(buff,string(stringID))" fails if the string is not
NUL terminated! (cpyarg, strcpy, strcat...) Too bad, cuz it saved 200 bytes/file
immediately. (400 bytes more DS: space would be nice...)

				if (i > 1) if (op[i - 2] == '\377') --i; /* un-count NUL */
*/
				if (amt + i > size) {
					clprintf("OOPS -- Total size of text strings is too large!\r\n");
					clprintf("I give up!\r\n");
					phase= 'X';
					break;
				}
				amt += i;		/* total count */
				op += i;		/* advance pointer */
				totl_size += i;		/* total string size */
			}
		}

/* End of file -- write the table out. First we have to fixup all the offsets
in the pointer table; the table is loaded with the text immediately following;
the offsets should be relative to the start of the table. Note that the offset
for xM0 will be zero, since it is the first thing in the buffer. */

		n= highest * 2;				/* table base offset */
		n += 4;					/* 0th plus marker */
		f= -1;					/* -1 == no msg yet */
		for (i= 0; i <= highest; i++) {
			if (i && !ptr[i]) {		/* report unused IDs */
				if (f == -1) {
					clprintf("Unused stringIDs:\r\n");
					f= 0;		/* reuse as output counter */
				}
				clprintf("%cM%-3d  ",phase,i);
				if (++f >= 8) {		/* 8 per line max */
					clprintf("\r\n");
					f= 0;
				}
			}
			ptr[i] += n;			/* fixup offset */
		}
		if (f > 0) clprintf("\r\n");
		ptr[highest + 1]= -1;			/* end-of-table marker */

		write(out,(char *)&ptr[0],n);		/* write table */
 		if (write(out,text,amt) != amt) {	/* then text */
			clprintf("OOPS -- Disk full!\r\n");
			clprintf("I give up!\r\n");
			phase= 'X';
		}

/* Switch to the next phase. */

		switch (phase) {
			case 'S': 			/* this phase complete */
				clprintf("SM Table String Size:   %8,d\r\n",amt);
				clprintf("Highest Element:           SM%03d\r\n",highest);
				closeout(out); phase= 'C'; break;

			case 'C': 
				clprintf("CM Table String Size:   %8,d\r\n",amt);
				clprintf("Highest Element:           CM%03d\r\n",highest);
				closeout(out); phase= 'X'; break;
		}
	}
	clprintf("\r\n");
	clprintf("Total String Size:      %8,d\r\n",totl_size);
	clprintf("Total String Elements:  %8,d\r\n",totl_strings);
	close(in);
	close(logfile);
}

/* Generate the CRC and close the file. */

closeout(f)
int f;
{
int i;
char c;

	i= crcfile(f);
	c= i >> 8; write(f,&c,1);			/* MS byte first */
	c= i; write(f,&c,1);				/* then LS byte */
	close(f);
}

/* CRC a file. */

crcfile(f)
int f;
{
int i,n;

	lseek(f,0L,0);					/* go over the file */
	clrcrc();
	while (n= read(f,text,size)) {			/* generate the CRC */
		for (i= 0; i < n; i++) updcrc(text[i]);
	}
	return(fincrc());				/* complete the CRC */
}

/* Get a line from the file; return 0 if the file is empty. */

getline(f,buff,len)
int f;
char *buff;
{
char *cp;

	while (rline(f,buff,len)) {			/* read a line of source, */
		cp= skip_delim(buff);
		if (*cp == ';') continue;		/* ignore comments */
		if (! *cp) continue;			/* and blank lines */
		return(1);				/* got one */
	}
	return(0);					/* file empty */
}

/* Make an output file and basic header. */

makeout(ext)
char *ext;
{
char *cp,buff[SS];
int out,i;

	strcpy(buff,fname);			/* make full name */
	strcat(buff,ext);
	clprintf("\r\n");
	printf("Creating output file: \"%s\"\r\n",buff);
	out= creat(buff,2);
	if (out == -1) {
		printf("File creation error!\r\n");
		exit(1);
	}
	for (cp= (char *) &lang_hdr, i= sizeof(struct _lang_hdr); i--;) *cp++= NUL;

	strcat(lang_hdr.junk,"This file is part of the Fido/FidoNet system by\r\n");
	strcat(lang_hdr.junk,"Tom Jennings/Fido Software, Box 77731, San\r\n");
	strcat(lang_hdr.junk,"Francisco CA 94107, USA. You may use this file with\r\n");
	strcat(lang_hdr.junk,"Fido/FidoNet only. Do not modify this file.\r\n");
	strcat(lang_hdr.junk,"\032");			/* Control-Z */

	strcpy(lang_hdr.compiler,compiler_name);	/* who dun it */
	lang_hdr.version= LANGVER;			/* set version, */
	write(out,&lang_hdr,sizeof(struct _lang_hdr));	/* write out the header */

	return(out);					/* file handle */
}

/* Copy a script arg; either a single word or a quoted string. */

cpyscrarg(dp,sp)
char *dp,*sp;
{
char lastc,c,q;

	if (*sp == '"') q= *sp++; else q= NUL;		/* optional quote stripping */
	lastc= NUL;
	while (c= *sp) {
		++sp;				/* next ... */
		if ((c == q) && (lastc != '\\')) /* if the quote char & not quoted */
			break;			/* end of argument */
		if (!q && delim(c)) break;	/* else stop if a delimiter */
		*dp++= c;			/* else part of same arg */
		lastc= c;			/* remember last char */
	}
	*dp= NUL;
}

/* Convert C-like control characters. */

static scrmac(dp,sp)
char *dp,*sp;
{
char *cp,c;
int i;

	while (c= *sp++) {
		if (c == '\\') {			/* if literal, */
			c= *sp;				/* get the next char */
			if (c) ++sp;			/* (dont skip the NUL!) */
			switch (tolower(c)) {
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':		/* octal! */
					c= stonum(--sp,8); 
					while (isdigit(*sp)) ++sp;
					break;

				case 'r': c= CR; break;
				case 'n': c= LF; break;
				case 'b': c= BS; break;
				case 't': c= TAB; break;

				default: break;		/* else leave literal as-is */
			}
		}
		if (c) *dp++= c;
	}
	*dp= NUL;
}

clprintf(f)
char *f;
{
char buf[400],*p;

	_spr(buf,&f);
	p= buf;
	write (logfile,p,strlen(p));
	while (*p) bdos(2,*p++);

}
/* Return true if the FBIT is set. */

fbit(bit)
int bit;
{
	return(fido.fbits[bit / sizeof(char)] & (1 << bit % sizeof(char)));
}
