/*
						mkgaiji.c (MaKe GAIJI)

									July 13, 1993,  written by SHIMA


	Make GAIJI corresponding to  g  sub-option of the options  -p=l, -p=m
	of dviprt Ver.2.39.1.

	Usage: mkgaiji [-m] [-d=<dir>] [-f=<par>] [[<] file]

           -m      : merge FONT
           -d=<dir>: directory where the FONT are made
           -f=<font>[.<dot>]=<type>;<flag>;<num>;<num>;...;<font>[.<dot>]=...

         <font> : substring of TFM name (min, min8, goth,...)
         <dot>  : width indicated by dviout/dviprt (<dot>-<dot> is valid)
         <type> : - or an integer (Skip or Mincho, Gothic, Kaisho, Gyousho,...)
         <flag> : a sequence of 0 or 1  (Outline, Shadow, Italic,...)
         <num>  : a certain number      (any parameters, dot,...)

    Ex.     mkgaiji -d=b:\font -f=goth=1;min.50=2;01001;21;18;min=-

  --------------------------------------------------------------------------
							<<< Customization >>>

  It will be sufficient to rewrite the functions  initialize()  and  get_font()
to customize MKGAIJI.


   initialize() -> get_font(code) -> get_font(code) -> ... -> initialize() ->
   <font = 1>                                                 <font = 2>

   get_font(code) -> ....

									-----
void initialize(void)
{
     By the data

		struct FONT f; int type, max_flag, max_par; int flag[], par[];

  we determine which kind of font we will make and prepare to do so.

     The deafult values are all 0 and they are changed by reading the 
  parameters -f=..., the current font name(by TFM file) and the desirable
  <dot> size of the width of the font.

     f.width x f.height  is the desirable size of font (by dot) and here we 
  may change them according to the above data.

  Example:
	if (max_par > 0) f.width  = par[0];
	if (max_par > 1) f.height = par[1];

     type is usually used for the  FONT (eg. 0: Mincho(default), 1: Gothic, 
  2: Maru Gochic, 3: Kaicho etc..)

     flag[]  take value in {0, 1} and are usually used for attributes for the 
  font (eg. Outline, Italic, Circulate etc.)

     par[]  are used in any purpose, which are non_negative integers.

     f.width   and  f.height  will be used to make header of the FONT file and
  therefore we shoud set them correctly according to the FONT we will make.
}

									-----

Example: mkgaiji -d=b:\font -f=goth=1;min.50=2;01001;21;18

TFM file  goth8.tfm,  any f.width
          -> type = 1, max_flag = max_par = 0

TFM file  min10.tfm,  f.width = 50
          -> type = 2, max_flag = 5, max_par = 2
             flag[0] = 1, flag[1] = 0, flag[2] = 0, flag[3] = 1, flag[4] = 0
             par[0] = 21, par[1] = 18


TFM file  min8.tfm,  f.width = 40
          -> type = max_flag = max_par = 0

									-----

void get_font(int code)
{
     Make the bit image of the character with the given JIS code and output
  to stream FILE *w_fp.
}
  --------------------------------------------------------------------------
 */

			/* to make a real programm the following is set 0 */
#define	DEBUG	1

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<io.h>
#include	<malloc.h>

			/* One may change the following 4 constants */

#define	MAX_CHAR		4000	/* maximal number of charcters in a FONT */
#define	LINE_BUF_SIZE	0x200	/* size of a general line buffer 		 */
#define	MAX_FLAG		32		/* maximal number of flags for a FONT	 */
#define	MAX_PAR			16		/* maximal number of integer parameters  */
								/* for a font							 */
#ifndef	MAX_PATH
#define	MAX_PATH	80
#endif

#define	KG_EXT			".kg"	/* extension of KG FONT file			 */
#define	TMP_EXT			".$$"	/* extension of temporary file			 */

FILE *r_fp;						/* input stream							 */
FILE *w_fp;						/* output stream						 */
FILE *f_fp;						/* FONT file to read for merge			 */

