/*
 *		check Fonts (version of AMS FONTS)
 *                         Dec 1990, written by SHIMA
 *						   Apr 1992, second version
 *						   May 1992, third  version
 *						   Sep 1992, fifth  version
 *						   May 1996, sixth  version
 *
 * Usage: chkfont [-s] [-c] [-{f|F}<font_data_file>] [-d] dvi_file_name
 *        chkfont [-s] [-c] [-{f|F}<font_data_file>] [-t] tfm_file_name
 *        chkfont [-s] [-c] [-{f|F}<font_data_file>] [-p] font_file_name
 *
 * "c" means check mode
 * "s" means silent check mode
 * "f" means user defined font data
 * "F" means user defined font data and ignore default data
 * "t" means tfm_file
 * "p" means font_file
 *
 * errorlevel is the sum of th following number
 *           1: cmbsy5-9, cmcsc8-9, cmex7-9, cmmib5-9
 *           2: new font: eu??, msam, msbm, wncy??, font_data_file
 *           4: new eufm5-10
 *           8: unknown eufm
 *          16: old font: msxm, msym, amcsc, mcyr, mcyb, font_data_file
 *          32: old eufm5-10
 *
 * If errorlevel is larger than 240,
 *         248: error in font_data_file
 *         249: cannot find font_data_file
 *         250: bad parameter
 *         251: old and new eufm
 *         252: bad dvi file
 *         253: error in dvi file
 *         254: not dvi/tfm file or unknown font ID
 *         255: cannot find dvi/tfm/font file
 *
 *
 *  1 point = 65536(=2^(16)) scaled point
 *  1 inch  = 72.27 point = 2.54 cm
 *
 */

#include <stdio.h>
#include <stdlib.h>
#ifndef UNIX
#include <dos.h>
#ifdef MSVC
#include "msvcdir.h"
#else
#include <dir.h>
#endif
#include <io.h>
#endif
#include <string.h>

#ifdef	GCC
#include <ctype.h>
#endif

#include "dd.h"

#define	ID			2
#define ID_PTEX       3
#define	END_DVI		223
#define	MAX_NAME	16
#define	MAX_FONT	1024
#define	BUF_SIZE	1024

#define	HALF_POINT	(1L<<8)

#define	NEW_FONT	1
#define	NEW_POINT	2
#define	NEW_EUFM	4
#define	BAD_EUFM	8
#define	OLD_FONT	16
#define	OLD_EUFM	32

#define	DVI_FILE	0
#define	TFM_FILE	1
#define	PXL_FILE	2
#define	PK_FILE		3
#define	VF_FILE		4
#define	VF_FILE_MAP	5

char buf[BUF_SIZE];
char filename[MAXPATH];

/*
 * eufm: point, checksum old, checksum new
 */

struct EUFM {
	int point;	/*	point number	*/
	unsigned int oldd;	/*	old checksum	*/
	unsigned int neww;	/* 	new checksum	*/
};

/*
 * Multiple lines for a point are allowed.
 * The last line should be 0 point.
 */
struct EUFM eufm[] =
{
	5, 0x0891FD38L, 0x246A686BL,
	5, 0x0891FD38L, 0xB8C9AFD4L,
	6, 0x2F8EDF02L, 0x9793B1E1L,
	6, 0x2F8EDF02L, 0x65167BC4L,
	7, 0x37CDA321L, 0x05184980L,
	7, 0x37CDA321L, 0x58ED2602L,
	8, 0x37CCA321L, 0x2564B2F5L,
	8, 0x37CCA321L, 0x5C978225L,
	9, 0x3F977CF3L, 0x1F6C4283L,
	9, 0x3F977CF3L, 0xF833F3AFL,
	10, 0xBF989013L, 0x8F256EB2L,
	0, 0L, 0L
};

struct CHKFONT {
	char f_name[MAX_NAME];	/* font name with point */
	unsigned long oldd;	/* old checksum */
	unsigned long neww;	/* new checksum */
};

struct CHKFONT chkfont[MAX_FONT];

/*
 * old AMS FONTS
 */

char *oldfont[] =
{"msxm", "msym", "amcsc", "mcyr", "mcyb", ""};

/*
 * add CM fonts, start point, end point
 */

struct NEWPOINT {
	char *font_name;	/* font name		*/
	int top;	/* smallest point	*/
	int end;	/* largest point		*/
};

struct NEWPOINT newpoint[] =
{
	"cmbsy", 5, 9,
	"cmcsc", 8, 9,
	"cmex", 7, 9,
	"cmmib", 5, 9,
	"", 0, 0
};

/*
 * new AMS FONTS
 */

char *newfont[] =
{"euex", "eufb", "eurb", "eurm", "eusb", "eusm",
 "msam", "msbm",
 "wncyb", "wncyi", "wncyr", "wncysc", "wncyss",
 ""
};

struct FONT_ATR {
	/* parameters of font_def; See "TeX:The program". */
	long k, c, s, d;
	int a, l;
	char *n;
};

struct VFFONT_ATR {
	/* parameters of font_def; See "TeX:The program". */
	long k, c, d;
	double s;
	int a, l;
	char *n;
};

