/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/
/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: wchrtbl.c 1.3 90/06/22 $@(#)wchrtbl:wchrtbl.c	1.0"				
/*								*/
/* 	wchrtbl - generate character class definition table	*/
/*			for supplementary code set		*/
/*								*/
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
#include <string.h>
#include <signal.h>

/* #ifndef CCS */
#include <widec.h>
#include <wctype.h>
/* #else
#define wchar_t long
#include "wctype.h"
#endif */

/*	Definitions	*/

#define HEX    1
#define OCTAL  2
#define RANGE  1
#define UL_CONV 2
#define	CSLEN	10
#define SIZE	2 * 257 + CSLEN 	/* SIZE must be multiple of 4	*/
#define	START_CSWIDTH	(2 * 257)
#define	START_NUMERIC	(2 * 257) + 7
#define	ISUPPER		1
#define ISLOWER		2
#define ISDIGIT		4 
#define ISSPACE		8	
#define ISPUNCT		16
#define ISCNTRL		32
#define ISBLANK		64
#define ISXDIGIT	128
#define UL		0xff
#define	ISWCHAR1	0x00000100	/* phonogram (international use) */
#define	ISWCHAR2	0x00000200	/* ideogram (international use) */
#define	ISWCHAR3	0x00000400	/* English (international use) */
#define	ISWCHAR4	0x00000800	/* number (international use) */
#define	ISWCHAR5	0x00001000	/* special (international use) */
#define	ISWCHAR6	0x00002000	/* reserved (international use) */
#define	ISWCHAR7	0x00004000	/* reserved (international use) */
#define	ISWCHAR8	0x00008000	/* reserved (international use) */
#define	ISWCHAR9	0x00010000
#define	ISWCHAR10	0x00020000
#define	ISWCHAR11	0x00040000
#define	ISWCHAR12	0x00080000
#define	ISWCHAR13	0x00100000
#define	ISWCHAR14	0x00200000
#define	ISWCHAR15	0x00400000
#define	ISWCHAR16	0x00800000
#define	ISWCHAR17	0x01000000
#define	ISWCHAR18	0x02000000
#define	ISWCHAR19	0x04000000
#define	ISWCHAR20	0x08000000
#define	ISWCHAR21	0x10000000
#define	ISWCHAR22	0x20000000
#define	ISWCHAR23	0x40000000
#define	ISWCHAR24	0x80000000
#define	LC_CTYPE	10
#define	CSWIDTH		11
#define	NUMERIC		12
#define	LC_NUMERIC	13
#define LC_CTYPE1	21
#define LC_CTYPE2	22
#define LC_CTYPE3	23
#define	WSIZE		16*1024
#define SIZE_SET1	ctype[START_CSWIDTH]
#define SIZE_SET2	ctype[START_CSWIDTH + 1]
#define SIZE_SET3	ctype[START_CSWIDTH + 2]
/*
 * versions of the wctype.h macros which use ctype[] rather than _ctype[].
 */
#define 	_wgetpcw (SIZE_SET1 > sizeof(unsigned short)	|| \
		SIZE_SET2 > sizeof(unsigned short)	|| \
		SIZE_SET3 > sizeof(unsigned short))	 ? \
			sizeof(unsigned long)			 : \
			sizeof(unsigned short)
#define	wiscodeset1(c)	(( _wgetpcw ) > sizeof(unsigned short)? _iscodeset1(c) \
						:((c) & EUCMASK) == P11)
#define	wiscodeset2(c)	(( _wgetpcw ) > sizeof(unsigned short)? _iscodeset2(c) \
						:((c) & EUCMASK) == P01)
#define	wiscodeset3(c)	(( _wgetpcw ) > sizeof(unsigned short)? _iscodeset3(c) \
						:((c) & EUCMASK) == P10)

extern	char	*malloc();
extern  void	perror();
extern	void	exit();
extern	int	unlink();
extern	int	atoi();

/*   Internal functions  */

static	void	error();
static	void	init();
static	void	process();
static	void	create1();
static	void	create2();
static	void	create3();
static	void	pre_process();
static	void	create0w();
static	void	create1w();
static	void	create2w();
static	void	setmem();
static  void	parse();
static	int	empty_line();
static	void	check_chrclass();
static	void	clean();
static	void	comment1();
static	void	comment2();
static	void	comment3();
static	void	comment4();
static	void	comment5();
static 	int	cswidth();
static 	int	setnumeric();
static	int	check_digit();
static	unsigned  char	*clean_line();

/*	Static variables	*/

