/*
*   This program converts AFM files to TeX TFM files, and optionally
*   to TeX VPL files that retain all kerning and ligature information.
*   Both files make the characters not normally encoded by TeX available
*   by character codes greater than 127.
*/

/*   (Modified by Don Knuth from Tom Rokicki's pre-VPL version.) */
/*   VM/CMS port by J. Hafner ([email protected]), based on
*   the port by Alessio Guglielmi ([email protected])
*   and Marco Prevedelli ([email protected]).
*   This port is still in test state.  No guarantees.
*   11/3/92: more corrections to VM/CMS port. Now it looks correct
*   and will be supported by J. Hafner.
*
*/
/*
*   More changes, primarily from Karl Berry, enough for a new version
*   number to 8.0; 1 December 1996.  Note that this version computes
*   checksums differently (more intelligently).
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#if defined(SYSV) || defined(VMS) || defined(__THINK__) || defined(MSDOS) || defined(OS2) || defined(ATARIST)
#include <string.h>
#else
#include <strings.h>
#endif
#include <math.h>
#ifdef ATARIST
#include <float.h>
#endif

/* JLH: added these to make the code easier to read and remove some
  ascii<->ebcdic dependencies */
#define ASCII_A 65
#define ASCII_Z 90
#define ASCII_a 97
#define ASCII_z 122
#define ASCII_0 48
#define ASCII_9 57

#ifdef VMCMS
#define interesting lookstr  /* for 8 character truncation conflicts */
#include "dvipscms.h"
extern FILE *cmsfopen() ;
extern char ebcdic2ascii[] ;
extern char ascii2ebcdic[] ;
#ifdef fopen
#undef fopen
#endif
#define fopen cmsfopen
#endif

#if defined MSDOS || defined OS2
#define WRITEBIN "wb"
#else
#ifdef VMCMS
#define WRITEBIN "wb, lrecl=1024, recfm=f"
#else
#define WRITEBIN "w"
#endif
#endif

#if (defined(MSDOS) && defined(__TURBOC__)) || (defined(OS2) && defined(_MSC_VER))
#define SMALLMALLOC
#endif

struct encoding {
  char *name ;
  char *vec[256] ;
} ;
struct encoding staticencoding = {
 "TeX text",
 {"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma",
  "Upsilon", "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle",
  "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", "acute",
  "caron", "breve", "macron", "ring", "cedilla", "germandbls", "ae", "oe",
  "oslash", "AE", "OE", "Oslash", "space", "exclam", "quotedbl", "numbersign",
  "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
  "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one",
  "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon",
  "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C",
  "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
  "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
  "bracketright", "circumflex", "underscore", "quoteleft", "a", "b", "c", "d",
  "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
  "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
  "tilde", "dieresis", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
   "", "", "", "", "", "", "" } } ;
/*
*   It's easier to put this in static storage and parse it as we go
*   than to build the structures ourselves.
*/
char *staticligkern[] = {
  "% LIGKERN space l =: lslash ; space L =: Lslash ;",
  "% LIGKERN question quoteleft =: questiondown ;",
  "% LIGKERN exclam quoteleft =: exclamdown ;",
  "% LIGKERN hyphen hyphen =: endash ; endash hyphen =: emdash ;",
  "% LIGKERN quoteleft quoteleft =: quotedblleft ;",
  "% LIGKERN quoteright quoteright =: quotedblright ;",
  "% LIGKERN space {} * ; * {} space ; zero {} * ; * {} zero ;",
  "% LIGKERN one {} * ; * {} one ; two {} * ; * {} two ;",
  "% LIGKERN three {} * ; * {} three ; four {} * ; * {} four ;",
  "% LIGKERN five {} * ; * {} five ; six {} * ; * {} six ;",
  "% LIGKERN seven {} * ; * {} seven ; eight {} * ; * {} eight ;",
  "% LIGKERN nine {} * ; * {} nine ;",
/* Kern accented characters the same way as their base. */
 "% LIGKERN Aacute <> A ; aacute <> a ;",
 "% LIGKERN Acircumflex <> A ; acircumflex <> a ;",
 "% LIGKERN Adieresis <> A ; adieresis <> a ;",
 "% LIGKERN Agrave <> A ; agrave <> a ;",
 "% LIGKERN Aring <> A ; aring <> a ;",
 "% LIGKERN Atilde <> A ; atilde <> a ;",
 "% LIGKERN Ccedilla <> C ; ccedilla <> c ;",
 "% LIGKERN Eacute <> E ; eacute <> e ;",
 "% LIGKERN Ecircumflex <> E ; ecircumflex <> e ;",
 "% LIGKERN Edieresis <> E ; edieresis <> e ;",
 "% LIGKERN Egrave <> E ; egrave <> e ;",
 "% LIGKERN Iacute <> I ; iacute <> i ;",
 "% LIGKERN Icircumflex <> I ; icircumflex <> i ;",
 "% LIGKERN Idieresis <> I ; idieresis <> i ;",
 "% LIGKERN Igrave <> I ; igrave <> i ;",
 "% LIGKERN Ntilde <> N ; ntilde <> n ;",
 "% LIGKERN Oacute <> O ; oacute <> o ;",
 "% LIGKERN Ocircumflex <> O ; ocircumflex <> o ;",
 "% LIGKERN Odieresis <> O ; odieresis <> o ;",
 "% LIGKERN Ograve <> O ; ograve <> o ;",
 "% LIGKERN Oslash <> O ; oslash <> o ;",
 "% LIGKERN Otilde <> O ; otilde <> o ;",
 "% LIGKERN Scaron <> S ; scaron <> s ;",
 "% LIGKERN Uacute <> U ; uacute <> u ;",
 "% LIGKERN Ucircumflex <> U ; ucircumflex <> u ;",
 "% LIGKERN Udieresis <> U ; udieresis <> u ;",
 "% LIGKERN Ugrave <> U ; ugrave <> u ;",
 "% LIGKERN Yacute <> Y ; yacute <> y ;",
 "% LIGKERN Ydieresis <> Y ; ydieresis <> y ;",
 "% LIGKERN Zcaron <> Z ; zcaron <> z ;",
/*
*   These next are only included for deficient afm files that
*   have the lig characters but not the lig commands.
*/
  "% LIGKERN f i =: fi ; f l =: fl ; f f =: ff ; ff i =: ffi ;",
  "% LIGKERN ff l =: ffl ;",
  0 } ;
/*
*   The above layout corresponds to TeX Typewriter Type and is compatible
*   with TeX Text because the position of ligatures is immaterial.
*/
struct encoding *outencoding = 0 ;
struct encoding *inencoding = 0 ;
char *outenname, *inenname ;/* the file names for input and output encodings */
int boundarychar = -1 ;     /* the boundary character */
int ignoreligkern ;         /* do we look at ligkern info in the encoding? */
/*
*   This is what we store Adobe data in.
*/
struct adobeinfo {
  struct adobeinfo *next ;
  int adobenum, texnum, width ;
  char *adobename ;
  int llx, lly, urx, ury ;
  struct lig *ligs ;
  struct kern *kerns ;
  struct adobeptr *kern_equivs ;
  struct pcc *pccs ;
  int wptr, hptr, dptr, iptr ;
} *adobechars, *adobeptrs[256], *texptrs[256],
 *uppercase[256], *lowercase[256] ;
int nexttex[256] ; /* for characters encoded multiple times in output */
/*
*   These are the eight ligature ops, in VPL terms and in METAFONT terms.
*/
char *vplligops[] = {
  "LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/", "/LIG/>", "/LIG/>>", 0
} ;
char *encligops[] = {
  "=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>", 0
} ;
struct lig {
  struct lig *next ;
  char *succ, *sub ;
  short op, boundleft ;
} ;
struct kern {
  struct kern *next ;
  char *succ ;
  int delta ;
} ;
struct adobeptr {
  struct adobeptr *next;
  struct adobeinfo *ch;
};
struct pcc {
  struct pcc *next ;
  char * partname ;
  int xoffset, yoffset ;
} ;