int mag[] =
{1095, 1200, 1315, 1440, 1577, 1728, 1893, 2074, 2272, 2488,
 2726, 2986, 3271, 3583, 3925, 4300, 4710, 5160, 32000};

char *font_ext[] = {
	"gf", "pk", "pxl", "far", "gth", NULL
};

#define	read_byte(x)	(uchar)getc(x)

void usage(void);
char *name_link(char *, char *);
void read_post(DVIFILE_INFO *);
void get_font_list(DVIFILE_INFO *);
void font_define(int, FILE *);
void vf_define(int, FILE *);
int read_uint(FILE *);
long read_mit(FILE *);
long read_long(FILE *);
char *read_str(FILE *, int);
long read_n(FILE *, int);
long signed_read_n(FILE *, int);
void check_font(void);
void usage(void);
void show_dvi_data(DVIFILE_INFO *);
void get_list(char *);
void type(char *);
void tfm_define(FILE *);
void pxl_define(FILE *);
int chk_font_file(FILE *);
int check_mag(int);
long to_long(uchar *);

DVIFILE_INFO dvi_info;
struct FONT_ATR font;
struct VFFONT_ATR vffont;

int f_v = -1;	/* verbose mode */
int f_d;	/* ignore default data */
int f_t;	/* file_type */
int n_data;	/* number of font_data */
uint f_type;
long pmag;
long hppp;
long vppp;

struct FINFO {
	char f_atrib;
	uchar f_time[2];
	uchar f_date[2];
	uchar f_size[4];
	char f_name[14];
	uchar f_pos[4];
};

struct FARDIR {
	char f_name[10];
	uchar f_time[4];
	uchar f_size[4];
	uchar f_pos[4];
};

uchar gth_buf[sizeof(struct FINFO)];
long f_end;
int  f_cont;

int	strcmpl(const char *s, const char *t)
{
	int	i;

        i = strlen(t);
        if(strlen(s) < i ) return -1;
        s += strlen(s);
        while(i-- > 0){
                s--;
                if(toupper(*s) != toupper(t[i]))
		        return -1;
	}
        return 0;
}


#ifdef UNIX
char *strlwr(char *p)
{
  char *original = p;

  while (*p != '\0') {
  	*p = tolower(*p);
  	++p;
  }
  return original;
}
#endif

int main(int argc, char **argv)
{
	int i, code;
	char *pt;

	if (argc < 2)
		usage();
	for (i = 1; i < argc - 1; i++) {
		if (argv[i][0] != '-')
			usage();
		switch (argv[i][1]) {
		  case 'S':
		  case 's':
			  f_v = 0;
			  break;
		  case 'c':
			  f_v = 1;
			  break;
		  case 'F':
			  f_d = 1;
		  case 'f':
			  get_list(argv[i] + 2);
			  break;
		  case 'd':
			  f_t = -1;
			  break;
		  case 't':
			  f_t = 1;
			  break;
		  case 'p':
			  f_t = 2;
			  break;
		  default:
			  usage();
		}
	}

				/* check extension */
	if (!f_t){
		pt = argv[argc-1];
		i = strlen(pt);
		while(--i > 0){
			if( pt[i] == '.' ) break;
			if( pt[i] == '\\' || pt[i] == '/' || pt[i] == ':' ){
				i = 1;
			}
		}
		if (i > 0){
			pt += i+1;
			if( strcmpl(pt, "dvi") == 0 )
				f_t = -1;
			else{
				if( strcmpl(pt, "tfm") == 0 ||  strcmpl(pt, "ofm") == 0 )
					f_t = 1;
				else{
					if( strcmpl(pt, "vf") == 0 )
						f_t = 2; /* ovf is also included */
					else{
						for(i = 0; font_ext[i]; i++){
							if( strcmpl(pt, font_ext[i]) == 0 ){
								f_t = 2;
								break;
							}
						}
					}
				}
			}
		}
	}
	if(f_t == -1) f_t = 0;
	strcpy(filename, dvi_info.file_name = argv[argc - 1]);
	if (f_t != PXL_FILE && strchr(argv[argc - 1], '.') == NULL)
		dvi_info.file_name = name_link(argv[argc - 1],
									   (f_t) ? "tfm" : "dvi");
	if ((dvi_info.file_ptr = fopen(dvi_info.file_name, "rb")) == NULL) {
		fprintf(stderr, "Cannot find %s\n", dvi_info.file_name);
		exit(255);
	}
	if (f_t != DVI_FILE) {
		font.n = dvi_info.file_name;
		if (f_t == TFM_FILE)
			tfm_define(dvi_info.file_ptr);
		else {
			if (chk_font_file(dvi_info.file_ptr)) {
				pxl_define(dvi_info.file_ptr);
				if (f_t == VF_FILE) {
					f_t = VF_FILE_MAP;
					code = read_byte(dvi_info.file_ptr);
					while (code >= FNT_DEF_1 && code < FNT_DEF_1 + 4) {
						vf_define(code, dvi_info.file_ptr);
						check_font();
						code = read_byte(dvi_info.file_ptr);
					}
				}
			}
		}
	}
	else {
		read_post(&dvi_info);
		if (f_v)
			show_dvi_data(&dvi_info);
		get_font_list(&dvi_info);
		if (f_v != -1
			&& (f_type & (OLD_EUFM + NEW_EUFM)) == (OLD_EUFM + NEW_EUFM)) {
			fprintf(stderr, "old and new eufm fonts\n");
			exit(251);
		}
	}
	if (f_v != -1) {
		if (f_v != 0)
			printf("\n\n");
		printf("EXIT Code (errorlevel)\t\t= %d\n", f_type);
		if (f_type & OLD_EUFM)
			printf("Old eufm ");
		if (f_type & NEW_EUFM)
			printf("New eufm ");
		if (f_type & OLD_FONT)
			printf("Old font ");
		if (f_type & (NEW_FONT | NEW_POINT))
			printf("New font");
		if (f_type == 0)
			printf("No AMS Font");
	}
	printf("\n");
	exit(f_type);
}