struct  classname	{
	char	*name;
	int	num;
	char	*repres;
}  cln[]  =  {
	"isupper",	ISUPPER,	"_U",
	"islower",	ISLOWER,	"_L",
	"isdigit",	ISDIGIT,	"_N",
	"isspace",	ISSPACE,	"_S",
	"ispunct",	ISPUNCT,	"_P",
	"iscntrl",	ISCNTRL,	"_C",
	"isblank",	ISBLANK,	"_B",
	"isxdigit",	ISXDIGIT,	"_X",
	"iswchar1",	ISWCHAR1,	"_E1",
	"isphonogram",	ISWCHAR1,	"_E1",
	"iswchar2",	ISWCHAR2,	"_E2",
	"isideogram",	ISWCHAR2,	"_E2",
	"iswchar3",	ISWCHAR3,	"_E3",
	"isenglish",	ISWCHAR3,	"_E3",
	"iswchar4",	ISWCHAR4,	"_E4",
	"isnumber",	ISWCHAR4,	"_E4",
	"iswchar5",	ISWCHAR5,	"_E5",
	"isspecial",	ISWCHAR5,	"_E5",
	"iswchar6",	ISWCHAR6,	"_E6",
	"iswchar7",	ISWCHAR7,	"_E7",
	"iswchar8",	ISWCHAR8,	"_E8",
	"iswchar9",	ISWCHAR9,	"_E9",
	"iswchar10",	ISWCHAR10,	"_E10",
	"iswchar11",	ISWCHAR11,	"_E11",
	"iswchar12",	ISWCHAR12,	"_E12",
	"iswchar13",	ISWCHAR13,	"_E13",
	"iswchar14",	ISWCHAR14,	"_E14",
	"iswchar15",	ISWCHAR15,	"_E15",
	"iswchar16",	ISWCHAR16,	"_E16",
	"iswchar17",	ISWCHAR17,	"_E17",
	"iswchar18",	ISWCHAR18,	"_E18",
	"iswchar19",	ISWCHAR19,	"_E19",
	"iswchar20",	ISWCHAR20,	"_E20",
	"iswchar21",	ISWCHAR21,	"_E21",
	"iswchar22",	ISWCHAR22,	"_E22",
	"iswchar23",	ISWCHAR23,	"_E23",
	"iswchar24",	ISWCHAR24,	"_E24",
	"ul",		UL,		NULL,
	"LC_CTYPE",	LC_CTYPE,	NULL,
	"cswidth",	CSWIDTH,	NULL,
	"LC_NUMERIC",	LC_NUMERIC,	NULL,
	"numeric",	NUMERIC,	NULL,
	"LC_CTYPE1",	LC_CTYPE1,	NULL,
	"LC_CTYPE2",	LC_CTYPE2,	NULL,
	"LC_CTYPE3",	LC_CTYPE3,	NULL,
	NULL,		NULL,		NULL
};

int	readstd;			/* Process the standard input */
unsigned char	linebuf[256];		/* Current line in input file */
unsigned char	 *p = linebuf;
int	chrclass = 0;			/* set if LC_CTYPE is specified */
int	lc_numeric;			/* set if LC_NUMERIC is specified */
int	lc_ctype;
char	chrclass_name[20];		/* save current chrclass name */
int	chrclass_num;			/* save current chrclass number */
int	ul_conv = 0;			/* set when left angle bracket
					 * is encountered. 
					 * cleared when right angle bracket
					 * is encountered */
int	cont = 0;			/* set if the description continues
					 * on another line */
int	action = 0;			/*  action = RANGE when the range
					 * character '-' is ncountered.
					 *  action = UL_CONV when it creates
					 * the conversion tables.  */
int	in_range = 0;			/* the first number is found 
					 * make sure that the lower limit
					 * is set  */
int	ctype[SIZE];			/* character class and character
					 * conversion table */
int	range = 0;			/* set when the range character '-'
					 * follows the string */
int	width;				/* set when cswidth is specified */
int	numeric;			/* set when numeric is specified */
char	tablename1[24];			/* save name of the first data file */
char	tablename2[24];			/* save name of the second date file */
char	*cmdname;			/* Save command name */
char	input[24];			/* Save input file name */
char	tokens[] = ",:\0";
int	codeset=0;			/* code set number */
int	codeset1=0;			/* set when charclass1 found */
int	codeset2=0;			/* set when charclass2 found */
int	codeset3=0;			/* set when charclass3 found */
unsigned *wctyp[3];			/* tmp table for wctype */
wchar_t	*wconv[3];			/* tmp table for conversion */
struct	_wctype	*wcptr[3];		/* pointer to ctype table */
struct	_wctype	wctbl[3];		/* table for wctype */
unsigned char *index[3];		/* code index	*/
unsigned *type[3];			/* code type	*/
wchar_t	*code[3];			/* conversion code	*/
int	cnt_index[3];			/* number of index	*/
int	cnt_type[3];			/* number of type	*/
int	cnt_code[3];			/* number conversion code */

/* Error  messages */
/* vprintf() is used to print the error messages */

char	*msg[] = {
/*    0    */ "Usage: wchrtbl [ - | file ] ",
/*    1    */ "the name of the output file for \"LC_CTYPE\" is not specified",
/*    2    */ "incorrect character class name \"%s\"",
/*    3    */ "--- %s --- left angle bracket  \"<\" is missing",
/*    4    */ "--- %s --- right angle bracket  \">\" is missing",
/*    5    */ "--- %s --- wrong input specification \"%s\"",
/*    6    */ "--- %s --- number out of range \"%s\"",
/*    7    */ "--- %s --- nonblank character after (\"\\\") on the same line",
/*    8    */ "--- %s --- wrong upper limit \"%s\"",
/*    9    */ "--- %s --- wrong character \"%c\"",
/*   10    */ "--- %s --- number expected",
/*   11    */ "--- %s --- too many range \"-\" characters",
/*   12    */ "--- %s --- wrong specification, %s",
/*   13    */ "malloc error",
/*   14	   */ "--- %s --- wrong specification, %s",
/*   15    */ "the name of the output file for \"LC_NUMERIC\" is not specified",
/*   16    */ "numeric editing information \"numeric\" must be specified",
/*   17    */ "character classification and conversion information must be specified",
/*   18    */ "the same output file name was used in \"LC_CTYPE\" and \"LC_NUMERIC\"",
/*   19    */ "character class \"LC_CTYPE%d\" duplicated",
/*   20    */ "character class table \"LC_CTYPE%d\" exhausted"
};

static char cswidth_info[] = "\n\nCSWIDTH FORMAT: n1[[:s1][,n2[:s2][,n3[:s3]]]]\n\tn1 byte width (SUP1)\n\tn2 byte width (SUP2)\n\tn3 byte width (SUP3)\n\ts1 screen width (SUP1)\n\ts2 screen width (SUP2)\n\ts3 screen width (SUP3)";

