/*
 * readdef.c
 *
 * Read DLL Binder definition files
 *
 *  1994-1998 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamic Linking System (SDLS)
 *
 * SDLS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * SDLS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SDLS.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

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

#include "hashtable.h"
#include "error.h"
#include "readdef.h"
#include "swiv.h"
#include "swis.h"

#define CHUNKSIZE 40

static char *readdef_token=0;

static void addChar(char **buffer,int *length,int *size,char c)
{
  char *p;
  if (*length==*size)
  {
    if (p=realloc(*buffer,*size+CHUNKSIZE),!p)
      error(NOMEM);
    *buffer=p;
    (*size)+=CHUNKSIZE;
  }
  (*buffer)[(*length)++]=c;
}

#define add(c) addChar(&readdef_token,&len,&size,(c))

int cistrcmp(const char *s1,const char *s2)
{
  char c1;
  char c2;
  while (*s1 || *s2)
  {
    c1=tolower(*(s1++));
    c2=tolower(*(s2++));
    if (c1!=c2)
      return (c1-c2);
  }
  return (0);
}

enum
{
  _xxx,
  _IDENT,
  _STRING,
  _BRA,
  _KET,
  _EQUAL,
  _CONST,
  _EOF
};

static char readdef__ungot[2];
static int readdef__ungotc=0;

#define GET(fp) (readdef__ungotc ? \
                  readdef__ungot[--readdef__ungotc] : \
                  getc(fp))

#define UNGET(c) ((void)((c)==EOF ? \
                   EOF : \
                   (readdef__ungot[readdef__ungotc++]=(c))))

static int readdef__getToken(FILE *fp)
{
  int ch;
  int type=_xxx;
  int qu;
  int len=0;
  int size=0;

  if (readdef_token)
    free(readdef_token);
  readdef_token=0;

  while (!type)
  {
    /* --- Skip whitespace --- */
    while (isspace(ch=GET(fp)))
      /* blank */;

    switch (ch)
    {
      case ';':
      case '|':
      case '#':
        while (ch=GET(fp),ch!='\n' && ch!=EOF)
          /* blank */;
        break;
      case '/':
        switch (ch=GET(fp))
        {
          case '*':
            while (ch=GET(fp),ch!='*' ||
                              (ch=GET(fp),UNGET(ch),ch!='/'))
              /* blank */;
            (void)GET(fp);
            break;
          case '/':
            while (ch=GET(fp),ch!='\n' && ch!=EOF)
              /* blank */;
            break;
          default:
            UNGET(ch);
            add('/');
            break;
        }
        break;
      case '{':
        type=_BRA;
        break;
      case '}':
        type=_KET;
        break;
      case '=':
        type=_EQUAL;
        break;
      case EOF:
        type=_EOF;
        break;
      case '\'':
      case '\"':
        qu=ch;
        while (ch=GET(fp),1)
        {
          if (ch==qu)
          {
            ch=GET(fp);
            if (ch!=qu)
            {
              UNGET(ch);
              break;
            }
          }
          if (ch==EOF)
          {
            error(QBFEOF,qu);
            break;
          }
          else if (ch=='\n')
          {
            error(QBFNL,qu);
            break;
          }
          add(ch);
        }
        add(0);
        type=_STRING;
        break;
      default:
        add(ch);
        while (ch=GET(fp),ch!=EOF && !isspace(ch))
          add(ch);
        add(0);
        UNGET(ch);
        type=_IDENT;
        break;
    }
  }
  return (type);
}

static char *readdef__tokAsString(int type)
{
  switch (type)
  {
    case _EOF:
      return ("<EOF>");
    case _BRA:
      return ("{");
    case _KET:
      return ("}");
    case _EQUAL:
      return ("=");
    case _CONST:
      return ("<integer>");
    case _STRING:
      return ("<string literal>");
    case _IDENT:
      return (readdef_token);
  }
  return (0);
}

static int readdef__getOrderedTable(FILE *fp,hashtable h)
{
  int done=0;
  int tok;
  char *name;
  unsigned int ord;

  switch (tok=readdef__getToken(fp))
  {
    case _BRA:
      break;
    default:
      error(JUNKNOBRA,readdef__tokAsString(tok));
      return (1);
      break;
  }

  tok=readdef__getToken(fp);
  while (!done)
  {
    switch (tok)
    {
      case _IDENT:
        name=readdef_token;
        readdef_token=0;
        tok=readdef__getToken(fp);
        ord=0xFFFFFFFF;
        if (tok==_EQUAL)
        {
          tok=readdef__getToken(fp);
          if (tok==_IDENT)
          {
            sscanf(readdef_token,"%i",&ord);
            tok=readdef__getToken(fp);
          }
        }
        if (!hash_addWithOrd(h,name,ord))
        {
          error(NOMEM);
          return (1);
        }
        free(name);
        break;
      case _KET:
        done=1;
        break;
      default:
        error(JUNKNOKET,readdef__tokAsString(tok));
        return (1);
    }
  }
  return (0);
}

