
/* Program a2ac,  Petr Olsak */
/* ************************* */

/* ver.0 <September 94> The program was created. */

/* ver.1 <June 95>
   - The new format of description file: the order of actions are not fixed
   - Abbverbations of kern data are allowed
   - The function k for kern value introduced
   - Mistake corrections:
     . correct the nestability of unix version of a2ac on DOS files (^M)
     . if the Composites field is missing on input, the program add its
     . the output is no created if an error in input occurs
     . the Czech docummentation corrected and English added */

/* for more information see the a2ac-cz.doc or a2ac-eng.doc file */

/* If your linker needs information about libraries, you have to load
   the math. libm.a explicitly. For example you can use the UNIX command:
   cc a2ac.c -lm -o a2ac
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#define POOLSIZE 6000
#define MAXLINE 256
#define MAXP 10

#define DOSCR 13

#define COMMENT "Converted by a2ac to obtain more composites and kerns"
#define ISFIXEDPITCH     "IsFixedPitch"
#define ITALICANGLE      "ItalicAngle"
#define FONTNAME         "FontName"
#define CAPHEIGHT        "CapHeight"
#define XHEIGHT          "XHeight"
#define ASCENDER         "Ascender"
#define DESCENDER        "Descender"

#define STARTCHARMETRICS "StartCharMetrics"
#define ENDCHARMETRICS   "EndCharMetrics"
#define STARTKERNPAIRS   "StartKernPairs"
#define ENDKERNPAIRS     "EndKernPairs"
#define STARTCOMPOSITES  "StartComposites"
#define ENDCOMPOSITES    "EndComposites"
#define ENDFONTMETRICS   "EndFontMetrics"

#define STARTDEFVARIABLES  "StartDefVariables"
#define ENDDEFVARIABLES    "EndDefVariables"
#define STARTNEWCOMPOSITES "StartNewComposites"
#define ENDNEWCOMPOSITES   "EndNewComposites"
#define STARTNEWKERNS      "StartNewKerns"
#define ENDNEWKERNS        "EndNewKerns"
#define REDUCEKERNS        "ReduceKerns"
#define STARTWXCORRECTIONS "StartWXCorrections"
#define ENDWXCORRECTIONS   "EndWXCorrections"

char pool[POOLSIZE] ;
int lastpool, poolwrite ;

char line[MAXLINE], buf[20] ;
int currspace, eofline;

typedef struct CHARINFO {
  int code ;
  char *name ;
  int wx ;
  int bb[4] ;
  char *rest ;
} CHARINFO ;

typedef struct CHARLIST {
  CHARINFO *chi ;
  struct CHARLIST *next ;
} CHARLIST ;

CHARLIST *firstchar, *lastchar;

typedef struct KERNINFO {
  char *name ;
  int kern ;
  struct KERNINFO *next ;
} KERNINFO ;

typedef struct KERNLIST {
  char *name ;
  KERNINFO *ki ;
  struct KERNLIST *next ;
} KERNLIST ;

KERNLIST *firstkern, *lastkern;

int sumkerns, rewkerns, onlysmall;
int SUMkern, SUMrewritekern, SUMremovekern;
int inputkerns=0;

typedef struct COMLIST {
  char *name ;
  char *lin ;
  struct COMLIST *next ;
} COMLIST ;

COMLIST *firstcom, *lastcom;

typedef struct VARLIST {
  char *name ;
  int value ;
  struct VARLIST *next ;
} VARLIST ;

VARLIST *firstvar, *lastvar;

int ind, isfixed;

char P[3][MAXP]; int S[2][MAXP];  char *N[MAXP];

FILE *input, *afminput, *output, *corrfile;
#define FGETC(INPUT) (feof(INPUT)||i>=MAXLINE ? '\n' : fgetc(INPUT))

#define MAX(A,B) ((A>B) ? (A) : (B))
#define MIN(A,B) ((A>B) ? (B) : (A))

int endfile=0;
double slant;

#define ERROR(TEXT) {printf(">>>>ERROR<<<< %s\n",TEXT);printline();exit(1);}


void printline ()  /* prints the line with error */
/*---------------*/
{
  int i=0;

  while (i < currspace) if (line[i++] == 0)  line[i-1] = ' ';

  printf ("Line: %s\n", line);
  return;
}

int round (f)  /* the round function */
/*-----------*/
  double f;
{
  return (int) (f >= 0) ? (f + 0.5) : (f - 0.5) ;
}

void *myalloc (size)  /* alloc with own check */
/*------------------*/
  int size;
{
  void *p;
  p = malloc (size);
  if (myalloc == NULL) ERROR ("no memory, malloc failed");
  return p;
}

