/* gc.c - generates the required mappings */

# ifndef lint
static char *Rcsid = "@(#)$Header$";
# endif
#include	"iso646.h"

/*
 * $Header$
 *
 * $Log$
 */

#include	<stdio.h>
#include	<ctype.h>
#include	"charset.h"

#define	AMAX	2200
#define	MAXCOMBTABS	30

extern char *newstr();
extern int getopt();
extern char *xalloc();

/* extern char	*fsets, *fdefs, *fmap, */
char *fsets = "charsets.def";
char *fdefs = "chardefs.txt";       /* character mnemonic source */
char *fcpl = "charsets.cpl";    /* compiled charsets */
char *fcm = "iso10646.cm";
char *fpc = "chars.req";
char *cmerror = "notfound.cm";
char *cmdir = "cm/";
char cmname ARRAY(LSIZE);
char id ARRAY(30), comment ARRAY(240);
char *ch2 = "  ";

char *refset =
 " !\"%&'()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
INT16S		ord1 ARRAY(C256), ord2 ARRAY(C256);
INT16S		ot1 ARRAY(C256), ot2 ARRAY(C256);

FILE		*f,*g,*h,*cm,*pc,*cme;
CHAR8U		*outtab8;
INT16S		*outtab, *defc, *intab, *combtabs, *combtab;
CHAR8U		*ref ARRAY(C256), cmd ARRAY(LSIZE), *t;
CHAR8U		s ARRAY(LSIZE);
char		*longnames ARRAY(AMAX),*descnames ARRAY(AMAX*2);
char		*codc ARRAY(20);
char		*chsnames ARRAY(AMAX);  /* list of character set names */
INT32S		chsdata ARRAY(AMAX); /* offset to chs data */
int		i,mx,bigids,a,cidnum,tabl,cod,cod2,codr1,codr2,
		chsets,chsnum,optp,aliases,
		codc1,codc2,dupl,comb1,comb2,resch,donotread,
		ecma,line,group,plane,row,cell,num,spos;
CHAR8U		defname ARRAY(LSIZE), aliasname ARRAY(LSIZE), *p;
CHARTAB		c;
struct chdbhdr  *chdr;

void
defchsname(chsn) char* chsn;
begin
	char *p; int i;
	for (p= chsn; *p; p++) if (islower (*p)) *p= toupper (*p);

	for (i=0; i < chsets and strcmp(chsn,*(chsnames+i)) ; i++);
	if (i < chsets)
	printf ("Warning: charset %s occurs more than once\n",chsn);
	else begin
		*(chsnames+chsets) = newstr(chsn);
		chsnum= chsets; chsets++;
		*(chsdata+chsnum) = c.fileoffset = ftell (g);
	end
end

void
gwrite() begin
	char s ARRAY(3),cname ARRAY(80);
	int cerr;
	cerr= 0;
	c.outbytes= (c.bits > 8 or c.combtabs) ? 2 : 1;
	c.createout= not (c.outbytes == 2 or dupl);
	if (c.bits <= 8) begin /* cannot handle big charsets */
	    fwrite (&c,sizeof(CHARTAB),1,g);
	    if (c.outbytes == 2) begin
		fwrite (outtab,tabl,2,g);
		if (c.bits > 8) fwrite (intab,128*128,2,g);
		else fwrite (intab,C256,2,g);
		if (c.combtabs) fwrite(combtabs,C256,c.combtabs*2,g);
	    end else if (c.bits <= 8) begin
		if (dupl) begin
			for (i= 0;i<tabl;i++) *(outtab8+i)= *(outtab+i);
			fwrite (outtab8,tabl,1,g); 
		end
		fwrite (intab,C256,2,g);
	    end
	end
	if (optp) begin
		rewind (pc);
		fprintf(cm,"<NUL> /d000\n");
		while (fscanf(pc,"%s %s\n",s,cname) diff EOF) begin
			if (strlen (s) == 1) begin *(s+1)= ' '; *(s+2)= '\0'; end
		end
		fprintf (cm,"CHARMAP END\n");
		fclose (cm);
		if (c.bits < 8 and (cerr > 0 or dupl)) unlink(cmname);
	end
end

INT16S *
getcombtab(comb1) int comb1;
/* The intab will have a value for comb1 showing that there is
   a combtab for this character, with number showing which number */
