/*  %kw # %v    %n    %d    %t # */
/* Version # 2    CHKFNT.C    8-Jun-93    11:41:30 # */
/* ***********************************************************
   METAGRAPHICS SOFTWARE CORPORATION (c) 1988
   FILE: CHKFNT.C
   Checks the font file validity, updates to ver 2.1, dumps info
************************************************************** 

NOTE:
    When compiling on SCO UNIX, the "-pack" option must be used.

************************************************************ */

/* Written in MetaWINDOW Version 4 style */

#define STANDALONE    1         /* define for stand alone program */
/* #define debug       1     */

#include "stdio.h"
#include "stdlib.h"
#include "ctype.h"
#include "string.h"

#include "metawndo.h"

#if  SCO
# define  SWITCH_CHAR(X)	( X == '-' )
#else
# include "conio.h"
# define  SWITCH_CHAR(X)	(( X == '-' ) || ( X == '/' ))
#endif

/* error code defines */
#define ER_INP_NAME -100      /* can't open input file name */
#define ER_FORMAT   -101      /* input file is not in proper format */
#define ER_TMCHARS  -102      /* more characters defines that stated */
#define ER_BMAP     -103      /* character bitmap info bad format */
#define ER_SIZE     -104      /* font bitmap size larger than stated */
#define ER_TOOBIG   -105      /* font record would exceed 64K */
#define ER_NOMEM    -106      /* not enough memory to allocate font record */
#define ER_OUT_NAME -107      /* can't open output file name */
#define ER_WRITE    -108      /* error writing file */
#define ER_DEFAULT  -109      /* default char not included in set */
#define ER_PIXBIT   -110      /* not single bit per pixel */
#define ER_PIXPLANE -111      /* not single plane */

/* inform function codes */
#define IN_SAY           1         /* print string */
#define IN_CHAR_CNT      2         /* character count */
#define IN_CHAR_NUM      3         /* character encoding */
#define IN_LINE          4         /* line number */
#define IN_WARN          5         /* warning */

#define OK                    0
#define ERROR                 -1
#define MAX_LINE              255

/* warning codes */
#define W_EXIST              -2
#define W_SMALL              -3
#define W_LARGE              -4
#define W_RANGE              -5
#define W_SAME               -6
#define W_INVAL              -7
#define W_STRSHORT           -8
#define W_STRLONG            -9
#define W_STRCONT            -10
#define W_TSMALL             -11
#define W_TLARGE             -12
#define W_WEIRD              -13
#define W_LAST               -14
#define W_64K                -15
#define W_VER                -16
#define W_FLAGS              -17    


/* font version 2.0 flag */
static int ver_20;   

/* global function pointer to informing routine */
static void (*sayfunc)( int, void * );

/* global flag indicating warning issued */
static int warned;

/* global flag indicating a dump is required */
static int dump_it;

int chkfnt( fontRcd *, unsigned long, int, void(*)( int, void * ) );
int chkstr( int, char * );
void warn( int, int, char * );
int cmptbl( const void *, const void *);
long findtblsize( long[], long );
void chkbm( long, fontRcd *, grafMap * );
void dump( char * );
int chkhdr( fontRcd *, unsigned long );
int chktbls( fontRcd *);

int chkfnt( fnt, length, info, inform )
fontRcd *fnt;
unsigned long length;
int info;
void (*inform)( int, void *);

/*
This function checks the font record at 'fnt', and issues warnings
about any non-standard settings.
If 'info' is true, it will also dump out the font header info, as
well as the bitmap, rowtables, location table, offset/ width table,
and misc. strings.
Uses the function at 'inform' to report about progress, or NULL if no
informing necessary.

Returns OK if ok, True if warnings issued, ERROR if isn't a font file.
*/

{

     
     /* set global function pointer */
     sayfunc = inform;

     /* clear global warning flag */
     warned = False;

     /* set global dump flag */
     dump_it = info;

     /* check the header out */
     if( chkhdr( fnt, length ) )
          return ERROR;

     /* check the tables out */
     chktbls( fnt );

     return warned;

}