int chk_font_file(FILE * fp)
{
	int i, f_num;
	long start;
	struct FINFO *finfo;
	struct FARDIR *fardir;

	finfo = (struct FINFO *)gth_buf;
	fardir = (struct FARDIR *)gth_buf;

	if (fread(gth_buf, sizeof(struct FINFO), 1, fp)<1) goto err;

	if (strcmp(gth_buf + 8, "gather") == 0)
		goto gth;

	fseek(fp, -8L, SEEK_END);
	f_num = read_uint(fp);
	start = read_long(fp);
	if (read_uint(fp) != (int)('f' + 'o' + 'n' + 't' + 'a' + 'r' + 'c' + 'h' + 'i' + 'v' + 'e')) {
		fseek(fp, 0L, SEEK_SET);
		return (1);
	}
	/* FAR file */
	if (f_v){
		f_cont = 1;
		printf("\t\"%s\" is a FAR file which contains...\n", filename);
	}
	while (f_num-- > 0) {
		if (f_v)
			printf("\n");
		fseek(fp, start, SEEK_SET);
		if (fread((char *)fardir, sizeof(struct FARDIR), 1, fp)<1) goto err;

		start = ftell(fp);
		font.n = fardir->f_name;
		f_end = to_long(fardir->f_pos) + to_long(fardir->f_size);
		fseek(fp, to_long(fardir->f_pos), SEEK_SET);
		pxl_define(fp);
		if (f_v)
			printf("\n");
	}
	return (0);

  gth:							/* GTH file */
	if (f_v){
		f_cont = 1;
		printf("\t\"%s\" is a GTH file which contains...\n", filename);
	}
	start = gth_buf[0] + (gth_buf[1] << 8) + (gth_buf[2] << 16)
                + (gth_buf[3] << 24);
	f_num = gth_buf[4] + (gth_buf[5]<< 8);
	while (f_num-- > 0) {
		if (f_v)
			printf("\n");
		fseek(fp, start, SEEK_SET);
		if (fread(gth_buf, sizeof(struct FINFO), 1, fp)<1) goto err;

		start = ftell(fp);
		font.n = finfo->f_name;
		printf("%s:%ld(%ld)\n",
			font.n, to_long(finfo->f_pos), to_long(finfo->f_size));
		f_end = to_long(finfo->f_pos) + to_long(finfo->f_size);
		fseek(fp, to_long(finfo->f_pos), SEEK_SET);
		pxl_define(fp);
		if (f_v)
			printf("\n");
	}
	return (0);

  err:
    fprintf(stderr, "Cannot read data\n");
    exit(250);
}

void show_dvi_data(DVIFILE_INFO *dvi)
{
	int len, x, y;
	long s_width, s_hight;

	printf("dvi file name\t\t\t= %s\n", dvi->file_name);
	fseek(dvi->file_ptr, 14L, SEEK_SET),
		len = (uchar)read_byte(dvi->file_ptr);
	printf("comment\t\t\t\t=%s\n", read_str(dvi->file_ptr, len));
	printf("factor converting to 10^{-5}m\t= %ld/%ld\n",
		   dvi->num, dvi->den);
	printf("magnification\t\t\t= %ld\n", dvi->mag);
	printf("maximum width\t\t\t= %ld scaled points = %ld points\n",
		   dvi->u, (dvi->u + HALF_POINT) >> 16);
	printf("maximum height+depth\t\t= %ld scaled points = %ld points\n",
		   dvi->l, (dvi->l + HALF_POINT) >> 16);
	printf("maximum stack depth\t\t= %d\n", dvi->stack_depth);
	printf("number of pages\t\t\t= %d\n", dvi->total_page);
	s_width = ((dvi->u >> 6) * 2540) / 7227;	/* scated 2^(-10) mm */
	s_hight = ((dvi->l >> 6) * 2540) / 7227;	/* scaled 2^(-10) mm */
	x = (s_width + 512) / 1024;
	y = (s_hight + 512) / 1024;
	printf("page size\t\t\t= %2d.%d cm x %2d.%d cm\n",
		   x / 10, x % 10, y / 10, y % 10);
	s_width = s_width * dvi->mag / 1000;
	s_hight = s_hight * dvi->mag / 1000;
	x = (s_width + 512) / 1024;
	y = (s_hight + 512) / 1024;
	printf("page size magnificated\t\t= %2d.%d cm x %2d.%d cm",
		   x / 10, x % 10, y / 10, y % 10);
}

