/* A filter that scans for lines of the form:
 * DCL_KEYWORD2(id, "string")
 * and "optimizes" them into a form requiring no run-time initialion
 * and compatible with gcc.
 */

#include <stdio.h>
#include "sys-params.h"
#include <_G_config.h>

int HandleEscapes(srcPtr, dstPtr, endChar)
     char **srcPtr; /* input: quoted C string, buf[-1] assumed to be '"' */
     char **dstPtr; /* output: C escapes (and initial '"') have been removed */
     int endChar;
     /* returns: -1 on error, else length of string (in buf) */
     /* terminating '"' is set to '\0' to aid further parsing */
{
    register char *src = *srcPtr, *dst = *dstPtr;
    for (;;) {
	int val;
	char c = *src++;
	if (c == endChar) {
	    int length = dst - *dstPtr;
	    *dst = 0;
	    *srcPtr = src;
	    *dstPtr = dst;
	    return length;
	}
	else if (c == '\\') {
	    char c = *src++;
	    switch (c) {
	      case '\\': case '"': case '\'': case '?':
		*dst++ = c; break;
	      case 'n': *dst++ = '\n'; break;
	      case 'r': *dst++ = '\r'; break;
	      case 'b': *dst++ = '\b'; break;
	      case 't': *dst++ = '\t'; break;
	      case 'v': *dst++ = '\v'; break;
	      case 'f': *dst++ = '\f'; break;
	      case 'a': *dst++ = '\a'; break;
	      case 'x':
		val = 0;
		for (;; src++) {
		    c = src[0];
		    if (c >= '0' && c <= '9')
			val = 16 * val + c - '0';
		    else if (c >= 'A' && c <= 'F')
			val = 16 * val + c - 'A' + 10;
		    else if (c >= 'a' && c <= 'f')
			val = 16 * val + c - 'a' + 10;
		    else
			break;
		    
		}
		*dst++ = val;
		break;
	      default:
		if (c < '0' || c > '7') { *dst++ = c; break; /*goto fail?*/ }
		c -= '0';
		if (src[0] >= '0' && src[0] <= '7') {
		    c = c * 8 + *src++ - '0';
		    if (src[0] >= '0' && src[0] <= '7')
			c = c * 8 + *src++ - '0';
		}
		*dst++ = c;
		break;
	     }
	}
	else if (c < ' ' || c >= 127) goto fail;
	else  *dst++ = c;
    }
  fail:
    *srcPtr = src;
    *dstPtr = dst;
    return -1;
}

enum SymbolKind {
    StringKind,
    KeywordKind, /* Symbol in KEYWORD package */
    CLispKind, /* Symbol in COMMON-LISP package */
    SchemeKind, /* Symbol in SCHEME package */
    BuiltinKind, /* Symbol in BUILTIN package */
    SymbolKind, /* General symbol in name package. */
};

static void do_pass(int);
static FILE *in = NULL;
static int encode_max = 100;
static char *encoded;
static int line_max = 500;
static char* line;
static char* string;
static char* start_sym = NULL;
static char* end_sym = NULL;
extern char* strdup();

int
main(argc, argv)
     int argc; char** argv;
{
    register FILE *out = stdout;

    string = (char*)malloc(line_max);
    line = (char*)malloc(line_max);

    encoded = (char*)malloc(encode_max);
    if (argc > 1)
	in = fopen(argv[1], "r");
    if (in == NULL) {
	fprintf(stderr, "Cannot open input file\n");
	exit(-1);
    }
    fprintf(out, "/* DO NOT EDIT THIS FILE! */\n");
    fprintf(out, "/* IT HAS BEEN AUTOMATICALLY GENERATED BY: */\n");
    fprintf(out, "/* %s %s. */\n\n", argv[0], argc > 1 ? argv[1] : "<stdin>");

    fprintf(out, "struct Symbol {\n");
    fprintf(out, "  char *vt;\n");
    fprintf(out, "  struct NilClass *_property_list;\n");
    fprintf(out, "  const char *_pchars;\n");
    fprintf(out, "  struct Package *_package;\n");
    fprintf(out, "  void *_value;\n");
    fprintf(out, "  int _flags : 8;\n");
    fprintf(out, "  int _plength : 24;};\n");
    fprintf(out, "struct StringC {\n");
    fprintf(out, "  char *vt;\n");
    fprintf(out, "  const char *_pchars;\n");
    fprintf(out, "  int _plength;};\n"); /*FIXME - size_t */
    fprintf(out, "extern struct NilClass NilSymbol;\n");
    fprintf(out, "extern char VT_StringC[1] asm(\"%s\");\n",
	    emit_vtable_name ("StringC"));
    fprintf(out, "extern char VT_Symbol[1] asm(\"%s\");\n",
	    emit_vtable_name ("Symbol"));
   fprintf(out, "\
extern struct Package KeywordPackage asm(\"__pkKEYWORD\");\n\
extern struct Package BuiltinPackage asm(\"__pkBUILTIN\");\n\
extern struct Package CLispPackage asm(\"__pkCOMMON___LISP\");\n\
extern struct Package SchemePackage asm(\"__pkSCHEME\");\n\
extern struct Package UserPackage asm(\"__pkUSER\");\n");

    do_pass(0);
    fseek(in, 0, 0);
    do_pass(1);
    fprintf(out, "struct Symbol *__START_SYM = &%s;\n", start_sym);
    fprintf(out, "struct Symbol *__END_SYM = &%s;\n", end_sym);
    exit(0);
}