int
chkhdr( fnt, size )
fontRcd *fnt;
unsigned long size;
/*
Check out the font header. Return OK or ERROR if not a version 2.0 or 2.1
font file. 
*/
{
     char buf[100];
     int i;

     /* figure out what version we got */
     switch( fnt->fontVer ) {

          case 0x02:
               ver_20 = True;
               sprintf( buf, "fontVer = 2.0" );
               warn( W_VER, True, buf );
               fnt->fontVer = 0x12;
               break;

          case 0x12:
               ver_20 = False;
               sprintf( buf, "fontVer = 2.1" );
               warn( W_VER, False, buf );
               fnt->fontVer = 0x12;
               /* must update with fontwndo */
               break;

          default:
               ver_20 = False;
					/* must be major rev 2 */
					if( (fnt->fontVer & 0x0F ) != 0x02 )
	               return ERROR;
               break;

     }

     dump("");
     dump( "Font Header Information:" );

     sprintf( buf, "fontVer     = %xh", fnt->fontVer );
     dump( buf );

     sprintf( buf, "fontRev     = %d", fnt->fontRev );
     dump( buf );

     /* must update 2.0 name to new name and suffix(s) */
     if( ver_20 ) {

          if( fnt->fontNameLen > 45 )
               fnt->fontNameLen = 45;

          strncpy( buf, fnt->fontBaseName, fnt->fontNameLen );
          buf[45] = '\0';

          fnt->fontBaseName[15] = '\0';

          if( fnt->fontNameLen > 15 )  {
               strncpy( fnt->fontSuffix1, &buf[16], 9 );
               fnt->fontSuffix1[9] = '\0';
          }
          else
               fnt->fontSuffix1[0] = '\0';

          if( fnt->fontNameLen > 26 ) {
               strncpy( fnt->fontSuffix2, &buf[27], 9 );
               fnt->fontSuffix2[9] = '\0';
          }
          else
               fnt->fontSuffix2[0] = '\0';

          if( fnt->fontNameLen > 37 ) {
               strncpy( fnt->fontSuffix3, &buf[38], 9 );
               fnt->fontSuffix3[9] = '\0';
          }
          else
               fnt->fontSuffix3[0] = '\0';
      
          if( fnt->fontNameLen > 15 ) 
               fnt->fontNameLen = 15;

     }

     sprintf( buf, "fontNameLen = %d", fnt->fontNameLen );
     dump( buf );
     if( fnt->fontNameLen > 15 ) {
          warn( W_LARGE, True, buf );
          fnt->fontNameLen = 15;
     }
     
     /* check the length, add one for a null (must have a null) */
     i = chkstr( fnt->fontNameLen + 1, fnt->fontBaseName);
     if( i != ( fnt->fontNameLen + 1 ) ) {     
          if( i < 0 )
               warn( i, True, "fontBaseName" );
          else
               warn( W_STRSHORT, False, "fontBaseName" );
     }
     sprintf( buf, "fontBaseName= %s", fnt->fontBaseName );
     dump( buf );

     i = chkstr( 10, fnt->fontSuffix1 );
     if( i < 0 )
          warn( i, True, "fontSuffix1");
     sprintf( buf, "fontSuffix1 = %s", fnt->fontSuffix1 );
     dump( buf );

     i = chkstr( 10, fnt->fontSuffix2 );
     if( i < 0 )
          warn( i, True, "fontSuffix2");
     sprintf( buf, "fontSuffix2 = %s", fnt->fontSuffix2 );
     dump( buf );

     i = chkstr( 10, fnt->fontSuffix3 );
     if( i < 0 )
          warn( i, True, "fontSuffix3");
     sprintf( buf, "fontSuffix3 = %s", fnt->fontSuffix3 );
     dump( buf );

     /* field didn't exist in version 2.0 */
     if( ver_20 )
          fnt->fontFacing = 0;

     sprintf( buf, "fontFacing  = %xh", fnt->fontFacing ); 
     dump( buf );
     if( ~0x3F & fnt->fontFacing ) {
          warn( W_FLAGS, True, buf  );
          fnt->fontFacing &= 0x3F;
     }
    
     if( strncmp( fnt->fontSign, "METAFONT", 8 ) ) {
          warn( W_EXIST, True, "fontSign" );
          strcpy( fnt->fontSign, "METAFONT"); 
     }

     if( fnt->fontSize > 0xFFFFL ) {
          warn( W_64K, False, "" );
     }

     sprintf( buf, "fontSize    = %lu", fnt->fontSize );
     dump( buf );
     sprintf( buf, "fontSize    = %lu s/b %lu", fnt->fontSize, size );
     if( (unsigned long) fnt->fontSize < size ) {
          warn( W_SMALL, True, buf );
          fnt->fontSize = size;
     }
     if( (unsigned long) fnt->fontSize > size + 10L ) {
          warn( W_LARGE, True, buf );
          fnt->fontSize = size;
     }

     /* these fields don't exist in version 2.0 */
     if( ver_20 ) {
          fnt->fontWeight = 80;    
          fnt->fontCoding = 0; 
     }

     sprintf( buf, "fontWeight  = %d", fnt->fontWeight );
     dump( buf );

     sprintf( buf, "fontCoding  = %d", fnt->fontCoding );
     dump( buf );
     if( fnt->fontCoding > 5 ) {
          warn( W_RANGE, True, buf );
          fnt->fontCoding = 0;
     }

     sprintf( buf, "fontPtSize  = %d", fnt->fontPtSize );
     dump( buf );

     sprintf( buf, "fontfamily  = %d", fnt->fontfamily );
     dump( buf );

     sprintf( buf, "fontStyle   = %xh", fnt->fontStyle );
     dump( buf );
     if( ~0x00FF & fnt->fontStyle ) {
          warn( W_FLAGS, True, buf);
          fnt->fontStyle &= 0x00FF;
     }

     sprintf( buf, "fontFlags   = %xh", fnt->fontFlags );
     dump( buf );
     if( ~0x03FF & fnt->fontFlags ) {
          warn( W_FLAGS, True, buf );
          fnt->fontFlags &= 0x03FF;
     }

     sprintf(buf, "fontColor[0]= %ld", fnt->fontColor[0] );
     dump( buf );
     sprintf(buf, "fontColor[1]= %ld", fnt->fontColor[1] );
     dump( buf );
     if( fnt->fontColor[0] == fnt->fontColor[1] ) {
          warn( W_SAME, True, "fontColor[0] fontColor[1]" );
          fnt->fontColor[0] = ~fnt->fontColor[1];
     }
     if( fnt->fontColor[0] != White ) 
          warn( W_WEIRD, False, "fontColor[0]" );
     if( fnt->fontColor[1] != Black ) 
          warn( W_WEIRD, False, "fontColor[1]" );
        

     /* if 2.0 set the fontMin and fontMax values */
     /* and add one for the bad charater */
     if( ver_20 ) {
          fnt->fontMin = fnt->minChar;
          fnt->fontMax = fnt->maxChar;
          if( fnt->maxChar < 0xFF )
               fnt->maxChar++;
          fnt->fontMax++;
     }

     sprintf( buf, "fontMax     = %u", fnt->fontMax );
     dump( buf );
     i = fnt->fontMax;
     if( fnt->fontMax < fnt->fontMin ) {
          warn( W_SMALL, True, buf );
          fnt->fontMax = fnt->fontMin;
     }
     sprintf( buf, "fontMin     = %u", fnt->fontMin );
     dump( buf );
     if( fnt->fontMin > fnt->fontMax ) {
          warn( W_LARGE, True, buf );
          fnt->fontMin = i;
     }

     sprintf( buf, "maxChar     = %u", fnt->maxChar );
     dump( buf );
     i = (int) fnt->maxChar;
     if( fnt->maxChar < fnt->minChar ) {
          warn( W_SMALL, True, buf );
          fnt->maxChar = fnt->minChar;
     }
     sprintf( buf, "minChar     = %u", fnt->minChar );
     dump( buf );
     if( fnt->minChar > fnt->maxChar ) {
          warn( W_LARGE, True, buf );
          fnt->minChar = (unsigned char) i;
     }

     /* in version 2.0 the bad character was always the last charater */
     if( ver_20 ) 
          fnt->chBad = fnt->fontMax;

     sprintf( buf, "chBad       = %u", fnt->chBad );
     dump( buf );
     if( fnt->chBad > fnt->fontMax ) {
          warn( W_LARGE, True, buf );
          fnt->chBad = fnt->fontMax;
     }
     if( fnt->chBad < fnt->fontMin ) {
          warn( W_SMALL, True, buf );
          fnt->chBad = fnt->fontMin;
     }


     sprintf( buf, "chWidth     = %d", fnt->chWidth );
     dump( buf );

     sprintf( buf, "chHeight    = %d", fnt->chHeight );
     dump( buf );
     if( fnt->chHeight != ( fnt->ascent + fnt->descent ) ) {
          warn( W_INVAL, True, buf );
          fnt->chHeight = fnt->ascent + fnt->descent;
     }

     sprintf( buf, "chKern      = %d", fnt->chKern );
     dump( buf );

     sprintf( buf, "ascent      = %d", fnt->ascent );
     dump( buf );

     sprintf( buf, "descent     = %d", fnt->descent );
     dump( buf );

     sprintf( buf, "lnSpacing     = %d", fnt->lnSpacing );
     dump( buf );
     if( fnt->lnSpacing >= ( 2 * fnt->chHeight) ) {
          warn( W_WEIRD, False, buf );
     }

     sprintf( buf, "chCenter[0] = %d", fnt->chCenter[0] );
     dump( buf );
     if( fnt->chCenter[0] > fnt->chWidth ) {
          warn( W_LARGE, True, buf );
          fnt->chCenter[0] = fnt->chWidth / 2;
     }

     sprintf( buf, "chCenter[1] = %d", fnt->chCenter[1] );
     dump( buf );
     if( fnt->chCenter[1] > fnt->chHeight ) {
          warn( W_LARGE, True, buf );
          fnt->chCenter[1] = fnt->chHeight / 2;
     }

     sprintf( buf, "chAngle     = %d", fnt->chAngle );
     dump( buf );
     if( fnt->chAngle > 0 || fnt->chAngle <= -1800 ) {
          warn( W_RANGE, True, buf );
          fnt->chAngle = -200;
     }

     sprintf( buf, "chUnder     = %d", fnt->chUnder );
     dump( buf );

     sprintf( buf, "chScore     = %d", fnt->chScore );
     dump( buf );

     return OK;

}


