/*
 * Motif
 *
 * Copyright (c) 2025-2026 Tim Hentenaar
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: wmloutkey.c /main/8 1997/04/14 12:55:51 dbl $"
#endif
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/*
 * This module contains routines responsible for writing the .h files which
 * define the UIL lexer's keyword (token) tables. All files are written
 * into the current directory.
 *
 * Input:
 *	The resolved objects
 *	.dat files required to specify objects defined in Uil.y:
 *		keyword.dat
 *		reserved.dat
 *
 * Output:
 *	UilKeyTab.h
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wml.h"

/*
 * Routines used only in this module
 */
static void wmlKeyWBuildTables(void);
static void wmlKeyWClassTokens(void);
static void wmlKeyWGrammarTokens(void);
static int wmlKeyWGrammarClass(const char *token);
static void wmlKeyWArgTokens(void);
static void wmlKeyWReasonTokens(void);
static void wmlKeyWCharsetTokens(void);
static void wmlKeyWEnumvalTokens(void);
static void wmlKeyWChildTokens(void);
static void wmlKeyWMakeTokens(char *sens_name, int class, ObjectPtr obj);
static void wmlOutputUilKeyTab(void);
static void wmlOutputUilKeyTabBody(FILE *outfil,
                                   DynamicHandleListDefPtr tokvec,
                                   unsigned long *maxlen,
                                   unsigned long *maxkey);
static void wmlTokenClassString(char *dststg, WmlKeyWTokenPtr tok);
static void wmlTokenSymKString(char *dststg, size_t n, WmlKeyWTokenPtr tok);
static void wmlTokenTokenString(char *dststg, size_t n, WmlKeyWTokenPtr tok);
static void wmlOutputUilTokName(void);

/*
 * globals
 */

static const char canned_warn[] =
"/*\n\
**\tThis file is automatically generated.  Do not edit it by hand.\n\
**/\n\n";

#define	GrTokenMax 200 /* limit on grammar tokens */
static int grtok_max_val = 0;
static WmlGrammarTokenPtr grtok_vec[GrTokenMax];

/**
 * Output control routine
 */
void wmlOutputKeyWordFiles(void)
{
	wmlKeyWBuildTables();
	wmlOutputUilKeyTab();
	wmlOutputUilTokName();
}

/**
 * Routine to construct token tables for building UilKeyTab.h
 *
 * This routine constructs token entries for each token class which appears
 * in UilKeyTab.h (the token classes are defined in UilKeyDef.h). These
 * tokens are defined both by WML objects in object vectors, and by
 * tokens defined in Uil.y.
 */
static void wmlKeyWBuildTables(void)
{
	if (!wmlInitHList(wml_tok_sens_ptr,   1000, TRUE, TRUE) ||
	    !wmlInitHList(wml_tok_insens_ptr, 1000, TRUE, TRUE)) {
		fputs("Out of memory allocating token vectors\n", stderr);
		exit(EXIT_FAILURE);
	}

	memset(grtok_vec, 0, sizeof grtok_vec);

	/* Read and enter the tokens from Uil.y (via tokens.dat) */
	wmlKeyWGrammarTokens();

	/**
 	 * Enter the class, argument, reason, charset, child, and enumval tokens
 	 */
	wmlKeyWClassTokens();
	wmlKeyWArgTokens();
	wmlKeyWReasonTokens();
	wmlKeyWCharsetTokens();
	wmlKeyWEnumvalTokens();
	wmlKeyWChildTokens();
}

/**
 * Routine to read and enter tokens defined in Uil.y into the token tables.
 */