char *newpool (s)  /* adds the (probably) new string s to the pool */
/*---------------*/
  char *s;
{
  int i = 0;
  while (i < lastpool)
  {
    if (strcmp(&pool[i], s) == 0)  return &pool[i] ;
    i += strlen(&pool[i]) + 1;
  }
  if (!poolwrite) ERROR ("Undefined identifier");
  i = lastpool;
  strcpy (&pool[i], s);
  lastpool += strlen(&pool[i]) + 1;
  return &pool[i];
}

char *readstr ()  /* returns the next string from line separated by ' ' */
/*--------------*/
{
  int i, j;
  if (eofline) return NULL;
  currspace++;
  while (line[currspace] == ' ' || line[currspace] == '\t') currspace++;
  i = currspace;
  while (line[i] != ' ' && line[i] != '\t' && line[i] != 0) i++;
  if (line[i] == 0) eofline = 1;
  line[i] = 0;
  j = currspace; currspace = i;
  return &line[j];
}

void readline ()  /* reads line with check for MAXLINE and skip blank lines */
/*--------------*/
{
  register int i=0;

  do
  {
    while ((line[i]=FGETC(input)) != '\n') i++;

    if (i==MAXLINE)
    {
      printf ("a2ac: bad input file format\n");
      exit (1);
    }
    if (feof(input))
    {
      if (endfile) return ;
      printf ("a2ac: end of input not expected, bad format\n");
      exit (1);
    }
    if (line[i-1] == DOSCR && i > 0) i--;
    i = line[i] = 0;
    while (line[i] == ' ' || line[i] == '\t') i++;
  }
  while  (line[i] == 0) ; /* skipping blank line */

  eofline = 0;
  while (line[i] != 0 && line[i] != ' ') i++;
  if (line[i] == 0) eofline = 1;
  line[i] = 0;
  currspace = i;
  return;
}

int rawreadline ()  /* reads line without skip blank lines */
/*----------------*/
{
  register int i=0;

  while ((line[i]=FGETC(input)) != '\n') i++;

  if (line[i-1] == DOSCR && i > 0) i--;
  i = line[i] = 0;

  while (line[i] != 0 && line[i] != ' ') i++;
  if (line[i] == 0) currspace = i+1;
  else   currspace = i,   line[i] = 0 ;
  return feof(input) ;
}

int lincmp (name)  /* compares name with line, returns 1 if OK */
/*---------------*/
  char *name ;
{
  register int i=0, j=0;
  while (line[i] == name[j] && name[j]) i++, j++;
  return !name[j];
}

void newchar (code, wx, name, b0, b1, b2, b3)  /* allocates new char info */
/*-------------------------------------------*/
  char *name ;
  int code, wx, b0, b1, b2, b3 ;
{
  CHARINFO *c; int len;
  if (lastchar == NULL) firstchar = lastchar = myalloc (sizeof(CHARLIST)) ;
  else  lastchar = lastchar->next = myalloc (sizeof(CHARLIST)) ;
  lastchar->next = NULL;
  c = lastchar->chi = myalloc (sizeof(CHARINFO)) ;
  c->name = newpool (name);
  c->code = code;
  c->wx = wx;
  c->bb[0] = b0; c->bb[1] = b1; c->bb[2] = b2; c->bb[3] = b3;
  readstr ();
  if (eofline) c->rest = NULL ;
  else len = strlen(&line[currspace+1]) + 1 ,
       c->rest = myalloc(len) ,
       strcpy (c->rest, &line[currspace+1]) ;
  return ;
}

void newkern (name1, name2, kern)  /* allocates new kern info */
/*-------------------------------*/
  char *name1, *name2;
  int kern;
{
  KERNINFO *c, *cold;  KERNLIST *p;

  /* Check of presence name1 */

  p = firstkern ;

  while (p != NULL)
  {
    if (strcmp (p->name,name1) == 0)   /* the name1 is present */
    {
       c = p->ki ;
       do
       {
	 if (strcmp (c->name,name2) == 0)  { c->kern = kern ;  return ; }
	 cold = c ;
	 c = c->next;
       }
       while (c != NULL) ;

       c = cold->next = myalloc (sizeof(KERNINFO));
       c->name = newpool(name2);
       c->kern = kern;
       c->next = NULL;
       return;
    }
    p = p->next ;
  }

  /* Make new KERNLIST */

  if (lastkern == NULL) firstkern = lastkern = myalloc (sizeof(KERNLIST)) ;
  else lastkern = lastkern->next = myalloc (sizeof(KERNLIST)) ;
  lastkern->next = NULL;
  lastkern->name = newpool(name1);
  c = lastkern->ki = myalloc (sizeof(KERNINFO)) ;
  c->name = newpool(name2);
  c->kern = kern;
  c->next = NULL;
  return;
}