static char numeric_info[] = "\n\nNUMERIC FORMAT: d1[d2]\n\td1 decimal delimiter\n\td2 thousand delimiter";


main(argc, argv)
int	argc;
char	**argv;
{
	p = linebuf;

	if (cmdname = strrchr(*argv, '/'))
		++cmdname;
	else
		cmdname = *argv;
	if ( (argc != 1)  && (argc != 2) )
		error(cmdname, msg[0]);
	if ( argc == 1 || strcmp(argv[1], "-") == 0 )
		{
		readstd++;
		(void)strcpy(input, "standard input");
		}
	else
		(void)strcpy(input, argv[1]);
	if (signal(SIGINT, SIG_IGN) == SIG_DFL)
		(void)signal(SIGINT, clean);
	if (!readstd && freopen(argv[1], "r", stdin) == NULL)
		perror(argv[1]), exit(1);
	init();
	process();
	if (!lc_ctype && chrclass)
		error(input, msg[17]);
	if (lc_ctype) {
		if (!chrclass) 
			error(input, msg[1]);
		else {
			if (codeset1 || codeset2 || codeset3)
				create0w();
			create1();
			if (codeset1 || codeset2 || codeset3)
				create1w();
			create2();
			if (codeset1 || codeset2 || codeset3)
				create2w();
		}
	}
	if (lc_numeric && !numeric)
		error(input, msg[16]);
	if (numeric && !lc_numeric)
		error(input, msg[15]);
	if (strcmp(tablename1, tablename2) == NULL)	
		error(input, msg[18]);
	if (lc_numeric && numeric)
		create3();
	exit(0);
}


/* Initialize the ctype array */

static	void
init()
{
	register i;
	register j;
	for(i=0; i<256; i++)
		(ctype + 1)[257 + i] = i;
	ctype[START_CSWIDTH] = 1;
	ctype[START_CSWIDTH + 1] = 0;
	ctype[START_CSWIDTH + 2] = 0;
	ctype[START_CSWIDTH + 3] = 1;
	ctype[START_CSWIDTH + 4] = 0;
	ctype[START_CSWIDTH + 5] = 0;
	ctype[START_CSWIDTH + 6] = 1;
	for(i=0; i<3; i++){
		wcptr[i] = 0;
		cnt_index[i] = 0;
		cnt_type[i] = 0;
		cnt_code[i] = 0;
	}
}


/* Read line from the input file and check for correct
 * character class name */

static	void
process()
{

	unsigned char	*token();
	register  struct  classname  *cnp;
	register unsigned char *c;
	int	i;
	for (;;) {
		if (fgets((char *)linebuf, sizeof linebuf, stdin) == NULL ) {
			if (ferror(stdin)) {
				perror("wchrtbl (stdin)");
				exit(1);	
				}
				break;
	        }
		p = linebuf;
		/*  comment line   */
		if ( *p == '#' ) 
			continue; 
		/*  empty line  */
		if ( empty_line() )  
			continue; 
		if ( ! cont ) 
			{
			c = token();
			for (cnp = cln; cnp->name != NULL; cnp++) 
				if(strcmp(cnp->name, (char *)c) == NULL) 
					break; 
			}	
		switch(cnp->num) {
		default:
		case NULL:
			error(input, msg[2], c);
		case ISUPPER:
		case ISLOWER:
		case ISDIGIT:
		case ISXDIGIT:
		case ISSPACE:
		case ISPUNCT:
		case ISCNTRL:
		case ISBLANK:
		case ISWCHAR1:
		case ISWCHAR2:
		case ISWCHAR3:
		case ISWCHAR4:
		case ISWCHAR5:
		case ISWCHAR6:
		case ISWCHAR7:
		case ISWCHAR8:
		case ISWCHAR9:
		case ISWCHAR10:
		case ISWCHAR11:
		case ISWCHAR12:
		case ISWCHAR13:
		case ISWCHAR14:
		case ISWCHAR15:
		case ISWCHAR16:
		case ISWCHAR17:
		case ISWCHAR18:
		case ISWCHAR19:
		case ISWCHAR20:
		case ISWCHAR21:
		case ISWCHAR22:
		case ISWCHAR23:
		case ISWCHAR24:
		case UL:
				lc_ctype++;
				(void)strcpy(chrclass_name, cnp->name);
				chrclass_num = cnp->num;
				parse(cnp->num);
				break;
		case LC_CTYPE:
				chrclass++;
				if ( (c = token()) == NULL )
					error(input, msg[1]);
				(void)strcpy(tablename1, "\0");
				(void)strcpy(tablename1, (char *)c);
				if (freopen("wctype.c", "w", stdout) == NULL)
					perror("wctype.c"), exit(1);
				break;
		case LC_NUMERIC:
				lc_numeric++;
				if ( (c = token()) == NULL )
					error(input, msg[15]);
				(void)strcpy(tablename2, "\0");
				(void)strcpy(tablename2, (char *)c);
				break;
		case CSWIDTH:
				width++;
				(void)strcpy(chrclass_name, cnp->name);
				if (! cswidth() )
					error(input, msg[12], chrclass_name, cswidth_info);
				break;
		case NUMERIC:
				numeric++;
				(void)strcpy(chrclass_name, cnp->name);
				if (! setnumeric() )
					error(input, msg[14], chrclass_name, numeric_info);
				break;
		case LC_CTYPE1:
				if (codeset1)
					error(input,msg[19],codeset);
				codeset=1;
				codeset1=1;
				(void)setmem(1);
				break;
		case LC_CTYPE2:
				if (codeset2)
					error(input,msg[19],codeset);
				codeset=2;
				codeset2=1;
				(void)setmem(2);
				break;
		case LC_CTYPE3:
				if (codeset3)
					error(input,msg[19],codeset);
				codeset=3;
				codeset3=1;
				(void)setmem(3);
				break;
		}
	} /* for loop */
}