static void do_pass(int pass)
{
    char *package_name;
    char *package_abbrev;
    enum SymbolKind kind;

    for ( ;; ) {
	int line_len = 0;
	char *ptr, *string_ptr;
	int string_len;
	char *id_start, *id_comma;
	int c = getc(in);
	if (c == EOF)
	    return;
	while (c == ' ' || c == '\t') { putchar(c); c = getc(in); }
	/* Read remainder of line (including '\n', if any). */
	while (c != EOF) {
	    /* -1 to allow final '\0' */
	    if (line_len >= line_max - 1) {
		line_max += 500;
		line = (char*)realloc(line, line_max);
		string = (char*)realloc(string, line_max);
	    }
	    line[line_len++] = c;
	    if (c == '\n')
		break;
	    c = getc(in);
	}
	line[line_len] = '\0';
	if (strncmp(line, "DCL_KEYWORD2", 12) == 0) {
	    kind = KeywordKind;
	    ptr = line + 12;
	}
	else if (strncmp(line, "DCL_LISPSYM2", 12) == 0) {
	    kind = CLispKind;
	    ptr = line + 12;
	}
	else if (strncmp(line, "DclSchemeSym", 12) == 0) {
	    kind = SchemeKind;
	    ptr = line + 12;
	}
	else if (strncmp(line, "DclQ_Builtin", 12) == 0) {
	    kind = BuiltinKind;
	    ptr = line + 12;
	}
	else if (strncmp(line, "DclString", 9) == 0) {
	    kind = StringKind;
	    ptr = line + 9;
	}
	else
	    goto no_match;
	c = *ptr++;
	while (c == ' ' || c == '\t') c = *ptr++;
	if (c != '(') goto no_match;
	id_start = ptr;
	while (c != ',' && c != '\0') c = *ptr++;
	if (c != ',') goto no_match;
	id_comma = ptr-1;
	c = *ptr++;
	while (c == ' ' || c == '\t') c = *ptr++;
	if (c != '"') goto no_match;
	string_ptr = string;
	string_len = HandleEscapes(&ptr, &string_ptr, '"');
	if (string_len < 0) goto no_match;
	if (*ptr++ != ')') goto no_match;
	/* ok: - print it */
	*id_comma = '\0';
	if (2*string_len +10 >= encode_max) {
	    encode_max += 500;
	    encoded = (char*)realloc(encoded, encode_max);
	}
	encode_string_to_label(string, string_len, encoded);
#if 0
	if (strcmp(string, encoded) == 0)
	    printf("#ifdef __GNUC__\n");
#endif
	switch (kind) {
	  case KeywordKind:
	    package_name = "Keyword";
	    package_abbrev = "KW";
	    goto emit_symbol;
	  case CLispKind:
	    package_name = "CLisp";
	    package_abbrev = "cl";
	    goto emit_symbol;
	  case SchemeKind:
	    package_name = "Scheme";
	    package_abbrev = "sc";
	    goto emit_symbol;
	  case BuiltinKind:
	    package_name = "Builtin";
	    package_abbrev = "QB";
	    goto emit_symbol;
	  emit_symbol:
	    if (pass == 0)
		printf("static const char _s_%s[] = \"%s\"",
		       id_start, string);
	    else {
		printf("struct Symbol %s asm(\"__%s%s\") = {\n",
		       id_start, package_abbrev, encoded);
		printf(" VT_Symbol, &NilSymbol, _s_%s, &%sPackage, 0, 1, %d}",
		       id_start, package_name, string_len);
		if (start_sym == NULL)
		    start_sym = strdup(id_start);
		end_sym = id_start;
	    }
	    break;
	  case StringKind:
	    if (pass == 0)
		printf("static const char _s_%s[] = \"%s\"",
		       id_start, string);
	    else {
		printf("struct StringC %s asm(\"__ST%s\") = {",
		       id_start, encoded);
		printf("  VT_StringC, _s_%s, %d}",
		       id_start, string_len);
	    }
#if 0
	    printf("extern StringC %s asm(\"__ST%s\")", id_start, encoded);
#endif
	    break;
	  default:
	    abort();
	}

#if 0
	if (strcmp(string, encoded) == 0) {
	    printf("\n#else\n");
	    printf("extern Symbol KEY__%s;\n", encoded);
	    printf("#define %s KEY__%s\n", id_start, encoded);
	    printf("#endif");
	}
#endif
	fputs(ptr, stdout);
	continue;

      no_match:
	fwrite(line, 1, line_len, stdout);
    }
}