void newcom (name)  /* allocates new composite info */
/*----------------*/
  char *name;
{
  if (lastcom == NULL) firstcom = lastcom = myalloc (sizeof(COMLIST)) ;
  else lastcom = lastcom->next = myalloc (sizeof(COMLIST)) ;
  lastcom->next = NULL;
  lastcom->name = newpool(name);
  line[currspace] = ' ';
  lastcom->lin = myalloc(strlen(line)+1);
  strcpy (lastcom->lin, line);
  return;
}

void newvar (name, value)  /* allocates new variable info */
/*-----------------------*/
  char *name;
  int value;
{
  VARLIST *p;

  p = firstvar;
  while (p != NULL)
  {
    if (strcmp (p->name, name) == 0)
    {
      p->value = value;
      printf (">> %s = %d\n", name, value);
      return;
    }
    p = p->next;
  }
  if (lastvar == NULL) firstvar = lastvar = myalloc (sizeof(VARLIST)) ;
  else lastvar = lastvar->next = myalloc (sizeof(VARLIST)) ;
  lastvar->next = NULL;
  lastvar->name = newpool(name);
  lastvar->value = value;
  printf (">> %s = %d\n", name, value);
}

void readafm ()  /* reads the afm input (first step) */
/*-------------*/
{
  int c, wx, b0, b1, b2, b3, kern;
  char *name, *name1, *name2;

  /* read CharMetrics */

  isfixed = 0;  slant = 0.0;
  do
  {
    readline() ;
    if (lincmp (FONTNAME)) printf ("%s %s\n", FONTNAME, &line[currspace+1]);
    if (lincmp (ISFIXEDPITCH) && line[currspace+1] == 't') isfixed = 1 ;
    if (lincmp (ITALICANGLE))
                     slant = tan (-0.0174532 * (double) atoi (readstr ())) ;
    if (lincmp (CAPHEIGHT)) newvar (CAPHEIGHT, atoi (readstr ()));
    if (lincmp (XHEIGHT)) newvar (XHEIGHT, atoi (readstr ()));
    if (lincmp (ASCENDER)) newvar (ASCENDER, atoi (readstr ()));
    if (lincmp (DESCENDER)) newvar (DESCENDER, atoi (readstr ()));
  }
  while (!lincmp (STARTCHARMETRICS));

  while (1)
  {
    readline () ;
    if (lincmp(ENDCHARMETRICS)) break;

    c = atoi (readstr ()) ;

    while (!eofline && strcmp("WX",readstr()) != 0) ;
    if (eofline) ERROR("the WX attribute not present, bad AFM ??");
    wx = atoi (readstr ());

    while (!eofline && strcmp("N",readstr()) != 0) ;
    if (eofline) ERROR("the N attribute not present, bad AFM ??");
    name = readstr ();

    while (!eofline && strcmp("B",readstr()) != 0) ;
    if (eofline) ERROR("the B attribute not present, bad AFM ??");
    b0 = atoi (readstr ());   b1 = atoi (readstr ());
    b2 = atoi (readstr ());   b3 = atoi (readstr ());

    newchar (c, wx, name, b0, b1, b2, b3);
  }

  /* read KernPairs */

  do
  {
    readline ();
    if (lincmp (ENDFONTMETRICS)) return ;
    if (lincmp (STARTCOMPOSITES)) break ;
  }
  while (!lincmp (STARTKERNPAIRS)) ;

  if (lincmp (STARTKERNPAIRS))
  {
    while (1)
    {
      readline () ;
      if (lincmp (ENDKERNPAIRS)) break;

      name1 = readstr () ;    name2 = readstr () ;
      if (eofline) ERROR("the kern pair not compleete, bad AFM ??") ;
      kern = atoi (readstr ());

      newkern (name1, name2, kern);
      inputkerns++;
    }

  /* read Composites */

    do
    {
      readline() ;
      if (lincmp (ENDFONTMETRICS)) return ;
    }
    while (!lincmp (STARTCOMPOSITES));
  }

  while (1)
  {
    readline () ;
    if (lincmp (ENDCOMPOSITES)) break;

    line[currspace] = ' ';  name = readstr ();

    newcom (name);
  }

  return;
}

