/*  $Header

    subfont.c: an implementation of the subfont feature for dviout
               by Jin-Hwan Cho <chofchof@ktug.or.kr>
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>

#ifdef  UNIX
#include <unistd.h>
#else
#include <io.h>
#ifdef	MSVC
#include "msvcdir.h"
#else
#include <dir.h>
#endif
#endif

#ifdef USE_KPATHSEA
#include <kpathsea/progname.h>
#include <kpathsea/tex-file.h>
#else
int kpse_find(char *, char *, char *);
extern char *new_subfont_path;
#endif

#include "err.h"

#define NEW(n,type) (type *)(malloc(((size_t)(n))*sizeof(type)))
#define RENEW(p,n,type) (type *)(realloc((p),(n)*sizeof(type)))
#define RELEASE(p) free((p))

#define WORK_BUFFER_SIZE 1024
static char work_buffer[WORK_BUFFER_SIZE];
char *subfont_path;
char *new_subfont_path;

int current_sfd_id = -1;

struct a_sfd_record {
  char *sfd_name, *sub_name;
  unsigned int vector[256];
} *sfd_record = NULL;

#define	MAX_SFD_PATH  32
static struct a_sfd_file {
	char *sfd_name;
	char *path;	
} sfd_path[MAX_SFD_PATH];

static int num_sfd_records = 0, max_sfd_records = 0;
static char *start, *end, *p;
static FILE *sfd_file;

char *get_sfd_path(int num)
{
  return (num >= 0 && num < MAX_SFD_PATH)?sfd_path[num].path:NULL;
}

static void skip_white(char **start, char *end)
{
  while (*start < end && (isspace(**start) || **start == '%')) {
    if (**start == '%') { /* skip line */
      while (*start < end && **start != '\n' && **start != '\r')
        (*start)++;
      if (*start < end && **start == '\r')
        (*start)++;
      if (*start < end && **start == '\n')
        (*start)++;
    } else /* Skip the white char */
      (*start)++;
  }
}

static char *read_a_sfd_record(unsigned int *vector)
{
  int i;
  long lbegin, lend, offset;

  for (i = 0; i < 256; i++) vector[i] = 0;

  offset = 0;
  skip_white(&start, end);
  do {
    lbegin = strtol(start, &p, 0);
    if (start == p || lbegin < 0 || lbegin > 0xFFFF)
      return "Invalid subfont range (begin)";
    if (*p == ':') {
      if ((offset = lbegin) > 0xFF)
        return "Invalid subfont offset";
      start = ++p;
      skip_white(&start, end);
      continue;
    } else if (*p == '_') {
      start = ++p;
      if (!isdigit(*start))
        return "Invalid subfont range entry";
      lend = strtol(start, &p, 0);
      if (start == p || lend < 0 || lend > 0xFFFF)
        return "Invalid subfont range (end)";
      if (*p && !isspace(*p))
        return "Invalid subfont range entry";
      if (lend < lbegin)
        return "End of subfont range too small";
      if (offset + (lend - lbegin) > 255)
        return "Subfont range too large for current offset";
    } else if (isspace(*p) || !*p)
      lend = lbegin;

    for (; lbegin <= lend; lbegin++) {
      if (vector[offset] != 0)
        return "Overlapping subfont ranges";
      vector[offset++] = lbegin;
    }

    start = p;
    skip_white(&start, end);

    if (*start == '\\') {
      if ((start = fgets(work_buffer, WORK_BUFFER_SIZE, sfd_file)) == NULL)
        break;
      end = work_buffer + strlen(work_buffer);
      skip_white(&start, end);
    }
  } while (start < end);
  return NULL;
}

/* Make sure that sfd_name does not have the extension '.sfd'
   and it should end with the character '@' here. */
