parse.y   [plain text]


%{
/*-
 * Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
 *		at Electronni Visti IA, Kiev, Ukraine.
 *			All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/usr.bin/colldef/parse.y,v 1.31 2002/10/16 12:56:22 charnier Exp $");

#include <arpa/inet.h>
#include <err.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <unistd.h>
#include <sysexits.h>
#include <limits.h>
#include "collate.h"
#include "common.h"

#define PRI_UNDEFINED	(-1)
#define PRI_IGNORE	0
#define LINE_NONE	(-1)
#define LINE_NORMAL	0
#define LINE_ELLIPSIS	1
#define LINE_UNDEFINED	2
/* If UNDEFINED is specified with ellipses, we reposition prim_pri to
 * UNDEFINED_PRI, leaving gap for undefined characters. */
#define UNDEFINED_PRI	(COLLATE_MAX_PRIORITY - (COLLATE_MAX_PRIORITY >> 2))

extern FILE *yyin;
void yyerror(const char *fmt, ...) __printflike(1, 2);
int yyparse(void);
int yylex(void);
static void usage(void);
static void collate_print_tables(void);
static struct __collate_st_char_pri *getpri(int32_t);
static struct __collate_st_char_pri *haspri(int32_t);
static struct __collate_st_chain_pri *getchain(const wchar_t *, int);
static struct symbol *getsymbolbychar(wchar_t);
static struct symbol *hassymbolbychar(wchar_t);
static void setsymbolbychar(struct symbol *);
struct symbol *getstring(const wchar_t *);
static void makeforwardref(int, const struct symbol *, const struct symbol *);
static int charpricompar(const void *, const void *);
static int substcompar(const void *, const void *);
static int chainpricompar(const void *, const void *);
static void putsubst(int32_t, int, const wchar_t *);
static int hassubst(int32_t, int);
static const wchar_t *__collate_wcsnchr(const wchar_t *, wchar_t, int);
static int __collate_wcsnlen(const wchar_t *, int);
char *showwcs(const wchar_t *, int);
static char *charname(wchar_t);
static char *charname2(wchar_t);

char map_name[FILENAME_MAX] = ".";
wchar_t curr_chain[STR_LEN + 1];

char __collate_version[STR_LEN];
DB *charmapdb;
static DB *charmapdb2;
static DB *largemapdb;
static int nlargemap = 0;
static DB *substdb[COLL_WEIGHTS_MAX];
static int nsubst[COLL_WEIGHTS_MAX];
static DB *chaindb;
static int nchain = 0;
static DB *stringdb;
static DB *forward_ref[COLL_WEIGHTS_MAX];
static struct symbol *prev_weight_table[COLL_WEIGHTS_MAX];
static struct symbol *prev2_weight_table[COLL_WEIGHTS_MAX];
static struct symbol *weight_table[COLL_WEIGHTS_MAX];
static int prev_line = LINE_NONE;
static struct symbol *prev_elem;
static int weight_index = 0;
static int allow_ellipsis = 0;
static struct symbol sym_ellipsis = {SYMBOL_ELLIPSIS, PRI_UNDEFINED};
static struct symbol sym_ignore = {SYMBOL_IGNORE, PRI_IGNORE};
static struct symbol sym_undefined = {SYMBOL_CHAR, PRI_UNDEFINED};
static int order_pass = 0;

#undef __collate_char_pri_table
struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
struct __collate_st_chain_pri *__collate_chain_pri_table;
struct __collate_st_subst *__collate_substitute_table[COLL_WEIGHTS_MAX];
struct __collate_st_large_char_pri *__collate_large_char_pri_table;

int prim_pri = 2, sec_pri = 2;
#ifdef COLLATE_DEBUG
int debug;
#endif
struct __collate_st_info info = {{DIRECTIVE_FORWARD, DIRECTIVE_FORWARD}, 0, 0, 0, {PRI_UNDEFINED, PRI_UNDEFINED}};

/* Some of the code expects COLL_WEIGHTS_MAX == 2 */
int directive_count = COLL_WEIGHTS_MAX;

const char *out_file = "LC_COLLATE";
%}
%union {
	int32_t ch;
	wchar_t str[BUFSIZE];
}
%token SUBSTITUTE WITH
%token START_LC_COLLATE END_LC_COLLATE COLLATING_ELEMENT FROM COLLATING_SYMBOL
%token ELLIPSIS IGNORE UNDEFINED
%token ORDER RANGE ORDER_START ORDER_END ORDER_SECOND_PASS
%token <str> STRING
%token <str> DEFN
%token <str> ELEM
%token <ch> CHAR
%token <ch> ORDER_DIRECTIVE
%%
collate : datafile {
	FILE *fp;
	int localedef = (stringdb != NULL);
	int z;

	if (nchain > 0) {
		DBT key, val;
		struct __collate_st_chain_pri *t, *v;
		wchar_t *wp, *tp;
		int flags, i, len;

		if ((__collate_chain_pri_table = (struct __collate_st_chain_pri *)malloc(nchain * sizeof(struct __collate_st_chain_pri))) == NULL)
			err(1, "chain malloc");
		flags = R_FIRST;
		t = __collate_chain_pri_table;
		for(i = 0; i < nchain; i++) {
			if (chaindb->seq(chaindb, &key, &val, flags) != 0)
				err(1, "Can't retrieve chaindb %d", i);
			memcpy(&v, val.data, sizeof(struct __collate_st_chain_pri *));
			*t++ = *v;
			if ((len = __collate_wcsnlen(v->str, STR_LEN)) > info.chain_max_len)
				info.chain_max_len = len;
			flags = R_NEXT;
		}
		if (chaindb->seq(chaindb, &key, &val, flags) == 0)
			err(1, "More in chaindb after retrieving %d", nchain);
		qsort(__collate_chain_pri_table, nchain, sizeof(struct __collate_st_chain_pri), chainpricompar);
	}
	for(z = 0; z < directive_count; z++) {
		if (nsubst[z] > 0) {
			DBT key, val;
			struct __collate_st_subst *t;
			wchar_t *wp, *tp;
			int flags, i, j;
			int32_t cval;

			if ((__collate_substitute_table[z] = (struct __collate_st_subst *)calloc(nsubst[z], sizeof(struct __collate_st_subst))) == NULL)
				err(1, "__collate_substitute_table[%d] calloc", z);
			flags = R_FIRST;
			t = __collate_substitute_table[z];
			for(i = 0; i < nsubst[z]; i++) {
				if (substdb[z]->seq(substdb[z], &key, &val, flags) != 0)
					err(1, "Can't retrieve substdb[%d]", z);
				memcpy(&cval, key.data, sizeof(int32_t));
				/* we don't set the byte order of t->val, since we
				 * need it for sorting */
				t->val = cval;
				for(wp = (wchar_t *)val.data, tp = t->str, j = STR_LEN; *wp && j-- > 0;)
					*tp++ = htonl(*wp++);
				t++;
				flags = R_NEXT;
			}
			if (substdb[z]->seq(substdb[z], &key, &val, flags) == 0)
				err(1, "More in substdb[%d] after retrieving %d", z, nsubst[z]);
			qsort(__collate_substitute_table[z], nsubst[z], sizeof(struct __collate_st_subst), substcompar);
		}
	}
	if (nlargemap > 0) {
		DBT key, val;
		struct __collate_st_large_char_pri *t;
		struct __collate_st_char_pri *p;
		int flags, i, z;
		int32_t cval;

		if ((__collate_large_char_pri_table = (struct __collate_st_large_char_pri *)malloc(nlargemap * sizeof(struct __collate_st_large_char_pri))) == NULL)
			err(1, "nlargemap malloc");
		flags = R_FIRST;
		t = __collate_large_char_pri_table;
		for(i = 0; i < nlargemap; i++) {
			if (largemapdb->seq(largemapdb, &key, &val, flags) != 0)
				err(1, "Can't retrieve largemapdb %d", i);
			memcpy(&cval, key.data, sizeof(int32_t));
			memcpy(&p, val.data, sizeof(struct __collate_st_char_pri *));
			/* we don't set the byte order of t->val, since we
			 * need it for sorting */
			t->val = cval;
			for(z = 0; z < directive_count; z++)
				t->pri.pri[z] = htonl(p->pri[z]);
			t++;
			flags = R_NEXT;
		}
		if (largemapdb->seq(largemapdb, &key, &val, flags) == 0)
			err(1, "More in largemapdb after retrieving %d", nlargemap);
		qsort(__collate_large_char_pri_table, nlargemap, sizeof(struct __collate_st_large_char_pri), charpricompar);
	}

	if (info.undef_pri[0] == PRI_UNDEFINED) {
		int i;
		info.undef_pri[0] = prim_pri;
		for(i = 1; i < directive_count; i++)
			info.undef_pri[i] = -prim_pri;
	}

	if (localedef) {
		int ch, z, ret;
		if (sym_undefined.val == PRI_UNDEFINED) {
			int flags = R_FIRST;
			DBT key, val;
			struct symbol *v;
			while((ret = charmapdb->seq(charmapdb, &key, &val, flags)) == 0) {
				memcpy(&v, val.data, sizeof(struct symbol *));
				switch(v->type) {
				case SYMBOL_CHAR: {
					struct __collate_st_char_pri *p = haspri(v->u.wc);
					if (!p || p->pri[0] == PRI_UNDEFINED)
						warnx("<%s> was not defined", showwcs((const wchar_t *)key.data, key.size / sizeof(wchar_t)));
					break;
				}
				case SYMBOL_CHAIN: {
					struct __collate_st_chain_pri *p = getchain(v->u.str, EXISTS);
					if (p->pri[0] == PRI_UNDEFINED)
						warnx("<%s> was not defined", showwcs((const wchar_t *)key.data, key.size / sizeof(wchar_t)));
					break;
				}
				}
				flags = R_NEXT;
			}
			if (ret < 0)
				err(1, "Error retrieving from charmapdb");
		}
		for (ch = 1; ch < UCHAR_MAX + 1; ch++) {
			for(z = 0; z < directive_count; z++)
				if (__collate_char_pri_table[ch].pri[z] == PRI_UNDEFINED)
					__collate_char_pri_table[ch].pri[z] = (info.undef_pri[z] >= 0) ? info.undef_pri[z] : (ch - info.undef_pri[z]);
		}
		for (ch = 0; ch < nlargemap; ch++) {
			for(z = 0; z < directive_count; z++)
				if (__collate_large_char_pri_table[ch].pri.pri[z] == PRI_UNDEFINED)
					__collate_large_char_pri_table[ch].pri.pri[z] = (info.undef_pri[z] >= 0) ? info.undef_pri[z] : (__collate_large_char_pri_table[ch].val - info.undef_pri[z]);
		}
	} else {
		int ch, substed, ordered;
		int fatal = 0;
		for (ch = 1; ch < UCHAR_MAX + 1; ch++) {
			substed = hassubst(ch, 0);
			ordered = (__collate_char_pri_table[ch].pri[0] != PRI_UNDEFINED);
			if (!ordered && !substed) {
				fatal = 1;
				warnx("%s not found", charname(ch));
			}
			if (substed && ordered) {
				fatal = 1;
				warnx("%s can't be ordered since substituted", charname(ch));
			}
		}
		if (fatal)
			exit(1);
	}

	/* COLLATE_SUBST_DUP depends on COLL_WEIGHTS_MAX == 2 */
	if (localedef) {
		if (nsubst[0] == nsubst[1] && (nsubst[0] == 0 ||
		    memcmp(__collate_substitute_table[0], __collate_substitute_table[1], nsubst[0] * sizeof(struct __collate_st_subst)) == 0)) {
			info.flags |= COLLATE_SUBST_DUP;
			nsubst[1] = 0;
		}
	} else {
		info.flags |= COLLATE_SUBST_DUP;
		nsubst[1] = 0;
	}

	for(z = 0; z < directive_count; z++)
		info.subst_count[z] = nsubst[z];

	info.directive_count = directive_count;
	info.chain_count = nchain;
	info.large_pri_count = nlargemap;

	if ((fp = fopen(out_file, "w")) == NULL)
		err(EX_UNAVAILABLE, "can't open destination file %s",
		    out_file);

	strcpy(__collate_version, COLLATE_VERSION1_1A);
	if (fwrite(__collate_version, sizeof(__collate_version), 1, fp) != 1)
		err(EX_IOERR,
		"IO error writting collate version to destination file %s",
		    out_file);
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
	for(z = 0; z < directive_count; z++) {
		info.undef_pri[z] = htonl(info.undef_pri[z]);
		info.subst_count[z] = htonl(info.subst_count[z]);
	}
	info.chain_count = htonl(info.chain_count);
	info.large_pri_count = htonl(info.large_pri_count);
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */
	if (fwrite(&info, sizeof(info), 1, fp) != 1)
		err(EX_IOERR,
		"IO error writting collate info to destination file %s",
		    out_file);
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
	{
		int i, z;
		struct __collate_st_char_pri *p = __collate_char_pri_table;

		for(i = UCHAR_MAX + 1; i-- > 0; p++) {
			for(z = 0; z < directive_count; z++)
				p->pri[z] = htonl(p->pri[z]);
		}
	}
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */
	if (fwrite(__collate_char_pri_table,
		   sizeof(__collate_char_pri_table), 1, fp) != 1)
		err(EX_IOERR,
		"IO error writting char table to destination file %s",
		    out_file);
	for(z = 0; z < directive_count; z++) {
		if (nsubst[z] > 0) {
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
			struct __collate_st_subst *t = __collate_substitute_table[z];
			int i;
			for(i = nsubst[z]; i > 0; i--) {
				t->val = htonl(t->val);
				t++;
			}
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */
			if (fwrite(__collate_substitute_table[z], sizeof(struct __collate_st_subst), nsubst[z], fp) != nsubst[z])
				err(EX_IOERR,
				"IO error writting large substprim table %d to destination file %s",
				    z, out_file);
		}
	}
	if (nchain > 0) {
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
		int i, j, z;
		struct __collate_st_chain_pri *p = __collate_chain_pri_table;
		wchar_t *w;

		for(i = nchain; i-- > 0; p++) {
			for(j = STR_LEN, w = p->str; *w && j-- > 0; w++)
				*w = htonl(*w);
			for(z = 0; z < directive_count; z++)
				p->pri[z] = htonl(p->pri[z]);
		}
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */
		if (fwrite(__collate_chain_pri_table,
			   sizeof(*__collate_chain_pri_table), nchain, fp) !=
			   (size_t)nchain)
			err(EX_IOERR,
			"IO error writting chain table to destination file %s",
			    out_file);
	}

	if (nlargemap > 0) {
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
		struct __collate_st_large_char_pri *t = __collate_large_char_pri_table;
		int i;
		for(i = 0; i < nlargemap; i++) {
			t->val = htonl(t->val);
			t++;
		}
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */
		if (fwrite(__collate_large_char_pri_table, sizeof(struct __collate_st_large_char_pri), nlargemap, fp) != nlargemap)
			err(EX_IOERR,
			"IO error writting large pri tables to destination file %s",
			    out_file);
	}

	if (fclose(fp) != 0)
		err(EX_IOERR, "IO error closing destination file %s",
		    out_file);

#ifdef COLLATE_DEBUG
	if (debug)
		collate_print_tables();
#endif
	exit(EX_OK);
}
;
datafile : statment_list
	| blank_lines start_localedef localedef_sections blank_lines end_localedef blank_lines