FILE *afmin, *vplout, *tfmout ;
char inname[200], outname[200] ; /* names of input and output files */
char buffer[255]; /* input buffer (modified while parsing) */
char obuffer[255] ; /* unmodified copy of input buffer */
char *param ; /* current position in input buffer */
char *fontname = "Unknown" ;
char *codingscheme = "Unspecified" ;
#ifdef VMCMS
char *ebfontname ;
char *ebcodingscheme ;
#endif
float italicangle = 0.0 ;
char fixedpitch ;
char makevpl ;
char pedantic ;
int xheight = 400 ;
int fontspace ;
int bc, ec ;
long cksum ;
float efactor = 1.0, slant = 0.0 ;
float capheight = 0.8 ;
char *efactorparam, *slantparam ;
double newslant ;
char titlebuf[500] ;

void
error(s)
register char *s ;
{
  (void)fprintf(stderr, "%s\n", s) ;
  if (obuffer[0]) {
     (void)fprintf(stderr, "%s\n", obuffer) ;
     while (param > buffer) {
        (void)fprintf(stderr, " ") ;
        param-- ;
     }
     (void)fprintf(stderr, "^\n") ;
  }
  if (*s == '!')
     exit(1) ;
}

int
transform(x,y)
  register int x,y ;
{
  register double acc ;
  acc = efactor * x + slant *y ;
  return (int)(acc>=0? floor(acc+0.5) : ceil(acc-0.5) ) ;
}

int
gaetline() {
  register char *p ;
  register int c ;

  param = buffer ;
  for (p=buffer; (c=getc(afmin)) != EOF && c != '\n';)/* changed 10 to '\n' */
     *p++ = c ;
  *p = 0 ;
  (void)strcpy(obuffer, buffer) ;
  if (p == buffer && c == EOF)
     return(0) ;
  else
     return(1) ;
}

char *interesting[] = { "FontName", "ItalicAngle", "IsFixedPitch",
  "XHeight", "C", "KPX", "CC", "EncodingScheme", NULL} ;
#define FontName (0)
#define ItalicAngle (1)
#define IsFixedPitch (2)
#define XHeight (3)
#define C (4)
#define KPX (5)
#define CC (6)
#define EncodingScheme (7)
#define NONE (-1)
int
interest(s)
char *s ;
{
  register char **p ;
  register int n ;

  for (p=interesting, n=0; *p; p++, n++)
     if (strcmp(s, *p)==0)
        return(n) ;
  return(NONE) ;
}

char *
mymalloc(len)
unsigned long len ;
{
  register char *p ;
  int i ;

#ifdef SMALLMALLOC
  if (len > 65500L)
     error("! can't allocate more than 64K!") ;
#endif
  p = malloc((unsigned)len) ;
  if (p==NULL)
     error("! out of memory") ;
  for (i=0; i<len; i++)
     p[i] = 0 ;
  return(p) ;
}

char *
newstring(s)
char *s ;
{
  char *q = mymalloc((unsigned long)(strlen(s) + 1)) ;
  (void)strcpy(q, s) ;
  return q ;
}

char *
paramnewstring() {
  register char *p, *q ;

  p = param ;
  while (*p > ' ')
     p++ ;
  if (*p != 0)
     *p++ = 0 ;
  q = newstring(param) ;
  while (*p && *p <= ' ')
     p++ ;
  param = p ;
  return(q) ;
}

char *
paramstring() {
  register char *p, *q ;

  p = param ;
  while (*p > ' ')
     p++ ;
  q = param ;
  if (*p != 0)
     *p++ = 0 ;
  while (*p && *p <= ' ')
     p++ ;
  param = p ;
  return(q) ;
}

int
paramnum() {
  register char *p ;
  int i ;

  p = paramstring() ;
  if (sscanf(p, "%d", &i) != 1)
     error("! integer expected") ;
  return(i) ;
}

float
paramfloat() {
  register char *p ;
  float i ;

  p = paramstring() ;
  if (sscanf(p, "%f", &i) != 1)
     error("! number expected") ;
  return(i) ;
}

struct adobeinfo *
newchar() {
  register struct adobeinfo *ai ;

  ai = (struct adobeinfo *)mymalloc((unsigned long)sizeof(struct adobeinfo)) ;
  ai->adobenum = -1 ;
  ai->texnum = -1 ;
  ai->width = -1 ;
  ai->adobename = NULL ;
  ai->llx = -1 ;
  ai->lly = -1 ;
  ai->urx = -1 ;
  ai->ury = -1 ;
  ai->ligs = NULL ;
  ai->kerns = NULL ;
  ai->kern_equivs = NULL ;
  ai->pccs = NULL ;
  ai->next = adobechars ;
  adobechars = ai ;
  return(ai) ;
}

struct kern *
newkern() {
  register struct kern *nk ;

  nk = (struct kern *)mymalloc((unsigned long)sizeof(struct kern)) ;
  nk->next = NULL ;
  nk->succ = NULL ;
  nk->delta = 0 ;
  return(nk) ;
}

struct pcc *
newpcc() {
  register struct pcc *np ;

  np = (struct pcc *)mymalloc((unsigned long)sizeof(struct pcc)) ;
  np->next = NULL ;
  np->partname = NULL ;
  np->xoffset = 0 ;
  np->yoffset = 0 ;
  return(np) ;
}

struct lig *
newlig() {
  register struct lig *nl ;

  nl = (struct lig *)mymalloc((unsigned long)sizeof(struct lig)) ;
  nl->next = NULL ;
  nl->succ = NULL ;
  nl->sub = NULL ;
  nl->op = 0 ; /* the default =: op */
  nl->boundleft = 0 ;
  return(nl) ;
}

void
expect(s)
char *s ;
{
  if (strcmp(paramstring(), s) != 0) {
     (void)fprintf(stderr, "%s expected: ", s) ;
     error("! syntax error") ;
  }
}

void
handlechar() { /* an input line beginning with C */
  register struct adobeinfo *ai ;
  register struct lig *nl ;

  ai = newchar() ;
  ai->adobenum = paramnum() ;
  expect(";") ;
  expect("WX") ;
  ai->width = transform(paramnum(),0) ;
  if (ai->adobenum >= 0 && ai->adobenum < 256) {
     adobeptrs[ai->adobenum] = ai ;
  }
  expect(";") ;
  expect("N") ;
  ai->adobename = paramnewstring() ;
  expect(";") ;
  expect("B") ;
  ai->llx = paramnum() ;
  ai->lly = paramnum() ;
  ai->llx = transform(ai->llx, ai->lly) ;
  ai->urx = paramnum() ;
  ai->ury = paramnum() ;
  ai->urx = transform(ai->urx, ai->ury) ;
/* We need to avoid negative heights or depths. They break accents in
  math mode, among other things.  */
  if (ai->lly > 0)
     ai->lly = 0 ;
  if (ai->ury < 0)
     ai->ury = 0 ;
  expect(";") ;
/* Now look for ligatures (which aren't present in fixedpitch fonts) */
  while (*param == 'L' && !fixedpitch) {
     expect("L") ;
     nl = newlig() ;
     nl->succ = paramnewstring() ;
     nl->sub = paramnewstring() ;
     nl->next = ai->ligs ;
     ai->ligs = nl ;
     expect(";") ;
  }
}

struct adobeinfo *
findadobe(p)
char *p ;
{
  register struct adobeinfo *ai ;

  for (ai=adobechars; ai; ai = ai->next)
     if (strcmp(p, ai->adobename)==0)
        return(ai) ;
  return(NULL) ;
}


/*
*   The following comment no longer applies; we rely on the LIGKERN
*   entries to kill space kerns.  Also, the same applies to numbers.
*
* We ignore kerns before and after space characters, because (1) TeX
* is using the space only for Polish ligatures, and (2) TeX's
* boundarychar mechanisms are not oriented to kerns (they apply
* to both spaces and punctuation) so we don't want to use them.
*/
void
handlekern() { /* an input line beginning with KPX */
  register struct adobeinfo *ai ;
  register char *p ;
  register struct kern *nk ;

  p = paramstring() ;
  ai = findadobe(p) ;
  if (ai == NULL)
     error("kern char not found") ;
  else {
     nk = newkern() ;
     nk->succ = paramnewstring() ;
     nk->delta = transform(paramnum(),0) ;
     nk->next = ai->kerns ;
     ai->kerns = nk ;
   }
}