int valvar(c)  /* returns the value of variable c */
/*-----------*/
  char *c;
{
  int i=0;  char save;  VARLIST *p;

  while ( isalnum (c[i]) || c[i]=='_') i++;
  save = c[i];  c[i] = 0;
  p = firstvar;
  while (p != NULL)
  {
    if (strcmp (c,p->name) == 0)  break;
    p = p->next;
  }
  if (p == NULL) ERROR ("Variable is not defined");
  c[i] = save;
  ind += i;
  return p->value;
}

int valF(c)  /* returns the value of function */
/*---------*/
  char *c;
{
  int i, i0, j, v;  CHARLIST *p;

  i = 2;

  while (c[i] != ',' && c[i] != ')' && c[i] != 0) i++;
  if (c[i] == 0) ERROR ("Bad syntax for function");
  j = c[i]; c[i] = 0;
  p = firstchar;
  while (p != NULL)
  {
    if (strcmp (p->chi->name, &c[2]) == 0)  break ;
    p = p->next;
  }
  if (p == NULL) ERROR ("Uknown identifier as parameter of function");
  switch (c[0])
  {
    case 'b' : if (j != ',') ERROR ("two parameters expected in b function");
	       if (!isdigit (c[i+1]))  ERROR ("Bad index in b function");
	       if (c[i+2] != ')')  ERROR ("Long index in b function");
	       c[i+2] = 0;
	       j = atoi (&c[i+1]) ;
	       if (j > 4 || j == 0)
			   ERROR ("Index must be in 1..4 in B function");
	       ind += i+3;
	       return p->chi->bb[j-1];
    case 'w' : v = p->chi->bb[2] - p->chi->bb[0];  break;
    case 'h' : v = p->chi->bb[3] - p->chi->bb[1];  break;
    case 'k' : if (j != ',') ERROR ("two parameters expected in b function");
               i++; i0 = i;
               while (c[i] != ')' && c[i] != 0) i++;
               if (c[i] == 0) ERROR ("Bad syntax for function");
               j = c[i]; c[i] = 0;
               v = searchkern (&c[2], &c[i0]);
               break;
    case 'W' : v = p->chi->wx;
  }
  if (j != ')') ERROR ("one parameter expected in function");
  ind += i+1;
  return v ;
}

int number(c)  /* evaluates the number in the simple expressinon */
/*-----------*/
  char *c;
{
  int i; double f;  char save;

  if (!isdigit(c[0]) && c[0] != '.') ERROR ("Syntax error in expression");

  i = 0;
  while (isdigit (c[i]) || c[i] == '.') i++;
  save = c[i]; c[i] = 0;
  f = atof (c);
  ind += i;
  c[i] = save;

  if (!isalpha (c[i])) return round (f);

  if ((c[i]=='b'||c[i]=='w'||c[i]=='h'||c[i]=='W'||c[i]=='k') && c[i+1] == '(')
       return round (f * valF(&c[i])) ;
  return round (f * valvar(&c[i])) ;
}

int eval (c)  /* evaluates the simple expressinon */
/*----------*/
  char *c;
{
  int v, sg;

  ind = 0;  v = 0;  sg = 1;

  if (c[0] == '-')  sg = -1 , ind++;
  if (c[0] == '+')  ind++;

  while (1)
  {
    if ((c[ind]=='b'||c[ind]=='w'||c[ind]=='h'||c[ind]=='W'||c[ind]=='k')
	    && c[ind+1] == '(') v += sg * valF(&c[ind]) ;
    else if (isalpha (c[ind]))  v += sg * valvar(&c[ind]) ;
	 else                   v += sg * number(&c[ind]) ;

    if (c[ind] == ' ' || c[ind] == 0) break ;
    switch (c[ind++])
    {
      case '+' : sg =  1; break;
      case '.' : sg =  1; ind--; break;
      case '-' : sg = -1; break;
      default : ERROR ("Syntax error in expression");
    }
  }
  return v;
}

void removespaces ()  /* shift the characters in line to remove spaces */
/*------------------*/
{
  register int i=0, j=0;

  while (1)
  {
    while (line[i] != ' ' && line[i] != '\t' && line[i] != 0) i++;
    if (line[i] == 0) return;

    j = i;
    while (line[j++] != 0) line[j-1] = line[j];
    line[j-1] = 0;
  }
}