static	int
empty_line()
{
	register unsigned char *cp;
	cp = p;
	for (;;) {
		if ( (*cp == ' ') || (*cp == '\t') ) {
				cp++;
				continue; }
		if ( (*cp == '\n') || (*cp == '\0') )
				return(1); 
		else
				return(0);
	}
}

/* 
 * token() performs the parsing of the input line. It is sensitive
 * to space characters and returns a character pointer to the
 * function it was called from. The character string itself
 * is terminated by the NULL character.
 */ 

unsigned char *
token()
{
	register  unsigned char  *cp;
	for (;;) {
	check_chrclass(p);
	switch(*p) {
		case '\0':
		case '\n':
			in_range = 0;
			cont = 0;
			return(NULL);
		case ' ':
		case '\t':
			p++;
			continue;
		case '>':
			if (action == UL)
				error(input, msg[10], chrclass_name);
			ul_conv = 0;
			p++;
			continue;
		case '-':
			if (action == RANGE)
				error(input, msg[11], chrclass_name);
			action = RANGE;
			p++;
			continue;
		case '<':
			if (ul_conv)
				error(input, msg[4], chrclass_name);
			ul_conv++;
			p++;
			continue;
		default:
			cp = p;
			while(*p!=' ' && *p!='\t' && *p!='\n' && *p!='>' && *p!='-')  
				p++;   
			check_chrclass(p);
			if (*p == '>')
				ul_conv = 0;
			if (*p == '-')
				range++;
			*p++ = '\0';
			return(cp);

		}
	}
}


/* conv_num() is the function which converts a hexadecimal or octal
 * string to its equivalent integer represantation. Error checking
 * is performed in the following cases:
 *	1. The input is not in the form 0x<hex-number> or 0<octal-mumber>
 *	2. The input is not a hex or octal number.
 *	3. The number is out of range.
 * In all error cases a descriptive message is printed with the character
 * class and the hex or octal string.
 * The library routine sscanf() is used to perform the conversion.
 */


conv_num(s)
unsigned char	*s;
{
	unsigned char	*cp;
	int	i, j;
	int	num;
	cp = s;
	if ( *cp != '0' ) 
		error(input, msg[5], chrclass_name, s);
	if ( *++cp == 'x' )
		num = HEX;
	else
		num = OCTAL;
	switch (num) {
	case	HEX:
			cp++;
			for (j=0; cp[j] != '\0'; j++) 
				if ((cp[j] < '0' || cp[j] > '9') && (cp[j] < 'a' || cp[j] > 'f'))
					break;
				
				break;
	case   OCTAL:
			for (j=0; cp[j] != '\0'; j++)
				if (cp[j] < '0' || cp[j] > '7')
					break;
			break;
	default:
			error(input, msg[5], chrclass_name, s);
	}
	if ( num == HEX )  { 
		if (cp[j] != '\0' || sscanf((char *)s, "0x%x", &i) != 1)  
			error(input, msg[5], chrclass_name, s);
		if ((codeset == 0 && i > 0xff) ||
		    (codeset == 1 && !wiscodeset1(i)) ||
		    (codeset == 2 && !wiscodeset2(i)) ||
		    (codeset == 3 && !wiscodeset3(i)))
			error(input, msg[6], chrclass_name, s);
		else
			return(i);
	}
	if (cp[j] != '\0' || sscanf((char *)s, "0%o", &i) != 1) 
		error(input, msg[5], chrclass_name, s);
	if ((codeset == 0 && i > 0xff) ||
	    (codeset == 1 && !wiscodeset1(i)) ||
	    (codeset == 2 && !wiscodeset2(i)) ||
	    (codeset == 3 && !wiscodeset3(i)))
		error(input, msg[6], chrclass_name, s);
	else
		return(i);
/*NOTREACHED*/
}

/* parse() gets the next token and based on the character class
 * assigns a value to corresponding table entry.
 * It also handles ranges of consecutive numbers and initializes
 * the uper-to-lower and lower-to-upper conversion tables.
 */

static	void
parse(type)
int type;
{
	unsigned char	*c;
	int	ch1 = 0;
	int	ch2;
	int 	lower= 0;
	int	upper;
	while ( (c = token()) != NULL) {
		if ( *c == '\\' ) {
			if ( ! empty_line()  || strlen((char *)c) != 1) 
				error(input, msg[7], chrclass_name);
			cont = 1;
			break;
			}
		switch(action) {
		case	RANGE:
			upper = conv_num(c);
			if ( (!in_range) || (in_range && range) ) 
				error(input, msg[8], chrclass_name, c);
			if (codeset == 0)
				((ctype + 1)[upper]) |= type;
			else
				(wctyp[codeset - 1][(upper & 0x7f) | ((upper & 0x7f00) >> 1)]) |= type;
			if ( upper <= lower ) 
				error(input, msg[8], chrclass_name, c);
			while ( ++lower <= upper ) 
				if (codeset == 0)
					((ctype + 1)[lower]) |= type;
				else{
				 	(wctyp[codeset - 1][(lower & 0x7f) | ((lower & 0x7f00) >> 1)]) |= type;
				}
			action = 0;
			range = 0;
			in_range = 0;
			break;
		case	UL_CONV:
			ch2 = conv_num(c);
			if (codeset == 0){
				(ctype + 1)[ch1 + 257] = ch2;
				(ctype + 1)[ch2 + 257] = ch1;
			}
			else{
				wconv[codeset - 1][(ch1 & 0x7f) | ((ch1 & 0x7f00) >> 1)] = ch2;
				wconv[codeset - 1][(ch2 & 0x7f) | ((ch2 & 0x7f00) >> 1)] = ch1;
			}
			action = 0;
			break;   
		default:
			lower = ch1 = conv_num(c);
			in_range ++;
			if (type == UL) 
				if (ul_conv)
					{
					action = UL_CONV;
					break;
					}
				else
					error(input, msg[3], chrclass_name);
			else
				if (range)
					{
					action = RANGE;
					range = 0;
					}
				else
					;
			
			if (codeset == 0)
				((ctype + 1)[lower]) |= type;
			else
				(wctyp[codeset - 1][(lower & 0x7f) | ((lower & 0x7f00) >> 1)]) |= type;
			break;
		}
	}
	if (action)
		error(input, msg[10], chrclass_name);
}