int
chktbls( fnt )
fontRcd *fnt;
/* 
This function checks that the size if each table is resonable, and that
the content is resonable in some cases.
Returns OK or an error code
*/
{
     unsigned long tblsizes[12], size, req_size;
     int i, j, num_chars;
     char buf[100];
     struct font_rec_ptrs {
          fontRcd        *hd;
          grafMap        *bm;
          char           **rtbl;
          short          *loc;
          ofswid         *ofwd;
          char           *str[5];
          Byte           *fnt;
     } frec;


     /* convert relative font record table offsets to absolute */
     frec.hd    = fnt;
     frec.bm    = (grafMap *)    ( (long) fnt +  fnt->grafMapTbl );
     frec.rtbl  = (char **)      ( (long) fnt +  fnt->rowTbl );
     frec.loc   = (short *)      ( (long) fnt +  fnt->locTbl );
     frec.ofwd  = (ofswid *)     ( (long) fnt +  fnt->ofwdTbl );
     frec.fnt   = (Byte *)       ( (long) fnt +  fnt->fontTbl );
     frec.str[0] = ( char *)     ( (long) fnt +  fnt->fontNotice );
     frec.str[1] = ( char *)     ( (long) fnt +  fnt->fontSupplier );
     frec.str[2] = ( char *)     ( (long) fnt +  fnt->fontAuthor );
     frec.str[3] = ( char *)     ( (long) fnt +  fnt->fontInfo );
     frec.str[4] = ( char *)     ( (long) fnt +  fnt->fontDate );

     /* calculate the total number of table entries required */
     num_chars = fnt->fontMax - fnt->fontMin + 2;

     /* sort the table offsets, so that each table size may be caclulated */
     tblsizes[0] =  fnt->grafMapTbl;
     tblsizes[1] =  fnt->rowTbl;
     tblsizes[2] =  fnt->locTbl;
     tblsizes[3] =  fnt->ofwdTbl;
     tblsizes[4] =  fnt->fontTbl;                  
     tblsizes[5] =  fnt->fontNotice;
     tblsizes[6] =  fnt->fontSupplier;
     tblsizes[7] =  fnt->fontAuthor;
     tblsizes[8] =  fnt->fontInfo;
     tblsizes[9] =  fnt->fontDate;
     if( ver_20 ) {
          /* in version 2.0 these fields may be hash */
          tblsizes[5] =  0L;
          tblsizes[6] =  0L;
          tblsizes[7] =  0L;
          tblsizes[8] =  0L;
          tblsizes[9] =  0L;
     }
     tblsizes[10] = fnt->fontSize;

#ifdef debug
     printf("unsorted table\n");
     for( i = 0; i < 11; i++ )
          printf("%ld\n", tblsizes[i] );
#endif

     /* sort the table sizes */
     qsort( ( void *) tblsizes, (size_t) 11, sizeof( long ), cmptbl );

#ifdef debug
     printf("\nsorted table\n");
     for( i = 0; i < 11; i++ )
          printf("%ld\n", tblsizes[i] );
#endif

     /* check the size of the header itself */

     /* find the smallest offset */
     i = 0;
     while( tblsizes[i] == 0L )
          i++;
       
     sprintf( buf, "font header size = %lu bytes", tblsizes[i] );
     dump( buf );

     /* is it smaller than the size of a normal header ? */
     if( tblsizes[i] < sizeof( fontRcd ) ) {

          if( ver_20 ) {
               sprintf( buf, "header too small to update, use font editor");
               fnt->fontVer = 0x02;
               fnt->fontMax--;
               fnt->maxChar = ( unsigned char) fnt->fontMax;
          }

          warn( W_TSMALL, False, buf );
     }
     else {

          /* it's big enough, update these new fields */
          if( ver_20 ) {

               fnt->fontNotice   = 0L;
               fnt->fontSupplier = 0L;  
               fnt->fontAuthor   = 0L;    
               fnt->fontInfo     = 0L;      
               fnt->fontDate     = 0L;

               /* these fields are for precision fonts only */
               fnt->fontSpacing.fr = fnt->fontSpacing.wh =  0;
               fnt->fontLowHgt.fr =  fnt->fontLowHgt.wh = 0;
               fnt->fontCapHgt.fr =  fnt->fontCapHgt.wh = 0;
               fnt->fontAscent.fr =  fnt->fontAscent.wh = 0;
               fnt->fontDescent.fr = fnt->fontDescent.wh = 0;
               fnt->fontMaxWid.fr  = fnt->fontMaxWid.wh = 0;
               fnt->fontAvgWid.fr  = fnt->fontAvgWid.wh = 0;
               fnt->fontEmWid.fr   = fnt->fontEmWid.wh = 0;
               for( i = 0; i < 16; i++ ) 
                    fnt->offwidTbl[i] = fnt->ofwdTbl; 

          }
     }

     dump("");
     dump("Font GrafMap Information:");

     /* check the bitmap */
     sprintf( buf, "grafMap offset = %ld", fnt->grafMapTbl ); 
     dump( buf );
     if( fnt->grafMapTbl > fnt->fontSize ) {
          warn( W_RANGE, False, buf );
     }
     else {
          size = findtblsize( tblsizes, fnt->grafMapTbl );
          chkbm( size, fnt, frec.bm );
     }

     dump("");
     dump("Font RowTable:");

     /* check the rowtable */
     sprintf( buf, "rowTbl offset = %ld", fnt->rowTbl ); 
     dump( buf );
     if( fnt->rowTbl > fnt->fontSize ) {
          warn( W_RANGE, False, buf );
     }
     else {

          size = findtblsize( tblsizes, fnt->rowTbl );
          req_size = (long) ( frec.bm->pixHeight * frec.bm->pixPlanes * sizeof( Byte * ) );

          sprintf( buf, "row table size = %ld bytes", size );
          dump( buf );

          sprintf( buf, "rowTbl = %ld s/b %ld", size, req_size );
          if( size < req_size )
               warn( W_TSMALL, False, buf);
          if( size > req_size + 10L )
               warn( W_TLARGE, False, buf);

          size = size / 4;
          for( i = 0; i < (int ) size; i++ ) { 
               sprintf( buf, "  rowTable[%2d] = %lxh", i, frec.rtbl[i] );
               dump( buf );
          }

     }

     dump("");
     dump("Font Offset/Width Table:");


     /* check the offset width table */
     sprintf( buf, "ofwdTbl offset = %ld", fnt->ofwdTbl ); 
     dump( buf );
     if( fnt->ofwdTbl > fnt->fontSize ) 
          warn( W_RANGE, False, buf );
     else {
          size = findtblsize( tblsizes, fnt->ofwdTbl );
          req_size = (long) num_chars * (long) sizeof( int );

          sprintf( buf, "offset/width table size = %ld bytes", size );
          dump( buf );
          
          sprintf( buf, "offset/width table = %ld s/b %ld", size, req_size );
          if( size < req_size  ) 
               warn( W_TSMALL, False, buf );
          if( size > req_size + 10L )
               warn( W_TLARGE, False, buf );

     }

     /* check the bad char is defined */
     i = fnt->chBad - fnt->fontMin; 
     if(  frec.ofwd[i].ofs == -1 && frec.ofwd[i].wid == 255 ) {
          warn( W_INVAL, True, "offset\width entry for chBad" );
          fnt->chBad = fnt->fontMin;
     }


     dump("");
     dump("Font Location Table:");

     /* check the loctbl */
     sprintf( buf, "locTbl offset = %ld", fnt->locTbl ); 
     dump( buf );
     if( fnt->locTbl > fnt->fontSize ) {
          warn( W_RANGE, False, buf );
     }
     else {
          size = findtblsize( tblsizes, fnt->locTbl );
          req_size = (long) num_chars * (long) sizeof( int );

          sprintf( buf, "location table size = %ld bytes", size );
          dump( buf );

          sprintf( buf, "location table = %ld s/b %ld", size, req_size );
          if( size < req_size ) 
               warn( W_TSMALL, False, buf );
          if( size > req_size + 10L )
               warn( W_TLARGE, False, buf );

          /* check all entries in range of font bitmap */
          j = False;
          for( i = 0; i < num_chars; i++ ) {

               sprintf( buf, "  entry %3d, location = %4xh, offset = %4d, width = %3u",
                      i , frec.loc[i], frec.ofwd[i].ofs, frec.ofwd[i].wid );    
               dump( buf );
               if( frec.loc[i] > frec.bm->pixWidth ) 
                    j = True;
          }

          if( j )
               warn( W_RANGE, False, "location table entry" );

          /* check that all undefined entries have the correct
          location setting */
          j = False;
          for( i = num_chars - 2; i >= 0; i-- ) {
               
               if( frec.ofwd[ i ].ofs == -1 && frec.ofwd[ i ].wid == 255 ) {
                    if( frec.loc[i] != frec.loc[ i + 1 ] )
                         j = True;
               }
          }

          if( j )
               warn( W_INVAL, False, "location entry for an undefined char" );

     }

     dump("");
     dump("Font Table Information:");
      
     /* check the font table */
     sprintf( buf, "fontTbl offset = %ld", fnt->fontTbl ); 
     dump( buf );
     if( fnt->fontTbl > fnt->fontSize ) {
          warn( W_RANGE, False, buf );
     }
     else {
          size = findtblsize( tblsizes, fnt->fontTbl );
          req_size = (long) frec.bm->pixHeight * (long) frec.bm->pixBytes;

          sprintf( buf, "font table size = %ld bytes", size );
          dump( buf );

          sprintf( buf, "font table = %ld s/b %ld", size, req_size );
          if( size < req_size ) 
               warn( W_TSMALL, False, buf );
          if( size > req_size + 10L )
               warn( W_TLARGE, False, buf );

          if( tblsizes[9] != fnt->fontTbl )
               warn( W_LAST, False, "font table" );
     }


     /* check misc strings if not old version */
     if( !ver_20 ) {

          sprintf( buf, "fontNotice = %lx", fnt->fontNotice ); 
          dump( buf );
          if( fnt->fontNotice > fnt->fontSize ) {
               warn( W_RANGE, False, buf );
          }
          else {
               size = findtblsize( tblsizes, fnt->fontNotice );
               i = chkstr( (int) size, frec.str[0] );
               if( i < 0 ) 
                    warn( i, True, "fontNotice" );
               if( fnt->fontNotice ) {
                    sprintf( buf, "String = %s", frec.str[0] );
                    dump( buf );
               }
          }

          sprintf( buf, "fontSupplier = %lx", fnt->fontSupplier ); 
          dump( buf );
          if( fnt->fontSupplier > fnt->fontSize ) {
               warn( W_RANGE, False, buf );
          }
          else {
               size = findtblsize( tblsizes, fnt->fontSupplier );
               i = chkstr( (int) size, frec.str[1] );
               if( i < 0 ) 
                    warn( i, True, "fontSupplier" );
               if( fnt->fontSupplier ) {
                    sprintf( buf,"String = %s", frec.str[1] );
                    dump( buf );
               }
          }

          sprintf( buf, "fontAuthor = %lx", fnt->fontAuthor ); 
          dump( buf );
          if( fnt->fontAuthor > fnt->fontSize  ) {
               warn( W_RANGE, False, buf );
          }
          else {
               size = findtblsize( tblsizes, fnt->fontAuthor );
               i = chkstr( (int) size, frec.str[2] );
               if( i < 0 )
                    warn( i, True, "fontAuthor" );
               if( fnt->fontAuthor ) {
                    sprintf( buf, "String = %s", frec.str[2] );
                    dump( buf );
               }
          }

          sprintf( buf, "fontInfo = %lx", fnt->fontInfo ); 
          dump( buf );
          if( fnt->fontInfo > fnt->fontSize ) {
               warn( W_RANGE, False, buf );
          }
          else {
               size = findtblsize( tblsizes, fnt->fontInfo );
               i = chkstr( (int) size, frec.str[3] );
               if( i < 0 ) 
                    warn( i, True, "fontInfo" );
               if( fnt->fontInfo ) {
                    sprintf( buf, "String = %s", frec.str[3] );
                    dump( buf );
               }
          }

          sprintf( buf, "fontDate = %lx", fnt->fontDate ); 
          dump( buf );
          if( fnt->fontDate > fnt->fontSize ) {
               warn( W_RANGE, False, buf );
          }
          else {
               size = findtblsize( tblsizes, fnt->fontDate );
               i = chkstr( (int) size, frec.str[4] );
               if( i < 0 ) 
                    warn( i, True, "fontDate" );
               if( fnt->fontDate ) {
                    sprintf( buf, "String = %s", frec.str[4] );
                    dump( buf );
               }
          }

     }

     dump("");

     return OK;

}