;
statment_list : statment
	| statment_list '\n' statment
;
statment :
	| charmap
	| substitute
	| order
;
blank_lines :
	| '\n'
	| blank_lines '\n'
;
start_localedef : START_LC_COLLATE '\n' {
	int i;
	if ((stringdb = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen stringdb");
	directive_count = 0;
	for(i = 0; i < COLL_WEIGHTS_MAX; i++)
		info.directive[i] = DIRECTIVE_UNDEF;
}
;
end_localedef : END_LC_COLLATE '\n'
;
localedef_sections : localedef_preface localedef_order
;
localedef_preface : localedef_statment '\n'
	| localedef_preface localedef_statment '\n'
;
localedef_statment :
	| charmap
	| collating_element
	| collating_symbol
;
collating_element : COLLATING_ELEMENT ELEM FROM STRING {
	int len;
	struct symbol *s;
	if (wcslen($2) > CHARMAP_SYMBOL_LEN)
		yyerror("collating-element symbol name '%s' is too long", showwcs($2, CHARMAP_SYMBOL_LEN));
	if ((len = wcslen($4)) > STR_LEN)
		yyerror("collating-element string '%s' is too long", showwcs($4, STR_LEN));
	if (len < 2)
		yyerror("collating-element string '%s' must be at least two characters", showwcs($4, STR_LEN));
	s = getsymbol($2, NOTEXISTS);
	s->val = PRI_UNDEFINED;
	s->type = SYMBOL_CHAIN;
	wcsncpy(s->u.str, $4, STR_LEN);
	getchain($4, NOTEXISTS);
}
;
collating_symbol : COLLATING_SYMBOL ELEM {
	struct symbol *s;
	if (wcslen($2) > CHARMAP_SYMBOL_LEN)
		yyerror("collating-element symbol name '%s' is too long", showwcs($2, CHARMAP_SYMBOL_LEN));
	s = getsymbol($2, NOTEXISTS);
	s->val = PRI_UNDEFINED;
	s->type = SYMBOL_SYMBOL;
}
;
localedef_order : order_start order_lines1 order_second_pass order_lines2 order_end
;
order_start: ORDER_START order_start_list '\n'
;
order_second_pass: ORDER_SECOND_PASS {
	prev_line = LINE_NONE;
	prev_elem = NULL;
	order_pass++;
}
;
order_start_list : order_start_list_directives {
	if (directive_count > 0)
		yyerror("Multiple order_start lines not allowed");
	if ((info.directive[0] & DIRECTIVE_DIRECTION_MASK) == 0)
		info.directive[0] |= DIRECTIVE_FORWARD;
	directive_count++;
}
	| order_start_list ';' order_start_list_directives {
	if (directive_count >= COLL_WEIGHTS_MAX)
		yyerror("only COLL_WEIGHTS_MAX weights allowed");
	if ((info.directive[directive_count] & DIRECTIVE_DIRECTION_MASK) == 0)
		info.directive[directive_count] |= DIRECTIVE_FORWARD;
	directive_count++;
}
;
order_start_list_directives : ORDER_DIRECTIVE {
	info.directive[directive_count] = $1;
}
	| order_start_list_directives ',' ORDER_DIRECTIVE {
	int direction = ($3 & DIRECTIVE_DIRECTION_MASK);
	int prev = (info.directive[directive_count] & DIRECTIVE_DIRECTION_MASK);
	if (direction && prev && direction != prev)
		yyerror("The forward and backward directives are mutually exclusive");
	info.directive[directive_count] |= $3;
}
;
order_lines1 : order_line1 '\n'
	| order_lines1 order_line1 '\n'
;
order_line1 :
	| ELEM {
	struct symbol *s = getsymbol($1, EXISTS);
	if (s->val != PRI_UNDEFINED)
		yyerror("<%s> redefined", showwcs($1, CHARMAP_SYMBOL_LEN));
	if (prev_line == LINE_ELLIPSIS) {
		struct symbol *m;
		wchar_t i;
		int v;
		switch (s->type) {
		case SYMBOL_CHAIN:
			yyerror("Chain <%s> can't be endpoints of ellipsis", showwcs($1, CHARMAP_SYMBOL_LEN));
		case SYMBOL_SYMBOL:
			yyerror("Collating symbol <%s> can't be endpoints of ellipsis", showwcs($1, CHARMAP_SYMBOL_LEN));
		}
		if (s->u.wc <= prev_elem->u.wc)
			yyerror("<%s> is before starting point of ellipsis", showwcs($1, CHARMAP_SYMBOL_LEN));
		for(i = prev_elem->u.wc + 1, v = prev_elem->val + 1; i < s->u.wc; i++, v++) {
			m = getsymbolbychar(i);
			if (m->val != PRI_UNDEFINED)
				yyerror("<%s> was previously defined while filling ellipsis symbols", showwcs(m->name, CHARMAP_SYMBOL_LEN));
			m->val = v;
		}
		s->val = v;
	} else
		s->val = prim_pri;
	prim_pri = s->val + 1;
	weight_index = 0;
}				weights {
	int i;
	struct symbol *s = getsymbol($1, EXISTS);
	if (s->type == SYMBOL_SYMBOL) {
		if (weight_index != 0)
			yyerror("Can't specify weights for collating symbol <%s>", showwcs($1, CHARMAP_SYMBOL_LEN));
	} else if (weight_index == 0) {
		for(i = 0; i < directive_count; i++)
			weight_table[i] = s;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_NORMAL;
	prev_elem = s;
}
	| ELLIPSIS { weight_index = 0; allow_ellipsis = 1; } weights {
	int i;
	if (prev_line == LINE_ELLIPSIS)
		yyerror("Illegal sequential ellipsis lines");
	if (prev_line == LINE_UNDEFINED)
		yyerror("Ellipsis line can not follow UNDEFINED line");
	if (prev_line == LINE_NONE)
		yyerror("Ellipsis line must follow a collating identifier lines");
	if (weight_index == 0) {
		for(i = 0; i < directive_count; i++)
			weight_table[i] = &sym_ellipsis;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	for(i = 0; i < directive_count; i++) {
		if (weight_table[i]->type != SYMBOL_ELLIPSIS)
			continue;
		switch (prev_weight_table[i]->type) {
		case SYMBOL_CHAIN:
			yyerror("Startpoint of ellipsis can't be a collating element");
		case SYMBOL_IGNORE:
			yyerror("Startpoint of ellipsis can't be IGNORE");
		case SYMBOL_SYMBOL:
			yyerror("Startpoint of ellipsis can't be a collating symbol");
		case SYMBOL_STRING:
			yyerror("Startpoint of ellipsis can't be a string");
		}
	}
	memcpy(prev2_weight_table, prev_weight_table, sizeof(prev_weight_table));
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_ELLIPSIS;
	allow_ellipsis = 0;
}
	| UNDEFINED {
	if (sym_undefined.val != PRI_UNDEFINED)
		yyerror("Multiple UNDEFINED lines not allowed");
	sym_undefined.val = prim_pri++;
	weight_index = 0;
	allow_ellipsis = 1;
}				 weights {
	int i;
	if (weight_index == 0) {
		weight_table[0] = &sym_undefined;
		for(i = 1; i < directive_count; i++)
			weight_table[i] = &sym_ellipsis;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_UNDEFINED;
}
;
order_lines2 : order_line2 '\n'
	| order_lines2 order_line2 '\n'
;
order_line2 :
	| ELEM { weight_index = 0; } weights {
	int i;
	struct symbol *s = getsymbol($1, EXISTS);
	if (s->val == PRI_UNDEFINED)
		yyerror("<%s> undefined", showwcs($1, CHARMAP_SYMBOL_LEN));
	if (s->type == SYMBOL_SYMBOL) {
		if (weight_index != 0)
			yyerror("Can't specify weights for collating symbol <%s>", showwcs($1, CHARMAP_SYMBOL_LEN));
	} else if (weight_index == 0) {
		for(i = 0; i < directive_count; i++)
			weight_table[i] = s;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	if (prev_line == LINE_ELLIPSIS) {
		int w, x;
		for(i = 0; i < directive_count; i++) {
			switch (prev_weight_table[i]->type) {
			case SYMBOL_CHAR:
			case SYMBOL_CHAIN:
			case SYMBOL_IGNORE:
			case SYMBOL_SYMBOL:
				for (w = prev_elem->u.wc + 1; w < s->u.wc; w++) {
					struct __collate_st_char_pri *p = getpri(w);
					if (p->pri[i] != PRI_UNDEFINED)
						yyerror("Char 0x02x previously defined", w);
					p->pri[i] = prev_weight_table[i]->val;
				}
				break;
			case SYMBOL_ELLIPSIS:

				switch (weight_table[i]->type) {
				case SYMBOL_STRING:
					yyerror("Strings can't be endpoints of ellipsis");
				case SYMBOL_CHAIN:
					yyerror("Chains can't be endpoints of ellipsis");
				case SYMBOL_IGNORE:
					yyerror("IGNORE can't be endpoints of ellipsis");
				case SYMBOL_SYMBOL:
					yyerror("Collation symbols can't be endpoints of ellipsis");
				}
				if (s->val - prev_elem->val != weight_table[i]->val - prev2_weight_table[i]->val)
					yyerror("Range mismatch in weight %d", i);
				x = prev2_weight_table[i]->val + 1;
				for (w = prev_elem->u.wc + 1; w < s->u.wc; w++) {
					struct __collate_st_char_pri *p = getpri(w);
					if (p->pri[i] != PRI_UNDEFINED)
						yyerror("Char 0x02x previously defined", w);
					p->pri[i] = x++;
				}
				break;
			case SYMBOL_STRING:
				for (w = prev_elem->u.wc + 1; w < s->u.wc; w++) {
					struct __collate_st_char_pri *p = getpri(w);
					if (p->pri[i] != PRI_UNDEFINED)
						yyerror("Char 0x02x previously defined", w);
					putsubst(w, i, prev_weight_table[i]->u.str);
					p->pri[i] = prev_weight_table[i]->val;
				}
				break;
			}
		}
	}
	switch(s->type) {
	case SYMBOL_CHAR: {
		struct __collate_st_char_pri *p = getpri(s->u.wc);
		for(i = 0; i < directive_count; i++) {
			switch (weight_table[i]->type) {
			case SYMBOL_CHAR:
			case SYMBOL_CHAIN:
			case SYMBOL_IGNORE:
			case SYMBOL_SYMBOL:
				if (p->pri[i] != PRI_UNDEFINED)
					yyerror("Char 0x02x previously defined", s->u.wc);
				p->pri[i] = weight_table[i]->val;
				break;
			case SYMBOL_STRING:
				if (p->pri[i] != PRI_UNDEFINED)
					yyerror("Char 0x02x previously defined", s->u.wc);
				putsubst(s->u.wc, i, weight_table[i]->u.str);
				p->pri[i] = weight_table[i]->val;
				break;
			}
		}
		break;
	}
	case SYMBOL_CHAIN: {
		struct __collate_st_chain_pri *p = getchain(s->u.str, EXISTS);
		for(i = 0; i < directive_count; i++) {
			switch (weight_table[i]->type) {
			case SYMBOL_CHAR:
			case SYMBOL_CHAIN:
			case SYMBOL_IGNORE:
			case SYMBOL_SYMBOL:
				if (p->pri[i] != PRI_UNDEFINED)
					yyerror("Chain %s previously defined", showwcs(s->u.str, STR_LEN));
				p->pri[i] = weight_table[i]->val;
				break;
			case SYMBOL_STRING :
				if (wcsncmp(s->u.str, weight_table[i]->u.str, STR_LEN) != 0)
					yyerror("Chain/string mismatch");
				if (p->pri[i] != PRI_UNDEFINED)
					yyerror("Chain %s previously defined", showwcs(s->u.str, STR_LEN));
				/* negative value mean don't substitute
				 * the chain, but it is in an
				 * equivalence class */
				p->pri[i] = -weight_table[i]->val;
			}
		}
		break;
	}
	}
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_NORMAL;
	prev_elem = s;
}
	| ELLIPSIS { weight_index = 0; allow_ellipsis = 1; } weights {
	int i;
	if (prev_line == LINE_ELLIPSIS)
		yyerror("Illegal sequential ellipsis lines");
	if (prev_line == LINE_UNDEFINED)
		yyerror("Ellipsis line can not follow UNDEFINED line");
	if (prev_line == LINE_NONE)
		yyerror("Ellipsis line must follow a collating identifier lines");
	if (weight_index == 0) {
		for(i = 0; i < directive_count; i++)
			weight_table[i] = &sym_ellipsis;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	for(i = 0; i < directive_count; i++) {
		if (weight_table[i]->type != SYMBOL_ELLIPSIS)
			continue;
		switch (prev_weight_table[i]->type) {
		case SYMBOL_CHAIN:
			yyerror("Startpoint of ellipsis can't be a collating element");
		case SYMBOL_IGNORE:
			yyerror("Startpoint of ellipsis can't be IGNORE");
		case SYMBOL_SYMBOL:
			yyerror("Startpoint of ellipsis can't be a collating symbol");
		case SYMBOL_STRING:
			yyerror("Startpoint of ellipsis can't be a string");
		}
	}
	memcpy(prev2_weight_table, prev_weight_table, sizeof(prev_weight_table));
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_ELLIPSIS;
	allow_ellipsis = 0;
}
	| UNDEFINED { weight_index = 0; allow_ellipsis = 1; } weights {
	int i;

	if (weight_index == 0) {
		weight_table[0] = &sym_undefined;
		for(i = 1; i < directive_count; i++)
			weight_table[i] = &sym_ellipsis;
	} else if (weight_index != directive_count)
		yyerror("Not enough weights specified");
	for(i = 0; i < directive_count; i++) {
		switch (weight_table[i]->type) {
		case SYMBOL_CHAR:
		case SYMBOL_CHAIN:
		case SYMBOL_IGNORE:
		case SYMBOL_SYMBOL:
			info.undef_pri[i] = weight_table[i]->val;
			break;
		case SYMBOL_ELLIPSIS :
			/* Negative values mean that the priority is
			 * relative to the lexical value */
			info.undef_pri[i] = -sym_undefined.val;
			prim_pri = UNDEFINED_PRI;
			break;
		case SYMBOL_STRING :
			yyerror("Strings can't be used with UNDEFINED");
		}
	}
	memcpy(prev_weight_table, weight_table, sizeof(weight_table));
	prev_line = LINE_UNDEFINED;
}
;
weights :
	| weight
	| weights ';' weight
;
weight : ELEM {
	struct symbol *s;
	if (weight_index >= directive_count)
		yyerror("More weights than specified by order_start");
	s = getsymbol($1, EXISTS);
	if (order_pass && s->val == PRI_UNDEFINED)
		yyerror("<%s> is undefined", showwcs($1, CHARMAP_SYMBOL_LEN));
	weight_table[weight_index++] = s;
}
	| ELLIPSIS {
	if (weight_index >= directive_count)
		yyerror("More weights than specified by order_start");
	if (!allow_ellipsis)
		yyerror("Ellipsis weight not allowed");
	weight_table[weight_index++] = &sym_ellipsis;
}
	| IGNORE {
	if (weight_index >= directive_count)
		yyerror("More weights than specified by order_start");
	weight_table[weight_index++] = &sym_ignore;
}
	| STRING {
	if (weight_index >= directive_count)
		yyerror("More weights than specified by order_start");
	if (wcslen($1) > STR_LEN)
		yyerror("String '%s' is too long", showwcs($1, STR_LEN));
	weight_table[weight_index++] = getstring($1);
}
;
order_end : ORDER_END '\n'
;
charmap : DEFN CHAR {
	int len = wcslen($1);
	struct symbol *s;
	if (len > CHARMAP_SYMBOL_LEN)
		yyerror("Charmap symbol name '%s' is too long", showwcs($1, CHARMAP_SYMBOL_LEN));
	s = getsymbol($1, NOTEXISTS);
	s->type = SYMBOL_CHAR;
	s->val = PRI_UNDEFINED;
	s->u.wc = $2;
	setsymbolbychar(s);
}
;
substitute : SUBSTITUTE CHAR WITH STRING {
	if (wcslen($4) + 1 > STR_LEN)
		yyerror("%s substitution is too long", charname($2));
	putsubst($2, 0, $4);
}
;
order : ORDER order_list
;
order_list : item
	| order_list ';' item
;
chain : CHAR CHAR {
	curr_chain[0] = $1;
	curr_chain[1] = $2;
	if (curr_chain[0] == '\0' || curr_chain[1] == '\0')
		yyerror("\\0 can't be chained");
	curr_chain[2] = '\0';
}
	| chain CHAR {
	static wchar_t tb[2];
	tb[0] = $2;
	if (tb[0] == '\0')
		yyerror("\\0 can't be chained");
	if (wcslen(curr_chain) + 1 > STR_LEN)
		yyerror("Chain '%s' grows too long", curr_chain);
	(void)wcscat(curr_chain, tb);
}
;
item :  CHAR {
	struct __collate_st_char_pri *p = getpri($1);
	if (p->pri[0] >= 0)
		yyerror("%s duplicated", charname($1));
	p->pri[0] = p->pri[1] = prim_pri;
	sec_pri = ++prim_pri;
}
	| chain {
	struct __collate_st_chain_pri *c = getchain(curr_chain, NOTEXISTS);
	c->pri[0] = c->pri[1] = prim_pri;
	sec_pri = ++prim_pri;
}
	| CHAR RANGE CHAR {
	u_int i;
	struct __collate_st_char_pri *p;

	if ($3 <= $1)
		yyerror("Illegal range %s -- %s", charname($1), charname2($3));

	for (i = $1; i <= $3; i++) {
		p = getpri(i);
		if (p->pri[0] >= 0)
			yyerror("%s duplicated", charname(i));
		p->pri[0] = p->pri[1] = prim_pri++;
	}
	sec_pri = prim_pri;
}
	| '{' mixed_order_list '}' {
	prim_pri = sec_pri;
}
	| '(' sec_order_list ')' {
	prim_pri = sec_pri;
}
;
mixed_order_list : mixed_sub_list {
	sec_pri++;
}
	| mixed_order_list ';' mixed_sub_list {
	sec_pri++;
}
;
mixed_sub_list : mixed_sub_item
	| mixed_sub_list ',' mixed_sub_item 
;
sec_order_list : sec_sub_item
	| sec_order_list ',' sec_sub_item 
;
mixed_sub_item : CHAR {
	struct __collate_st_char_pri *p = getpri($1);
	if (p->pri[0] >= 0)
		yyerror("%s duplicated", charname($1));
	p->pri[0] = prim_pri;
	p->pri[1] = sec_pri;
}
	| CHAR RANGE CHAR {
	u_int i;
	struct __collate_st_char_pri *p;

	if ($3 <= $1)
		yyerror("Illegal range %s -- %s",
			charname($1), charname2($3));

	for (i = $1; i <= $3; i++) {
		p = getpri(i);
		if (p->pri[0] >= 0)
			yyerror("%s duplicated", charname(i));
		p->pri[0] = prim_pri;
		p->pri[1] = sec_pri;
	}
}
	| chain {
	struct __collate_st_chain_pri *c = getchain(curr_chain, NOTEXISTS);
	c->pri[0] = prim_pri;
	c->pri[1] = sec_pri;
}
sec_sub_item : CHAR {
	struct __collate_st_char_pri *p = getpri($1);
	if (p->pri[0] >= 0)
		yyerror("%s duplicated", charname($1));
	p->pri[0] = prim_pri;
	p->pri[1] = sec_pri++;
}
	| CHAR RANGE CHAR {
	u_int i;
	struct __collate_st_char_pri *p;

	if ($3 <= $1)
		yyerror("Illegal range %s -- %s",
			charname($1), charname2($3));

	for (i = $1; i <= $3; i++) {
		p = getpri(i);
		if (p->pri[0] >= 0)
			yyerror("%s duplicated", charname(i));
		p->pri[0] = prim_pri;
		p->pri[1] = sec_pri++;
	}
}
	| chain {
	struct __collate_st_chain_pri *c = getchain(curr_chain, NOTEXISTS);
	c->pri[0] = prim_pri;
	c->pri[1] = sec_pri++;
}
;
%%
int
main(int ac, char **av)
{
	int ch, z;

	if ((charmapdb = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen charmapdb");
	if ((charmapdb2 = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen charmapdb");
	if ((largemapdb = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen largemapdb");
	if ((substdb[0] = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen substdb[0]");
	if ((chaindb = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
		err(1, "dbopen chaindb");
	/* -1 means an undefined priority, which we adjust after parsing */
	for (ch = 0; ch <= UCHAR_MAX; ch++)
		for(z = 0; z < COLL_WEIGHTS_MAX; z++)
			__collate_char_pri_table[ch].pri[z] = PRI_UNDEFINED;
#ifdef COLLATE_DEBUG
	while((ch = getopt(ac, av, ":do:I:")) != -1) {
#else
	while((ch = getopt(ac, av, ":o:I:")) != -1) {
#endif
		switch (ch)
		{
#ifdef COLLATE_DEBUG
		  case 'd':
			debug++;
			break;
#endif
		  case 'o':
			out_file = optarg;
			break;

		  case 'I':
			strlcpy(map_name, optarg, sizeof(map_name));
			break;

		  default:
			usage();
		}
	}
	ac -= optind;
	av += optind;
	if (ac > 0) {
		if ((yyin = fopen(*av, "r")) == NULL)
			err(EX_UNAVAILABLE, "can't open source file %s", *av);
	}
	yyparse();
	return 0;
}

static struct __collate_st_char_pri *
getpri(int32_t c)
{
	DBT key, val;
	struct __collate_st_char_pri *p;
	int ret;

	if (c <= UCHAR_MAX)
		return &__collate_char_pri_table[c];
	key.data = &c;
	key.size = sizeof(int32_t);
	if ((ret = largemapdb->get(largemapdb, &key, &val, 0)) < 0)
		err(1, "getpri: Error getting %s", charname(c));
	if (ret != 0) {
		struct __collate_st_char_pri *pn;
		int z;
		if ((pn = (struct __collate_st_char_pri *)malloc(sizeof(struct __collate_st_char_pri))) == NULL)
			err(1, "getpri: malloc");
		for(z = 0; z < COLL_WEIGHTS_MAX; z++)
			pn->pri[z] = PRI_UNDEFINED;
		val.data = &pn;
		val.size = sizeof(struct __collate_st_char_pri *);
		if (largemapdb->put(largemapdb, &key, &val, 0) < 0)
			err(1, "getpri: Error storing %s", charname(c));
		nlargemap++;
	}
	memcpy(&p, val.data, sizeof(struct __collate_st_char_pri *));
	return p;
}

static struct __collate_st_char_pri *
haspri(int32_t c)
{
	DBT key, val;
	struct __collate_st_char_pri *p;
	int ret;

	if (c <= UCHAR_MAX)
		return &__collate_char_pri_table[c];
	key.data = &c;
	key.size = sizeof(int32_t);
	if ((ret = largemapdb->get(largemapdb, &key, &val, 0)) < 0)
		err(1, "haspri: Error getting %s", charname(c));
	if (ret != 0)
		return NULL;
	memcpy(&p, val.data, sizeof(struct __collate_st_char_pri *));
	return p;
}

static struct __collate_st_chain_pri *
getchain(const wchar_t *wcs, int exists)
{
	DBT key, val;
	struct __collate_st_chain_pri *p;
	int ret;

	key.data = (void *)wcs;
	key.size = __collate_wcsnlen(wcs, STR_LEN) * sizeof(wchar_t);
	if ((ret = chaindb->get(chaindb, &key, &val, 0)) < 0)
		err(1, "getchain: Error getting \"%s\"", showwcs(wcs, STR_LEN));
	if (ret != 0) {
		struct __collate_st_chain_pri *pn;
		int z;
		if (exists > 0)
			errx(1, "getchain: \"%s\" is not defined", showwcs(wcs, STR_LEN));
		if ((pn = (struct __collate_st_chain_pri *)malloc(sizeof(struct __collate_st_chain_pri))) == NULL)
			err(1, "getchain: malloc");
		for(z = 0; z < COLL_WEIGHTS_MAX; z++)
			pn->pri[z] = PRI_UNDEFINED;
		bzero(pn->str, sizeof(pn->str));
		wcsncpy(pn->str, wcs, STR_LEN);
		val.data = &pn;
		val.size = sizeof(struct __collate_st_chain_pri *);
		if (chaindb->put(chaindb, &key, &val, 0) < 0)
			err(1, "getchain: Error storing \"%s\"", showwcs(wcs, STR_LEN));
		nchain++;
	} else if (exists == 0)
		errx(1, "getchain: \"%s\" already exists", showwcs(wcs, STR_LEN));
	memcpy(&p, val.data, sizeof(struct __collate_st_chain_pri *));
	return p;
}

struct symbol *
getsymbol(const wchar_t *wcs, int exists)
{
	DBT key, val;
	struct symbol *p;
	int ret;

	key.data = (void *)wcs;
	key.size = wcslen(wcs) * sizeof(wchar_t);
	if ((ret = charmapdb->get(charmapdb, &key, &val, 0)) < 0)
		err(1, "getsymbol: Error getting \"%s\"", showwcs(wcs, CHARMAP_SYMBOL_LEN));
	if (ret != 0) {
		struct symbol *pn;
		if (exists > 0)
			errx(1, "getsymbol: \"%s\" is not defined", showwcs(wcs, CHARMAP_SYMBOL_LEN));
		if ((pn = (struct symbol *)malloc(sizeof(struct symbol))) == NULL)
			err(1, "getsymbol: malloc");
		pn->val = PRI_UNDEFINED;
		wcsncpy(pn->name, wcs, CHARMAP_SYMBOL_LEN);
		val.data = &pn;
		val.size = sizeof(struct symbol *);
		if (charmapdb->put(charmapdb, &key, &val, 0) < 0)
			err(1, "getsymbol: Error storing \"%s\"", showwcs(wcs, CHARMAP_SYMBOL_LEN));
	} else if (exists == 0)
		errx(1, "getsymbol: \"%s\" already exists", showwcs(wcs, CHARMAP_SYMBOL_LEN));
	memcpy(&p, val.data, sizeof(struct symbol *));
	return p;
}

static struct symbol *
getsymbolbychar(wchar_t wc)
{
	DBT key, val;
	struct symbol *p;
	int ret;

	key.data = &wc;
	key.size = sizeof(wchar_t);
	if ((ret = charmapdb2->get(charmapdb2, &key, &val, 0)) < 0)
		err(1, "getsymbolbychar: Error getting Char 0x%02x", wc);
	if (ret != 0)
		errx(1, "getsymbolbychar: Char 0x%02x is not defined", wc);
	memcpy(&p, val.data, sizeof(struct symbol *));
	return p;
}

static struct symbol *
hassymbolbychar(wchar_t wc)
{
	DBT key, val;
	struct symbol *p;
	int ret;

	key.data = &wc;
	key.size = sizeof(wchar_t);
	if ((ret = charmapdb2->get(charmapdb2, &key, &val, 0)) < 0)
		err(1, "hassymbolbychar: Error getting Char 0x%02x", wc);
	if (ret != 0)
		return NULL;
	memcpy(&p, val.data, sizeof(struct symbol *));
	return p;
}

static void
setsymbolbychar(struct symbol *s)
{
	DBT key, val;
	struct symbol *p;
	int ret;

	key.data = &s->u.wc;
	key.size = sizeof(wchar_t);
	val.data = &s;
	val.size = sizeof(struct symbol *);
	if (charmapdb2->put(charmapdb2, &key, &val, 0) < 0)
		err(1, "setsymbolbychar: Error storing <%s>", showwcs(s->name, CHARMAP_SYMBOL_LEN));
}

struct symbol *
getstring(const wchar_t *wcs)
{
	DBT key, val;
	struct symbol *p;
	int ret;

	key.data = (void *)wcs;
	key.size = wcslen(wcs) * sizeof(wchar_t);
	if ((ret = stringdb->get(stringdb, &key, &val, 0)) < 0)
		err(1, "getstring: Error getting \"%s\"", showwcs(wcs, STR_LEN));
	if (ret != 0) {
		struct symbol *pn;
		if ((pn = (struct symbol *)malloc(sizeof(struct symbol))) == NULL)
			err(1, "getstring: malloc");
		pn->type = SYMBOL_STRING;
		pn->val = prim_pri++;
		wcsncpy(pn->u.str, wcs, STR_LEN);
		val.data = &pn;
		val.size = sizeof(struct symbol *);
		if (stringdb->put(stringdb, &key, &val, 0) < 0)
			err(1, "getstring: Error storing \"%s\"", showwcs(wcs, STR_LEN));
	}
	memcpy(&p, val.data, sizeof(struct symbol *));
	return p;
}

static void
makeforwardref(int i, const struct symbol *from, const struct symbol * to)
{
}

static void
putsubst(int32_t c, int i, const wchar_t *str)
{
	DBT key, val;
	int ret;
	wchar_t clean[STR_LEN];

	if (!substdb[i])
		if ((substdb[i] = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
			err(1, "dbopen substdb[%d]", i);
	key.data = &c;
	key.size = sizeof(int32_t);
	bzero(clean, sizeof(clean));
	wcsncpy(clean, str, STR_LEN);
	val.data = clean;
	val.size = sizeof(clean);
	if ((ret = substdb[i]->put(substdb[i], &key, &val, R_NOOVERWRITE)) < 0)
		err(1, "putsubst: Error on %s", charname(c));
	if (ret != 0)
		errx(1, "putsubst: Duplicate substitution of %s", charname(c));
	nsubst[i]++;
}

static int
hassubst(int32_t c, int i)
{
	DBT key, val;
	int ret;

	if (!substdb[i])
		return 0;
	key.data = &c;
	key.size = sizeof(int32_t);
	if ((ret = substdb[i]->get(substdb[i], &key, &val, 0)) < 0)
		err(1, "hassubst: Error getting %s", charname(c));
	return (ret == 0);
}

static int
chainpricompar(const void *a, const void *b)
{
	return wcsncmp(((struct __collate_st_chain_pri *)a)->str, ((struct __collate_st_chain_pri *)b)->str, STR_LEN);
}

static int
charpricompar(const void *a, const void *b)
{
	return ((struct __collate_st_large_char_pri *)a)->val - ((struct __collate_st_large_char_pri *)b)->val;
}

static int
substcompar(const void *a, const void *b)
{
	return ((struct __collate_st_subst *)a)->val - ((struct __collate_st_subst *)b)->val;
}

static const wchar_t *
__collate_wcsnchr(const wchar_t *s, wchar_t c, int len)
{
	while (*s && len > 0) {
		if (*s == c)
			return s;
		s++;
		len--;
	}
	return NULL;
}

static int
__collate_wcsnlen(const wchar_t *s, int len)
{
	int n = 0;
	while (*s && n < len) {
		s++;
		n++;
	}
	return n;
}

static void
usage(void)
{
	fprintf(stderr, "usage: colldef [-o out_file] [-I map_dir] [filename]\n");
	exit(EX_USAGE);
}

void
yyerror(const char *fmt, ...)
{
	va_list ap;
	char msg[128];

	va_start(ap, fmt);
	vsnprintf(msg, sizeof(msg), fmt, ap);
	va_end(ap);
	errx(EX_UNAVAILABLE, "%s, near line %d", msg, line_no);
}

char *
showwcs(const wchar_t *t, int len)
{
	static char buf[8* CHARMAP_SYMBOL_LEN];
	char *cp = buf;

	for(; *t && len > 0; len--, t++) {
		if (*t >=32 && *t <= 126)
			*cp++ = *t;
		else {
			sprintf(cp, "\\x{%02x}", *t);
			cp += strlen(cp);
		}
	}
	*cp = 0;
	return buf;
}

static char *
charname(wchar_t wc)
{
	static char buf[CHARMAP_SYMBOL_LEN + 1];
	struct symbol *s = hassymbolbychar(wc);

	if (s)
		strcpy(buf, showwcs(s->name, CHARMAP_SYMBOL_LEN));
	else
		sprintf(buf, "Char 0x%02x", wc);
	return buf;
}

static char *
charname2(wchar_t wc)
{
	static char buf[CHARMAP_SYMBOL_LEN + 1];
	struct symbol *s = hassymbolbychar(wc);

	if (s)
		strcpy(buf, showwcs(s->name, CHARMAP_SYMBOL_LEN));
	else
		sprintf(buf, "Char 0x%02x", wc);
	return buf;
}

#ifdef COLLATE_DEBUG
static char *
show(int c)
{
	static char buf[5];

	if (c >=32 && c <= 126)
		sprintf(buf, "'%c' ", c);
	else
		sprintf(buf, "\\x{%02x}", c);
	return buf;
}

static void
collate_print_tables(void)
{
	int i, z;

	printf("Info: p=%d s=%d f=0x%02x m=%d dc=%d up=%d us=%d pc=%d sc=%d cc=%d lc=%d\n",
	    info.directive[0], info.directive[1],
	    info.flags, info.chain_max_len,
	    info.directive_count,
	    info.undef_pri[0], info.undef_pri[1],
	    info.subst_count[0], info.subst_count[1],
	    info.chain_count, info.large_pri_count);
	for(z = 0; z < info.directive_count; z++) {
		if (info.subst_count[z] > 0) {
			struct __collate_st_subst *p2 = __collate_substitute_table[z];
			if (z == 0 && (info.flags & COLLATE_SUBST_DUP))
				printf("Both substitute tables:\n");
			else
				printf("Substitute table %d:\n", z);
			for (i = info.subst_count[z]; i-- > 0; p2++)
				printf("\t%s --> \"%s\"\n",
					show(p2->val),
					showwcs(p2->str, STR_LEN));
		}
	}
	if (info.chain_count > 0) {
		printf("Chain priority table:\n");
		struct __collate_st_chain_pri *p2 = __collate_chain_pri_table;
		for (i = info.chain_count; i-- > 0; p2++) {
			printf("\t\"%s\" :", showwcs(p2->str, STR_LEN));
			for(z = 0; z < info.directive_count; z++)
				printf(" %d", p2->pri[z]);
			putchar('\n');
		}
	}
	printf("Char priority table:\n");
	{
		struct __collate_st_char_pri *p2 = __collate_char_pri_table;
		for (i = 0; i < UCHAR_MAX + 1; i++, p2++) {
			printf("\t%s :", show(i));
			for(z = 0; z < info.directive_count; z++)
				printf(" %d", p2->pri[z]);
			putchar('\n');
		}
	}
	if (info.large_pri_count > 0) {
		struct __collate_st_large_char_pri *p2 = __collate_large_char_pri_table;
		printf("Large priority table:\n");
		for (i = info.large_pri_count; i-- > 0; p2++) {
			printf("\t%s :", show(p2->val));
			for(z = 0; z < info.directive_count; z++)
				printf(" %d", p2->pri.pri[z]);
			putchar('\n');
		}
	}
}
#endif