void
handleconstruct() { /* an input line beginning with CC */
  register struct adobeinfo *ai ;
  register char *p ;
  register struct pcc *np ;
  register int n ;
  struct pcc *npp = NULL;

  p = paramstring() ;
  ai = findadobe(p) ;
  if (ai == NULL)
     error("! composite character name not found") ;
  n = paramnum() ;
  expect(";") ;
  while (n--) {
     if (strcmp(paramstring(),"PCC") != 0) return ;
       /* maybe I should expect("PCC") instead, but I'm playing it safe */
     np = newpcc() ;
     np->partname = paramnewstring() ;
     if (findadobe(np->partname)==NULL) return ;
     np->xoffset = paramnum() ;
     np->yoffset = paramnum() ;
     np->xoffset = transform(np->xoffset, np->yoffset) ;
     if (npp) npp->next = np ;
     else ai->pccs = np ;
     npp = np ;
     expect(";") ;
  }
}

struct encoding *readencoding() ;

void
makeaccentligs() {
  register struct adobeinfo *ai, *aci ;
  register char *p ;
  register struct lig *nl ;
  for (ai=adobechars; ai; ai=ai->next) {
     p = ai->adobename ;
     if (strlen(p)>2)
        if ((aci=findadobe(p+1)) && (aci->adobenum > 127)) {
           nl = newlig() ;
           nl->succ = mymalloc((unsigned long)2) ;
           *(nl->succ + 1) = 0 ;
           *(nl->succ) = *p ;
           nl->sub = ai->adobename ;
           nl->next = aci->ligs ;
           aci->ligs = nl ;
        }
  }
}

void
readadobe() {
  struct adobeinfo *ai ;
#ifdef VMCMS
   int i;
#endif

/*
*   We allocate a placeholder boundary char.
*/
  ai = newchar() ;
  ai->adobenum = -1 ;
  ai->adobename = "||" ; /* boundary character name */
  while (gaetline()) {
     switch(interest(paramstring())) {
case FontName:
        fontname = paramnewstring() ;
#ifdef VMCMS
/* fontname comes in as ebcdic but we need it asciified for tfm file
  so we save it in ebfontname and change it in fontname */
  ebfontname = newstring(fontname) ;
  i=0;
  while(fontname[i] != '\0') {
     fontname[i]=ebcdic2ascii[fontname[i]];
     i++;
  };
#endif
        break ;
case EncodingScheme:
        codingscheme = paramnewstring() ;
#ifdef VMCMS
/* for codingscheme, we do the same as we did for fontname */
  ebcodingscheme = newstring(codingscheme) ;
  i=0;
  while(codingscheme[i] != '\0') {
     codingscheme[i]=ebcdic2ascii[codingscheme[i]];
     i++;
  }
#endif
        break ;
case ItalicAngle:
        italicangle = paramfloat() ;
        break ;
case IsFixedPitch:
        if (*param == 't' || *param == 'T')
           fixedpitch = 1 ;
        else
           fixedpitch = 0 ;
        break ;
case XHeight:
        xheight = paramnum() ;
        break ;
case C:
        handlechar() ;
        break ;
case KPX:
        handlekern() ;
        break ;
case CC:
        handleconstruct() ;
        break ;
default:
        break ;
     }
  }
  fclose(afmin) ;
  afmin = 0 ;
}
/*
*   Re-encode the adobe font.  Assumes that the header file will
*   also contain the appropriate instructions!
*/
void
handlereencoding() {
  if (inenname) {
     int i ;
     struct adobeinfo *ai ;
     char *p ;

     ignoreligkern = 1 ;
     inencoding = readencoding(inenname) ;
     for (i=0; i<256; i++)
        if (0 != (ai=adobeptrs[i])) {
           ai->adobenum = -1 ;
           adobeptrs[i] = NULL ;
        }
     for (i=0; i<256; i++) {
        p = inencoding->vec[i] ;
        if (p && *p && 0 != (ai = findadobe(p))) {
           ai->adobenum = i ;
           adobeptrs[i] = ai ;
        }
     }
     codingscheme = inencoding->name ;
  }
  ignoreligkern = 0 ;
  if (outenname) {
     outencoding = readencoding(outenname) ;
  } else {
     outencoding = readencoding((char *)0) ;
  }
}

/*
*   This routine reverses a list.  We use it because we accumulate the
*   adobeinfo list in reverse order, but when we go to map the
*   characters, we would prefer to use the original ordering.  It just
*   makes more sense.
*/
struct adobeinfo *revlist(p)
struct adobeinfo *p ;
{
  struct adobeinfo *q = 0, *t ;

  while (p) {
     t = p->next ;
     p->next = q ;
     q = p ;
     p = t ;
  }
  return q ;
}

void
assignchars() {
  register char **p ;
  register int i, j ;
  register struct adobeinfo *ai, *pai ;
  int nextfree = 128 ;
  struct pcc *pcp ;

/*
*   First, we assign all those that match perfectly.
*/
  for (i=0, p=outencoding->vec; i<256; i++, p++)
     if ((ai=findadobe(*p)) && (ai->adobenum >= 0 || ai->pccs != NULL)) {
        if (ai->texnum >= 0)
           nexttex[i] = ai->texnum ; /* linked list */
        ai->texnum = i ;
        texptrs[i] = ai ;
     }
  if (pedantic)
     return ;
/*
*   Next, we assign all the others, retaining the adobe positions, possibly
*   multiply assigning characters.  Unless the output encoding was
*   precisely specified.
*/
  for (ai=adobechars; ai; ai=ai->next)
     if (ai->adobenum >= 0 && ai->texnum < 0 && texptrs[ai->adobenum]==0) {
        ai->texnum = ai->adobenum ;
        texptrs[ai->adobenum] = ai ;
     }
/*
*   Finally, we map all remaining characters into free locations beginning
*   with 128, if we know how to construct those characters.  We need to
*   make sure the component pieces are mapped.
*/
  adobechars = revlist(adobechars) ;
  for (ai=adobechars; ai; ai=ai->next)
     if (ai->texnum<0 && (ai->adobenum>=0 || ai->pccs != NULL)) {
        while (texptrs[nextfree]) {
           nextfree=(nextfree+1)&255 ;
           if (nextfree==128) goto finishup ; /* all slots full */
        }
        ai->texnum = nextfree ;
        texptrs[nextfree] = ai ;
     }
finishup:
/*
*   We now check all of the composite characters.  If any of the
*   components are not mapped, we unmap the composite character.
*/
  for (i=0; i<256; i++) {
     ai = texptrs[i] ;
     if (ai && ai->pccs != NULL && ai->texnum >= 0) {
        for (pcp = ai->pccs; pcp; pcp = pcp->next) {
           pai = findadobe(pcp->partname) ;
           if (pai == NULL || pai->texnum < 0) {
              texptrs[ai->texnum] = 0 ;
              ai->texnum = -1 ;
              break ;
           }
        }
     }
  }
/*
*   Now, if any of the characters are encoded multiple times, we want
*   ai->texnum to be the first one assigned, since that is most likely
*   to be the most important one.  So we reverse the above lists.
*/
  for (ai=adobechars; ai; ai=ai->next)
     if (ai->texnum >= 0 && ai->texnum < 256) {
        j = -1 ;
        while (nexttex[ai->texnum] >= 0) {
           i = nexttex[ai->texnum] ;
           nexttex[ai->texnum] = j ;
           j = ai->texnum ;
           ai->texnum = i ;
        }
        nexttex[ai->texnum] = j ;
     }
}