void
chkbm( size, fnt, bm )
long size;
fontRcd *fnt;
grafMap *bm;
{
     char buf[100];
     long req_size;
     int i;


     sprintf( buf, "grafMap (bitmap) table size = %ld bytes", size );
     dump( buf );

     req_size = 512L /* (long) sizeof( grafMap ) */;

     if( size < req_size  ) {
          sprintf( buf, "grafMap (bitmap) table = %ld s/b at least %ld", size, req_size );
          warn( W_TSMALL, False, buf);
     }

     if( size > req_size ) {
          sprintf( buf, "grafMap (bitmap) table = %ld s/b %ld", size, req_size );
          warn( W_TLARGE, False, buf);
     }

     sprintf( buf, "devClass    = %d", bm->devClass );
     dump( buf );
/*     if( !( bm->devClass == -1 || bm->devClass == 0 )  )  {
          warn( W_INVAL, True, buf );
          bm->devClass = -1;
     }
*/
     sprintf( buf, "devTech     = %xh", bm->devTech );
     dump( buf );
     if( bm->devTech != 0 )  {
          warn( W_INVAL, True, buf );
          bm->devClass = 0;
     }

     sprintf( buf, "pixBytes    = %u", bm->pixBytes );
     dump( buf );
     i =  (bm->pixWidth + 7) / 8;
     if( bm->pixBytes < i ) {
          warn( W_SMALL, True, buf );
          bm->pixBytes = i;
     }


     sprintf( buf, "pixWidth    = %d", bm->pixWidth );
     dump( buf );

     sprintf( buf, "pixHeight   = %d", bm->pixHeight );
     dump( buf );
     if( bm->pixHeight != fnt->chHeight ) {
          warn( W_INVAL, True, buf );
          bm->pixHeight = fnt->chHeight;
     }

     sprintf( buf, "pixResX     = %d", bm->pixResX );
     dump( buf );
     if( bm->pixResX == 0 ) {
          warn( W_INVAL, True, buf );
          bm->pixResX = 100;
     }

     sprintf( buf, "pixResY     = %d", bm->pixResY );
     dump( buf );
     if( bm->pixResY == 0 ) {
          warn( W_INVAL, True, buf );
          bm->pixResY = 100;
     }

     i = bm->pixBits;
     sprintf( buf, "pixBits     = %d", i );
     dump( buf );
     if( !(i == 1 || i == 2 || i == 4 || i == 8)  ) {
          warn( W_WEIRD, False, buf );
     }

     sprintf( buf, "pixPlanes   = %d", bm->pixPlanes );
     dump( buf );
     if( bm->pixPlanes != 1 ) {
          warn( W_WEIRD, False, buf );
     }


}