static void wmlKeyWGrammarTokens(void)
{
	FILE *infil;              /* input file (tokens.dat) */
	int scanres;              /* result of fscanf */
	int lineno = 0;           /* current line number */
	int tokval;               /* current token id (as value) */
	WmlGrammarTokenPtr grtok; /* new grammar token */
	char token[100];          /* current token */
	char class[100];          /* current class */
	char sens_name[100];      /* for case-insensitive name */
	size_t ndx;

	/**
	 * Read tokens.dat. Recognize and save all tokens. Some are saved in the
	 * global tokens vector so they will be put into UilKeyTab.h. All are
	 * saved in an ordered vector to write UilTokName.h
	 *
	 * Special handling is required for tokens whose yacc definition conflicts
	 * with common literals. In these cases, the token literal does not map
	 * directly to its keyword in the language, and must be mapped as a special
	 * case:
	 *	UILTRUE		-> true/TRUE
	 *	UILFALSE	-> false/FALSE
	 *	UILfile	 	-> file/FILE
	 *	UILeof	 	-> eof/EOF
	 */
	if (!(infil = fopen("tokens.dat", "r"))) {
		fputs("Couldn't open tokens.dat", stderr);
		return;
	}

	while ((scanres = fscanf(infil, "%s %d %s", token, &tokval, class)) != EOF) {
		++lineno;
		if (scanres != 3) {
			fprintf(stderr, "Malformatted line at tokens.dat:%d\n", lineno);
			continue;
		}

		/**
		 * Convert the token class, and construct a grammar token.
		 */
		if (!(grtok = malloc(sizeof *grtok))) {
			fprintf(stderr, "Out of memory at tokens.dat:%d\n", lineno);
			break;
		}

		grtok->class = wmlKeyWGrammarClass(class);
		grtok->token = wmlAllocateString(token);
		grtok->val   = tokval;

		/**
		 * Save the token in the grammar token vector, indexed by its
		 * value (for UilTokName.h)
		 */
		if (grtok->val < GrTokenMax) {
			grtok_vec[grtok->val] = grtok;
			if (grtok->val > grtok_max_val)
				grtok_max_val = grtok->val;
		} else {
			fprintf(stderr, "Token id %d for %s exceeds GrTokenMax\n",
			       grtok->val, grtok->token);
		}

		/**
		 * Enter tokens which appear in the keyword tables as keyword
		 * tokens. These have their lower case names entered as the
		 * case-insensitive keyword token string. Do special token
		 * literal mapping.
		 */
		if (grtok->class == WmlTokenClassKeyword ||
		    grtok->class == WmlTokenClassReserved) {
			strcpy(sens_name, grtok->token);
			for (ndx = 0; ndx < strlen(sens_name); ndx++)
				sens_name[ndx] = _lower(sens_name[ndx]);
			if (grtok->token[0] == 'U' && grtok->token[1] == 'I' && grtok->token[2] == 'L')
				memmove(sens_name, sens_name + 3, strlen(sens_name) - 2);
			wmlKeyWMakeTokens(sens_name, grtok->class, grtok);
		}
	}

	fclose(infil);
}

/**
 * This routine translates a string identifying a token class into
 * its matching internal literal.
 */
static int wmlKeyWGrammarClass(const char *token)
{
	assert(token);
	switch (*token) {
	case 'a':
		if (!strcmp(token, "argument"))
			return WmlTokenClassArgument;
		break;
	case 'c':
		switch (token[1]) {
		case 'h':
			if (!strcmp(token, "child"))
				return WmlTokenClassChild;
			if (!strcmp(token, "charset"))
				return WmlTokenClassCharset;
			break;
		case 'l':
			if (!strcmp(token, "class"))
				return WmlTokenClassClass;
			break;
		case 'o':
			if (!strcmp(token, "color"))
				return WmlTokenClassColor;
			break;
		}
		break;
	case 'e':
		if (!strcmp(token, "enumval"))
			return WmlTokenClassEnumval;
		break;
	case 'f':
		if (!strcmp(token, "font"))
			return WmlTokenClassFont;
		break;
	case 'i':
		if (!strcmp(token, "identifier"))
			return WmlTokenClassIdentifier;
		break;
	case 'k':
		if (!strcmp(token, "keyword"))
			return WmlTokenClassKeyword;
		break;
	case 'l':
		if (!strcmp(token, "literal"))
			return WmlTokenClassLiteral;
		break;
	case 'r':
		switch (token[2]) {
		case 'a':
			if (!strcmp(token, "reason"))
				return WmlTokenClassReason;
			break;
		case 's':
			if (!strcmp(token, "reserved"))
				return WmlTokenClassReserved;
			break;
		}
		break;
	case 's':
		if (!strcmp(token, "special"))
			return WmlTokenClassSpecial;
		break;
	case 'u':
		if (!strcmp(token, "unused"))
			return WmlTokenClassUnused;
		break;
	}

	fprintf(stderr, "Unrecognized token class %s\n", token);
	return 0;
}