void
upmap() { /* Compute uppercase mapping, when making a small caps font */
  register struct adobeinfo *ai, *Ai ;
  register char *p, *q ;
  register struct pcc *np, *nq ;
  int i ;
  char lwr[50] ;

/* JLH: changed some lines below to be ascii<->ebcdic independent
  any reason we don't use 'isupper'?.
  Looks like we should use isupper to me --karl.  */
  for (Ai=adobechars; Ai; Ai=Ai->next) {
     p = Ai->adobename ;
     if (isupper (*p)) {
        q = lwr ;
        for (; *p; p++)
           *q++ = tolower (*p);
        *q = '\0';   /* changed this too! */

        if (0 != (ai=findadobe(lwr))) {
           for (i = ai->texnum; i >= 0; i = nexttex[i])
              uppercase[i] = Ai ;
           for (i = Ai->texnum; i >= 0; i = nexttex[i])
              lowercase[i] = ai ;
        }
     }
  }
/* Note that, contrary to the normal true/false conventions,
* uppercase[i] is NULL and lowercase[i] is non-NULL when i is the
* ASCII code of an uppercase letter; and vice versa for lowercase letters */

  if (0 != (ai=findadobe("germandbls")))
     if (0 != (Ai=findadobe("S"))) { /* we also construct SS */
        for (i=ai->texnum; i >= 0; i = nexttex[i])
           uppercase[i] = ai ;
        ai->adobenum = -1 ;
        ai->width = Ai->width << 1 ;
        ai->llx = Ai->llx ;
        ai->lly = Ai->lly ;
        ai->urx = Ai->width + Ai->urx ;
        ai->ury = Ai->ury ;
        ai->kerns = Ai->kerns ;
        np = newpcc() ;
        np->partname = "S" ;
        nq = newpcc() ;
        nq->partname = "S" ;
        nq->xoffset = Ai->width ;
        np->next = nq ;
        ai->pccs = np ;
     }
  if ((ai=findadobe("dotlessi")))
     for (i=ai->texnum; i >= 0; i = nexttex[i])
        uppercase[i] = findadobe("I") ;
  if ((ai=findadobe("dotlessj")))
     for (i=ai->texnum; i >= 0; i = nexttex[i])
        uppercase[i] = findadobe("J") ;
}
/* The logic above seems to work well enough, but it leaves useless characters
* like `fi' and `fl' in the font if they were present initially,
* and it omits characters like `dotlessj' if they are absent initially */

/* Now we turn to computing the TFM file */

int lf, lh, nw, nh, nd, ni, nl, nk, ne, np ;

void
write16(what)
register short what ;
{
  (void)fputc(what >> 8, tfmout) ;
  (void)fputc(what & 255, tfmout) ;
}

void
writearr(p, n)
register long *p ;
register int n ;
{
  while (n) {
     write16((short)(*p >> 16)) ;
     write16((short)(*p & 65535)) ;
     p++ ;
     n-- ;
  }
}

void
makebcpl(p, s, n)
register long *p ;
register char *s ;
register int n ;
{
  register long t ;
  register long sc ;

  if (strlen(s) < n)
     n = strlen(s) ;
  t = ((long)n) << 24 ;
  sc = 16 ;
  while (n > 0) {
     t |= ((long)(*(unsigned char *)s++)) << sc ;
     sc -= 8 ;
     if (sc < 0) {
        *p++ = t ;
        t = 0 ;
        sc = 24 ;
     }
     n-- ;
  }
  *p++ = t ;
}

int source[257] ;
int unsort[257] ;

/*
*   Next we need a routine to reduce the number of distinct dimensions
*   in a TFM file. Given an array what[0]...what[oldn-1], we want to
*   group its elements into newn clusters, in such a way that the maximum
*   difference between elements of a cluster is as small as possible.
*   Furthermore, what[0]=0, and this value must remain in a cluster by
*   itself. Data such as `0 4 6 7 9' with newn=3 shows that an iterative
*   scheme in which 6 is first clustered with 7 will not work. So we
*   borrow a neat algorithm from METAFONT to find the true optimum.
*   Memory location what[oldn] is set to 0x7fffffffL for convenience.
*/
long nextd ; /* smallest value that will give a different mincover */
int
mincover(what,d) /* tells how many clusters result, given max difference d */
register long d ;
long *what ;
{
  register int m ;
  register long l ;
  register long *p ;

  nextd = 0x7fffffffL ;
  p = what+1 ;
  m = 1 ;
  while (*p<0x7fffffffL) {
     m++ ;
     l = *p ;
     while (*++p <= l+d) ;
     if (*p-l < nextd) nextd = *p-l ;
  }
  return (m) ;
}

void
remap(what, oldn, newn)
long *what ;
int oldn, newn ;
{
  register int i, j ;
  register long d, l ;

  what[oldn] = 0x7fffffffL ;
  for (i=oldn-1; i>0; i--) {
     d = what[i] ;
     for (j=i; what[j+1]<d; j++) {
        what[j] = what[j+1] ;
        source[j] = source[j+1] ;
     }
     what[j] = d ;
     source[j] = i ;
  } /* Tom, don't let me ever catch you using bubblesort again! -- Don */

  i = mincover(what, 0L) ;
  d = nextd ;
  while (mincover(what,d+d)>newn) d += d ;
  while (mincover(what,d)>newn) d = nextd ;

  i = 1 ;
  j = 0 ;
  while (i<oldn) {
     j++ ;
     l = what[i] ;
     unsort[source[i]] = j ;
     while (what[++i] <= l+d) {
        unsort[source[i]] = j ;
        if (i-j == oldn-newn) d = 0 ;
     }
     what[j] = (l+what[i-1])/2 ;
  }
}

long checksum() {
  int i ;
  unsigned long s1 = 0, s2 = 0 ;
  char *p ;
  struct adobeinfo *ai ;

  for (i=0; i<256; i++)
     if (0 != (ai=adobeptrs[i])) {
        s1 = ((s1 << 1) ^ (s1>>31)) ^ ai->width ; /* cyclic left shift */
        s1 &= 0xffffffff; /* in case we're on a 64-bit machine */
        for (p=ai->adobename; *p; p++)
#ifndef VMCMS
           s2 = (s2 * 3) + *p ;
#else
           s2 = (s2 * 3) + ebcdic2ascii[*p] ;
#endif
     }
  s1 = (s1 << 1) ^ s2 ;
  return s1 ;
}

/*
*   The next routine simply scales something.
*   Input is in 1000ths of an em.  Output is in FIXFACTORths of 1000.
*/
#define FIXFACTOR (0x100000L) /* 2^{20}, the unit fixnum */
long
scale(what)
long what ;
{
  return(((what / 1000) << 20) +
         (((what % 1000) << 20) + 500) / 1000) ;
}

long *header, *charinfo, *width, *height, *depth, *ligkern, *kern, *tparam,
    *italic ;
long *tfmdata ;