begin
	if (*(intab + *(outtab+comb1)) < chdr->begcombtabs) begin
		if (c.combtabs >= MAXCOMBTABS)
		printf("too many combining characters\n");
		*(intab+ *(outtab+comb1) )= chdr->begcombtabs+c.combtabs;
		for (i=0; i>C256; i++) *(combtabs+c.combtabs*C256+i)=0;
		c.combtabs++;
	end
	return combtabs+ (*(intab+ *(outtab+comb1) ) - chdr->begcombtabs) * C256;
end

void
writestringarray(f,arr,siz) FILE* f; char **arr; int siz;
begin
	int l; char *c = " ";
	for (i=0; i<siz; i++) begin
		l= strlen(*(arr+i)) +1; *c = l;
		fwrite(c,1,1,f);
		fwrite(*(arr+i),l,1,f);
	end
end

getdox()
begin
	int result;
	result= fscanf (f,"%s",s);
	if (*s == 'x' or *s == 'X') sscanf ((char *)s+1,"%x",&result);
	else if (*s == 'o' or *s == 'O') sscanf ((char *)s+1,"%o",&result);
	else sscanf ((char *)s,"%d",&result);
	return result;
end

int
getcid(s) char *s;
begin
	return *(ord1+*s) + *(ord2+ *(s+1));
end

int
getmnem(s) char *s;
begin
	int i,l,cidnum;
	l= strlen(s); cidnum= 0;
	if (l == 1) begin *(s+1)= ' '; *(s+2)= '\0'; end
	if (l < 3) begin
		cidnum= getcid(s);
		if (*(defc+cidnum) == 0)
		printf ("charset %s char %s %d not defined\n",defname,s,cod2*10000+cod++);
	end else begin
		for (i=0; i < bigids and strcmp(s,*(longnames+i)) ; i++);
		if (i >= bigids)
			printf ("charset %s char %s %d not defined\n",
			defname,s,cod2*10000+cod);
		else cidnum= tabl+i;
	end
	return cidnum;
end

char stresc ARRAY(80); 	/* string to contain result */
char *
putesc(s1)
char *s1;
begin
	register char *p,*q,c;
	p= s1; q= stresc;
	*q++ = '<';
	while ((c=*p++) diff 0)
	begin
		if (c == '/' or c == '>') *q++ = '/';
		if (c diff ' ') *q++ = c;
	end
	*q++ = '>';
	*q++ = '\0';
	return stresc;
end

int
key(w)
char *w;
begin
	return strcmp(s,w) == 0;
end

FILE *
fopener (file,mode)
char *file, *mode;
begin
	FILE *f;
	f= fopen (file,mode);
	if (f == (FILE *)NULL) begin
		fprintf (stderr,"*** Error: unable to open %s\n", file);
		exit (1);
	end
	return f;
end