/* create0w() produce wctype structure in memory */

static	void
create0w()
{
	register i,j,k,l;
	int	s,mask;
	unsigned char *index_addr;
	unsigned *type_addr;
	wchar_t  *code_addr;
	unsigned sv_type;
	if (codeset1)
		wcptr[0] = &wctbl[0];
	if (codeset2)
		wcptr[1] = &wctbl[1];
	if (codeset3)
		wcptr[2] = &wctbl[2];
	index_addr = (unsigned char *)(sizeof (struct _wctype) * 3);
	for (s = 0; s < 3; s++){
		if (wcptr[s] == 0) continue;
		for (i = 0; i < WSIZE && wctyp[s][i] ==0; i++)
			;	/* search minimum code in wctype table */
		if (i != WSIZE){
			wctbl[s].tmin = i;
			sv_type = wctyp[s][i];
			cnt_type[s] = 1;
			for (i = WSIZE - 1; i >= 0 && wctyp[s][i] == 0; i--)
			;	/* search maximum code in wctype table */
			wctbl[s].tmax = i;
			cnt_index[s] = wctbl[s].tmax - wctbl[s].tmin + 1;
			if (cnt_index[s] > WSIZE)
				error(input, msg[20],++s);
			if ((cnt_index[s] % 8) != 0)
				cnt_index[s] = ((cnt_index[s] / 8) + 1) * 8;
			if ((index[s] = (unsigned char *)(malloc((unsigned)cnt_index[s]))) == NULL ||
		    	    (type[s] = (unsigned *)(malloc((unsigned)cnt_index[s] * 4))) == NULL)
				error(cmdname,msg[13]);
			type[s][0] = sv_type;
			for (i = 0, j = 0, k = wctbl[s].tmin; i < cnt_index[s]; i++,k++){
				for (l = j; l >= 0; l--)
					if (type[s][l] == wctyp[s][k])
						break;
				if (l >= 0)
					index[s][i] = l;
				else{
					type[s][++j] = wctyp[s][k];
					index[s][i] = j;
					cnt_type[s]++;
					if (j > 256)
						error(input, msg[20],++s);
				}
			}
			if ((cnt_type[s] % 8) != 0)
				cnt_type[s] = ((cnt_type[s] / 8) + 1) * 8;
		}
		for (i = 0; i < WSIZE && wconv[s][i] == 0xffff; i++)
			;	/* search minimum code in conv table */
		if (i != WSIZE){
			wctbl[s].cmin = i;
			for (i = WSIZE - 1; i >= 0 && wconv[s][i] == 0xffff; i--)
			;	/* search maximum code in conv table */
			wctbl[s].cmax = i;
			cnt_code[s] = wctbl[s].cmax - wctbl[s].cmin + 1;
			if ((cnt_code[s] % 8) != 0)
				cnt_code[s] = ((cnt_code[s] / 8) + 1) * 8;
		    	if ((code[s] = (wchar_t *)(malloc((unsigned)cnt_code[s] * 4))) == NULL)
				error(cmdname,msg[13]);
			if (s == 0)
				mask = 0x8080;
			else if (s == 1)
				mask = 0x0080;
			else
				mask = 0x8000;
			for (i = 0, j = wctbl[s].cmin; i < cnt_code[s]; i++)
				code[s][i] = ((((j + i) & 0x3f80) << 1) | ((j + i) & 0x7f)) | mask;
			if (cnt_code[s] > WSIZE)
				error(input, msg[20],++s);
			for (i = 0, j = wctbl[s].cmin; i < cnt_code[s]; i++, j++){
				if (wconv[s][j] != 0xffff)
					code[s][i] = wconv[s][j];
			}
		}
		if (cnt_index[s] != 0)
			wctbl[s].index = index_addr;
		type_addr = (unsigned *)(index_addr + cnt_index[s]);
		if (cnt_type[s] != 0)
			wctbl[s].type = type_addr;
		code_addr = (wchar_t *)(type_addr + cnt_type[s]);
		if (cnt_code[s] != 0){
			wctbl[s].code = code_addr;
		index_addr = (unsigned char *)(code_addr + cnt_code[s]);
		}
	}
}



/* create1() produces a C source file based on the definitions
 * read from the input file. For example for the current ASCII
 * character classification where LC_CTYPE=ascii it produces a C source
 * file named wctype.c.
 */