int check_mag(int scale)
{
	int i;

	for (i = 0; scale > mag[i] + 1; i++);
	if (scale + 1 > mag[i])
		return (i + 1);
	return (-1);
}

void check_font(void)
{
	uchar point;
	char *pt;
	int ratio, cmag;
	int j;
	int f_point;

	if (f_v) {
		if (f_t != VF_FILE_MAP) {
			if (f_t == DVI_FILE)
				printf("\n\nFont %ld\n", font.k);
			printf("\tchecksum\t\t= %08lX\n", font.c);
			if (f_t != DVI_FILE)
				printf("\tdesign size\t\t= %ld 2^{-20} points = %ld points",
					   font.d, font.d >> 20);
			else
				printf("\tdesign size\t\t=%9ld scaled points =%3ld points",
					   font.d, font.d >> 16);
		}
		switch (f_t) {

		  case (VF_FILE_MAP):
			  printf("\n\n\tMapped Font %ld\n", vffont.k);
			//  printf("\t\tchecksum\t= %08lX\n", vffont.c); /* always zero, we can't get it without tfm */
			  printf("\t\tfont dsize\t= %ld 2^{-20} points = %ld points", vffont.d, vffont.d/(1<<20));
			  printf("\n\t\tfont at\t\t= %9.6lf", vffont.s/(1<<20));
			  printf("\n\t\tfont name\t= %s", vffont.n);
			  break;

		  case (VF_FILE):
			  break;

		  case (PK_FILE):
			  printf("\n\thorizontal\t\t= %ld 2^{-16} dots/point = %ld dpi",
					 hppp, ((((hppp >> 4) * 7227) >> 12) + 49) / 100);
			  printf("\n\tvertical\t\t= %ld 2^{-16} dots/point "
					 "= %ld dpi\n\t\t\t\t",
					 vppp, ((((vppp >> 4) * 7227) >> 12) + 49) / 100);
			  break;

		  case (PXL_FILE):
			  printf("\n\tmagnification\t\t= %ld (%ld dpi)\n\t\t\t\t",
					 pmag, (pmag + 2) / 5);
			  break;

		  case (TFM_FILE):
			  printf("\n\t\t\t\t");
			  break;

		  case (DVI_FILE):
			  printf("\n\tspace size\t\t=%9ld scaled points =%3ld points",
					 font.s, font.s >> 16);
			  ratio = 1000;
			  cmag = 0;
			  if (font.d > font.s)
				  ratio = (font.d * 100L + (font.s / 20L)) / (font.s / 10L);
			  else if (font.s > font.d)
				  ratio = (font.s * 100L + (font.d / 20L)) / (font.d / 10L);
			  if (ratio != 1000)
				  cmag = check_mag(ratio);
			  if (cmag > 0) {
				  printf("\n\tmagnification\t\t= %smagstep%d%s",
						 (font.d > font.s) ? "-" : "+", cmag / 2, (cmag & 1) ? ".5" : "");
			  }
			  else if (cmag < 0)
				  printf("\n\tmagnification\t\t= %d",
						 (font.d > font.s) ? ((int)((1000000L + ratio) / ratio)) : ratio);
			  printf("\n\tfont name\t\t= %s", font.n);
			  break;
		}
	}
	if (f_v == -1)
		return;
	for (j = 0; j < n_data; j++) {
		if (strcmp(chkfont[j].f_name, font.n) == 0) {
			if (chkfont[j].oldd == 0 || chkfont[j].oldd == font.c) {
				type("Old font");
				f_type |= OLD_FONT;
				return;
			}
			if (chkfont[j].neww == 0 || chkfont[j].neww == font.c) {
				type("New font");
				f_type |= NEW_FONT;
				return;
			}
		}
	}
	if (f_d)
		return;

	pt = font.n + font.a + font.l - 1;
	while (*pt >= '0' && *pt <= '9')
		pt--;
	point = atoi(++pt);
	*pt = 0;

	if (strcmp(font.n, "eufm") == 0) {
		j = f_point = 0;
		while (eufm[j].point != 0) {
			if (eufm[j].point == point) {
				f_point = 1;
				if (eufm[j].oldd == font.c) {
					f_type |= OLD_EUFM;
					type("Old AMSTeX");
					return;
				}
				else {
					if (eufm[j].neww == font.c) {
						f_type |= NEW_EUFM;
						type("AMSTeX 2.0 or newer"); /* V2.2 is available */
						return;
					}
				}
			}
			j++;
		}
		f_type |= BAD_EUFM;
		if (f_v)
			type(
					(f_point) ? "unknown checksum" : "unknown point size");
		return;
	}
	for (j = 0; oldfont[j][0] != 0; j++) {
		if (strcmp(font.n, oldfont[j]) == 0) {
			f_type |= OLD_FONT;
			type("Old AMS Font");
			return;
		}
	}

	for (j = 0; newfont[j][0] != 0; j++) {
		if (strcmp(font.n, newfont[j]) == 0) {
			f_type |= NEW_FONT;
			type("New AMS Font");
			return;
		}
	}

	for (j = 0; newpoint[j].font_name[0] != 0; j++) {
		if (strcmp(font.n, newpoint[j].font_name) == 0) {
			if (newpoint[j].top <= point && newpoint[j].end >= point) {
				f_type |= NEW_POINT;
				type("New AMS Font");
			}
			return;
		}
	}
}

