Skip to content
Snippets Groups Projects
baja.c 91.6 KiB
Newer Older
/* Synchronet command shell/module compiler */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
/* OS-specific */
#ifndef __unix__
	#include <io.h>
	#include <share.h>
#endif

/* ANSI */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
rswindell's avatar
rswindell committed
#include <errno.h>
rswindell's avatar
rswindell committed

/* Synchronet-specific */
#include "cmdshell.h"
#include "ars_defs.h"
#include "crc32.h"
#include "genwrap.h"    /* portability wrappers */
#include "dirwrap.h"    /* MAX_PATH */

#ifdef __BORLANDC__
unsigned _stklen = 20000; /* Set stack size in code, not header */
char **  label_name = NULL
, **goto_file = NULL
, **goto_label = NULL
, **call_file = NULL
, **call_label = NULL;
uint32_t *var_name = NULL, vars = 0;
char **   define_str = NULL
, **define_val = NULL;
char *    linestr = "%s %d: %s\n";
char      tmp[256];
uint *    label_indx = NULL
, *goto_indx = NULL
, *goto_line = NULL
, *call_indx = NULL
, *call_line = NULL;
char  bin_file[MAX_PATH + 1];
char  output_dir[MAX_PATH + 1];
char  include_dir[MAX_PATH + 1];
uint  display = 0, line = 0, labels = 0, gotos = 0, calls = 0, defines = 0, case_sens = 0;
BOOL  pause_on_error = FALSE;
	if (retval != 0) {
		if (bin_file[0] != 0)
		if (pause_on_error) {
			printf("\nHit enter to contiue...");
			getchar();
		}
	}
}

/****************************************************************************/
rswindell's avatar
rswindell committed
/* Converts an ASCII Hex string into an ulong								*/
/****************************************************************************/
deuce's avatar
deuce committed
uint32_t ahtoul(char *str)
	uint32_t l, val = 0;
	while ((l = (*str++) | 0x20) != 0x20)
		val = (l & 0xf) + (l >> 6 & 1) * 9 + val * 16;
	return val;
}

/* C Escape char */

uchar cesc(char ch)
{
rswindell's avatar
rswindell committed
		case 'r':
rswindell's avatar
rswindell committed
		case 'n':
rswindell's avatar
rswindell committed
		case 't':
rswindell's avatar
rswindell committed
		case 'b':
rswindell's avatar
rswindell committed
		case 'a':
rswindell's avatar
rswindell committed
		case 'f':
rswindell's avatar
rswindell committed
		case 'v':
rswindell's avatar
rswindell committed
		default:
rswindell's avatar
rswindell committed
	}