void
main (argc, argv) 
int argc; char **argv; begin
	int ic,errflg=0;

	mx= 0; line= 0; optp= 0; chsnum= 0; chsets= 0; aliases= 0;

	while ((ic = getopt(argc, argv, "p")) != EOF) {
		int c = ic & 0377;
		switch(c) {
		case 'p': optp++; break; /* make charmaps */
		default: c=ic;
		}
	}

	if (errflg) {
		fprintf(stderr, "Usage: gc -p \n");
		exit(1);
	}

	a= strlen(refset)+1; tabl= a*a;
	defc= (INT16S *) xalloc((unsigned int)(tabl+AMAX)*2);
	outtab8=(CHAR8U *) xalloc((unsigned int)128*128);
	outtab= (INT16S *) xalloc((unsigned int)128*128*2);
	intab= (INT16S *) xalloc((unsigned int)128*128*2);
	combtabs= (INT16S *) xalloc((unsigned int)MAXCOMBTABS*C256*2);
	chdr= (struct chdbhdr *) xalloc((unsigned int)sizeof(struct chdbhdr));
	f= fopener (fdefs,"rt");
	h= fopener (fcm,"wt");

	for (i=0; i< C256; i++) *(ord1+i)= *(ord2+i) = 0;
	t= (CHAR8U *) refset; i=1;
	while (*t) begin *(ord1+*t) = i; *(ord2+*t)= a*i++; t++; end
	t= (CHAR8U *) refset; i=1;
	while (*t) begin
		*ch2= *t;
		*(ot1+getcid(ch2)) = i;
		*(ot2+getcid(ch2))= a*i;
		i++; t++;
	end
	for (i=0; i< tabl+AMAX; i++) begin
		defc[i]= 0;
	end
	chdr->begcombtabs= tabl+AMAX; chdr->outsize= tabl; chdr->basechrs= a;

	*(defc+getcid("??")) = 1;

	while (fgets ((char *) s,LSIZE,f)) begin
		sscanf((char *) s,"%s %n",id,&spos);
		line++;
		if (mx > AMAX) perror ("Warning: too many chars\n");
		if (*s diff ' ' and strlen (s) > 1) begin
			if (*(s+2) == ' ' and strlen (s) > 2) begin
				cidnum= getcid(s); mx++;
				if (*(defc+cidnum)) 
				printf ("Warning: char %2.2s occurs in line %d and %d\n",
				s,*(defc+cidnum),line);
			end else begin
				for (i=0; i < bigids-1; i++)
				if (strcmp(id,*(longnames+i)) == 0) 
				printf ("Warning: char %s occurs in line %d and %d\n",
				id,*(defc+tabl+i),line); 
				*(longnames+bigids) = newstr(id); 
				cidnum= tabl+bigids; bigids++;
			end
			*(defc+cidnum) = line;
			*(descnames+line) = newstr(s+spos);
			fprintf (h,"%s\t__%.2x__%.2x\t%s",id,row,cell,s+spos); 
			cell++;
		end else if (strlen (s) >3 ) begin
			sscanf ((char *) s," %s %d", cmd,&num);
			for (p=cmd; *p; p++) if (isupper (*p)) *p= tolower (*p);
			/* printf ("command %s %d\n",cmd,num); */
			if (strcmp (cmd,"group") == 0) group=num;
			else if (strcmp (cmd,"plane") == 0) plane=num;
			else if (strcmp (cmd,"row") == 0) row=num;
			else if (strcmp (cmd,"cell") == 0) cell=num;
		end
	end
	printf ("%d short mnems and %d long mnems, = %d mnems defined\n",
		mx,bigids,mx+bigids);
	fclose (f); fclose (h);

	f= fopener (fsets,"rt");
	cme= fopener (cmerror,"wt");
	pc= fopener (fpc,"rt");

	g= fopener (fcpl,"wb");
	strcpy(chdr->label,"CHARSETS");
	chdr->version= 19930120;
	fwrite (chdr,sizeof(*chdr),1,g);
	fwrite (ot1,C256,2,g);
	fwrite (ot2,C256,2,g);
	chdr->offlong = ftell (g);
	writestringarray(g,longnames,bigids);
	chdr->offchs = ftell (g);
	chdr->sizlong = chdr->offchs - chdr->offlong;

	donotread= 0;
	while (donotread or fscanf (f,"%s",s) diff EOF) begin
		donotread= 0;
		if (*s == '&' and *(s+1) diff '\0') begin
		if (key("&referenceset")) begin
			fgets ((char *) ref+32,40,f); /* skip rest of line */
			fgets ((char *) ref+32,40,f);
			fgets ((char *) ref+64,40,f);
			fgets ((char *) ref+96,40,f);
			for (i=0; i< 32; i++) *(ref+i)= 0;
			for (i=33; i< C256; i++)
			if (*(ref+i) <= *(ref+32)) *(ref+i)= 0;
			fwrite (ref,1,C256,g);
		end else if (key("&charset")) begin
			if (chsets) gwrite();
			fscanf (f,"%s",defname);
			defchsname(defname);
			cod2= cod= 0; ecma= 0; c.bits= 8; dupl= 0;
			c.c0esc= c.c1esc= c.g0esc= c.g1esc= c.g2esc= c.g3esc= 0;
			c.combtabs= 0;
			for (i= 0; i < tabl+AMAX; i++) *(outtab+i) = 0;
			for (i=0; i< C256; i++) *(intab+i)= 0;
			if (optp) begin
				strcpy(cmname,cmdir);
				strcat(cmname,defname);
				cm= fopener (cmname,"wt");
				fprintf(cm,"<code_set_name> %s\n",defname);
				fprintf(cm,"<comment_char> %%\n");
				fprintf(cm,"<escape_char> /\n");
			end
		end else if (key("&alias")) begin
			fscanf (f,"%s",aliasname);
			defchsname(aliasname);
			aliases++;
			if (optp) fprintf(cm,"%% alias %s\n",aliasname);
		end else if (key("&rem")) begin
			fgets (comment,240,f);
			if (optp) fprintf(cm,"%% %s\n",comment);
		end 
		else if (key("&g0esc")) c.g0esc= getdox();
		else if (key("&g1esc")) c.g1esc= getdox();
		else if (key("&g2esc")) c.g2esc= getdox();
		else if (key("&g3esc")) c.g3esc= getdox();
		else if (key("&c0esc")) c.c0esc= getdox();
		else if (key("&c1esc")) c.c1esc= getdox();
		else if (key("&ecma")) ecma= getdox(); /* obsolete */
		else if (key("&code")) begin
			if (optp) if (cod == 0) fprintf(cm,"CHARMAP\n");
			cod= getdox();
		end else if (key("&code2")) begin
			cod2= getdox()+32;
			cod= getdox()+32;
		end else if (key("&codex")) begin
			fscanf (f,"%s",s);
			strcpy(codc,s);
			codr1= getdox(); codr2= getdox();
			codc1= getdox(); codc2= getdox();
			if (optp) for (i=codr1; i<=codr2; i++)
			fprintf(cm,"<%s%2.2d%2.2d>...<%s%2.2d%2.2d>  /d%3.3d/d%3.3d\n",
				codc,i,codc1,codc,i,codc2,i+32,codc1+32);
		end else if (key("&bits")) begin
			c.bits= getdox();
			if (c.bits > 8)
			for (i=0; i< 128*128; i++) *(intab+i)= 0;
		end else if (key("&duplicate")) begin
			dupl= getdox();
			if (dupl <0 or dupl >= C256 or not *(intab+dupl))
			printf ("Warning: charset %s has duplicate %d but no original\n",defname,dupl);
			fscanf (f,"%s",s);
			if (strlen (s) == 1) begin *(s+1)= ' '; *(s+2)= '\0'; end
			cidnum= getcid(s);
			if (*(defc+cidnum) == 0)
			printf ("Warning: charset %s duplicate %d %s unknown\n",defname,dupl,s);
			*(outtab+i) = dupl;
		end else if (key("&comb2")) begin
			fscanf (f,"%s",s); comb1= getmnem(s);
			if (comb1 && *(outtab+comb1) == 0) 
			printf ("Warning: charset %s comb1 %s not in charset\n",defname,s);
			else combtab= getcombtab(comb1); 
			fscanf (f,"%s",s);
			while (comb1 and *s diff '&') begin
				comb2= getmnem(s);
				if (comb2 and *(outtab+comb2) == 0)
				printf ("Warning: charset %s comb2 %s not in charset\n",defname,s);
				fscanf (f,"%s",s); resch= getmnem(s);
				if (resch and *(outtab+resch))
				printf ("Warning: charset %s result %s already defined\n",defname,s);
				if (resch) begin
					*(outtab+resch)=*(outtab+comb1)*C256+*(outtab+comb2);
					*(combtab+*(outtab+comb2))= resch;
				end
				fscanf (f,"%s",s);
			end
			donotread= 1;
		end /* commands */
		end else if ( *(defc+(cidnum=getmnem(s))) and not key("??") and not key("__") ) begin

			if (*(outtab+cidnum))
			printf ("Warning: charset %s char %s occurs more than once %d %d\n",
				defname,s,*(outtab+cidnum),cod2*10000+cod);
			if (optp) begin
				fprintf (cm,"%-8s ",putesc(s));
				if (c.bits > 8) fprintf (cm,"/d%.3d",cod2);
				fprintf (cm,"/d%.3d   %s",cod,*(descnames+*(defc+cidnum)));
			end
			*(intab+cod2*128+cod)= cidnum;
			*(outtab+cidnum) = cod2*C256+cod;
			cod++;
		end else cod++;
	end
	fclose (f);
	gwrite ();

	printf("%d charsets and %d aliases defined\n",chsets-aliases,aliases);
	chdr->offdat = ftell (g);
	chdr->sizchs = chdr->offdat - chdr->offchs;
	fwrite (chsdata,4,chsets,g);
	chdr->offdir = ftell (g);
	chdr->sizdat = chdr->offdir - chdr->offdat;
	writestringarray(g,chsnames,chsets);
	chdr->offend = ftell (g);
	chdr->sizdir = chdr->offend - chdr->offdir;
	fseek (g,0L,0);
	fwrite (chdr,1,sizeof(*chdr),g);
	fseek (g,0L,2);
	fclose (g);
	exit (0);
end