/**
 * Routine to process the class objects and enter them in the token tables.
 * Aliases are also entered, under their own names.
 */
static void wmlKeyWClassTokens(void)
{
	int ndx;               /* loop index */
	int alias_ndx;         /* alias loop index */
	WmlClassDefPtr clsobj; /* class object */

	/**
	 * Make tokens for all class entries
	 */
	for (ndx = 0; ndx < wml_obj_class_ptr->cnt; ndx++) {
		clsobj = (WmlClassDefPtr)wml_obj_class_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(clsobj->syndef->name, WmlTokenClassClass, (ObjectPtr)clsobj);
		for (alias_ndx = 0; alias_ndx < clsobj->syndef->alias_cnt; alias_ndx++) {
			wmlKeyWMakeTokens(clsobj->syndef->alias_list[alias_ndx],
			                  WmlTokenClassClass, (ObjectPtr)clsobj);
		}
	}
}

/**
 * Routine to process the argument objects and enter them in the token tables.
 * Aliases are also entered, under their own names.
 */
static void wmlKeyWArgTokens(void)
{
	int ndx;                  /* loop index */
	int alias_ndx;            /* alias loop index */
	WmlResourceDefPtr resobj; /* resource object */

	/**
	 * Make tokens for all argument entries
	 */
	for (ndx = 0; ndx < wml_obj_arg_ptr->cnt; ndx++) {
		resobj = (WmlResourceDefPtr)wml_obj_arg_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(resobj->syndef->name, WmlTokenClassArgument, (ObjectPtr)resobj);
		for (alias_ndx = 0; alias_ndx < resobj->syndef->alias_cnt; alias_ndx++) {
			wmlKeyWMakeTokens(resobj->syndef->alias_list[alias_ndx],
			                  WmlTokenClassClass, (ObjectPtr)resobj);
		}
	}
}

/**
 * Routine to process the reason objects and enter them in the token tables.
 */
static void wmlKeyWReasonTokens(void)
{
	int ndx;                  /* loop index */
	int alias_ndx;            /* alias loop index */
	WmlResourceDefPtr resobj; /* resource object */

	/**
	 * Make tokens for all reason entries
	 */
	for (ndx = 0; ndx < wml_obj_reason_ptr->cnt; ndx++) {
		resobj = (WmlResourceDefPtr)wml_obj_reason_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(resobj->syndef->name, WmlTokenClassReason, (ObjectPtr)resobj);
		for (alias_ndx = 0; alias_ndx < resobj->syndef->alias_cnt; alias_ndx++) {
			wmlKeyWMakeTokens(resobj->syndef->alias_list[alias_ndx],
			                  WmlTokenClassClass, (ObjectPtr)resobj);
		}
	}
}

/**
 * Routine to process the child objects and enter them in the token tables.
 */
static void wmlKeyWChildTokens(void)
{
	int ndx;                 /* loop index */
	WmlChildDefPtr childobj; /* child object */

	/**
	 * Make tokens for all child entries
	 */
	for (ndx = 0; ndx < wml_obj_child_ptr->cnt; ndx++) {
		childobj = (WmlChildDefPtr)wml_obj_child_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(childobj->syndef->name, WmlTokenClassChild, (ObjectPtr)childobj);
	}
}