void
buildtfm() {
  register int i, j ;
  register struct adobeinfo *ai ;

  header = tfmdata ;
  cksum = checksum() ;
  header[0] = cksum ;
  header[1] = 0xa00000 ; /* 10pt design size */
  makebcpl(header+2, codingscheme, 39) ;
  makebcpl(header+12, fontname, 19) ;
  lh = 17 ;
  charinfo = header + lh ;

  for (i=0; i<256 && adobeptrs[i]==NULL; i++) ;
  bc = i ;
  for (i=255; i>=0 && adobeptrs[i]==NULL; i--) ;
  ec = i;
  if (ec < bc)
     error("! no Adobe characters") ;

  width = charinfo + (ec - bc + 1) ;
  width[0] = 0 ;
  nw++ ;
  for (i=bc; i<=ec; i++)
     if (0 != (ai=adobeptrs[i])) {
        width[nw]=ai->width ;
        for (j=1; width[j]!=ai->width; j++) ;
        ai->wptr = j ;
        if (j==nw)
           nw++ ;
     }
  if (nw>256)
     error("! 256 chars with different widths") ;
  depth = width + nw ;
  depth[0] = 0 ;
  nd = 1 ;
  for (i=bc; i<=ec; i++)
     if (0 != (ai=adobeptrs[i])) {
        depth[nd] = -ai->lly ;
        for (j=0; depth[j]!=-ai->lly; j++) ;
        ai->dptr = j ;
        if (j==nd)
           nd++ ;
     }
  if (nd > 16) {
     remap(depth, nd, 16) ;
     nd = 16 ;
     for (i=bc; i<=ec; i++)
        if (0 != (ai=adobeptrs[i]))
           ai->dptr = unsort[ai->dptr] ;
  }
  height = depth + nd ;
  height[0] = 0 ;
  nh = 1 ;
  for (i=bc; i<=ec; i++)
     if (0 != (ai=adobeptrs[i])) {
        height[nh]=ai->ury ;
        for (j=0; height[j]!=ai->ury; j++) ;
        ai->hptr = j ;
        if (j==nh)
           nh++ ;
     }
  if (nh > 16) {
     remap(height, nh, 16) ;
     nh = 16 ;
     for (i=bc; i<=ec; i++)
        if (0 != (ai=adobeptrs[i]))
           ai->hptr = unsort[ai->hptr] ;
  }
  italic  = height + nh ;
  italic[0] = 0 ;
  ni = 1 ;
  for (i=bc; i<=ec; i++)
     if (0 != (ai=adobeptrs[i])) {
        italic[ni] = ai->urx - ai->width ;
        if (italic[ni]<0)
           italic[ni] = 0 ;
        for (j=0; italic[j]!=italic[ni]; j++) ;
        ai->iptr = j ;
        if (j==ni)
           ni++ ;
     }
  if (ni > 64) {
     remap(italic, ni, 64) ;
     ni = 64 ;
     for (i=bc; i<=ec; i++)
        if (0 != (ai=adobeptrs[i]))
           ai->iptr = unsort[ai->iptr] ;
  }

  for (i=bc; i<=ec; i++)
     if (0 != (ai=adobeptrs[i]))
        charinfo[i-bc] = ((long)(ai->wptr)<<24) +
                          ((long)(ai->hptr)<<20) +
                           ((long)(ai->dptr)<<16) +
                            ((long)(ai->iptr)<<10) ;

  ligkern = italic + ni ;
  nl = 0 ; /* ligatures and kerns omitted from raw Adobe font */
  kern = ligkern + nl ;
  nk = 0 ;

  newslant = (double)slant - efactor * tan(italicangle*(3.1415926535/180.0)) ;
  tparam = kern + nk ;
  tparam[0] = (long)(FIXFACTOR * newslant + 0.5) ;
  tparam[1] = scale((long)fontspace) ;
  tparam[2] = (fixedpitch ? 0 : scale((long)(300*efactor+0.5))) ;
  tparam[3] = (fixedpitch ? 0 : scale((long)(100*efactor+0.5))) ;
  tparam[4] = scale((long)xheight) ;
  tparam[5] = scale((long)(1000*efactor+0.5)) ;
  np = 6 ;
}

void
writesarr(what, len)
long *what ;
int len ;
{
  register long *p ;
  int i ;

  p = what ;
  i = len ;
  while (i) {
     *p = scale(*p) ;
     (void)scale(*p) ; /* need this kludge for some compilers */
     p++ ;
     i-- ;
  }
  writearr(what, len) ;
}

void
writetfm() {
  lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np ;
  write16(lf) ;
  write16(lh) ;
  write16(bc) ;
  write16(ec) ;
  write16(nw) ;
  write16(nh) ;
  write16(nd) ;
  write16(ni) ;
  write16(nl) ;
  write16(nk) ;
  write16(ne) ;
  write16(np) ;
  writearr(header, lh) ;
  writearr(charinfo, ec-bc+1) ;
  writesarr(width, nw) ;
  writesarr(height, nh) ;
  writesarr(depth, nd) ;
  writesarr(italic, ni) ;
  writearr(ligkern, nl) ;
  writesarr(kern, nk) ;
  writearr(tparam, np) ;
}

/* OK, the TFM file is done! Now for our next trick, the VPL file. */

/* For TeX we want to compute a character height that works properly
* with accents. The following list of accents doesn't need to be complete. */
/*
*   We only do this if the xheight has a reasonable value.
*   (>50)
*/
char *accents[] = { "acute", "tilde", "caron", "dieresis", NULL} ;
int
texheight(ai)
register struct adobeinfo *ai ;
{
  register char **p;
  register struct adobeinfo *aci, *acci ;
  if (xheight <= 50 || *(ai->adobename + 1)) return (ai->ury) ;
                                          /* that was the simple case */
  for (p=accents; *p; p++)  /* otherwise we look for accented letters */
     if (0 != (aci=findadobe(*p))) {
        strcpy(buffer,ai->adobename) ;
        strcat(buffer,*p) ;
        if (0 != (acci=findadobe(buffer)))
           return (acci->ury - aci->ury + xheight) ;
     }
  return (ai->ury) ;
}

/* modified tgr to eliminate varargs problems */

#define vout(s)  fprintf(vplout, s)
int level ; /* the depth of parenthesis nesting in VPL file being written */
void vlevout() {
  register int l = level ;
  while (l--) vout("   ") ;
}
void vlevnlout() {
  vout("\n") ;
  vlevout() ;
}
#define voutln(str) {fprintf(vplout,"%s\n",str);vlevout();}
#define voutln2(f,s) {fprintf(vplout,f,s);vlevnlout();}
#define voutln3(f,a,b) {fprintf(vplout,f,a,b);vlevnlout();}
#define voutln4(f,a,b,c) {fprintf(vplout,f,a,b,c);vlevnlout();}
void
vleft()
{
  level++ ;
  vout("(") ;
}

void
vright()
{
  level-- ;
  voutln(")") ;
}

int forceoctal = 0 ;

char vcharbuf[6] ;
char *vchar(c)
int c ;
{
  if (forceoctal == 0 && isalnum (c))
     (void) sprintf(vcharbuf,"C %c",
#ifndef VMCMS
     c) ;
#else
     ascii2ebcdic[c]) ;
#endif
  else (void) sprintf(vcharbuf,"O %o", (unsigned)c) ;
  return (vcharbuf) ;
}

char vnamebuf[100];
char *vname (c)
int c;
{
 if (!forceoctal && isalnum (c)) {
   vnamebuf[0] = 0;
 } else {
   sprintf (vnamebuf, " (comment %s)", texptrs[c]->adobename);
 }
 return vnamebuf;
}