deuce's avatar
deuce committed
int32_t val(char *src, char *p)
{
	static int inside;
	if (IS_DIGIT(*p) || *p == '-')      /* Dec, Hex, or Oct */
		l = strtol(p, &p, 0);
	else if (*p == '\'') {  /* Char */
rswindell's avatar
rswindell committed
			p++;
rswindell's avatar
rswindell committed
		else
			l = *p;
		p++;
	}
	else if (*p == '.')    /* Bit */
		l = 1L << strtol(p + 1, &p, 0);
rswindell's avatar
rswindell committed
	else {
		printf("!SYNTAX ERROR (expecting integer constant):\n");
		printf(linestr, src, line, *p ? p : "<end of line>");
		return 0;
	}
	if (inside) {
		return l;
	}
	inside = 1;
	while (*p)
		switch (*(p++)) {
rswindell's avatar
rswindell committed
			case '+':
rswindell's avatar
rswindell committed
				break;
			case '-':
rswindell's avatar
rswindell committed
				break;
			case '*':
rswindell's avatar
rswindell committed
				break;
			case '/':
rswindell's avatar
rswindell committed
				break;
			case '%':
rswindell's avatar
rswindell committed
				break;
			case '&':
rswindell's avatar
rswindell committed
				break;
			case '|':
rswindell's avatar
rswindell committed
				break;
			case '~':
rswindell's avatar
rswindell committed
				break;
			case '^':
rswindell's avatar
rswindell committed
				break;
			case '>':
rswindell's avatar
rswindell committed
					p++;
rswindell's avatar
rswindell committed
				break;
			case '<':
rswindell's avatar
rswindell committed
					p++;
rswindell's avatar
rswindell committed
				break;
rswindell's avatar
rswindell committed
			case '#':
				inside = 0;
				return l;
		}
	inside = 0;
	return l;
void writecstr(char *p)
	int  j = 0, inquotes = 0;
	while (*p) {
		if (*p == '"') {   /* ignore quotes */
			if (inquotes)
rswindell's avatar
rswindell committed
				break;
rswindell's avatar
rswindell committed
			p++;
			continue;
		}
		if (*p == '\\')    { /* escape */
rswindell's avatar
rswindell committed
			p++;
			if (IS_DIGIT(*p)) {
				sprintf(tmp, "%.3s", p);
				str[j] = atoi(tmp);       /* decimal, NOT octal */
				if (IS_DIGIT(*(++p)))    /* skip up to 3 digits */
					if (IS_DIGIT(*(++p)))
rswindell's avatar
rswindell committed
						p++;
				j++;
				continue;
			}
			switch (*(p++)) {
rswindell's avatar
rswindell committed
				case 'x':
					tmp[0] = *(p++);
					tmp[1] = 0;
					if (isxdigit((uchar) * p)) {   /* if another hex digit, skip too */
						tmp[1] = *(p++);
						tmp[2] = 0;
					}
					str[j] = (char)ahtoul(tmp);
rswindell's avatar
rswindell committed
					break;
rswindell's avatar
rswindell committed
				case 'r':
rswindell's avatar
rswindell committed
					break;
				case 'n':
rswindell's avatar
rswindell committed
					break;
				case 't':
rswindell's avatar
rswindell committed
					break;
				case 'b':
rswindell's avatar
rswindell committed
					break;
				case 'a':
rswindell's avatar
rswindell committed
					break;
				case 'f':
rswindell's avatar
rswindell committed
					break;
				case 'v':
					str[j] = 11;  /* VT */
rswindell's avatar
rswindell committed
					break;
				default:
			continue;
		}
		str[j++] = *(p++);
	}
	str[j] = 0;
	fwrite(str, 1, j + 1, out);
	while (*p) {
		if (*p == '"') {   /* ignore quotes */
rswindell's avatar
rswindell committed
			p++;
			continue;
		}
		if (*p == '\\' && *(p + 1) == '"' && *(p + 2))
rswindell's avatar
rswindell committed
			p++;
		str[j++] = *(p++);
	}
	str[j] = 0;
	fwrite(str, 1, j + 1, out);
	for (i = 0; str[i]; i++)
		if (str[i] == TAB)
			str[i] = ' ';
void newvar(char* src, char *in)
	char    name[128];
	int32_t i, l;
	if (IS_DIGIT(*in)) {
		printf("!SYNTAX ERROR (illegal variable name):\n");
		printf(linestr, src, line, (char*)in);
		bail(1);
	sprintf(name, "%.80s", in);
	if (strncmp(name, "var_", 4) == 0)   /* decompiled source? */
		l = strtoul(name + 4, NULL, 16);
		l = crc32(name, 0);
		for (i = 0; i < vars; i++)
			if (var_name[i] == l)
	if ((var_name = (uint32_t *)realloc_or_free(var_name, sizeof(int32_t) * (vars + 1))) == NULL) {
		printf("Too many (%" PRIu32 ") variables!\n", vars);
		bail(1);
	}
	var_name[vars] = l;
	if (display)
		printf("newvar(%08" PRIX32 ")=%s\n", l, in);
rswindell's avatar
rswindell committed
	vars++;
void writecrc(char *src, char *in)
	char    name[128];
	char*   p;
	int32_t l;
	int     i;
rswindell's avatar
rswindell committed
	/* Automatically terminate variable name Oct-09-2000 rswindell */
	sprintf(name, "%.80s", in);
	p = strchr(name, ' ');

	if (!stricmp(name, "STR") || !name[0])
		l = 0;
	else if (strncmp(name, "var_", 4) == 0)  /* decompiled source? */
		l = strtoul(name + 4, NULL, 16);
rswindell's avatar
rswindell committed
	else {
		for (i = 0; i < vars; i++)
			if (var_name[i] == l)
rswindell's avatar
rswindell committed
				break;
			printf("!SYNTAX ERROR (expecting variable name):\n");
			printf(linestr, src, line, *in ? (char*)in : "<end of line>");
			bail(1);
rswindell's avatar
rswindell committed
	}
	fwrite(&l, 4, 1, out);
int32_t isvar(char *arg)
	char    name[128], *p;
	int32_t i, l;

	if (!arg || !(*arg) || IS_DIGIT(*arg))
		return 0;

	sprintf(name, "%.80s", arg);
	if ((p = strchr(name, ' ')) != NULL)  /* Truncate at first space */
		*p = 0;
	if (strncmp(name, "var_", 4) == 0)   /* decompiled source? */
		return strtoul(name + 4, NULL, 16);
	if (!case_sens)
rswindell's avatar
rswindell committed
		strupr(name);
	for (i = 0; i < vars; i++)
		if (var_name[i] == l)
rswindell's avatar
rswindell committed
			break;
	if (i == vars)
		return 0;
	return l;
}

int str_cmp(char *s1, char *s2)
{
	if (case_sens)
		return strcmp(s1, s2);
	return stricmp(s1, s2);
void expdefs(char *line)
	char str[512], *p, *sp, sav[2] = {0};
	int  i;

	str[0] = 0;
	for (p = line; *p; p++) {
		if (*p == ' ') {
			strcat(str, " ");
			continue;
		}

		if (*p == '"') {               /* Skip quoted text */
			sp = strchr(p + 1, '"');
			strcat(str, p);
			if (!sp)
				break;
			strcat(str, "\"");
			p += strlen(p);
			continue;
		}
		for (sp = p; *sp; sp++)
			if (!IS_ALPHANUMERIC(*sp) && *sp != '_')
				break;
		sav[0] = *sp;         /* Save delimiter */
		sav[1] = 0;
		*sp = 0;
		for (i = 0; i < defines; i++)
			if (!str_cmp(define_str[i], p))
				break;
		if (i < defines)
			strcat(str, define_val[i]);
rswindell's avatar
rswindell committed
		else
			strcat(str, p);
		if (!sav[0])         /* Last argument */
		p += strlen(p);
		strcat(str, sav);    /* Restore delimiter */
	}
	strcpy(line, str);
#define SKIPCTRLSP(p) while (*(p) <= ' ' && *(p) > 0) (p) ++

void compile(char *src)
{
	char *   str, *save, *p, *sp, *tp, *arg, *arg2, *arg3, *arg4, ch;
	uchar *  ar;
	char     path[MAX_PATH + 1];
deuce's avatar
deuce committed
	uint16_t i;
	uint16_t j;
	int32_t  l;
	int      savline;
	FILE *   in;
	if ((in = fopen(src, "rb")) == NULL) {
		printf("error %d opening %s for read\n", errno, src);
		bail(1);
	}
	line = 0;
	if ((str = malloc(1024)) == NULL) {
rswindell's avatar
rswindell committed
		printf("malloc error\n");
rswindell's avatar
rswindell committed
	}
	if ((save = malloc(1024)) == NULL) {
rswindell's avatar
rswindell committed
		printf("malloc error\n");
rswindell's avatar
rswindell committed
	}
	while (!feof(in) && !ferror(in)) {
		if (!fgets(str, 1000, in))
rswindell's avatar
rswindell committed
		truncsp(str);
		cvttab(str);
		line++;
		strcpy(save, str);
		p = str;
		SKIPCTRLSP(p);   /* look for beginning of command */
		if ((*p) == 0)
			continue;
		if (*p == '#')             /* remarks start with # */
			continue;
		expdefs(p);             /* expand defines */
		if (display)
			printf("%s\n", p);
		sp = strchr(p, ' ');
		arg = arg2 = arg3 = arg4 = "";
		if (sp) {
			*sp = 0;
			arg = sp + 1;
			sp = strchr(arg, ' ');
			if (sp) {
				arg2 = sp + 1;
				sp = strchr(arg2, ' ');
				if (sp) {
					arg3 = sp + 1;
					sp = strchr(arg3, ' ');
					if (sp) {
						arg4 = sp + 1;
		if (!stricmp(p, "!INCLUDE")) {
			savline = line;
			sp = strchr(arg, ' ');
			sprintf(path, "%s%s", include_dir, arg);
			line = savline;
			continue;
		if (!stricmp(p, "!DEFINE")) {                     /* define */
			sp = strchr(arg, ' ');
			if (sp)
				*sp = 0;
rswindell's avatar
rswindell committed
				break;
			tp = strrchr(arg2, '\"');
			if (!tp)
				tp = arg2;
			sp = strchr(tp, '#');
			if (sp)
				*sp = 0;
rswindell's avatar
rswindell committed
			truncsp(arg2);
			if ((define_str = (char **)realloc_or_free(define_str, sizeof(char *) * (defines + 1)))
			    == NULL) {
				printf("Too many defines.\n");
				bail(1);
			}
			if ((define_str[defines] = (char *)malloc(strlen(arg) + 1)) == NULL) {
				printf("Too many defines.\n");
				bail(1);
			}
			if ((define_val = (char **)realloc_or_free(define_val, sizeof(char *) * (defines + 1)))
			    == NULL) {
				printf("Too many defines.\n");
				bail(1);
			}
			if ((define_val[defines] = (char *)malloc(strlen(arg2) + 1)) == NULL) {
				printf("Too many defines.\n");
				bail(1);
			}
			strcpy(define_str[defines], arg);
			strcpy(define_val[defines], arg2);
rswindell's avatar
rswindell committed
			defines++;
			continue;
		}

		if (!stricmp(p, "!GLOBAL")) {             /* declare global variables */
			for (p = arg; *p && *p != '#';) {
				sp = strchr(p, ' ');
				newvar(src, p);
				if (!sp)
rswindell's avatar
rswindell committed
					break;
		if (!stricmp(p, "PATCH")) {
				tmp[0] = *p++;
				tmp[1] = *p++;
				tmp[2] = 0;
				if (!tmp[0])
rswindell's avatar
rswindell committed
					break;
				ch = ahtoul(tmp);
				fputc(ch, out);
			}
			continue;
		}
		if (!stricmp(p, "SHOW_VARS")) {
			fputc(CS_VAR_INSTRUCTION, out);
			fputc(SHOW_VARS, out);
			continue;
		}
		if (!stricmp(p, "COMPARE_ARS")) {
rswindell's avatar
rswindell committed
			strupr(arg);
			ar = arstr(&i, arg, NULL, NULL);
			if (ar != NULL) {
				fprintf(out, "%c%c", CS_COMPARE_ARS, (uchar)i);
				fwrite(ar, i, 1, out);
			continue;
		}

		if (!stricmp(p, "CHKSYSPASS")) {
			fprintf(out, "%c", CS_CHKSYSPASS);
			continue;
		}
		if (!stricmp(p, "INFO_SYSTEM")) {
			fprintf(out, "%c", CS_INFO_SYSTEM);
			continue;
		}
		if (!stricmp(p, "INFO_SUBBOARD")) {
			fprintf(out, "%c", CS_INFO_SUBBOARD);
			continue;
		}
		if (!stricmp(p, "INFO_DIRECTORY")) {
			fprintf(out, "%c", CS_INFO_DIRECTORY);
			continue;
		}
		if (!stricmp(p, "INFO_VERSION")) {
			fprintf(out, "%c", CS_INFO_VERSION);
			continue;
		}
		if (!stricmp(p, "INFO_USER")) {
			fprintf(out, "%c", CS_INFO_USER);
			continue;
		}
		if (!stricmp(p, "INFO_XFER_POLICY")) {
			fprintf(out, "%c", CS_INFO_XFER_POLICY);
			continue;
		}
		if (!stricmp(p, "LOGKEY")) {
			fprintf(out, "%c", CS_LOGKEY);
			continue;
		}
		if (!stricmp(p, "LOGKEY_COMMA")) {
			fprintf(out, "%c", CS_LOGKEY_COMMA);
			continue;
		}
		if (!stricmp(p, "LOGSTR")) {
			fprintf(out, "%c", CS_LOGSTR);
			continue;
		}

		if (!stricmp(p, "ONLINE")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_ONLINE);
			continue;
		}
		if (!stricmp(p, "OFFLINE")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_OFFLINE);
			continue;
		}
		if (!stricmp(p, "NEWUSER")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_NEWUSER);
			continue;
		}
		if (!stricmp(p, "LOGON")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_LOGON);
			continue;
		}
		if (!stricmp(p, "LOGOUT")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_LOGOUT);
			continue;
		}
		if (!stricmp(p, "EXIT")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_EXIT);
			continue;
		}

		if (!stricmp(p, "LOOP") || !stricmp(p, "LOOP_BEGIN")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_LOOP_BEGIN);
			continue;
		}
		if (!stricmp(p, "CONTINUE") || !stricmp(p, "CONTINUE_LOOP")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_CONTINUE_LOOP);
			continue;
		}
		if (!stricmp(p, "BREAK") || !stricmp(p, "BREAK_LOOP")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_BREAK_LOOP);
			continue;
		}
		if (!stricmp(p, "END_LOOP")) {
			fprintf(out, "%c%c", CS_ONE_MORE_BYTE, CS_END_LOOP);
			continue;
		}

		if (!stricmp(p, "USER_EVENT")) {
			if (!(*arg))
				break;
			if ((l = isvar(arg)) != 0) {
				fputc(CS_USE_INT_VAR, out);
				fwrite(&l, 4, 1, out); /* variable */
				fputc(2, out);       /* int offset */
				fputc(1, out);       /* int length */
				ch = 0;
			}                       /* place holder */
				ch = val(src, arg);
			fprintf(out, "%c%c", CS_TWO_MORE_BYTES, CS_USER_EVENT);
			fwrite(&ch, 1, 1, out);
			continue;
		}

		if (!stricmp(p, "PUT_NODE")) {
			fprintf(out, "%c", CS_PUT_NODE);
			continue;
		}
		if (!stricmp(p, "SYNC")) {
			fprintf(out, "%c", CS_SYNC);
			continue;
		}
		if (!stricmp(p, "ASYNC")) {
			fprintf(out, "%c", CS_ASYNC);
			continue;
		}
		if (!stricmp(p, "RIOSYNC")) {     /* deprecated */
			fprintf(out, "%c", CS_SYNC);
			continue;
		}
		if (!stricmp(p, "GETTIMELEFT")) {
			fprintf(out, "%c", CS_GETTIMELEFT);
			continue;
		}
		if (!stricmp(p, "SAVELINE")) {
			fprintf(out, "%c", CS_SAVELINE);
			continue;
		}
		if (!stricmp(p, "RESTORELINE")) {
			fprintf(out, "%c", CS_RESTORELINE);
			continue;
		}
		if (!stricmp(p, "IF_TRUE") || !stricmp(p, "IF_EQUAL")) {
			fprintf(out, "%c", CS_IF_TRUE);
			continue;
		}
		if (!stricmp(p, "IF_FALSE") || !stricmp(p, "IF_NOT_EQUAL")) {
			fprintf(out, "%c", CS_IF_FALSE);
			continue;
		}
		if (!stricmp(p, "IF_GREATER")) {
			fprintf(out, "%c", CS_IF_GREATER);
			continue;
		}
		if (!stricmp(p, "IF_GREATER_OR_EQUAL")
		    || !stricmp(p, "IF_EQUAL_OR_GREATER")) {
			fprintf(out, "%c", CS_IF_GREATER_OR_EQUAL);
			continue;
		}
		if (!stricmp(p, "IF_LESS")) {
			fprintf(out, "%c", CS_IF_LESS);
			continue;
		}
		if (!stricmp(p, "IF_LESS_OR_EQUAL")
		    || !stricmp(p, "IF_EQUAL_OR_LESS")) {
			fprintf(out, "%c", CS_IF_LESS_OR_EQUAL);
			continue;
		}
		if (!stricmp(p, "ENDIF") || !stricmp(p, "END_IF")) {
			fprintf(out, "%c", CS_ENDIF);
			continue;
		}
		if (!stricmp(p, "ELSE")) {
			fprintf(out, "%c", CS_ELSE);
			continue;
		}
		if (p[0] == ':') {                     /* :label */
rswindell's avatar
rswindell committed
			p++;
			sp = strchr(p, ' ');
			if (sp)
				*sp = 0;
			for (i = 0; i < labels; i++)
				if (!stricmp(label_name[i], p))
rswindell's avatar
rswindell committed
					break;
				printf("!SYNTAX ERROR (duplicate label name):\n");
				printf(linestr, src, line, p);
				bail(1);
			}
			if ((label_name = (char **)realloc_or_free(label_name, sizeof(char *) * (labels + 1)))
			    == NULL) {
rswindell's avatar
rswindell committed
				printf("Too many labels.\n");
				bail(1);
			}
			if ((label_indx = (uint *)realloc_or_free(label_indx, sizeof(int) * (labels + 1)))
			    == NULL) {
rswindell's avatar
rswindell committed
				printf("Too many labels.\n");
				bail(1);
			}
			if ((label_name[labels] = (char *)malloc(strlen(p) + 1)) == NULL) {
				printf("Too many labels.\n");
				bail(1);
			}
			strcpy(label_name[labels], p);
			label_indx[labels] = ftell(out);
rswindell's avatar
rswindell committed
			labels++;
			continue;
		}
		if (!stricmp(p, "GOTO")) {           /* goto */
			sp = strchr(arg, ' ');
			if (sp)
				*sp = 0;
			if ((goto_label = (char **)realloc_or_free(goto_label, sizeof(char *) * (gotos + 1)))
			    == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			if ((goto_file = (char **)realloc_or_free(goto_file, sizeof(char *) * (gotos + 1)))
			    == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			if ((goto_indx = (uint *)realloc_or_free(goto_indx, sizeof(int) * (gotos + 1)))
			    == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			if ((goto_line = (uint *)realloc_or_free(goto_line, sizeof(int) * (gotos + 1)))
			    == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			if ((goto_label[gotos] = (char *)malloc(strlen(arg) + 1)) == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			if ((goto_file[gotos] = (char *)malloc(strlen(str) + 1)) == NULL) {
				printf("Too many gotos.\n");
				bail(1);
			}
			strcpy(goto_label[gotos], arg);
			strcpy(goto_file[gotos], str);
			goto_indx[gotos] = ftell(out);
			goto_line[gotos] = line;
rswindell's avatar
rswindell committed
			gotos++;
			fprintf(out, "%c%c%c", CS_GOTO, 0xff, 0xff);
			continue;
		}
		if (!stricmp(p, "CALL")) {          /* call */
			sp = strchr(arg, ' ');
			if (sp)
				*sp = 0;
			if ((call_label = (char **)realloc_or_free(call_label, sizeof(char *) * (calls + 1)))
			    == NULL) {
				printf("Too many calls.\n");
				bail(1);
			}
			if ((call_file = (char **)realloc_or_free(call_file, sizeof(char *) * (calls + 1)))
			    == NULL) {
				printf("Too many calls.\n");
				bail(1);
			}
			if ((call_indx = (uint *)realloc_or_free(call_indx, sizeof(int) * (calls + 1)))
			    == NULL) {
				printf("Too many calls.\n");
				bail(1);
			}
			if ((call_line = (uint *)realloc_or_free(call_line, sizeof(int) * (calls + 1)))
			    == NULL) {
				printf("Too many calls.\n");
				bail(1);
			}
			if ((call_label[calls] = (char *)malloc(strlen(arg) + 1)) == NULL) {
				printf("Too many calls.\n");
				bail(1);
			}
			if ((call_file[calls] = (char *)malloc(strlen(src) + 1)) == NULL) {
				printf("Too many calls.\n");
			strcpy(call_label[calls], arg);
			strcpy(call_file[calls], src);
			call_indx[calls] = ftell(out);
			call_line[calls] = line;
rswindell's avatar
rswindell committed
			calls++;
			fprintf(out, "%c%c%c", CS_CALL, 0xff, 0xff);
			continue;
		}

		if (!stricmp(p, "RETURN")) {
			fprintf(out, "%c", CS_RETURN);
			continue;
		}
		if (!stricmp(p, "CMD_HOME")) {
			fprintf(out, "%c", CS_CMD_HOME);
			continue;
		}
		if (!stricmp(p, "CMDKEY")) {
			if (!stricmp(arg, "DIGIT"))
				ch = CS_DIGIT;
			else if (!stricmp(arg, "EDIGIT"))
				ch = CS_EDIGIT;
				ch = toupper(*arg);
			if (ch == '/')
				ch = *(arg + 1) | 0x80; /* high bit indicates slash required */
			else if (ch == '^' && (*(arg + 1) >= 0x40))
				ch = *(arg + 1) - 0x40; /* ctrl char */
			else if (ch == '\\')
				ch = cesc(*(arg + 1));
			else if (ch == '\'')
				ch = *(arg + 1);
			fprintf(out, "%c%c", CS_CMDKEY, ch);
			continue;
		}
		if (!stricmp(p, "CMDCHAR")) {
			fprintf(out, "%c%c", CS_CMDKEY, *arg);
			continue;
		}
		if (!stricmp(p, "SETLOGIC") || !stricmp(p, "SET_LOGIC")) {
			if (!stricmp(arg, "TRUE") || !stricmp(arg, "EQUAL"))
				ch = LOGIC_TRUE;
			else if (!stricmp(arg, "GREATER"))
				ch = LOGIC_GREATER;
			else if (!stricmp(arg, "LESS"))
				ch = LOGIC_LESS;
rswindell's avatar
rswindell committed
			else
				ch = LOGIC_FALSE;
			fprintf(out, "%c%c", CS_SETLOGIC, ch);
			continue;
		}

		if (!stricmp(p, "DEFINE_STR_VAR") || !stricmp(p, "STR")) {
			for (p = arg; *p && *p != '#';) {
				sp = strchr(p, ' ');
				fputc(CS_VAR_INSTRUCTION, out);
				fputc(DEFINE_STR_VAR, out);
				newvar(src, p);
				writecrc(src, p);
				if (!sp)
rswindell's avatar
rswindell committed
					break;
			continue;
		}
		if (!stricmp(p, "DEFINE_INT_VAR") || !stricmp(p, "INT")) {
			for (p = arg; *p && *p != '#';) {
				sp = strchr(p, ' ');
				fputc(CS_VAR_INSTRUCTION, out);
				fputc(DEFINE_INT_VAR, out);
				newvar(src, p);
				writecrc(src, p);
				if (!sp)
rswindell's avatar
rswindell committed
					break;
			continue;
		}
		if (!stricmp(p, "DEFINE_GLOBAL_STR_VAR") || !stricmp(p, "GLOBAL_STR")) {
			for (p = arg; *p && *p != '#';) {
				sp = strchr(p, ' ');
				fputc(CS_VAR_INSTRUCTION, out);
				fputc(DEFINE_GLOBAL_STR_VAR, out);
				newvar(src, p);
				writecrc(src, p);
				if (!sp)
rswindell's avatar
rswindell committed
					break;