int
cmptbl( a, b )
const void *a, *b;
/*
Long compare routine for sorting
*/
{
    long *al, *bl;

    al = (long *)a;
    bl = (long *)b;

     if( *al > *bl )
          return 1;

     if( *al < *bl )
          return -1;

     return 0;
}

long
findtblsize( tbls, the_table )
long tbls[], the_table;
/*
Find the size of  'the_table' in tbls
*/
{
     int i;
     long size;

     if( the_table == 0L )
          return 0L;

     for( i = 0; i < 12; i++ )
          if( tbls[i] == the_table )
               break;

     size = tbls[ i + 1 ] - tbls[ i ];
     return size;

}

int
chkstr( len, str )
int len;
char *str;
/*
This function checks the length and content of 'string'. If the content
is invalid, it is corrected, if there is no null up to and including 'len'
characters then one is inserted at len.
It returns the computed string length or a warning code.
*/
{
     int i, w;


     if( str == NULL || len == 0 )
          return 0;

     w = False;
     for( i = 1; i < len && *str; i++ ) {

          if( !isprint(*str) ) {
               w = True;
               *str &= 0x7F;
               if( *str < ' ' )
                    *str = ' ';
          }

          str++;
     }

     if( *str != '\0' ) {
          *str = '\0';
          return W_STRLONG;
     }

     if( w )
          return W_STRCONT;

     return i;

}