unsigned char f_head[32];		/* header of KG file 						*/
char buf[LINE_BUF_SIZE];		/* buffer for input stream					*/
char sub_buf[LINE_BUF_SIZE];	/* sub_buffer for input stream				*/

char font_par[LINE_BUF_SIZE];	/* parameter given by -f= option			*/
char font_dir[MAX_PATH+1];		/* directory name given by -d=<dir> option	*/
char font_path[MAX_PATH+1];		/* path name of FONT we will make			*/
char tmp_path[MAX_PATH+1];		/* path name of temporary FONT file			*/
char tfm_name[MAX_PATH+1];		/* current tfm name given by input stream	*/
char file_name[MAX_PATH+1];		/* current FONT file name by input stream 	*/

int f_merge;					/* flag for merge							*/
int old_total;					/* # of characters in existent FONT file	*/

int font;						/* font count								*/
int total;						/* total number of characters in a font 	*/
int max_flag;					/* number of current flags					*/
int max_par;					/* number of current integer parameters		*/

int table[MAX_CHAR];			/* character codes of FONT we will make		*/
unsigned char flag[MAX_FLAG];	/* current flags read from parameteres 		*/
unsigned int par[MAX_FLAG];		/* current integer parameters				*/
unsigned int type;				/* current font number read from parameters */

struct FONT {
	int font_code;		/* font_code in DVI file			*/
	int font_type;		/* font_type in dviout/prt			*/
	int width;			/* desired width					*/
	int height;			/* desired height					*/
	int w_width;		/* real width used by dviout/prt	*/
	int w_height;		/* real height used by dviout/prt	*/
};

struct FONT f;

void initialize(void);
void get_font(int);

int get_num(char *, int *);
void font_make(void);
void usage(void);
void check_par(void);
void make_font_path(void);
void open_font(void);
void close_font(void);
void interpret(void);

void clear_code(void);
void save_code(int);
int check_code(int);

#define	jis_to_num(x) (((x>>8)-0x21)*94 + (x & 0xff) - 0x20)
#define	MAX_SAVE	1000

char f_code[MAX_SAVE];

void save_code(int code)
{
	unsigned int num;

	if ((num = jis_to_num(code)) >= (8*MAX_SAVE)){
		fprintf(stderr,
			"JIS code error: %04X in the file %s\n", code, font_path);
		exit(1);
	}
	f_code[num>>3] |= (1<<(num&7));
}

void clear_code(void)
{
	int	i;

	for (i = 0; i < MAX_SAVE; i++)
		f_code[i] = 0;
}

int check_code(int code)
{
	unsigned int num;

	if ((num = jis_to_num(code)) >= (8*MAX_SAVE)){
		fprintf(stderr,
			"JIS code error: %04X in the stream\n", code);
		exit(1);
	}
	return f_code[num>>3] & (1<<(num&7));
}

void check_existence(void)
{
	int i, j;

	for (i = 0; i < total; i++)
		if (check_code(table[i])) table[i] = 0;
	for (i = j = 0; i < total; i++){
		if (table[i]){
			if (i != j) table[j] = table[i];
			j++;
		}
	}
	total = j;
}


/*
 *  Make a font
 */
void font_make(void)
{
	int	i;

	check_par();
	initialize();
	make_font_path();
	open_font();
	for (i = 0; i < total; i++)
		get_font(table[i]);
	close_font();
}