char *name_link(char *base, char *ext)
	/* combine basename and extension. */
{
	char *name;
	int base_len;

	base_len = strlen(base);
	name = (char *)malloc(base_len + 5);
	strcpy(name, base);
	name[base_len] = '.';
	strcpy(name + base_len + 1, ext);
	return (name);
}

void read_post(DVIFILE_INFO *dvi)
	/* read data from POSTAMBLE. */
{
	long endofs;
	int code;

	if ((uchar)read_byte(dvi->file_ptr) != PRE ||
		(uchar)read_byte(dvi->file_ptr) != ID) {
	  err:fprintf(stderr, "%s is not correct DVI file\n", dvi->file_name);
		exit(254);
	}
	for (endofs = -3L; fseek(dvi->file_ptr, endofs, SEEK_END),
		 (code = (uchar)read_byte(dvi->file_ptr)) != ID; endofs--)
		/* Search id number */
		if (code == ID_PTEX) {
			printf("This is extended DVI file [ID = 3] for pTeX.\n");
			break;
		}
		else if (code == EOF || code != END_DVI)
			goto err;

	fseek(dvi->file_ptr, endofs - 4L, SEEK_END);
	if ((dvi->post = read_long(dvi->file_ptr)) <= 0)
		goto err;
	/* Read the position of POSTAMBLE */

	fseek(dvi->file_ptr, dvi->post, SEEK_SET);
	/* Set file-ptr at POSTAMBLE */

	if ((uchar)read_byte(dvi->file_ptr) != POST)
		goto err;

	if ((dvi->last_bop = read_long(dvi->file_ptr)) <= 0)
		goto err;

	dvi->num = read_long(dvi->file_ptr);
	dvi->den = read_long(dvi->file_ptr);
	dvi->mag = read_long(dvi->file_ptr);

	dvi->l = (SCALED_PT)read_long(dvi->file_ptr);
	dvi->u = (SCALED_PT)read_long(dvi->file_ptr);

	dvi->stack_depth = read_uint(dvi->file_ptr);
	dvi->total_page = read_uint(dvi->file_ptr);

	if (dvi->num <= 0 || dvi->den <= 0 || dvi->mag <= 0)
		goto err;

	if (dvi->stack_depth < 0 || dvi->total_page <= 0)
		goto err;
}

void get_font_list(DVIFILE_INFO *dvi)
{
	int code, count;

	fseek(dvi->file_ptr, dvi->post + 29, SEEK_SET);
	while ((code = getc(dvi->file_ptr)) != POST_POST)
		if (code >= FNT_DEF_1 && code < FNT_DEF_1 + 4) {
			font_define(code, dvi->file_ptr);
			check_font();
		}
		else if (code != NOP) {
			fprintf(stderr, "Command error code:%d\n", code);
			exit(253);
		}
}