void
warn( code, fix, str )
int code, fix;
char *str;
/*
This procedure issues a warning code 'code' about string 'string'.
It also will tell you if it tried to fix it if 'fix' is true.
*/
{
     char *s, *fs, buf[100];


     switch( code ) {

          case W_VER:         s = "older version     ";     break;
          case W_FLAGS:       s = "reserved flags set";     break;
          case W_EXIST:       s = "field not set     ";     break;
          case W_SMALL:       s = "value too small   ";     break;
          case W_LARGE:       s = "value too large   ";     break;
          case W_RANGE:       s = "value out of range";     break;
          case W_SAME:        s = "set to same value ";     break;
          case W_INVAL:       s = "invalid setting   ";     break;
          case W_STRSHORT:    s = "string too short  ";     break;
          case W_STRLONG:     s = "string too long   ";     break;
          case W_STRCONT:     s = "string content    ";     break;
          case W_TSMALL:      s = "table too small   ";     break;
          case W_TLARGE:      s = "table too large   ";     break;
          case W_WEIRD:       s = "unusual setting   ";     break;
          case W_LAST:        s = "not the last table";     break;
          case W_64K:         s = "font larger than 64K";   break;
          default:            s = "unknown warning   ";     break;

     }

     if( fix ) {
          fs = ", fix attempted";
          warned = True;
     }
     else
          fs = "";

     sprintf( buf, "Warning %2d- %s: %s%s", abs(code), s, str, fs );

     if( sayfunc )
         (*sayfunc)( IN_WARN, buf );

}