static	void
create1()
{
	struct  field {
		unsigned char  ch[20];
	} out[8];
	unsigned char	*hextostr();
	unsigned char	outbuf[256];
	int	cond = 0;
	int	flag=0;
	int i, j, index1, index2;
	int	line_cnt = 0;
	register struct classname *cnp;
	int 	num;
	int	k;


	comment1();
	(void)sprintf((char *)outbuf,"unsigned char\t_ctype[] =  { 0,");
	(void)printf("%s\n",outbuf);
	
	index1 = 0;
	index2 = 7;
	while (flag <= 1) {
		for (i=0; i<=7; i++)
			(void)strcpy((char *)out[i].ch, "\0");
		for(i=index1; i<=index2; i++) {
			if ( ! ((ctype + 1)[i]) )  {
				(void)strcpy((char *)out[i - index1].ch, "0");
				continue; }
			num = (ctype + 1)[i];
			if (flag) {      
				(void)strcpy((char *)out[i - index1].ch, "0x");  
				(void)strcat((char *)out[i - index1].ch, (char *)hextostr(num));
				continue; }
			while (num)  {
				for(cnp=cln;cnp->num != UL;cnp++) {
					if(!(num & cnp->num))  
						continue; 
					if ( (strlen((char *)out[i - index1].ch))  == NULL)  
						(void)strcat((char *)out[i - index1].ch,cnp->repres);
					else  {
						(void)strcat((char *)out[i - index1].ch,"|");
						(void)strcat((char *)out[i - index1].ch,cnp->repres); }  
				num = num & ~cnp->num;  
					if (!num) 
						break; 
				}  /* end inner for */
			}  /* end while */
		} /* end outer for loop */
		(void)sprintf((char *)outbuf,"\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,",
		out[0].ch,out[1].ch,out[2].ch,out[3].ch,out[4].ch,out[5].ch,
		out[6].ch,out[7].ch);
		if ( ++line_cnt == 32 ) {
			line_cnt = 0;
			flag++; 
			cond = flag; }
		switch(cond) {
		case	1:
			(void)printf("%s\n", outbuf);
			comment2();
			(void)printf("\t0,\n");
			index1++;
			index2++;
			cond = 0;
			break;
		case	2:
			(void)printf("%s\n", outbuf);
			(void)printf("\n\t/* multiple byte character width information */\n\n");
			if (codeset1 || codeset2 || codeset3)
				k = CSLEN - 1;
			else
				k = 6;
			for(j=0; j<k; j++){
				if ((j%8 == 0) && (j !=0))
					(void)printf("\n");
				(void)printf("\t%d,", ctype[START_CSWIDTH + j]);
			}
			(void)printf("\t%d\n", ctype[START_CSWIDTH + k]);
			(void)printf("};\n");
			break;
		default:
			printf("%s\n", outbuf);
			break;
		}
		index1 += 8;
		index2 += 8;
	}  /* end while loop */
	if (width)
		comment3();
}



/* create1w() produces a C source program for supplementary code sets */


static	void
create1w()
{
	struct  field {
		unsigned char  ch[100];
	} out[8];
	unsigned char	*hextostr();
	unsigned char	outbuf[256];
	unsigned char	*cp;
	int	cond = 0;
	int	flag=0;
	int i, j, index1, index2;
	int	line_cnt = 0;
	register struct classname *cnp;
	int 	num;
	int	s;


	comment4();
	(void)sprintf((char *)outbuf,"struct _wctype wctbl[3] = {\n");
	(void)printf("%s",outbuf);
	for (s = 0; s < 3; s++){
		(void)printf("\t{");
		if (wctbl[s].tmin == 0)
			(void)printf("0,\t");
		else
			(void)printf("0x%s,\t",hextostr(wctbl[s].tmin));
		if (wctbl[s].tmin == 0)
			(void)printf("0,\t");
		else
			(void)printf("0x%s,\t",hextostr(wctbl[s].tmax));
		if (wctbl[s].index == 0)
			(void)printf("0,\t");
		else
			(void)printf("(unsigned char *)0x%s,\t",hextostr(wctbl[s].index));
		if (wctbl[s].type == 0)
			(void)printf("0,\t");
		else
			(void)printf("(unsigned *)0x%s,\t",hextostr(wctbl[s].type));
		if (wctbl[s].cmin == 0)
			(void)printf("0,\t");
		else
			(void)printf("0x%s,\t",hextostr(wctbl[s].cmin));
		if (wctbl[s].cmax == 0)
			(void)printf("0,\t");
		else
			(void)printf("0x%s,\t",hextostr(wctbl[s].cmax));
		if (wctbl[s].code == 0)
			(void)printf("0");
		else
			(void)printf("(wchar_t *)0x%s",hextostr(wctbl[s].code));
		if (s < 2)
			(void)printf("},\n");
		else
			(void)printf("}\n");
	}
	(void)printf("};\n");
	

	comment5();
   for (s = 0; s < 3; s++){
	index1 = 0;
	index2 = 7;
	flag = 0;
	while (flag <= 2) {
		if (line_cnt == 0){
			if (flag == 0 && cnt_index[s])
				(void)printf("unsigned char index%d[] = {\n",s+1);
			else if ((flag == 0 || flag == 1) && cnt_type[s]){
				(void)printf("unsigned type%d[] = {\n",s+1);
				flag = 1;
			}
			else if (cnt_code[s]){
				(void)printf("wchar_t code%d[] = {\n",s+1);
				flag = 2;
			}
			else
				break;
		}
		for (i=0; i<=7; i++)
			(void)strcpy((char *)out[i].ch, "\0");
		for(i=index1; i<=index2; i++) {
			if ((flag == 0 && !index[s][i]) ||
			    (flag == 1 && !type[s][i]) ||
			    (flag == 2 && !code[s][i])){ 
				(void)strcpy((char *)out[i - index1].ch, "0");
				continue; }
			if (flag == 0)
				num = index[s][i];
			else if (flag == 1)
				num = type[s][i];
			else
				num = code[s][i];
			if (flag == 0 || flag ==2) {      
				(void)strcpy((char *)out[i - index1].ch, "0x");  
				(void)strcat((char *)out[i - index1].ch, (char *)hextostr(num));
				continue; }
			while (num)  {
				for(cnp=cln;cnp->num != UL;cnp++) {
					if(!(num & cnp->num))  
						continue; 
					if ( (strlen((char *)out[i - index1].ch))  == NULL)  
						(void)strcat((char *)out[i - index1].ch,cnp->repres);
					else  {
						(void)strcat((char *)out[i - index1].ch,"|");
						(void)strcat((char *)out[i - index1].ch,cnp->repres); }  
				num = num & ~cnp->num;  
					if (!num) 
						break; 
				}  /* end inner for */
			}  /* end while */
		} /* end outer for loop */
		(void)sprintf((char *)outbuf,"\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,\t%s,",
		out[0].ch,out[1].ch,out[2].ch,out[3].ch,out[4].ch,out[5].ch,
		out[6].ch,out[7].ch);
		line_cnt++;
		if ((flag == 0 && line_cnt == cnt_index[s]/8) ||
		    (flag == 1 && line_cnt == cnt_type[s]/8) ||
		    (flag == 2 && line_cnt == cnt_code[s]/8)){
			line_cnt = 0;
			flag++; 
			cond = flag; }
		switch(cond) {
		case	1:
		case	2:
		case	3:
			cp = outbuf + strlen(outbuf);
			*--cp = ' ';
			*++cp = '\0';
			(void)printf("%s\n", outbuf);
			(void)printf("};\n");
			index1 = 0;
			index2 = 7;
			cond = 0;
			break;
		default:
			(void)printf("%s\n", outbuf);
			index1 += 8;
			index2 += 8;
			break;
		}
	}  /* end while loop */
   }
}

