/*
 *   parse.c
 *
 *   This file is part of the ttf2pk package.
 *
 *   Copyright 1997-1998 by
 *     Frederic Loyer <loyer@ensta.fr>
 *     Werner Lemberg <wl@gnu.org>
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>         /* for size_t */
#include <ctype.h>
#include <string.h>

#include "ttf2tfm.h"
#include "errormsg.h"
#include "newobj.h"
#include "ligkern.h"
#include "texenc.h"
#include "parse.h"
#include "filesrch.h"


/*
 *   Here we get a token from the encoding 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.
 *
 *   `bufferp' is a pointer to the current line; the offset of the beginning
 *   of the token to be parsed relative to `bufferp' will be returned in
 *   `offsetp'.  On the first call of gettoken() `init' must be set to 1 and
 *   to 0 on the following calls.
 *
 *   If `ignoreligkern' is `True', no LIGKERN data will be extracted from the
 *   encoding file.
 *
 *   Don't modify `bufferp'!
 *
 *   The memory management of `bufferp' will be done by gettoken() itself;
 *   nevertheless, it returns a pointer to the current token which should be
 *   freed after it has been used.
 */

static char *
gettoken(char **bufferp, size_t *offsetp, FILE *f, Font *fnt,
         Boolean ignoreligkern, Boolean init)
{
  char *p, *q;
  char tempchar;
  static char *curp;


  if (init)
    curp = NULL;

  while (1)
  {
    while (curp == NULL || *curp == '\0')
    {
      if (*bufferp)
        free(*bufferp);

      if (getline(bufferp, f) == False)
        oops("Premature end in encoding file.");

      curp = *bufferp;

      for (p = *bufferp; *p; p++)
        if (*p == '%')
        {
          if (ignoreligkern == False)
            checkligkern(p, fnt);
          *p = '\0';
          break;
        }
    }

    while (isspace(*curp))
      curp++;

    *offsetp = curp - *bufferp;

    if (*curp)
    {
      if (*curp == '[' || *curp == ']' ||
          *curp == '{' || *curp == '}')
        q = curp++;
      else if (*curp == '/' ||
               *curp == '-' || *curp == '_' || *curp == '.' ||
               ('0' <= *curp && *curp <= '9') ||
               ('a' <= *curp && *curp <= 'z') ||
               ('A' <= *curp && *curp <= 'Z'))
      {
        q = curp++;
        while (*curp == '-' || *curp == '_' || *curp == '.' ||
               ('0' <= *curp && *curp <= '9') ||
               ('a' <= *curp && *curp <= 'z') ||
               ('A' <= *curp && *curp <= 'Z'))
          curp++;
      }
      else
        q = curp;

      tempchar = *curp;
      *curp = '\0';
      p = newstring(q);
      *curp = tempchar;
      return p;
    }
  }
}


/*
 *   This routine reads in an encoding file, given the name.  It returns
 *   the final total structure.  It performs a number of consistency checks.
 */

encoding *
readencoding(char **enc, Font *fnt, Boolean ignoreligkern)
{
  char *real_encname;
  FILE *enc_file;
  char *p, *q, c;
  char *buffer;
  char numbuf[9];
  size_t offset;
  int i;
  long l;
  encoding *e = (encoding *)mymalloc(sizeof (encoding));


  if (enc && *enc)
  {
    real_encname = TeX_search_encoding_file(enc);
    if (!real_encname)
      oops("Cannot find encoding file `%s'.", *enc);

    enc_file = fopen(real_encname, "rt");
    if (enc_file == NULL)
      oops("Cannot open encoding file `%s'.", real_encname);

    buffer = NULL;
    p = gettoken(&buffer, &offset, enc_file, fnt, ignoreligkern, True);
    if (*p != '/' || p[1] == '\0')
      boops(buffer, offset,
        "First token in encoding must be literal encoding name.");
    e->name = newstring(p + 1);
    free(p);

    p = gettoken(&buffer, &offset, enc_file, fnt, ignoreligkern, False);
    if (strcmp(p, "["))
      boops(buffer, offset,
        "Second token in encoding must be mark ([) token.");
    free(p);

    for (i = 0; i < 256; i++)
    {
      p = gettoken(&buffer, &offset, enc_file, fnt, ignoreligkern, False);
      if (*p != '/' || p[1] == 0)
        boops(buffer, offset,
          "Tokens 3 to 257 in encoding must be literal names.");

      /* now we test for a generic code point resp. glyph index value */

      c = p[2];
      if (p[1] == '.' && (c == 'c' || c == 'g') && '0' <= p[3] && p[3] <= '9')
      {
        l = strtol(p + 3, &q, 0);
        if (*q != '\0' || l < 0 || l > 0xFFFF)
          boops(buffer, offset, "Invalid encoding token.");
        sprintf(numbuf, ".%c0x%x", c, (unsigned int)l);
        e->vec[i] = newstring(numbuf);
      }
      else
        e->vec[i] = newstring(p + 1);

      free(p);
    }

    p = gettoken(&buffer, &offset, enc_file, fnt, ignoreligkern, False);
    if (strcmp(p, "]"))
      boops(buffer, offset,
        "Token 258 in encoding must be make-array (]).");
    free(p);

    while (getline(&buffer, enc_file))
    {
      for (p = buffer; *p; p++)
        if (*p == '%')
        {
          if (ignoreligkern == False)
            checkligkern(p, fnt);
          *p = '\0';
          break;
        }
    }

    fclose(enc_file);

    if (ignoreligkern == False && fnt->sawligkern == False)
      getligkerndefaults(fnt);
  }
  else
  {
    if (ignoreligkern == False)
    {
      e = &staticencoding;
      getligkerndefaults(fnt);
    }
    else
      e = NULL;
  }

  return e;
}


/*
 *   We scan a glyph replacement file.
 *   `%' is the comment character.
 */

void
get_replacements(Font *fnt)
{
  char *real_replacement_name;
  FILE *replacement_file;
  char *buffer = NULL, *oldbuffer = NULL;
  char *p;
  char *old_name, *new_name;
  stringlist *sl;


  if (!fnt->replacementname)
    return;

  real_replacement_name = TeX_search_replacement_file(&fnt->replacementname);
  if (!real_replacement_name)
    oops("Cannot find replacement file `%s'.", fnt->replacementname);

  replacement_file = fopen(real_replacement_name, "rt");
  if (replacement_file == NULL)
    oops("Cannot open replacement file `%s'.", real_replacement_name);

  while (getline(&buffer, replacement_file))
  {
    for (p = buffer; *p; p++)
      if (*p == '%')
      {
        *p = '\0';
        break;
      }

    if (oldbuffer)
      free(oldbuffer);
    oldbuffer = newstring(buffer);

    p = buffer;

    while (isspace(*p))
      p++;
    if (!*p)
      continue;

    old_name = p;

    while (*p && !isspace(*p))
      p++;
    if (*p)
      *p++ = '\0';      

    while (*p && isspace(*p))
      p++;
    if (!*p)
      boops(oldbuffer, old_name - oldbuffer, "Replacement glyph missing.");

    new_name = p;

    while (*p && !isspace(*p))
      p++;
    if (*p)
      *p++ = '\0';      

    while (*p && isspace(*p))
      p++;
    if (*p)
      boops(oldbuffer, p - oldbuffer, "Invalid replacement syntax.");

    sl = newstringlist();
    sl->new_name = newstring(new_name);
    sl->old_name = newstring(old_name);
    sl->next = fnt->replacements;
    fnt->replacements = sl;
  }

  fclose(replacement_file);
}


/* end */