void
dump( str )
char *str;
/*
Output 'str' if dump flag is on
*/
{

     if( dump_it && sayfunc )
          (*sayfunc)( IN_SAY, str );
}


#ifdef STANDALONE

int get_args( int, char *[], int *, int *, char ** );
void extend( char *, char *, int );
void print_me( int, void * );

void
main (argc, argv)
int argc;
char *argv[];
{
     char *version, buf[80], *p, *fontfile;
     int i, list, save;
     fontRcd *fnt;
     FILE *fp;
     size_t fsize;
 
     version = "1.1";

     printf("Metagraphics Font Check Utility Version %s\n", version );
     list = save = False;
     fontfile = NULL;
     if( get_args( argc, argv, &list, &save, &fontfile ) ) {
          printf("usage: chkfnt -d -w fontname\n");
          printf("Checks font file validity\n");
          printf("-d = dump font file information\n");
          printf("-w = write the fixes (otherwise ignored)\n");
          exit(1);
     }

     /* give it a .fnt if no extention */
     strncpy( buf, fontfile, 75 );
     buf[75] = '\0';
     extend( buf, ".fnt", False );

     fp = fopen( buf, "rb" );
     if( fp == NULL ) {
          printf("Can't open file %s\n", buf );
          exit(1);
     }

     /* get file size */
     fseek( fp, 0L, SEEK_END );
     fsize = (size_t) ftell( fp );
     fseek( fp, 0L, SEEK_SET );

     /* get input file into memory */
     fnt = malloc( fsize+2 );
     if( fnt == NULL ) {
          printf("Not enough memory to load font\n");
          exit(1);
     }

     if( fread( fnt, fsize, 1, fp ) == 0 ) {
          free( fnt );
          printf("Error loading font file\n");
          exit(1);
     }

     i = chkfnt( fnt, (unsigned long) fsize, list, print_me );
     if( i == ERROR ) {
          free( fnt );
          printf("Error: %s is not a font file\n", buf);
          exit(1);
     }

     if( i ) {      /* we had warnings */

          if( save ) {

               if( fwrite( fnt, fsize, 1, fp ) == 0 ) {
                    free( fnt );
                    printf("Error saving font file\n");
                    exit(1);
               }

               printf("Changes written to file %s\n", buf );
          }
          else
               printf("Changes NOT written\n");
     }

     free( fnt );
     exit(0);
}