void calculatecompose (name, num)  /* calculates new BoudingBox nad CC */
/*-------------------------------*/
  char *name;
  int num;
{
  int i, j;  CHARLIST *p, *p1, *pc;  COMLIST *pcc;
  int b0, b1, b2, b3;

  /* calculate new BoudingBoxes and recalculate CC parameters */

/*  printf ("CC input: %s, %s: %d %d, %s: %d %d\n",
    name, N[0], S[0][0], S[1][0], N[1], S[0][1], S[1][1]); */

  pc = firstchar;
  while (pc != NULL)
  {
    if (strcmp (name, pc->chi->name) == 0)
    {
      if (pc->chi->code > 0 && line[0] != '!') return ;
      if (line[0] == 'N') return ;
      break ;
    }
    pc = pc->next;
  }

  p = firstchar;
  while (p != NULL)
  {
    if (strcmp(N[0], p->chi->name) == 0) break ;
    p = p->next;
  }
  if (p == NULL) ERROR ("Undefined identifier");

  b0 = p->chi->bb[0] + S[0][0] ;  b2 = p->chi->bb[2] + S[0][0];
  b1 = p->chi->bb[1] + S[1][0] ;  b3 = p->chi->bb[3] + S[1][0];

  for (i=1; i<num; i++)
  {
     if (P[0][i] != 'P') ERROR ("Character P exepted as first");

     p1 = firstchar;
     while (p1 != NULL)
     {
       if (strcmp(N[i], p1->chi->name) == 0) break ;
       p1 = p1->next;
     }
     if (p1 == NULL) ERROR ("Undefined identifier");

     if (P[2][i] != 'C')
     {
       if (P[2][i] != 'T') ERROR ("The third premisse have to be C or T");

       S[1][i] -= p1->chi->bb[3] ;
     }
     b1 = MIN (b1, (p1->chi->bb[1] + S[1][i]));
     b3 = MAX (b3, (p1->chi->bb[3] + S[1][i]));

     if (P[1][i] != 'C')
     {
	if (P[1][i] != 'A') ERROR ("The second premisse have to be C or A");

	S[0][i] += (p->chi->wx - p1->chi->wx)/2 + (int)(slant * S[1][i]) ;
     }
     b0 = MIN (b0, (p1->chi->bb[0] + S[0][i]));
     b2 = MAX (b2, (p1->chi->bb[2] + S[0][i]));
  }

  /* write results to line[] */

  pcc = firstcom;
  while (pcc != NULL)
  {
    if (strcmp (name, pcc->name) == 0)
    {
      if (line[0] == 'N') break;
    }
    pcc = pcc->next;
  }

  sprintf (line, "CC %s %d ; PCC %s %d %d ; PCC %s %d %d ;",
    name, num,
    N[0], S[0][0], S[1][0],  N[1], S[0][1], S[1][1]) ;
  j = strlen (line);

  for (i=2; i<num; i++)
  {
    sprintf (&line[j], " PCC %s %d %d ;", N[i], S[0][i], S[1][i]);
    j = strlen (line);
  }
  currspace = j+2;
  printf ("%s\n", line);

  if (pcc == NULL)  newcom (name);
  else
  {
    pcc->lin = myalloc (strlen (line) + 1);
    strcpy (pcc->lin, line);
  }

  /* save new Bouding Box parameters */

  if (pc != NULL)
  {
    pc->chi->code = -1;  pc->chi->wx = p->chi->wx;
    pc->chi->bb[0] = b0;	pc->chi->bb[1] = b1;
    pc->chi->bb[2] = b2;	pc->chi->bb[3] = b3;
  }
  else  newchar (-1, p->chi->wx, name, b0, b1, b2, b3);

  return;
}

void reducekerns (value)  /* removes kerns with abs value less than value */
/*----------------------*/
  int value;
{
  KERNLIST *p, **p0; KERNINFO *pi, **pi0; int i=0;  char s[2];

  printf ("ReduceKerns. Abs. value: %d .", value);

  p = firstkern; p0 = &firstkern;
  while (p != NULL)
  {
    pi = p->ki;  pi0 = &(p->ki);
    while (pi != NULL)
    {
      if (abs(pi->kern) <= value)  *pi0 = pi->next, i++, free(pi) ;
      else pi0 = &(pi->next) ;

      pi = *pi0;
    }
    if (p->ki == NULL) *p0 = p->next, free(p);
    else p0 = &(p->next);

    p = *p0;
  }

  p = firstkern;
  while (p != NULL)  lastkern = p, p = p->next;

  s[1] = 0;
  if (i == 1) s[0] = 0; else s[0] = 's';
  printf (" Removed: %d kern pair%s\n", i, s);
  SUMremovekern += i;
  return ;
}