void tfm_define(FILE * fp)
{
	int i, ch, nt, ofmlevel;
	long lf, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np, fontdir, sum, size,
	    headerlength, topchar, topwidth, topheight, topdepth, topitalic, ncw, nlw, neew,
	    nco, npc, nki, nwi, nkf, nwf, nkm, nwm, nkr, nwr, nkg, nwg, nkp, nwp;
	char *s, *t, *u;

	font.n[strlen(font.n) - 4] = 0;
	s = font.n + strlen(font.n);
	while (*--s >= '0' && *s <= '9' && s >= font.n);
	t = s;
	while (*s != ':' && *s != '\\' && *s != '/' && s-- > font.n);
	font.n = s + 1;
	font.a = t - s;
	font.l = 0;

	/* initialize for standard TFM */
	ch = 't';
	u = "";
	nt = 0;
	ofmlevel = 0;
	headerlength = 6;
	topchar = 255;
	fontdir = 0;
	nco = npc = nki = nwi = nkf = nwf = nkm = nwm = nkr = nwr = nkg = nwg = nkp = nwp = 0;

	if ((i=read_uint(fp)) == 0) {			/* (temporary) lf */
		/* assume OFM */
		ch = 'o';
		ofmlevel = read_uint(fp);
		if (ofmlevel == 0)
			headerlength = 14;
		else
			headerlength = 29;
		topchar = 1114111L;
		lf = read_long(fp);			/* lf */
		lh = read_long(fp);			/* lh */
	}
	else {
		if (i == 11 || i == 9) {
			/* assume JFM */
			ch = 'j';
			if( i == 9 ) u = "(tate)";
			headerlength = 7;
			nt = read_uint(fp);		/* nt */
			lf = read_uint(fp);		/* lf */
		}
		else {
			/* assume TFM */
			lf = i;				/* lf */
		}
		lh = read_uint(fp);			/* lh */
	}

	/*
	   [OFM format]
	     Each entry is a 32-bit integer, non-negative and less than 2^31, and
	       bc - 1 <= ec <= 1114111L  (cf. 65535 in texmf-dist/doc/omega/base/doc-1.8.tex)
	       lf = 14 + lh + 2 * (ec - bc + 1) + nw + nh + nd + ni + 2 * nl + nk + 2 * ne + np  (for level 0)
	       lf = 29 + lh +               ncw + nw + nh + nd + ni + 2 * nl + nk + 2 * ne + np
	            + nki + nwi + nkf + nwf + nkm + nwm + nkr + nwr + nkg + nwg + nkp + nwp      (for level 1)
	       nki + nkf + nkm + nkr + nkg + nkp < 32  (for level 1)
	   [JFM format]
	     Each entry is a 16-bit integer, non-negative and less than 2^15, and
	       0 <= ec <= 255
	       bc = 0
	       lf = 7 + nt + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ng + np
	   [TFM format]
	     Each entry is a 16-bit integer, non-negative and less than 2^15, and
	       bc - 1 <= ec <= 255
	       ne <= 256
	       lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np
	*/
	if (ch == 'o') {
		bc = read_long(fp);			/* bc */
		ec = read_long(fp);			/* ec */
		nw = read_long(fp);
		nh = read_long(fp);
		nd = read_long(fp);
		ni = read_long(fp);
		nl = read_long(fp);
		nk = read_long(fp);
		ne = read_long(fp);
		np = read_long(fp);
		fontdir = read_long(fp);
		nlw = 2 * nl;
		neew = 2 * ne;
		topchar = 1114111L;
		topwidth = 1114111L;
		topheight = 255;
		topdepth = 255;
		topitalic = 255;
		if (ofmlevel == 0) {
			ncw = 2 * ( ec - bc + 1 );
		}
		else {
			nco = read_long(fp);
			ncw = read_long(fp);
			npc = read_long(fp);
			nki = read_long(fp);
			nwi = read_long(fp);
			nkf = read_long(fp);
			nwf = read_long(fp);
			nkm = read_long(fp);
			nwm = read_long(fp);
			nkr = read_long(fp);
			nwr = read_long(fp);
			nkg = read_long(fp);
			nwg = read_long(fp);
			nkp = read_long(fp);
			nwp = read_long(fp);
		}
	}
	else {
		bc = read_uint(fp);			/* bc */
		ec = read_uint(fp);			/* ec */
		nw = read_uint(fp);
		nh = read_uint(fp);
		nd = read_uint(fp);
		ni = read_uint(fp);
		nl = read_uint(fp);
		nk = read_uint(fp);
		ne = read_uint(fp);	/* for JFM, this corresponds to ng instead */
		np = read_uint(fp);
		ncw = ec - bc + 1;
		nlw = nl;
		neew = ne;
		topwidth = 255;
		topheight = 15;
		topdepth = 15;
		topitalic = 63;
	}
	if (lf != (headerlength + nt + lh + ncw + nw + nh + nd + ni + nlw + nk + neew
	    + np + nki + nwi + nkf + nwf + nkm + nwm + nkr + nwr + nkg + nwg + nkp + nwp)) {
		printf("\n\"%s\" is not a %cfm file\n", filename, ch);
		exit(254);
	}
	if (ch == 'j') {
		if (bc != 0 || ec < 0 || ec > topchar) {
			printf("\nThe %cfm file \"%s\" has illegal character code range: %ld -> %ld\n", ch, filename, bc, ec);
			exit(254);
		}
	}
	else {
		if (bc < 0 || bc > ec + 1 || ec > topchar) {
			printf("\nThe %cfm file \"%s\" has illegal character code range: %ld -> %ld\n", ch, filename, bc, ec);
			exit(254);
		}
	}
#if 0
	/* more strict check */
	long ligsize = 800000L; /* set to 800000 for OFM, 32510 for TFM/JFM */
	if (lh < 2)
		printf("Warning: The header length is only %ld!\n", lh);
	if (nl > ligsize)
		printf("Warning: The lig/kern program is too long!\n");
	if (nw == 0 || nh == 0 || nd == 0 || ni == 0)
		printf("Warning: Incomplete subfiles for character dimensions!");
	if (ch != 'j' && ne > topchar + 1)
		printf("Warning: There are %ld extensible recipes!", ne);
#endif

	if (f_v != 0) {
		if (ch == 'o')
			printf("\t\"%s\" is a %cfm level %d file :%3ld  -> %3ld\n",
				font.n, ch, ofmlevel, bc, ec);
		else
			printf("\t\"%s\" is a %cfm%s file :%3ld  -> %3ld\n",
				font.n, ch, u, bc, ec);
	}

	font.c = read_n(fp,4); 		/* header[0] */
	font.d = read_long(fp);		/* header[1] */
	fclose(fp);
	check_font();
}

#define PK_PRE		247
#define PK_ID		89
#define	GF_ID		131
#define	VF_ID		202
#define	PKD_ID		('p'+'k'+'d')
#define PK_POST		245
#define GF_POST		223

#define	VFW_ID		((PK_PRE<<8)+VF_ID)
#define	GFW_ID		((PK_PRE<<8)+GF_ID)
#define	PKW_ID		((PK_PRE<<8)+PK_ID)
#define	PKDW_ID		((PK_PRE<<8)+PKD_ID)
#define	JXL4_0_ID	(0x0001)
#define	JXL4_1_ID	(0x8B25)