int
get_args( argc, argv, list, save, filename )
int argc;
char *argv[];
int *list, *save;
char **filename;
/*
Get command line arguments and set list and save.
Returns OK or ERROR.
*/
{
    char c, *arg;
    int i;

    if( argc < 2 )
   	return ERROR;

    /* scan for switches */          
    for ( i=1 ; i<argc ; i++ )
    {
	arg = argv[i];
	if ( ! SWITCH_CHAR( *arg ) )
	    break;

	while ( *(++arg) != (char)0 ) {
	    switch( *arg )
	    {
		case 'd':  *list = True;   break;
		case 'w':  *save = True;   break;
		default :   return ERROR;
	    }
	}
    }

    /* assign the file name (last argument) */
    if ( i+1 != argc )
	return ERROR;

    *filename = argv[i];

    return OK;
}

void
extend( name, ext, force )
char *name, *ext;
int force;
/*
Extend filename 'name' to include extention 'ext'. If force is true,
force it to have the 'ext' extention, if false, only put it on if one
doesn't already exist.
Note: 'name' buffer must be long enough to hold the added extention
      'ext' must include the '.'
*/
{
     char *p;

     /* search for end of name */
     p = name;
     while( *p ) {
        if( *p == '.' ) 
            if( *(p+1) != '.' && *(p+1) != '\\' )
                break;
          p++;
    }

     if( (*p != '.') || force )
          strcpy( p, ext );

}

void
print_me( code, var )
int code;
void *var;
/*
Inform user of actions occuring.
Code is an indicator of the action to inform, var is a general purpose
parameter.
*/
{

     switch( code ) {
          case IN_WARN:  
          case IN_SAY :
	      printf("%s\n", (char *) var);
	      break;
     }

}
#endif /* STANDALONE */