/**
 * Routine to process the charset objects and enter them in the token tables.
 */
static void wmlKeyWCharsetTokens(void)
{
	int ndx;                /* loop index */
	int alias_ndx;          /* alias loop index */
	WmlCharSetDefPtr csobj; /* character set object */

	/**
	 * Make tokens for all charset entries
	 */
	for (ndx = 0; ndx < wml_obj_charset_ptr->cnt; ndx++) {
		csobj = (WmlCharSetDefPtr)wml_obj_charset_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(csobj->syndef->name, WmlTokenClassCharset, (ObjectPtr)csobj);
		for (alias_ndx = 0; alias_ndx < csobj->syndef->alias_cnt; alias_ndx++) {
			wmlKeyWMakeTokens(csobj->syndef->alias_list[alias_ndx],
			                  WmlTokenClassCharset, (ObjectPtr)csobj);
		}
	}
}

/**
 * Routine to process the enumval objects and enter them in the token tables.
 */
static void wmlKeyWEnumvalTokens(void)
{
	int ndx;                  /* loop index */
	WmlEnumValueDefPtr esobj; /* enumeration value object */

	/**
	 * Make tokens for all enumval entries
	 */
	for (ndx = 0; ndx < wml_obj_enumval_ptr->cnt; ndx++) {
		esobj = (WmlEnumValueDefPtr)wml_obj_enumval_ptr->hvec[ndx].objptr;
		wmlKeyWMakeTokens(esobj->syndef->name, WmlTokenClassEnumval, (ObjectPtr)esobj);
	}
}

/**
 * Routine to create tokens and enter them in the token list.
 *
 * This routine constructs a case-sensitive and a case-insensitive token
 * and enters them the token vectors.
 */
static void wmlKeyWMakeTokens(char *sens_name, int class, ObjectPtr obj)
{
	size_t ndx;
	char insens_name[100];
	WmlKeyWTokenPtr senstok;   /* case-sensitive token */
	WmlKeyWTokenPtr insenstok; /* case-insensitive token */

	/**
	 * Create both tokens, with one having an upper-case name. The names
	 * are entered only in the order vector, not in the token itself.
	 */
	if (!(senstok   = malloc(sizeof *senstok)) ||
	    !(insenstok = malloc(sizeof *insenstok))) {
	    fputs("wmlKeyWMakeTokens: Out of memory", stderr);
	    abort();
	}

	senstok->class    = class;
	senstok->objdef   = obj;
	insenstok->class  = class;
	insenstok->objdef = obj;

	for (ndx = 0; ndx < strlen(sens_name) && ndx < sizeof(insens_name) - 1; ndx++)
		insens_name[ndx] = _upper(sens_name[ndx]);
	insens_name[ndx] = '\0';
	wmlInsertInKeyList(wml_tok_sens_ptr,   sens_name,   senstok);
	wmlInsertInKeyList(wml_tok_insens_ptr, insens_name, insenstok);
}

/**
 * Routine to output UilKeyTab.h
 *
 * This routine dumps the tokens defined in the token tables into
 * UilKeyTab.h. Both the case-sensitive and case-insensitive token
 * lists are used.
 */