void
writevpl()
{
  register int i, j, k ;
  register struct adobeinfo *ai ;
  register struct lig *nlig ;
  register struct kern *nkern ;
  register struct pcc *npcc ;
  struct adobeinfo *asucc, *asub, *api ;
  struct adobeptr *kern_eq;
  int xoff, yoff, ht ;
  char unlabeled ;

  voutln2("(VTITLE Created by %s)", titlebuf) ;
  voutln("(COMMENT Please edit that VTITLE if you edit this file)") ;
  (void)sprintf(obuffer, "TeX-%s%s%s%s", outname,
     (efactor==1.0? "" : "-E"), (slant==0.0? "" : "-S"),
                (makevpl==1? "" : "-CSC")) ;
  if (strlen(obuffer)>19) { /* too long, will retain first 9 and last 10 */
     register char *p, *q ;
     for (p = &obuffer[9], q = &obuffer[strlen(obuffer)-10] ; p<&obuffer[19];
             p++, q++) *p = *q ;
     obuffer[19] = '\0' ;
  }
  voutln2("(FAMILY %s)" , obuffer) ;
  {
     char tbuf[300] ;
     char *base_encoding =
#ifndef VMCMS
        codingscheme ;
#else
        ebcodingscheme ;
#endif

     if (strcmp (outencoding->name, base_encoding) == 0) {
       sprintf(tbuf, "%s", outencoding->name);
     } else {
       sprintf(tbuf, "%s + %s", base_encoding, outencoding->name);
     }

     if (strlen(tbuf) > 39) {
        error("Coding scheme too long; shortening to 39 characters.") ;
        tbuf[39] = 0 ;
     }
     voutln2("(CODINGSCHEME %s)", tbuf) ;
  }
  voutln("(DESIGNSIZE R 10.0)") ;
  voutln("(DESIGNUNITS R 1000)") ;
  voutln("(COMMENT DESIGNSIZE (1 em) IS IN POINTS)") ;
  voutln("(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/1000)") ;
  /* Let vptovf compute the checksum. */
  /* voutln2("(CHECKSUM O %lo)",cksum ^ 0xffffffff) ; */
  if (boundarychar >= 0)
     voutln2("(BOUNDARYCHAR O %lo)", (unsigned long)boundarychar) ;
  vleft() ; voutln("FONTDIMEN") ;
  if (newslant)
     voutln2("(SLANT R %f)", newslant) ;
  voutln2("(SPACE D %d)", fontspace) ;
  if (! fixedpitch) {
     voutln2("(STRETCH D %d)", transform(200,0)) ;
     voutln2("(SHRINK D %d)", transform(100,0)) ;
  }
  voutln2("(XHEIGHT D %d)", xheight) ;
  voutln2("(QUAD D %d)", transform(1000,0)) ;
  voutln2("(EXTRASPACE D %d)", fixedpitch ? fontspace : transform(111, 0)) ;
  vright() ;
  vleft() ; voutln("MAPFONT D 0");
  voutln2("(FONTNAME %s)", outname) ;
  /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum) ; */
  vright() ;
  if (makevpl>1) {
     vleft() ; voutln("MAPFONT D 1");
     voutln2("(FONTNAME %s)", outname) ;
     voutln2("(FONTAT D %d)", (int)(1000.0*capheight+0.5)) ;
     /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum) ; */
     vright() ;
  }

  for (i=0; i<256 && texptrs[i]==NULL; i++) ;
  bc = i ;
  for (i=255; i>=0 && texptrs[i]==NULL; i--) ;
  ec = i;

  vleft() ; voutln("LIGTABLE") ;
  ai = findadobe("||") ;
  unlabeled = 1 ;
  for (nlig=ai->ligs; nlig; nlig=nlig->next)
     if (0 != (asucc=findadobe(nlig->succ))) {
        if (0 != (asub=findadobe(nlig->sub)))
           if (asucc->texnum>=0)
              if (asub->texnum>=0) {
                 if (unlabeled) {
                    voutln("(LABEL BOUNDARYCHAR)") ;
                    unlabeled = 0 ;
                 }
                 for (j = asucc->texnum; j >= 0; j = nexttex[j]) {
                    voutln4("(%s %s O %o)", vplligops[nlig->op],
                        vchar(j), (unsigned)asub->texnum) ;
                 }
              }
      }
  if (! unlabeled) voutln("(STOP)") ;
  for (i=bc; i<=ec; i++)
     if ((ai=texptrs[i]) && ai->texnum == i) {
        unlabeled = 1 ;
        if (uppercase[i]==NULL) /* omit ligatures from smallcap lowercase */
           for (nlig=ai->ligs; nlig; nlig=nlig->next)
              if (0 != (asucc=findadobe(nlig->succ)))
                 if (0 != (asub=findadobe(nlig->sub)))
                    if (asucc->texnum>=0)
                       if (asub->texnum>=0) {
                          if (unlabeled) {
                             for (j = ai->texnum; j >= 0; j = nexttex[j])
                                voutln3("(LABEL %s)%s", vchar(j), vname(j)) ;
                             unlabeled = 0 ;
                          }
                          for (j = asucc->texnum; j >= 0; j = nexttex[j]) {
                             voutln4("(%s %s O %o)", vplligops[nlig->op],
                                  vchar(j), (unsigned)asub->texnum) ;
                             if (nlig->boundleft)
                                break ;
                          }
                       }
        for (nkern = (uppercase[i] ? uppercase[i]->kerns : ai->kerns);
                   nkern; nkern=nkern->next)
           if (0 != (asucc=findadobe(nkern->succ)))
              for (j = asucc->texnum; j >= 0; j = nexttex[j]) {
                 if (uppercase[j]==NULL) {
                    if (unlabeled) {
                       for (k = ai->texnum; k >= 0; k = nexttex[k])
                          voutln3("(LABEL %s)%s", vchar(k), vname(k)) ;
                       unlabeled = 0 ;
                    }
                    /* If other characters have the same kerns as this
                       one, output the label here.  This makes the TFM
                       file much smaller than if we output all the
                       kerns again under a different label.  */
                    for (kern_eq = ai->kern_equivs; kern_eq;
                         kern_eq = kern_eq->next) {
                       k = kern_eq->ch->texnum;
                       if (k >= 0 && k < 256)
                         voutln3("(LABEL %s)%s", vchar(k), vname(k)) ;
                    }
                    ai->kern_equivs = 0; /* Only output those labels once. */
                    if (uppercase[i]) {
                       if (lowercase[j]) {
                          for (k=lowercase[j]->texnum; k >= 0; k = nexttex[k])
                             voutln4("(KRN %s R %.1f)%s", vchar(k),
                                   capheight*nkern->delta, vname(k)) ;
                       } else voutln4("(KRN %s R %.1f)%s",
                                vchar(j), capheight*nkern->delta, vname(j)) ;
                    } else {
                       voutln4("(KRN %s R %d)%s", vchar(j),
                               nkern->delta, vname(j)) ;
                       if (lowercase[j])
                          for (k=lowercase[j]->texnum; k >= 0; k = nexttex[k])
                             voutln4("(KRN %s R %.1f)%s", vchar(k),
                               capheight*nkern->delta, vname(k)) ;
                    }
                 }
              }
        if (! unlabeled) voutln("(STOP)") ;
     }
  vright() ;

  for (i=bc; i<=ec; i++)
     if (0 != (ai=texptrs[i])) {
        vleft() ; fprintf(vplout, "CHARACTER %s", vchar(i)) ;
        if (*vcharbuf=='C') {
           voutln("") ;
        } else
           voutln2(" (comment %s)", ai->adobename) ;
        if (uppercase[i]) {
           ai=uppercase[i] ;
           voutln2("(CHARWD R %.1f)", capheight * (ai->width)) ;
           if (0 != (ht=texheight(ai)))
              voutln2("(CHARHT R %.1f)", capheight * ht) ;
           if (ai->lly)
              voutln2("(CHARDP R %.1f)", -capheight * ai->lly) ;
           if (ai->urx > ai->width)
              voutln2("(CHARIC R %.1f)", capheight * (ai->urx - ai->width)) ;
        } else {
           voutln2("(CHARWD R %d)", ai->width) ;
           if (0 != (ht=texheight(ai)))
              voutln2("(CHARHT R %d)", ht) ;
           if (ai->lly)
              voutln2("(CHARDP R %d)", -ai->lly) ;
           if (ai->urx > ai->width)
              voutln2("(CHARIC R %d)", ai->urx - ai->width) ;
        }
        if (ai->adobenum != i || uppercase[i]) {
           vleft() ; voutln("MAP") ;
           if (uppercase[i]) voutln("(SELECTFONT D 1)") ;
           if (ai->pccs && ai->adobenum < 0) {
              xoff = 0 ; yoff = 0 ;
              for (npcc = ai->pccs; npcc; npcc=npcc->next)
                 if (0 != (api=findadobe(npcc->partname)))
                    if (api->texnum>=0) {
                       if (npcc->xoffset != xoff) {
                          if (uppercase[i]) {
                             voutln2("(MOVERIGHT R %.1f)",
                                     capheight * (npcc->xoffset - xoff)) ;
                          } else voutln2("(MOVERIGHT R %d)",
                                     npcc->xoffset - xoff) ;
                          xoff = npcc->xoffset ;
                       }
                       if (npcc->yoffset != yoff) {
                          if (uppercase[i]) {
                             voutln2("(MOVEUP R %.1f)",
                                     capheight * (npcc->yoffset - yoff)) ;
                          } else voutln2("(MOVEUP R %d)",
                                     npcc->yoffset - yoff) ;
                          yoff = npcc->yoffset ;
                       }
                       voutln2("(SETCHAR O %o)", (unsigned)api->adobenum) ;
                       xoff += texptrs[api->texnum]->width ;
                    }
           } else voutln2("(SETCHAR O %o)", (unsigned)ai->adobenum) ;
           vright() ;
        }
        vright() ;
     }
  if (level) error("! I forgot to match the parentheses") ;
}