static int readdef__getTable(FILE *fp,hashtable h)
{
  int done=0;
  int tok;

  switch (tok=readdef__getToken(fp))
  {
    case _BRA:
      break;
    default:
      error(JUNKNOBRA,readdef__tokAsString(tok));
      return (1);
      break;
  }

  while (!done)
  {
    switch (tok=readdef__getToken(fp))
    {
      case _IDENT:
        if (!hash_add(h,readdef_token))
        {
          error(NOMEM);
          return (1);
        }
        break;
      case _KET:
        done=1;
        break;
      default:
        error(JUNKNOKET,readdef__tokAsString(tok));
        return (1);
    }
  }
  return (0);
}

static int readdef__getVersion(FILE *fp,int *v)
{
  int tok;
  int minor;
  int major;
  char *dot;

  switch (tok=readdef__getToken(fp))
  {
    case _IDENT:
      dot=strchr(readdef_token,'.');
      if (!dot)
      {
        sscanf(readdef_token,"%i",&major);
        *v=major*100;
      }
      else
      {
        *dot=0;
        major=atoi(readdef_token);
        switch (strlen(dot+1))
        {
          case 0:
            *v=major*100;
            break;
          case 1:
            minor=atoi(dot+1);
            *v=major*100+minor*10;
            break;
          case 2:
            minor=atoi(dot+1);
            *v=major*100+minor;
            break;
          default:
            error(BADVER,readdef__tokAsString(tok));
            return (1);
        }
      }
      break;
    default:
      error(JUNKNOVER,readdef__tokAsString(tok));
      return (1);
  }
  return (0);
}

static int readdef__getName(FILE *fp,char *name)
{
  int tok;

  switch (tok=readdef__getToken(fp))
  {
    case _IDENT:
    case _STRING:
      strcpy(name,readdef_token);
      break;
    default:
      error(JUNKNONAME,readdef__tokAsString(tok));
      return (1);
  }
  return (0);
}

static int readdef__getCopyright(FILE *fp,char *name)
{
  int tok;
  char timeBuff[5];

  switch (tok=readdef__getToken(fp))
  {
    case _STRING:
      timeBuff[0]=3;
      _swi(OS_Word,_inr(0,1),14,timeBuff);
      _swi(OS_ConvertDateAndTime,_inr(0,3),timeBuff,name,256,readdef_token);
      break;
    default:
      error(JUNKNOCRIGHT,readdef__tokAsString(tok));
      return (1);
  }
  return (0);
}

static int readdef__getItem(FILE *fp,readdef_info *info)
{
  int tok;

  switch (tok=readdef__getToken(fp))
  {
    case _EOF:
      return (-1);
      break;
    case _IDENT:
      break;
    default:
      error(JUNKNOITEM,readdef__tokAsString(tok));
      return (1);
  }

  if (!cistrcmp(readdef_token,"exports"))
    return (readdef__getOrderedTable(fp,info->sym));
  else if (!cistrcmp(readdef_token,"extentry"))
    return (readdef__getTable(fp,info->vsym));
  else if (!cistrcmp(readdef_token,"objects"))
    return (readdef__getTable(fp,info->obj));
  else if (!cistrcmp(readdef_token,"version"))
    return (readdef__getVersion(fp,&info->version));
  else if (!cistrcmp(readdef_token,"name"))
    return (readdef__getName(fp,info->name));
  else if (!cistrcmp(readdef_token,"author"))
    return (readdef__getCopyright(fp,info->copyright));
  else if (!cistrcmp(readdef_token,"NonAPCS"))
  {
    info->flags|=rdFlag_shortEntry;
    return (0);
  }
  else if (!cistrcmp(readdef_token,"OmitNames"))
  {
    info->flags|=rdFlag_noNames;
    return (0);
  }
  else
  {
    error(BADITEM,readdef_token);
    return (1);
  }
}

int readdef(char *file,readdef_info *info)
{
  FILE *fp=fopen(file,"r");

  if (!fp)
  {
    error(NOOPENIN,file);
    return(1);
  }

  info->version=-1;
  *info->name=0;
  *info->copyright=0;
  info->errored=0;
  info->flags=0;

  while (readdef__getItem(fp,info)!=-1)
    /* blank */;
  fclose(fp);

  return (0);
}