void usage(void)
{
	fprintf(stderr,
		"\t\t<<< MaKe GAIJI for dviout/dviprt v2.39.1- >>>\n"
		"\t\t\tVer 0.1"
#if	DEBUG
		" TEST"
#endif
		", 1993 written by SHIMA\n\n"
		"Usage:\tmkgaiji [-m] [-d=<dir>] [-f=<par>] [[<] file]\n\n"
		"\t-m      : merge FONT\n"
		"\t-d=<dir>: directory where the FONT are made\n"
		"\t-f=<font>[.<dot>]=<type>;<flag>;<num>;<num>;...;"
		"<font>[.<dot>]=...\n\n"
		"\t <font> : substring of TFM name\t(min, min8, goth,...)\n"
		"\t <dot>  : width indicated by dviout/dviprt (<dot>-<dot> is valid)\n"
		"\t <type> : - or an integer (Skip or"
		" Mincho, Gothic, Kaisho, Gyousho,...)\n"
		"\t <flag> : a sequence of 0 or 1\t(Outline, Shadow, Italic,...)\n"
		"\t <num>  : a certain number\t(any parameters, dot,...)\n\n"
		"Ex.\tmkgaiji -d=b:\\font -f=goth=1;min.50=2;01001;21;18;min=-\n"
	);
	exit(1);
}

/*
 *				MAIN (get command parameters)
 */
void main(int argc, char **argv)
{
	int	i, j, k;

	r_fp = stdin;

	if (isatty( fileno( stdin ) ) && argc ==  1) usage();

	for (i = 1; i < argc; i++){
		if (argv[i][0] == '-'){
			switch(tolower(argv[i][1])){

				case 'd':				/* get directory */
					strncpy(font_dir, argv[i]+3, MAX_PATH - 2);
					j = strlen(font_dir) - 1;
					if (font_dir[j] != '\\' && font_dir[j] != ':'){
						font_dir[j+1] = '\\';
						font_dir[j+2] = 0;
					}

				case 'f':				/* get font parameter */
					strncpy(font_par, argv[i]+3, LINE_BUF_SIZE);
					break;

				case 'm':				/* flag for merge */
					f_merge = 1;
					break;
			}
		}
		else if (r_fp == stdin && (r_fp = fopen(argv[i], "rb")) == NULL ){
			fprintf(stderr, "Cannot open %s\n", argv[i]);
			exit(1);
		}
	}
	interpret();
}

/*
 *	interpret the input stream
 */
void interpret(void)
{
	int	i, j, pos, k;
	struct FONT pf;

	font = 0;

	while( fgets(buf, LINE_BUF_SIZE, r_fp) != NULL ){

		if (buf[0] == '#'){
			if (buf[1] == '#'){								/* ## ..... */
				fprintf(stderr, "%s", buf);
				continue;
			}

											/* get tfm_name and file_name */
			if (buf[1] == ' '){
				pos = strlen(buf) - 1;
				j = 0;
				while(j < 4 && --pos >= 0){
					if (buf[pos] <= ' '){
						if (j & 1) j++;
					}
					else if (!(j & 1)) j++;
				}
				if (pos < 1) continue;
				font++;
				strcpy(sub_buf, buf);
														/* get font metric */
				fgets(buf, LINE_BUF_SIZE, r_fp);
				i = 0;

				while(isspace(buf[++i]));
				pf.font_code = get_num(buf, &i);
				pf.font_type = get_num(buf, &i);
				pf.width     = get_num(buf, &i);
				pf.height    = get_num(buf, &i);
				pf.w_width   = get_num(buf, &i);
				pf.w_height  = get_num(buf, &i);

				if ( pf.font_code >= 0 && pf.font_type >= 0
				  && pf.width > 0      && pf.height > 0
				  && pf.w_width > 0    && pf.w_height > 0 ){
															/* make font */
					if (total) font_make();
				}
				else{
					fprintf(stderr, "Illegal line:%s\n", buf);
					exit(1);
				}
				for ( j = 0; (k = sub_buf[++pos]) > ' '; j < MAX_PATH)
					tfm_name[j++] = k;
				tfm_name[j] = 0;
				while(sub_buf[++pos] <= ' ');
				for ( j = 0; (k = sub_buf[pos++]) > ' '; j < MAX_PATH)
					file_name[j++] = k;
				file_name[j] = 0;

				f.font_code = pf.font_code;
				f.font_type = pf.font_type;
				f.width     = pf.width;
				f.height    = pf.height;
				f.w_width   = pf.w_width;
				f.w_height  = pf.w_height;
				total = 0;
				continue;
			}
		}
		if (!isxdigit(buf[0])
		 || !isxdigit(buf[1])
		 || !isxdigit(buf[2])
		 || !isxdigit(buf[3])
		 || buf[4] > ' ' ) continue;

		for (i = j = 0; i < 4; i++){					/* HEX -> integer */
			k = buf[i] - '0';
			if (k > 9)  k -= ('A' - '0' - 10);
			if (k > 15) k -= ('a' - 'A');
 			j = j*16 + k;
		}
														/* get JIS code */
		if (total < MAX_CHAR - 1) table[total++] = j;
	}
	if (total) font_make();
}