void usage(f)
FILE *f ;
{
  (void)fprintf(f,
"afm2tfm 8.1, Copyright 1990-97 by Radical Eye Software\n") ;
  (void)fprintf(f,
"Usage: afm2tfm foo[.afm] [-O] [-u] [-v|-V bar[.vpl]]\n") ;
  (void)fprintf(f,
"                 [-e expansion] [-s slant] [-c capheight]\n") ;
  (void)fprintf(f,
"                 [-p|-t|-T encodingfile] [foo[.tfm]]\n") ;
}

void
openfiles(argc, argv)
int argc ;
char *argv[] ;
{
  register int lastext ;
  register int i ;
  int arginc ;

  tfmout = (FILE *)NULL ;
  if (argc == 1) {
     usage(stdout) ;
     exit(0) ;
  }

#if defined(MSDOS) || defined(OS2) || defined(ATARIST)
  /* Make VPL file identical to that created under Unix */
  (void)sprintf(titlebuf, "afm2tfm %s", argv[1]) ;
#else
#ifdef VMCMS
  /* Make VPL file identical to that created under Unix */
  (void)sprintf(titlebuf, "afm2tfm %s", argv[1]) ;
#else
  (void)sprintf(titlebuf, "%s %s", argv[0], argv[1]) ;
#endif
#endif
  (void)strcpy(inname, argv[1]) ;
  lastext = -1 ;
  for (i=0; inname[i]; i++)
     if (inname[i] == '.')
        lastext = i ;
     else if (inname[i] == '/' || inname[i] == ':')
        lastext = -1 ;
  if (lastext == -1) (void)strcat(inname, ".afm") ;

  while (argc>2 && *argv[2]=='-') {
     arginc = 2 ;
     i = argv[2][1] ;
     if (i == '/')
        i = argv[2][2] - 32 ; /* /a ==> A for VMS */
     switch (i) {
case 'V': makevpl++ ;
case 'v': makevpl++ ;
        (void)strcpy(outname, argv[3]) ;
        lastext = -1 ;
        for (i=0; outname[i]; i++)
           if (outname[i] == '.')
              lastext = i ;
           else if (outname[i] == '/' || outname[i] == ':')
              lastext = -1 ;
        if (lastext == -1) (void)strcat(outname, ".vpl") ;
#ifndef VMCMS
#ifndef ATARIST
        if ((vplout=fopen(outname, WRITEBIN))==NULL)
#else
        if ((vplout=fopen(outname, "w"))==NULL)
#endif
#else
        if ((vplout=fopen(outname, "w"))==NULL)
#endif
           error("! can't open vpl output file") ;
        break ;
case 'e': if (sscanf(argv[3], "%f", &efactor)==0 || efactor<0.01)
           error("! Bad extension factor") ;
        efactorparam = argv[3] ;
        break ;
case 'c':
        if (sscanf(argv[3], "%f", &capheight)==0 || capheight<0.01)
           error("! Bad small caps height") ;
        break ;
case 's': if (sscanf(argv[3], "%f", &slant)==0)
           error("! Bad slant parameter") ;
        slantparam = argv[3] ;
        break ;
case 'P':
case 'p':
        inenname = argv[3] ;
        break ;
case 'T':
        inenname = outenname = argv[3] ;
        break ;
case 't':
        outenname = argv[3] ;
        break ;
case 'O':
        forceoctal = 1 ;
        arginc = 1 ;
        break ;
case 'u':
        pedantic = 1 ;
        arginc = 1 ;
        break ;
default: (void)fprintf(stderr, "Unknown option %s %s will be ignored.\n",
                        argv[2], argv[3]) ;
     }
     for (i=0; i<arginc; i++) {
        (void)sprintf(titlebuf + strlen(titlebuf), " %s", argv[2]) ;
        argv++ ;
        argc-- ;
     }
  }

  if ((afmin=fopen(inname, "r"))==NULL)
     error("! can't open afm input file") ;

  if (argc>3 || (argc==3 && *argv[2]=='-')) {
     usage(stderr) ;
     error("! incorrect usage") ;
  }

  if (argc == 2) (void)strcpy(outname, inname) ;
  else (void)strcpy(outname, argv[2]) ;

  lastext = -1 ;
  for (i=0; outname[i]; i++)
     if (outname[i] == '.')
        lastext = i ;
     else if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
        lastext = -1 ;
  if (argc == 2) {
     outname[lastext] = 0 ;
     lastext = -1 ;
  }
  if (lastext == -1) {
     lastext = strlen(outname) ;
     (void)strcat(outname, ".tfm") ;
  }
  if (tfmout == NULL && (tfmout=fopen(outname, WRITEBIN))==NULL)
     error("! can't open tfm output file") ;
  outname[lastext] = 0 ;
/*
*   Now we strip off any directory information, so we only use the
*   base name in the vf file.  We accept any of /, :, or \ as directory
*   delimiters, so none of these are available for use inside the
*   base name; this shouldn't be a problem.
*/
  for (i=0, lastext=0; outname[i]; i++)
     if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
        lastext = i + 1 ;
  if (lastext)
     strcpy(outname, outname + lastext) ;
}
/*
*   Some routines to remove kerns that match certain patterns.
*/
struct kern *rmkernmatch(k, s)
struct kern *k ;
char *s ;
{
  struct kern *nkern ;

  while (k && strcmp(k->succ, s)==0)
     k = k->next ;
  if (k) {
     for (nkern = k; nkern; nkern = nkern->next)
        while (nkern->next && strcmp(nkern->next->succ, s)==0)
           nkern->next = nkern->next->next ;
  }
  return k ;
}
/*
*   Recursive to one level.
*/
void rmkern(s1, s2, ai)
char *s1, *s2 ;
struct adobeinfo *ai ;
{
  if (ai == 0) {
     if (strcmp(s1, "*") == 0) {
        for (ai=adobechars; ai; ai = ai->next)
           rmkern(s1, s2, ai) ;
        return ;
     } else {
        ai = findadobe(s1) ;
        if (ai == 0)
           return ;
     }
  }
  if (strcmp(s2, "*")==0)
     ai->kerns = 0 ; /* drop them on the floor */
  else
     ai->kerns = rmkernmatch(ai->kerns, s2) ;
}
/* Make the kerning for character S1 equivalent to that for S2.
  If either S1 or S2 do not exist, do nothing.
  If S1 already has kerning, do nothing.  */