void pxl_define(FILE * fp)
{
	int i, tmp, ch, code;
	char *type, *s, *t, *u;
	long endofs;

	strlwr(font.n);
	s = u = font.n + strlen(font.n);
	ch = 0;
	while (*s != ':' && *s != '\\' && *s != '/' && *s != '.' &&
		   s-- >= font.n);
	if (*s == '.') {
		ch = *(u = s);
		*s = 0;
	}
	s = font.n + strlen(font.n);
	while (*--s >= '0' && *s <= '9' && s >= font.n);
	t = s;
	while (*s != ':' && *s != '\\' && *s != '/' && s-- > font.n);
	font.n = s + 1;
	font.a = t - s;
	font.l = 0;

	switch (tmp = read_uint(fp)) {
	  case (0):
		  switch (tmp = read_uint(fp)) {
			case (1001):
				type = "PXL1001";
				break;

			case (1002):
				type = "PXL1002";
				break;

			case (1003):
				type = "PXL1003";
				break;

			default:
				printf("\nUnknown id:0000%04X\n", tmp);
				exit(254);
		  }
		  if (f_end == 0)
			  fseek(fp, -20L, SEEK_END);
		  else
			  fseek(fp, f_end - 20, SEEK_SET);
		  font.c = read_n(fp,4);
		  pmag = read_long(fp);
		  font.d = read_long(fp);
		  break;

	  case (JXL4_0_ID):
		  if (read_long(fp) == JXL4_1_ID) {
			  type = "JXL4";
			  if (f_end == 0)
				  fseek(fp, -28L, SEEK_END);
			  else
				  fseek(fp, f_end - 28, SEEK_END);
			  font.c = read_n(fp,4);
			  pmag = read_long(fp);
			  font.d = read_long(fp);
		  }
		  else
			  goto id_err;
		  break;

	  case (VFW_ID):
		  type = "VF";
		  f_t = VF_FILE;
		  code = read_byte(fp);
		  for (i = 0; i < code; i++)
			  read_byte(fp);
		  for (i = 0; i < 4; i++)
			  font.c += read_byte(fp)<<(8*(3-i));
		  for (i = 0; i < 4; i++)
			  font.d += read_byte(fp)<<(8*(3-i));
		  break;

	  case (GFW_ID):
		  type = "GF";
		  for (endofs = -3L; fseek(fp, endofs, SEEK_END),
			   (code = (uchar)read_byte(fp)) == GF_POST; endofs--);
		  /* Search id number	*/
		  if (code != GF_ID) {
			  fprintf(stderr, "This is not correct GF file\n");
			  exit(252);
		  }
		  fseek(fp, endofs - 4, SEEK_END);
		  endofs = read_long(fp);
		  fseek(fp, endofs + 5, SEEK_SET);
		  goto pk3;

	  case (PKDW_ID):
		  type = "PKD";
		  goto pk2;

	  case (PKW_ID):
		  type = "PK";
		pk2:tmp = read_byte(fp);
		  while (tmp--)
			  (void)read_byte(fp);
		pk3:f_t = PK_FILE;
		  font.d = read_long(fp);
		  font.c = read_n(fp,4);
		  hppp = read_long(fp);
		  vppp = read_long(fp);
		  break;

	  default:
		id_err:
		  printf("\nUnknown id:%04X\n", tmp);
		  exit(254);
	}
	if (f_v != 0) {
		*u = ch;
		printf("\t\"%s\" is in %s format\n", font.n, type);
		*u = 0;
	}
	check_font();
}

void font_define(int code, FILE * dvifile)
{
	int i, len, lenm;

	font.k = read_n(dvifile, code - FNT_DEF_1 + 1);
	font.c = read_n(dvifile,4);
	font.s = read_long(dvifile);
	font.d = read_long(dvifile);
	font.a = read_byte(dvifile);
	font.l = read_byte(dvifile);

	if (font.a == EOF || font.l == EOF) {
		fprintf(stderr, "Bad DVI file\n");
		exit(252);
	}
	font.n = read_str(dvifile, font.a + font.l);
}

void vf_define(int code, FILE * vffile)
{
	int i, len, lenm;

	vffont.k = read_n(vffile, code - FNT_DEF_1 + 1);
	vffont.c = read_n(vffile,4);
	vffont.s = read_long(vffile);
	vffont.d = read_long(vffile);
	vffont.a = read_byte(vffile);
	vffont.l = read_byte(vffile);

	if (vffont.a == EOF || vffont.l == EOF) {
		fprintf(stderr, "Bad VF file\n");
		exit(252);
	}
	vffont.n = read_str(vffile, vffont.a + vffont.l);
}

int read_uint(FILE * fp)
{	   /* read unsigned integer */
	int d;

	d = (uchar)getc(fp);
	d = (d << 8) | (uchar)getc(fp);
	return (d);
}

long read_mit(FILE * fp)
{	   /* read three bytes as long integer */
	long d;

	d = (uchar)getc(fp);
	if (d > 127)
		d -= 256;
	d = (d << 8) | (uchar)getc(fp);
	d = (d << 8) | (uchar)getc(fp);
	return (d);
}

long read_long(FILE * fp)
{	   /* read 4 bytes as long integer */
	long d;

	d = (uchar)getc(fp);
	if (d > 127)
		d -= 256;
	d = (d << 8) | (uchar)getc(fp);
	d = (d << 8) | (uchar)getc(fp);
	d = (d << 8) | (uchar)getc(fp);
	return (d);
}