/* create2() & create2w() produces a data file containing the ctype array
 * elements. The name of the file is the same as the value
 * of the environment variable LC_CTYPE.
 */


static	void
create2()
{
	register   i=0;
	int	j;
	if (freopen(tablename1, "w", stdout) == NULL){
		perror(tablename1), exit(1);
		exit(1);
	}
	if (codeset1 || codeset2 || codeset3)
		j = SIZE;
	else
		j = SIZE - CSLEN + 7;
	for (i=0; i< j; i++)
		(void)printf("%c", ctype[i]);
}

static	void
create2w()
{
	int	s;
	if (fwrite(wctbl,(sizeof (struct _wctype)) * 3,1,stdout) == NULL)
		perror(tablename1), exit(1);
	for (s = 0; s < 3; s++){
		if (cnt_index[s] != 0)
			if (fwrite(&index[s][0],cnt_index[s], 1, stdout) == NULL)
				perror(tablename1), exit(1);
		if (cnt_type[s] != 0)
			if (fwrite(&type[s][0],4,cnt_type[s], stdout) == NULL)
				perror(tablename1), exit(1);
		if (cnt_code[s] != 0)
			if (fwrite(&code[s][0],(sizeof((wchar_t) 0)),cnt_code[s], stdout) == NULL)
				perror(tablename1), exit(1);
	}
}
	
static void
create3()
{
	int	length;
	char	*numeric_name;

	if (freopen(tablename2, "w", stdout) == NULL) {
		perror(tablename2);
		exit(1);
	}
	(void)printf("%c%c", ctype[START_NUMERIC], ctype[START_NUMERIC + 1]);
}

/* Convert a hexadecimal number to a string */

unsigned char *
hextostr(num)
int	num;
{
	unsigned char	*idx;
	static unsigned char	buf[64];
	idx = buf + sizeof(buf);
	*--idx = '\0';
	do {
		*--idx = "0123456789abcdef"[num % 16];
		num /= 16;
	  } while (num);
	return(idx);
}

static void
comment1()
{
	if (codeset1 || codeset2 || codeset3){
		(void)printf("#include <ctype.h>\n");
		(void)printf("#include <widec.h>\n");
		(void)printf("#include <wctype.h>\n\n\n");
	}
	else
		(void)printf("#include <ctype.h>\n\n\n");
	(void)printf("\t/*\n");
	(void)printf("\t ************************************************\n");
	(void)printf("\t *		%s  CHARACTER  SET                \n", tablename1);
	(void)printf("\t ************************************************\n");
	(void)printf("\t */\n\n");
	(void)printf("\t/* The first 257 characters are used to determine\n");
	(void)printf("\t * the character class */\n\n");
}

static	void
comment2()
{
	(void)printf("\n\n\t/* The next 257 characters are used for \n");
	(void)printf("\t * upper-to-lower and lower-to-upper conversion */\n\n");
}

static void
comment3()
{
	(void)printf("\n\n\t/*  CSWIDTH INFORMATION                           */\n");
	(void)printf("\t/*_____________________________________________   */\n");
	(void)printf("\t/*                    byte width <> screen width  */\n");
	(void)printf("\t/* SUP1	  		     %d    |     %d         */\n",
		ctype[START_CSWIDTH], ctype[START_CSWIDTH + 3]);
	(void)printf("\t/* SUP2			     %d    |     %d         */\n",
		ctype[START_CSWIDTH + 1], ctype[START_CSWIDTH + 4]);
	(void)printf("\t/* SUP3			     %d    |     %d         */\n",
		ctype[START_CSWIDTH + 2], ctype[START_CSWIDTH + 5]);
	(void)printf("\n\t/* MAXIMUM CHARACTER WIDTH        %d               */\n",
		ctype[START_CSWIDTH + 6]);
}

static	void
comment4()
{
	(void)printf("\n\n\t/* The next entries point to wctype tables */\n");
	(void)printf("\t/*          and have upper and lower limit */\n\n");
}

static	void
comment5()
{
	(void)printf("\n\n\t/* The folowing table is used to determine\n");
	(void)printf("\t * the character class for supplementary code sets */\n\n");
}