void
addkern (s1, s2)
   char *s1, *s2;
{
 struct adobeinfo *ai1 = findadobe (s1);
 struct adobeinfo *ai2 = findadobe (s2);
 if (ai1 && ai2 && !ai1->kerns) {
   /* Put the new one at the head of the list, since order is immaterial.  */
   struct adobeptr *ap
     = (struct adobeptr *) mymalloc((unsigned long)sizeof(struct adobeptr));
   ap->next = ai2->kern_equivs;
   ap->ch = ai1;
   ai2->kern_equivs = ap;
 }
}
int sawligkern ;
/*
*   Reads a ligkern line, if this is one.  Assumes the first character
*   passed is `%'.
*/
void checkligkern(s)
char *s ;
{
  char *oparam = param ;
  char *mlist[5] ;
  int n ;

  s++ ;
  while (*s && *s <= ' ')
     s++ ;
  if (strncmp(s, "LIGKERN", 7)==0) {
     sawligkern = 1 ;
     s += 7 ;
     while (*s && *s <= ' ')
        s++ ;
     param = s ;
     while (*param) {
        for (n=0; n<5;) {
           if (*param == 0)
              break ;
           mlist[n] = paramstring() ;
           if (strcmp(mlist[n], ";") == 0)
              break ;
           n++ ;
        }
        if (n > 4)
           error("! too many parameters in lig kern data") ;
        if (n < 3)
           error("! too few parameters in lig kern data") ;
        if (n == 3 && strcmp(mlist[1], "{}") == 0) { /* rmkern command */
           rmkern(mlist[0], mlist[2], (struct adobeinfo *)0) ;
        } else if (n == 3 && strcmp(mlist[1], "<>") == 0) { /* addkern */
           addkern(mlist[0], mlist[2]) ;
        } else if (n == 3 && strcmp(mlist[0], "||") == 0 &&
                             strcmp(mlist[1], "=") == 0) { /* bc command */
           struct adobeinfo *ai = findadobe("||") ;

           if (boundarychar != -1)
              error("! multiple boundary character commands?") ;
           if (sscanf(mlist[2], "%d", &n) != 1)
              error("! expected number assignment for boundary char") ;
           if (n < 0 || n > 255)
              error("! boundary character number must be 0..255") ;
           boundarychar = n ;
           if (ai == 0)
              error("! internal error: boundary char") ;
           ai->texnum = n ; /* prime the pump, so to speak, for lig/kerns */
        } else if (n == 4) {
           int op = -1 ;
           struct adobeinfo *ai ;

           for (n=0; encligops[n]; n++)
              if (strcmp(mlist[2], encligops[n])==0) {
                 op = n ;
                 break ;
              }
           if (op < 0)
              error("! bad ligature op specified") ;
           if (0 != (ai = findadobe(mlist[0]))) {
              struct lig *lig ;

              if (findadobe(mlist[2]))     /* remove coincident kerns */
                 rmkern(mlist[0], mlist[1], ai) ;
              if (strcmp(mlist[3], "||") == 0)
                 error("! you can't lig to the boundary character!") ;
              if (! fixedpitch) { /* fixed pitch fonts get *0* ligs */
                 for (lig=ai->ligs; lig; lig = lig->next)
                    if (strcmp(lig->succ, mlist[1]) == 0)
                       break ; /* we'll re-use this structure */
                 if (lig == 0) {
                    lig = newlig() ;
                    lig->succ = newstring(mlist[1]) ;
                    lig->next = ai->ligs ;
                    ai->ligs = lig ;
                 }
                 lig->sub = newstring(mlist[3]) ;
                 lig->op = op ;
                 if (strcmp(mlist[1], "||")==0) {
                    lig->boundleft = 1 ;
                    if (strcmp(mlist[0], "||")==0)
                       error("! you can't lig boundarychar boundarychar!") ;
                 } else
                    lig->boundleft = 0 ;
              }
           }
        } else
           error("! bad form in LIGKERN command") ;
     }
  }
  param = oparam ;
}
/*
*   Here we get a token from the AFM file.  We parse just as much PostScript
*   as we expect to find in an encoding file.  We allow commented lines and
*   names like 0, .notdef, _foo_.  We do not allow //abc.
*/
char smbuffer[100] ;    /* for tokens */
char *gettoken() {
  char *p, *q ;

  while (1) {
     while (param == 0 || *param == 0) {
        if (gaetline() == 0)
           error("! premature end in encoding file") ;
        for (p=buffer; *p; p++)
           if (*p == '%') {
              if (ignoreligkern == 0)
                 checkligkern(p) ;
              *p = 0 ;
              break ;
           }
     }
     while (*param && *param <= ' ')
        param++ ;
     if (*param) {
        if (*param == '[' || *param == ']' ||
            *param == '{' || *param == '}') {
           smbuffer[0] = *param++ ;
           smbuffer[1] = 0 ;
           return smbuffer ;
        } else if (*param == '/' || *param == '-' || *param == '_' ||
                   *param == '.' ||
                   ('0' <= *param && *param <= '9') ||
                   ('a' <= *param && *param <= 'z') ||
                   ('A' <= *param && *param <= 'Z')) {
           smbuffer[0] = *param ;
           for (p=param+1, q=smbuffer+1;
                       *p == '-' || *p == '_' || *p == '.' ||
                       ('0' <= *p && *p <= '9') ||
                       ('a' <= *p && *p <= 'z') ||
                       ('A' <= *p && *p <= 'Z'); p++, q++)
              *q = *p ;
           *q = 0 ;
           param = p ;
           return smbuffer ;
        }
     }
  }
}
void getligkerndefaults() {
  int i ;

  for (i=0; staticligkern[i]; i++) {
     strcpy(buffer, staticligkern[i]) ;
     strcpy(obuffer, staticligkern[i]) ;
     param = buffer ;
     checkligkern(buffer) ;
  }
}
/*
*   This routine reads in an encoding file, given the name.  It returns
*   the final total structure.  It performs a number of consistency checks.
*/
struct encoding *readencoding(enc)
char *enc ;
{
  char *p ;
  int i ;
  struct encoding *e =
     (struct encoding *)mymalloc((unsigned long)sizeof(struct encoding)) ;

  sawligkern = 0 ;
  if (afmin)
     error("! oops; internal afmin error") ;
  if (enc) {
     afmin = fopen(enc, "r") ;
     param = 0 ;
     if (afmin == 0)
        error("! couldn't open that encoding file") ;
     p = gettoken() ;
     if (*p != '/' || p[1] == 0)
        error("! first token in encoding must be literal encoding name") ;
     e->name = newstring(p+1) ;
     p = gettoken() ;
     if (strcmp(p, "["))
        error("! second token in encoding must be mark ([) token") ;
     for (i=0; i<256; i++) {
        p = gettoken() ;
        if (*p != '/' || p[1] == 0)
           error("! tokens 3 to 257 in encoding must be literal names") ;
        e->vec[i] = newstring(p+1) ;
     }
     p = gettoken() ;
     if (strcmp(p, "]"))
        error("! token 258 in encoding must be make-array (])") ;
     while (gaetline()) {
        for (p=buffer; *p; p++)
           if (*p == '%') {
              if (ignoreligkern == 0)
                 checkligkern(p) ;
              *p = 0 ;
              break ;
           }
     }
     fclose(afmin) ;
     afmin = 0 ;
     if (ignoreligkern == 0 && sawligkern == 0)
        getligkerndefaults() ;
  } else {
     e = &staticencoding ;
     getligkerndefaults() ;
  }
  param = 0 ;
  return e ;
}
/*
*   This routine prints out the line that needs to be added to psfonts.map.
*/
void conspsfonts() {
  (void)printf("%s %s", outname,
#ifndef VMCMS
  fontname) ;
#else /* VM/CMS: fontname is ascii, so we use ebfontname */
  ebfontname) ;
#endif
  if (slantparam || efactorparam || inenname) {
     (void)printf(" \"") ;
     if (slantparam)
        (void)printf(" %s SlantFont", slantparam) ;
     if (efactorparam)
        (void)printf(" %s ExtendFont", efactorparam) ;
     if (inenname)
        (void)printf(" %s ReEncodeFont", inencoding->name) ;
     (void)printf(" \"") ;
     if (inenname)
        (void)printf(" <%s", inenname) ;
  }
  (void)printf("\n") ;
}
#ifndef VMS
int
#endif
main(argc, argv)
int argc ;
char *argv[] ;
{
  int i ;

  for (i=0; i<256; i++)
     nexttex[i] = -1 ; /* encoding chains have length 0 */
  tfmdata = (long *)mymalloc((unsigned long)40000L) ;
  openfiles(argc, argv) ;
  readadobe() ;
  if (fontspace == 0) {
     struct adobeinfo *ai ;

     if (0 != (ai = findadobe("space")))
        fontspace = ai->width ;
     else if (adobeptrs[32])
        fontspace = adobeptrs[32]->width ;
     else
        fontspace = transform(500, 0) ;
  }
  handlereencoding() ;
  buildtfm() ;
  writetfm() ;
  conspsfonts() ;
  if (makevpl) {
     assignchars() ;
     if (makevpl>1) upmap() ;
     writevpl() ;
  }
  exit(0) ;
  /*NOTREACHED*/
}