int load_sfd_record (char *sfd_name, char *sub_name)
{
  int len, result, pos;
  char *full_filename, *msg;

  if ((len = strlen(sfd_name)) < 2)
    return (current_sfd_id = -1);

  for (result = 0; result < num_sfd_records; result++)
    if (!strcmp(sfd_record[result].sfd_name, sfd_name) &&
      !strcmp(sfd_record[result].sub_name, sub_name))
      return (current_sfd_id = result);

  strcpy(work_buffer, sfd_name);
  work_buffer[len-1] = 0;
  strcat(work_buffer, ".sfd");

  for(pos = 0; sfd_path[pos].sfd_name && pos < MAX_SFD_PATH; pos++){
    if(!strcmp(sfd_path[pos].sfd_name, sfd_name)){
      full_filename = sfd_path[pos].path;
      pos = MAX_SFD_PATH;
      goto find;
     }
  }
#ifdef USE_KPATHSEA
  /* Change the program name temporarily to find SFD files from
   * the texmf/ttf2pk directory (contributed by Akira Kakuto). */
  /* Actually kpse_reset_program_name("ttf2pk"); do not needed
   * because kpse_program_text_format is needed only for ttf2pk. */
  full_filename = kpse_find_file(work_buffer, kpse_program_text_format, 1);
#else
  full_filename = kpse_find(work_buffer, new_subfont_path, work_buffer)?
  	work_buffer:NULL;
#endif
  if (full_filename == NULL)
    error(FILE_FAULT, "Could not fetch the filename for %s\n", work_buffer);
find:
  if ((sfd_file = fopen(full_filename, "r")) == NULL)
    error(FILE_FAULT, "Could not open %s\n", full_filename);
  result = -1;
  if(pos < MAX_SFD_PATH){
    sfd_path[pos].path = NEW (strlen(full_filename)+1, char);
    strcpy(sfd_path[pos].path, full_filename);
  }

  while ((start = fgets(work_buffer, WORK_BUFFER_SIZE, sfd_file)) != NULL) {
    end = work_buffer + strlen(work_buffer);
    skip_white(&start, end);
    if (start >= end) continue;
    if (*start == '#') continue;

    p = start;
    while (*start && !isspace(*start)) start++; /* read sub_name */
    *(start++) = '\0';
    if (!strcmp(p, sub_name)) { /* found an entry matched with sub_name */
      if (num_sfd_records >= max_sfd_records) {
        max_sfd_records += 5;
        sfd_record = RENEW (sfd_record, max_sfd_records, struct a_sfd_record);
      }
      sfd_record[num_sfd_records].sfd_name = NEW (strlen(sfd_name)+1, char);
      strcpy(sfd_record[num_sfd_records].sfd_name, sfd_name);
      sfd_record[num_sfd_records].sub_name = NEW (strlen(sub_name)+1, char);
      strcpy(sfd_record[num_sfd_records].sub_name, sub_name);
      if((msg = read_a_sfd_record(sfd_record[num_sfd_records].vector)) != NULL)
        error(PROGRAM_STOP, "%s.\nsubfont:%s(%s)", msg, full_filename, sub_name);
      if(pos < MAX_SFD_PATH)
        sfd_path[pos].sfd_name = sfd_record[num_sfd_records].sfd_name;
      result = num_sfd_records++;
      break;
    }
  }

  fclose(sfd_file);
  return (current_sfd_id = result);
}

unsigned int lookup_sfd_record(int subfont_id, unsigned char s)
{
  if (sfd_record && subfont_id >= 0 && subfont_id < num_sfd_records)
    return (sfd_record[subfont_id].vector)[s];
  else {
    error(PROGRAM_STOP, "lookup_sfd_record: not defined.\n"
      "subfont:%s.sfd(%d)[%d]", sfd_record[subfont_id].sfd_name, 
      sfd_record[subfont_id].sub_name, s);
    return 0;
  }
}

void release_sfd_record(void)
{
  int i;

  if (sfd_record) {
    for (i = 0; i < num_sfd_records; i++) {
      RELEASE (sfd_record[i].sfd_name);
      RELEASE (sfd_record[i].sub_name);
    }
    RELEASE (sfd_record);
  }
  num_sfd_records = max_sfd_records = 0;
  sfd_record = NULL;
  for(i = 0; i < MAX_SFD_PATH; i++) {
	if(!sfd_path[i].path)
	  break;
	RELEASE (sfd_path[i].path);
	sfd_path[i].path = sfd_path[i].sfd_name = NULL;
  }
}

int sfd_count(int mode)
{
  int i;

  switch(mode){
    case 1:
      return max_sfd_records;
    case 2:
      for(i = 0; sfd_path[i].path && i < MAX_SFD_PATH; i++);
      return i;
  }
  return 0;
}