void setnewkern (name1, name2, kern)  /* creates new kern(s) */
/*----------------------------------*/
  char *name1, *name2;
  int kern;
{
  KERNLIST *p; KERNINFO *pi; 

  p = firstkern;
  while (p != NULL)
  {
    if (strcmp (p->name, name1) == 0)
    {
      pi = p->ki;
      while (pi != NULL)
      {
	if (strcmp (pi->name, name2) == 0) /* rewrite orig. kern info ? */
	{
	  if (line[0] != 'N') pi->kern = kern, sumkerns++, rewkerns++;
	  return;
	}
	if (pi->next == NULL)            /* make new kern info */
	{
	  pi = pi->next = myalloc (sizeof (KERNINFO));
	  pi->next = NULL;
	  pi->name = newpool (name2);
	  pi->kern = kern;
	  sumkerns++;
	  return;
	}
	pi = pi->next;
      }
    }
    p = p->next;
  }
  newkern (name1, name2, kern);
  sumkerns++;
  return;
}

int searchkern (name1, name2)  /* returns the kern value of pair name1 name2 */
/*---------------------------*/
  char *name1, *name2;
{
  KERNLIST *p;  KERNINFO *pi;

  p = firstkern;
  while (p != NULL)
  {
    if (strcmp (p->name, name1) == 0)
    {
      pi = p->ki;
      while (pi != NULL)
      {
	if (strcmp (pi->name, name2) == 0)  return pi->kern;
	pi = pi->next;
      }
    }
    p = p->next;
  }
  return 0;
}


void testkernline (name1, name2, name3, name4)  /* test patterns */
/*--------------------------------------------*/
  char *name1, *name2, *name3, *name4;
{
  if (name3[0] == '.') onlysmall = 1, name3[0] = '*';
  if (name4[0] == '.') onlysmall = 1, name4[0] = '*';
  if (name1[0] == '.') onlysmall = 1, name1[0] = '*';
  if (name2[0] == '.') onlysmall = 1, name2[0] = '*';

  if (name1[0] == '*' && name2[0] == '*' ||
      name3[0] != '*' && name1[0] == '*' ||
      name4[0] != '*' && name2[0] == '*' ) ERROR ("inconsistent mask");
}


void makenewkerns (name1, name2, name3, name4, expr)  /* one pattern */
/*--------------------------------------------------*/
  char *name1, *name2, *name3, *name4, *expr;
{
  KERNLIST *p, *p1; KERNINFO *pi, *pi1, *spi; int v; int i; char *np;

  if (name1[0] == '(')
  {
    i = 1; np = &name1[i];
    while (name1[i] != 0 && name1[i] != ')')
    {
      if (name1[i] == ',')
      {
	 name1[i] = 0 ;
         makenewkerns (np, name2, name3, name4, expr);
	 name1[i] = ',';
	 np = &name1[i+1];
      }
      i++;
    }
    if (name1[i] != ')') ERROR ("The ')' expected");
    name1[i] = 0 ;
    makenewkerns (np, name2, name3, name4, expr);
    name1[i] = ')' ;
    return;
  }

  if (name2[0] == '(')
  {
    i = 1; np = &name2[i];
    while (name2[i] != 0 && name2[i] != ')')
    {
      if (name2[i] == ',')
      {
	 name2[i] = 0 ;
	 makenewkerns (name1, np, name3, name4, expr);
	 name2[i] = ',';
	 np = &name2[i+1];
      }
      i++;
    }
    if (name2[i] != ')') ERROR ("The ')' expected");
    name2[i] = 0 ;
    makenewkerns (name1, np, name3, name4, expr);
    name2[i] = ')' ;
    return;
  }

  /* name1 and name2 are one symbolic name (or "*", "."), no a list */

  if (expr == NULL) v = 0;
  else v = eval (expr);

  if (name3==NULL)
  {
    setnewkern (name1, name2, v);
    return;
  }

  if (name3[0] == '*') name3 = name1;
  if (name4[0] == '*') name4 = name2;

  if (name1[0] != '*' && name2[0] != '*')
  {
    setnewkern (name1, name2, searchkern (name3, name4)+v);
    return;
  }

  /* more kerns */

  if (name2[0] == '*')
  {
    p = firstkern;
    while (p != NULL)
    {
      if (strcmp (p->name, name3) == 0)
      {
	pi = p->ki;
	while (pi != NULL)
	{
	  if (!onlysmall || islower (pi->name[0]))
            setnewkern (name1, pi->name, pi->kern+v);
	  pi = pi->next;
	}
      }
      p = p->next;
    }
    return;
  }
  p = firstkern;
  while (p != 0)
  {
    pi = p->ki;
    while (pi != NULL)
    {
      if (strcmp (pi->name, name4) == 0)
      {
	if (!onlysmall || islower (p->name[0]))
          setnewkern (p->name, name2, pi->kern+v);
	break;
      }
      pi = pi->next;
    }
    p = p->next;
  }
  return;
}