int get_num(char *b, int *pt)
{
	int	i, num;

	i = *pt;
	num = 0;

	if (!isdigit(b[i])) return -1;
	while(isdigit(b[i])){
		num = num * 10 + b[i++] - '0';
	}
	*pt = i+1;
	return num;
}

/*
 *	output 2 byte integer from the lower byte
 */
void put_int(int num)
{
	fputc(num & 0xff, w_fp);
	fputc(num >> 8, w_fp);
}


void make_font_path(void)
{
	int	i, j, ch;

	strcpy(font_path, font_dir);
	i = strlen(font_path);

	for(j = i; isalpha(font_path[i] = tfm_name[i-j]); i++);
	font_path[i] = 0;
	sprintf(font_path+i, "%d", f.width);
	strncat(font_path, KG_EXT, MAX_PATH);
}

/*
 *		Interpret the command parameter corresponding to the current FONT
 *		and store the result in
 *
 *					 type, max_flag, flag[], max_par, par[]
 *
 *			default: type = max_flag = max_par = flag[*] = par[*] = 0
 */
void check_par(void)
{
	int	i, j, k, last, ch;

	type = 0;
	for (i = 0; i < MAX_FLAG; i++)	flag[i] = 0;
	for (i = 0; i < MAX_PAR; i++)	par[i] = 0;

	last = strlen(font_par);
	for(i = 0; i < last; ){
		for (k = i; isalnum(font_par[k]); k++);
		for (j = 0; font_par[i] == tfm_name[j] && i < k;i++, j++);
		if (i < k){
			i = k;
			goto skip;
		}
		if (font_par[i++] == '.'){
			k = get_num(font_par, &i);
			if (font_par[i-1] == '-'){
				if (isdigit(font_par[i]))			/* <dot>-<dot>= */
					j = get_num(font_par, &i);
				else{								/* <dot>-=		*/
					i++;
					j = 0x7fff;
				}
			}
			else j = k;								/* <dot>=		*/
			if (f.width < k || f.width > j) goto skip;
		}
		if (font_par[i] == '-'){
			total = 0;
			break;
		}
		type = get_num(font_par, &i);
		if (i >= last) break;
		if (isalpha(font_par[i])) continue;
		for (max_flag = 0; max_flag < MAX_FLAG; max_flag++){
			k = font_par[i++] - '0';
			if (k == 1 || k == 0) flag[max_flag] = k;
			else break;
		}
		if (i >= last) break;
		if (isalpha(font_par[i])) continue;
		for (max_par = 0;
		  max_par < MAX_PAR && i < last && isdigit(font_par[i]); max_par++)
			par[max_par] = get_num(font_par, &i);
skip:	while(i < last && !isalpha(font_par[i])) i++;
	}
}

void read_fat(void)
{
	int	i, code;
	char tmp[34];

	if (fread(tmp, 34, 1, f_fp) != 1){
r_err:	fprintf(stderr, "Read error:%s\n", font_path);
		exit(1);
	}

	for (i = 0; i < 32; i++){
		if (tmp[i] != f_head[i]){
			fprintf(stderr, "Different Header:%s\n", font_path);
			exit(1);
		}
	}
	old_total = *(int *)(tmp+32);
	clear_code();

	for (i = 0; i < old_total; i++){
		if ((code = fgetc(f_fp)) == -1) goto r_err;
		code += (fgetc(f_fp)<<8);
		save_code(code);
	}
	check_existence();
}