/*VARARGS*/
static	void
error(va_alist)
va_dcl
{
	va_list	args;
	char	*fmt;
	char	*file;
	va_start(args);
	file = va_arg(args, char *);
	(void)fprintf(stderr, "ERROR in %s: ", file);
	fmt = va_arg(args, char *);
	(void)vfprintf(stderr, fmt, args);
	(void)fprintf(stderr, "\n");
	va_end(args);
	clean();
}

static	void
check_chrclass(cp)
unsigned char	*cp;
{
	if (chrclass_num != UL)
		if (*cp == '<' || *cp == '>')
			error(input, msg[9], chrclass_name, *cp);
		else
			;
	else
		if (*cp == '-')
			error(input, msg[9], chrclass_name, *cp);
		else
			;
}

static	void
clean()
{
	(void)signal(SIGINT, SIG_IGN);
	(void)unlink("wctype.c");
	(void)unlink(tablename1);
	(void)unlink(tablename2);
	exit(1);
}

/*
 *
 * 	n1[:[s1][,n2:[s2][,n3:[s3]]]]
 *      
 *	n1	byte width (supplementary code set 1)
 *	n2	byte width (supplementary code set 2)
 *	n3	byte width (supplementary code set 3)
 *	s1	screen width (supplementary code set 1)
 *	s2	screen width (supplementary code set 2)
 *	s3	screen width (supplementary code set 3)
 *
 */
static int
cswidth()
{
        char *byte_width[3];
	char *screen_width[3];
	char *buf;
	int length;
	unsigned int len;
	int suppl_set = 0;
	unsigned char *cp;

	if (*(p+strlen((char *)p)-1) != '\n') /* terminating newline? */
		return(0);
	p = clean_line(p);
	if (!(length = strlen((char *)p))) /* anything left */
		return(0);
	if (! isdigit((char)*p) || ! isdigit((char)*(p+length-1)) )
		return(0);
        if ((buf = malloc((unsigned)length + 1)) == NULL)
		error(cmdname, msg[13]);
	(void)strcpy(buf, (char *)p);
	cp = p;
	while (suppl_set < 3) {
		if ( !(byte_width[suppl_set] = strtok((char *)cp, tokens)) || ! check_digit(byte_width[suppl_set]) ){
			return(0);
		}
		ctype[START_CSWIDTH  + suppl_set] = atoi(byte_width[suppl_set]);
		if ( p + length == (unsigned char *)(byte_width[suppl_set] + 1) )
			break;
		len = (unsigned char *)(byte_width[suppl_set] + 1) - p;
		if (*(buf + len) == ',') {
			cp = (unsigned char *)(byte_width[suppl_set] + 2);
			suppl_set++;
			continue;
		}
		tokens[0] = ',';
		if ( !(screen_width[suppl_set] = strtok((char *)0, tokens)) || ! check_digit(screen_width[suppl_set]) )  {
			return(0);
		}
		ctype[START_CSWIDTH + suppl_set + 3] = atoi(screen_width[suppl_set]);
		if ( p + length == (unsigned char *)(screen_width[suppl_set] + 1) )
			break;
		cp = (unsigned char *)(screen_width[suppl_set] + 2);
		tokens[0] = ':';
		suppl_set++;
	}
	suppl_set = 0;
	while (suppl_set < 3) {
		if ( (ctype[START_CSWIDTH + suppl_set]) && !(ctype[START_CSWIDTH + suppl_set + 3]) )
			ctype[START_CSWIDTH + suppl_set + 3] = ctype[START_CSWIDTH + suppl_set];
		suppl_set++;
	}
	ctype[START_CSWIDTH + 6] = ctype[START_CSWIDTH + 1] > ctype[START_CSWIDTH + 2] ? ctype[START_CSWIDTH + 1] : ctype[START_CSWIDTH + 2];
	if (ctype[START_CSWIDTH + 6] > 0)
		++ctype[START_CSWIDTH + 6];
	if (ctype[START_CSWIDTH] > ctype[START_CSWIDTH + 6])
		ctype[START_CSWIDTH + 6] = ctype[START_CSWIDTH];
return(1);
}

static unsigned char  *
clean_line(s)
unsigned char *s;
{
	unsigned char  *ns;

	*(s + strlen((char *)s) -1) = (char) 0; /* delete newline */
	if (!strlen((char *)s))
		return(s);
	ns = s + strlen((char *)s) - 1; /* s->start; ns->end */
	while ((ns != s) && (isspace((char)*ns))) {
		*ns = (char)0;	/* delete terminating spaces */
		--ns;
		}
	while (*ns)             /* delete beginning white spaces */
		if (isspace((char)*s))
			++s;
		else
			break;
	return(s);
}

static int
check_digit(s)
char *s;
{
	if (strlen(s) != 1 || ! isdigit(*s))
		return(0);
	else
		return(1);
}

static int
setnumeric()
{
	int	len;
	if (*(p+strlen((char *)p)-1) != '\n')
		return(0);
	p = clean_line(p);
	if ((len = strlen((char *)p)) != 1 && len != 2)
		return(0);
	ctype[START_NUMERIC] = (int)p[0];
	if (len == 2)
		ctype[START_NUMERIC + 1] = (int)p[1];
	return(1);
}

static	void
setmem(codeset)
	int	codeset;
{
	register i;
	if ((wctyp[codeset-1] = (unsigned *)(malloc(WSIZE * 4))) == NULL ||
	    (wconv[codeset-1] = (wchar_t *)(malloc(WSIZE * 4))) == NULL) 
		error(cmdname,msg[13]);
	for (i=0; i<WSIZE; i++){
		wctyp[codeset-1][i] = 0;
		wconv[codeset-1][i] = 0xffff;
	}
}