void makenews ()  /* variables, new composites, width corrs and kerns */
/*--------------*/
{
  int i, j;
  char *name, *name1, *name2, *name3, *name4, *expr, *p;
  VARLIST *pv;  CHARLIST *pc;
  KERNLIST *pk; KERNINFO *pi;
  char s[2];

  s[1]=0; SUMkern=0; SUMrewritekern=0; SUMremovekern=0;

  endfile = 1;
  do
  {
    readline ();

    if (lincmp (">>"))             /* Variable setting */
    {
      if (!eofline) line[currspace] = ' ';
      removespaces () ;
      i = 0;
      while (line[i] != '=' && line[i] != 0) i++;
      if (line[i] == 0 || i==2) ERROR ("Missing '=' or variable identifier");

      line[i] = 0;  name = newpool(&line[2]);  line[i] = '=';
      newvar (name, eval(&line[i+1]));
      continue;
    }
    if (lincmp ("NC") || lincmp ("RC") || lincmp ("!C"))   /* New composite */
    {
      name = newpool(readstr ());

      i = 0;  poolwrite = 0;
      while (1)
      {
        while (!eofline && strcmp (readstr (), ";") != 0) ;
        p = readstr ();
        if (eofline) break;
        P[0][i] = p[0], P[1][i] = p[1]; P[2][i] = p[2];
        N[i] = newpool (readstr ());
        S[0][i] = eval (readstr ());
        if (eofline) ERROR ("Uncomplete compose definition (param missing)");
        S[1][i] = eval (readstr ());
        if (eofline) ERROR ("Uncomplete compose definition (';' missing)");
        i++;
      }
      if (i < 2) ERROR ("Uncomplete composite definition");
      calculatecompose (name, i);
      poolwrite = 1;
      continue;
    }
    if (isfixed) continue;

    if (lincmp ("RWX"))  /* Width correction */
    {
      name = readstr ();
      if (eofline) ERROR ("Uncomplete WX correction line");
      pc = firstchar;
      while (pc != NULL)
      {
        if (strcmp (pc->chi->name, name) == 0)
        {
          p = readstr ();
          pc->chi->wx = eval (p) ;
          printf ("RWX %s ; WX %d\n", name, pc->chi->wx) ;
          break;
        }
        pc = pc->next;
      }
      if (pc == NULL) ERROR ("Uknown identifier");
      continue;
    }
    if (lincmp (REDUCEKERNS))      /* ReduceKerns */
    {
      reducekerns (eval (readstr ()));
      continue;
    }
    if (lincmp("NK") || lincmp("RK"))            /* Kern */
    {
      poolwrite = 0;
      if (eofline) ERROR ("Short kern definition");

      onlysmall = rewkerns = sumkerns = 0;

      name1 = readstr ();  name2 = readstr ();
      if (eofline) ERROR ("Short kern definition");

      if (name2[0] == ':')
      {
        name2 = readstr ();
        expr = readstr ();
        makenewkerns (name1, "*", name2, "*", expr);
        makenewkerns ("*", name1, "*", name2, expr);
      }
      else
      {
        expr = readstr ();

        if (expr[0] == ':')   /* Mask definition */
        {
          name3 = readstr (); name4 = readstr (); expr = readstr ();
          testkernline (name1, name2, name3, name4);
          makenewkerns (name1, name2, name3, name4, expr);
        }
        else                            /* only One new kern */
          makenewkerns (name1, name2, NULL, NULL, expr);
      }
      SUMkern += sumkerns; SUMrewritekern += rewkerns;
      if (sumkerns > 0)
      {
	for (j=0; j<currspace; j++)
	{
	  if (line[j] == 0) line[j] = ' ';
	  if (line[j] == '*' && onlysmall) line[j] = '.';
	}
	printf ("%s", line);
        if (sumkerns == 1) s[0] = 0 ; else s[0] = 's' ;
        printf (" -- %d kern pair%s added", sumkerns, s);
        if (rewkerns == 1) s[0] = 0 ; else s[0] = 's' ;
	if (rewkerns) printf (" (%d pair%s rewritten)\n", rewkerns, s) ;
        else printf ("\n") ;
      }
      poolwrite = 1;
      continue;
    }
  }
  while (!feof (input)) ;
  if (!isfixed)
  {
    printf ("Kern summary: %d added, %d rewitten, %d removed.\n",
	      SUMkern, SUMrewritekern, SUMremovekern);

    i = 0; pk = firstkern;
    while (pk != NULL)
    {
      pi = pk->ki;
      while (pi != NULL) pi = pi->next, i++;
      pk = pk->next;
    }
    printf ("              Total kerns: input %d, output %d.\n",
	    inputkerns, i);
  }
  return;
}