static void wmlOutputUilKeyTab(void)
{
static const char * const canned1 =
"\n/*    case sensitive keyword table    */\n\
static key_keytable_entry_type key_table_vec[] =\n\
  {\n";

static const char * const canned2 =
"  };\n\
externaldef(uil_sym_glbl) key_keytable_entry_type *key_table =\n\
\t\tkey_table_vec;\n\n\
/*    Maximum length of a keyword, and table size    */\n\
externaldef(uil_sym_glbl) unsigned long key_k_keyword_max_length = %lu;\n\
externaldef(uil_sym_glbl) unsigned long key_k_keyword_count = %lu;\n\n\
/*    case insensitive keyword table    */\n\
static key_keytable_entry_type key_table_case_ins_vec[] =\n\
  {\n";

static const char * const canned3 =
"  };\n\
externaldef(uil_sym_glbl) key_keytable_entry_type *key_table_case_ins =\n\
\t\tkey_table_case_ins_vec;\n";

	FILE *outfil;             /* output file */
	unsigned long maxlen = 0; /* max keyword length */
	unsigned long maxkey = 0; /* # entries in keyword table */

	/**
 	 * Open the output file.
 	 */
	if (!(outfil = fopen("UilKeyTab.h", "w+"))) {
		fputs("Couldn't open UilKeyTab.h", stderr);
		return;
	}
	fputs(canned_warn, outfil);

	/**
 	 * Print the case sensitive and insensitive tables
 	 */
	fputs(canned1, outfil);
	wmlOutputUilKeyTabBody(outfil, wml_tok_sens_ptr, &maxlen, &maxkey);
	fprintf(outfil, canned2, maxlen, maxkey);
	wmlOutputUilKeyTabBody(outfil, wml_tok_insens_ptr, &maxlen, &maxkey);
	fputs(canned3, outfil);

	puts("Created UilKeyTab.h");
	fclose(outfil);
}

/**
 * Routine to output the body of a keyword table
 */
static void wmlOutputUilKeyTabBody(FILE *outfil,
                                   DynamicHandleListDefPtr tokvec,
                                   unsigned long *maxlen,
                                   unsigned long *maxkey)
{
	int ndx;             /* loop index */
	WmlKeyWTokenPtr tok; /* current token */
	const char *tokname; /* string for token (keyword) */
	char tkclass[100];   /* token class string */
	char tksym[100];     /* token sym_k string */
	char tktoken[100];   /* token tkn_k_num string */

	/**
 	 * Loop over all tokens, and put out an entry for each.
 	 */
	for (ndx = 0; ndx < tokvec->cnt; ndx++) {
		tok     = (WmlKeyWTokenPtr)tokvec->hvec[ndx].objptr;
		tokname = tokvec->hvec[ndx].objname;
		wmlTokenClassString(tkclass, tok);
		wmlTokenSymKString(tksym,    sizeof tksym,   tok);
		wmlTokenTokenString(tktoken, sizeof tktoken, tok);
		fprintf(outfil, "    {%s, %s, %lu, %s, \"%s\"},\n",
	            tkclass, tksym, (unsigned long)strlen(tokname),
	            tktoken, tokname);

		if (strlen(tokname) > (size_t)*maxlen)
			*maxlen = strlen(tokname);
		*maxkey += 1;
	}
}

/**
 * Routine to return the string for a token class, tkn_k_class_...
 */
static void wmlTokenClassString(char *dststg, WmlKeyWTokenPtr tok)
{
	assert(dststg);
	assert(tok);

	switch (tok->class) {
	case WmlTokenClassArgument: strcpy(dststg, "tkn_k_class_argument"); break;
	case WmlTokenClassCharset:  strcpy(dststg, "tkn_k_class_charset");  break;
	case WmlTokenClassEnumval:  strcpy(dststg, "tkn_k_class_enumval");  break;
	case WmlTokenClassReason:   strcpy(dststg, "tkn_k_class_reason");   break;
	case WmlTokenClassClass:    strcpy(dststg, "tkn_k_class_class");    break;
	case WmlTokenClassChild:    strcpy(dststg, "tkn_k_class_child");    break;
	case WmlTokenClassKeyword:  strcpy(dststg, "tkn_k_class_keyword");  break;
	case WmlTokenClassReserved: strcpy(dststg, "tkn_k_class_reserved"); break;
	default: *dststg = '\0';
	}
}

/**
 * Routine to return the string for a sym_k_... for some object
 */