void open_font(void)
{
	int	i, code, b_width, size;
	long total_size;

	b_width = (f.width + 7)/8;

	f_head[2] = 247;
	f_head[3] = 'G';
	f_head[4] = f.width & 0xff;
	f_head[5] = (f.width >> 8);
	f_head[6] = f.height & 0xff;
	f_head[7] = (f.height >> 8);
	f_head[8] = b_width & 0xff;
	f_head[9] = (b_width >> 8);

	f_fp = NULL;
	old_total = 0;
	if ( f_merge && (f_fp = fopen(font_path, "rb")) != NULL ){

		strcpy(tmp_path, font_path);
		i = strlen(tmp_path) - 3;
		strcpy(tmp_path + i, TMP_EXT);

		read_fat();

		if (!total){
			close_font();
			return;
		}
#if	!DEBUG
		if ((w_fp = fopen(tmp_path, "wb")) == NULL){
			fprintf(stderr, "Cannot open temporary file %s\n", tmp_path);
			exit(1);
		}
	}
	else if ( (w_fp = fopen(font_path, "wb")) == NULL ) goto w_err;

	if (fwrite(f_head, 32, 1, w_fp) != 1){
w_err:	fprintf(stderr, "Cannot open %s\n", font_path);
		exit(1);
	}
	if (f_fp) fseek(r_fp, 34L, SEEK_SET);

	put_int(total + old_total);

	for (i = 0; i < old_total; i++){
		code = fgetc(f_fp);
		code += (fgetc(f_fp)<<8);
		put_int(code);
	}

	for (i = 0; i < total; i++)
		put_int(table[i]);

	total_size = ((long)((f.width+7)/8)*f.height)*old_total;
	while(total_size){
		size = (total_size > MAX_SAVE)?MAX_SAVE:total_size;
		fread((char*)f_code, size, 1, f_fp);
		fwrite((char*)f_code, size, 1, w_fp);
		total_size -= size;
#endif
	}
	printf("Output to: %s\n", font_path);
}

void close_font(void)
{
#if DEBUG
	printf("End of %s\n\n", font_path);
#endif
	if (f_fp) fclose(f_fp);
	if (w_fp) fclose(w_fp);
	if (f_fp != NULL && w_fp != NULL){
		unlink(font_path);
		rename(tmp_path, font_path);
	}
	f_fp = w_fp = NULL;
}

#if	DEBUG
void initialize(void)
{
	int	i;

	printf("## Making font for %s %s (%dx%d)\n",
		tfm_name, file_name, f.width, f.height);
	printf("## type %d: flag", type);
	for (i = 0; i < max_flag; i++) printf(" %d", flag[i]);
	printf(": par ");
	for (i = 0; i < max_par; i++) printf(" %d", par[i]);
	printf("\n");
}

void get_font(int code)
{
	printf("%04X\n", code);
}

#else
void initialize(void)
{
	/*		struct FONT f; int type, max_flag, max_par; int flag[], par[];  */
	/* May change f.width, f.height etc. 									*/

	int	i;

	printf("\n## Making font for %s %s (%dx%d)\n",
		tfm_name, file_name, f.width, f.height);
	printf("## type %d: flag", type);
	for (i = 0; i < max_flag; i++) printf(" %d", flag[i]);
	printf(": par ");
	for (i = 0; i < max_par; i++) printf(" %d", par[i]);
	printf("\n");
	/*
		Here we define the font to make and prepare it.
	 */
}


void get_font(int code)
{
	/*	Write the raster data of a character with JIS CODE <code> */

	int i, j, b_width;

	b_width = (width+7)/8;

	/*
		Here we get the raster data of the character and output to w_fp.
	 */
	for (j = 0; j < height; j++){
		for (i = 0; i < b_width; i++)
			if ((fputc(0, w_fp) == EOF){
				fprintf(stderr, "Error in writing FONT file\n");
				exit(1);
			}
	}
	printf("%04X ", code);
}
#endif