void writeafm ()  /* writes the output */
/*--------------*/
{

  CHARLIST *p; KERNLIST *pk; KERNINFO *pi; COMLIST *c;
  int i;

  /* Write header */

  rawreadline (); line[currspace] = ' ';
  fprintf(output, "%s\nComment %s\n", line, COMMENT);

  while (1)
  {
    rawreadline ();
    if (lincmp (STARTCHARMETRICS)) break ;
    line[currspace] = ' ';
    fprintf (output, "%s\n", line);
  }

  /* Write CharMetrics */

  i = 0;  p = firstchar;
  while (p != NULL) p = p->next, i++;

  fprintf (output, "%s %d\n", STARTCHARMETRICS, i);

  p = firstchar;
  while (p != NULL)
  {
     fprintf (output, "C %d ; WX %d ; N %s ; B %d %d %d %d ;",
	p->chi->code,
	p->chi->wx,
	p->chi->name,
	p->chi->bb[0],
	p->chi->bb[1],
	p->chi->bb[2],
	p->chi->bb[3]) ;

     if (p->chi->rest == NULL) fprintf (output, "\n") ;
     else  fprintf (output, " %s\n", p->chi->rest) ;

     p = p->next;
  }
  while (!lincmp (ENDCHARMETRICS)) rawreadline ();
  fprintf (output, "%s\n", line);

  /* Write KernPairs */

  while (1)
  {
    if (rawreadline ()) return;
    if (lincmp (ENDFONTMETRICS)) goto SKIP ;
    if (lincmp (STARTKERNPAIRS)) break ;
    if (lincmp (STARTCOMPOSITES)) goto SKIP ;
    line[currspace] = ' ';
    fprintf (output, "%s\n", line);
  }

  i = 0; pk = firstkern;
  while (pk != NULL)
  {
    pi = pk->ki;
    while (pi != NULL) pi = pi->next, i++;
    pk = pk->next;
  }
  fprintf (output, "%s %d\n\n", STARTKERNPAIRS, i);

  pk = firstkern;
  while (pk != NULL)
  {
    pi = pk->ki;
    while (pi != NULL)
    {
      fprintf (output, "KPX %s %s %d\n", pk->name, pi->name, pi->kern);
      pi = pi->next;
    }
    fprintf (output, "\n");
    pk = pk->next;
  }

  while (!lincmp (ENDKERNPAIRS)) rawreadline ();
  fprintf (output, "%s\n", line);

  /* Write Composites */

  while (1)
  {
    if (rawreadline ()) return;
    if (lincmp (ENDFONTMETRICS)) break ;
    if (lincmp (STARTCOMPOSITES)) break ;
    line[currspace] = ' ';
    fprintf (output, "%s\n", line);
  }

  SKIP:
  i = 0; c = firstcom;
  while (c != NULL) c = c->next, i++;
  fprintf (output, "%s %d\n", STARTCOMPOSITES, i);

  c = firstcom;
  while (c != NULL)  fprintf (output, "%s\n", c->lin), c = c->next;

  fprintf (output, "%s\n", ENDCOMPOSITES);
  if (lincmp (STARTCOMPOSITES))
  {
     while (!lincmp (ENDCOMPOSITES)) rawreadline ();
     while (rawreadline() == 0)
     {
       line[currspace] = ' ';
       fprintf (output, "%s\n", line);
     }
  }
  else fprintf (output, "%s\n", ENDFONTMETRICS);
  return;
}

int main (argc, argv)  /* main program */
/*-------------------*/
  int argc;
  char *argv[];
{
  lastpool = 0;
  firstchar = lastchar = NULL;
  firstkern = lastkern = NULL;
  firstcom = lastcom = NULL;
  firstvar = lastvar = NULL;
  poolwrite = 1;

  printf ("Program a2ac, ver.1, Copyright (C) 1994,1995, Petr Olsak\n");

  if (argc != 4)
    return printf ("usage: a2ac input.afm corr.tab output.afm\n"), 2 ;

  afminput = fopen (argv[1], "r");
  if (afminput == NULL) ERROR ("can't open afm input file");
  corrfile = fopen (argv[2], "r");
  if (corrfile == NULL) ERROR ("can't open corr file");

  input = afminput;  readafm () ;
  input = corrfile;  makenews () ;

  input = afminput;  rewind (input);
  output   = fopen (argv[3], "w");
  if (output == NULL) ERROR ("can't write to output file");

  writeafm () ;
  return 0;
}