static void wmlTokenSymKString(char *dststg, size_t n, WmlKeyWTokenPtr tok)
{
	assert(dststg);
	assert(tok);
	assert(n >= 15);

	switch (tok->class) {
	case WmlTokenClassArgument:
		snprintf(dststg, n, "sym_k_%s_arg",
		         ((WmlResourceDefPtr)tok->objdef)->tkname);
		break;
	case WmlTokenClassCharset:
		snprintf(dststg, n, "sym_k_%s_charset",
		         ((WmlCharSetDefPtr)tok->objdef)->syndef->name);
		break;
	case WmlTokenClassEnumval:
		snprintf(dststg, n, "sym_k_%s_enumval",
		         ((WmlEnumValueDefPtr)tok->objdef)->syndef->name);
		break;
	case WmlTokenClassReason:
		snprintf(dststg, n, "sym_k_%s_reason",
		         ((WmlResourceDefPtr)tok->objdef)->tkname);
		break;
	case WmlTokenClassClass:
		snprintf(dststg, n, "sym_k_%s_object",
		         ((WmlClassDefPtr)tok->objdef)->tkname);
		break;
	case WmlTokenClassChild:
		snprintf(dststg, n, "sym_k_%s_child",
		         ((WmlChildDefPtr)tok->objdef)->tkname);
		break;
	case WmlTokenClassKeyword:
	case WmlTokenClassReserved:
		dststg[0] = '0';
		dststg[1] = '\0';
		break;
	default: *dststg = '\0';
	}
}

/**
 * Routine to return the string for a token number, tkn_k_num_...
 */
static void wmlTokenTokenString(char *dststg, size_t n, WmlKeyWTokenPtr tok)
{
	WmlGrammarTokenPtr grtok; /* grammar token */

	assert(dststg);
	assert(tok);
	assert(n);

	switch (tok->class) {
	case WmlTokenClassArgument: strcpy(dststg, "ARGUMENT_NAME"); break;
	case WmlTokenClassCharset:  strcpy(dststg, "CHARSET_NAME");  break;
	case WmlTokenClassEnumval:  strcpy(dststg, "ENUMVAL_NAME");  break;
	case WmlTokenClassReason:   strcpy(dststg, "REASON_NAME");   break;
	case WmlTokenClassClass:    strcpy(dststg, "CLASS_NAME");    break;
	case WmlTokenClassChild:    strcpy(dststg, "CHILD_NAME");    break;
	case WmlTokenClassKeyword:
	case WmlTokenClassReserved:
		grtok = (WmlGrammarTokenPtr)tok->objdef;
		assert(n > strlen(grtok->token));
        strncpy(dststg, grtok->token, n - 1);
        break;
	default: *dststg = '\0';
	}
}

/**
 * routine to output UilTokName.h
 */
static void wmlOutputUilTokName(void)
{
static const char * const canned1 =
"/*\tToken name table */\n\
static char *tok_token_name_table_vec[] = \n\
  {\n";

static const char * const canned2 =
"  };\n\
externaldef(uil_sym_glbl) char **tok_token_name_table =\n\
\t\ttok_token_name_table_vec;\n\n\
/*\tNumber of entries in table */\n\
externaldef(uil_sym_glbl) int tok_num_tokens = %d;\n";

	FILE *outfil; /* output file */
	int ndx;      /* loop index */

	/**
 	 * Open the output file.
 	 */
	if (!(outfil = fopen("UilTokName.h", "w+"))) {
		fputs("Couldn't open UilTokName.h", stderr);
		return;
	}
	fputs(canned_warn, outfil);
	fputs(canned1, outfil);

	/**
 	 * Print the token name entries
 	 * Note: vector size is max_val + 1 for zeroth token
 	 */
	for (ndx=0; ndx < grtok_max_val+1; ndx++) {
		fprintf(outfil, "    \"%s\",\n",
		        grtok_vec[ndx] ? grtok_vec[ndx]->token : "UNKNOWN_TOKEN");
	}

	fprintf(outfil, canned2, grtok_max_val+1);
	puts("Created UilTokName.h");
	fclose(outfil);
}