long to_long(uchar *pt)
{	   /* read 4 bytes as long integer */
	long d;

	d = (pt[3] << 8) + pt[2];
	d = (d << 16) + (pt[1] << 8) + pt[0];
	return (d);
}


char *read_str(FILE * fp, int len)
{
	char *pt0, *pt;

	if ((pt = pt0 = (char *)malloc(len + 1)) != NULL) {
		while (len-- > 0)
			*pt++ = getc(fp);
		*pt = '\0';

	}
	return (pt0);
}

long read_n(FILE * fp, int n)
{   /* read 1--4 bytes as unsigned long integer */
	ulong d;

	d = (uchar)getc(fp);

	switch (n) {
	  case 4:
		  d = (d << 8) | (uchar)getc(fp);
	  case 3:
		  d = (d << 8) | (uchar)getc(fp);
	  case 2:
		  d = (d << 8) | (uchar)getc(fp);
	}
	return ((long)d);
}

long signed_read_n(FILE * fp, int n)
{
	long d;

	d = (uchar)getc(fp);
	if (d > 127)
		d -= 256;
	switch (n) {
	  case 4:
		  d = (d << 8) | (uchar)getc(fp);
	  case 3:
		  d = (d << 8) | (uchar)getc(fp);
	  case 2:
		  d = (d << 8) | (uchar)getc(fp);
	}
	return (d);
}

void type(char *s)
{
	if (f_v)
		printf(" (%s) ", s);
}

void get_list(char *fname)
{
	FILE *fp;

	if ((fp = fopen(fname, "r")) == NULL) {
		fprintf(stderr, "Cannot find %s.\n", fname);
		exit(249);
	}
	for (n_data = 0; n_data < MAX_FONT;) {
		if (fgets(buf, BUF_SIZE, fp) == NULL)
			return;
		if (buf[0] == '#' || buf[0] == ';')
			continue;
		if (sscanf(buf, "%15s %lX %lX",
				   chkfont[n_data].f_name,
				   &(chkfont[n_data].oldd),
				   &(chkfont[n_data].neww)) != 3) {
			fprintf(stderr, "error in %s[%s]\n", fname, buf);
			exit(248);
		}
		if (chkfont[n_data].oldd == chkfont[n_data].neww) {
			fprintf(stderr, "same check sum in %s[%s]\n", fname, buf);
			exit(248);
		}
		n_data++;
	}
	if (n_data >= MAX_FONT) {
		fprintf(stderr, "Too many font data in %s\n", fname);
		exit(248);
	}
}

void usage()
{
	fprintf(stderr,
		"\t<<< CHecK dvi/tfm/font file and tell informations of FONTs >>>\n"
			"\t\t\t\tVer 1.8, written by SHIMA, 1990/1992\n\n"
	  "Usage: chkfont [-s] [-c] [-{f|F}<font_data_file>] [-d] dvi_file_name\n"
	  "       chkfont [-s] [-c] [-{f|F}<font_data_file>] [-t] tfm_file_name\n"
	"       chkfont [-s] [-c] [-{f|F}<font_data_file>] [-p] font_file_name\n\n"
			"Option\t -c: check mode\n"
			"\t -s: silent check mode\n"
			"\t -f: use font_data_file (-F: and ignore default data)\n"
			"\t -d: force DVI mode\n"
			"\t -t: force TFM/OFM mode\n"
			"\t -p: force FONT mode\n"
			"\t     (supported formats: vf/ovf/gf/pk/pkd/pxl/gth/far)\n"
			"\t By default, mode is guessed from the file extension.\n\n"
			"Input  M  to see more.  Push Return to quit");
	switch (getc(stdin)) {
	  case ('M'):
	  case ('m'):
		  goto show;
	  default:
		  exit(250);
	}
 exit(1);

  show:
	fprintf(stderr,
			"\n\nErrorlevel is the sum of the following number\n"
			"\t  1: cmbsy5-9, cmcsc8-9, cmex7-9, cmmib5-9\n"
			"\t  2: new font: eu??, msam, msbm, wncy??, font_data_file\n"
			"\t  4: new eufm5-10\n"
			"\t  8: unknown eufm\n"
		  "\t 16: old font: msxm, msym, amcsc, mcyr, mcyb, font_data_file\n"
			"\t 32: old eufm5-10\n"
			"If errorlevel is larger than 240,\n"
			"\t248: error in font_data_file\n"
			"\t249: cannot find font_data_file\n"
			"\t250: bad parameter\n"
			"\t251: old and new eufm\n"
			"\t252: bad dvi file\n"
			"\t253: error in dvi file\n"
			"\t254: not dvi/tfm file or unknown font ID\n"
			"\t255: cannot find dvi/tfm/font file\n\n"
			"\t\t<<< Example of font_data_file >>>\n"
			"# font_name old_check_sum new_check_sum  (in HEX, 0 means existence check)\n"
			"eufm5	0891FD38	246A686B\n"
			"eufm10	BF989013	8F256EB2\n"
			"# The line beginning with # or ; is ignored.\n"
		);
	exit(250);
}

