/*
 * chdrgen.c
 *
 * Generate csapph headers
 *
 *  1995-1998 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This program 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.
 *
 * This program 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 this program.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

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


#if defined(__riscos)
  #include "kernel.h"
  #define lasterr (_kernel_last_oserror()->errmess)
#else
  #define lasterr (strerror(errno))
#endif

static char buf[256];

static int xtoi(const char *p)
{
  int base=10;
  int flags=0;
  int a=0;
  unsigned t;
  char ch;
  int b10=0;

  for (;;)
  {
    ch=*p++;
    switch (ch)
    {
      case '&':
      case '%':
        if (flags & 0x3f) goto end;
        if (ch=='&') base=16; else base=2;
        flags|=(1<<5);
        break;

      case '_':
        if ((flags & 0x30) || !(flags & (1<<2))) goto end;
        base=b10;
        flags=(flags & ~0xA) | (1<<5);
        a=0;
        break;

      case '-':
      case '+':
        if (flags & 0x7f) goto end;
        flags|=(1<<6);
        if (ch=='-') flags|=(1<<7);
        break;

      default:
        t=ch-'A';
        if (t>=26) t=ch-'a';
        if (t<26) flags|=(1<<0);
        else
        {
          t=ch-'0';
          if (t>=10) goto end;
        }
        if (t<10)
        {
          b10=(b10*10)+t;
          flags|=(1<<2);
        }
        else
          flags|=(1<<4);
        if (!(flags&(1<<3)))
        {
          if (t<base)
          {
            a=(a*base)+t;
            flags|=(1<<1);
          }
          else
            flags|=(1<<3);
        }
        break;
    }
  }

end:
  if (flags & (1<<7)) a=-a;
  return (a);
}

static void dogen(FILE *in,FILE *out,char *inn,char *outn)
{
  char *p,*q,*r,*s;
  int ch;
  time_t t;
  unsigned int var = 0;
  int macro=0;
  int type=0;
  int i;
  char *footer="";
  int flags=2;

  time(&t);
  strftime(buf,256,"%d %B %Y",localtime(&t));

  p=outn; q=p;
  while (*p)
  {
    if (*p=='.') q=p+1;
    p++;
  }

  p=inn; r=p;
  while (*p)
  {
    if (*p=='.') r=p+1;
    p++;
  }

  fprintf(out,
          "/*\n"
          " * %s.h\n"
          " *\n"
          " * [Generated from %s, %s]\n"
          " */\n"
          "\n"
          "#if !defined(__CC_NORCROFT) || !defined(__arm)\n"
          "#  error You must use the Norcroft ARM Compiler for Sapphire "
          				"programs\n"
          "endif\n"
          "\n"
          "#pragma include_only_once\n"
          "#pragma force_top_level\n"
          "\n"
          "#ifndef __%s_h\n"
          "#define __%s_h\n"
          "\n"
          "#ifndef __sapphire_h\n"
          "#  include \"sapphire.h\"\n"
          "#endif\n"
          "\n",
          q,r,buf,q,q);

  do
  {
    p=buf;
    while (ch=getc(in),ch!=EOF && ch!='\n')
      *p++=ch;
    *p++=0;
    p=buf;
    while (!isspace(*p) && *p)
      p++;
    s=p;
    while (isspace(*p) && *p)
      p++;
    q=p;
    while (!isspace(*q) && *q)
      q++;
    r=q;
    while (isspace(*r) && *r)
      r++;
    if (*buf!=';') *q=*s=0;

    if (!strcmp(p,"MACRO"))
      macro=1;
    else if (!strcmp(p,"MEND"))
      macro=0;
    else if (macro)
      /* ... */;
    else if (*buf==';')
    {
      if (flags & 2)
        continue;
      else if (type!=4)
        { fputs(footer,out); type=4; footer=" */\n\n"; flags=0; }
      else
        flags=1;
      switch (buf[1])
      {
        case '-':
          fputs("/*",out);
          i=3;
          p=buf+1;
          while (*p=='-') { putc(*p++,out); i++; }
          while (*p!='-') { putc(*p++,out); i++; }
          while (i<76) { putc('-',out); i++; }
          ch=getc(in);
          fputs(ch==';' ? "*\n" : "*/\n",out);
          ungetc(ch,in);
          if (ch!=';') footer="\n";
          break;
        case '+':
          /* A BAS directive.  Yawn. */
          type=0; footer="";
          break;
        case ' ':
          if (buf[2]=='-')
          {
            fputs(flags & 1 ? " *" : "/*",out);
            fputs(buf+1,out);
            ch=getc(in);
            fputs(ch==';' ? " *\n" : " */\n",out);
            ungetc(ch,in);
            if (ch!=';') footer="\n";
            break;
          }
        default:
          fputs(flags & 1 ? " *" : "/*",out);
          fputs(buf+1,out);
          putc('\n',out);
          break;
      }
    }
    else if (r==buf)
    {
      if (type!=0) { fputs(footer,out); type=0; footer=""; }
      flags=0;
    }
    else if (!strcmp(p,"IMPORT"))
    {
      if (type!=1) { fputs(footer,out); type=1; footer="\n"; }
      fprintf(out,"extern routine %s;\n",r);
    }
    else if (!strcmp(p,"EQU"))
    {
      if (type!=2) { fputs(footer,out); type=2; footer="\n"; }
      fprintf(out,"#define %s (",buf);
      i=0;
      while (*r)
      {
        switch (*r)
        {
          case '&': while (i--) putc(' ',out); fputs("0x",out); i=0; break;
          case ' ': case 9: i++; break;
          case ';': goto end;
          default:  while (i--) putc(' ',out); putc(*r,out); i=0; break;
        }
        r++;
      }
    end:
      fputs(")\n",out);
    }
    else if (!strcmp(p,"^"))
    {
      var=xtoi(r);
      type=0;
    }
    else if (!strcmp(p,"#"))
    {
      if (type!=3) { fputs(footer,out); type=3; footer="\n"; }
      if (*buf) fprintf(out,"#define %s %i\n",buf,var);
      var+=xtoi(r);
    }
  }
  while (!feof(in));

  if (type!=0) putc('\n',out);
  fputs("#endif\n",out);
}

int main(int argc,char *argv[])
{
  FILE *in,*out;
  if (argc!=3)
  {
    fprintf(stderr,"Usage: chdrgen <asm-header> <c-header>\n");
    exit (1);
  }
  if (in=fopen(argv[1],"r"),!in) goto tidy_0;
  if (out=fopen(argv[2],"w"),!out) goto tidy_1;
  dogen(in,out,argv[1],argv[2]);
  fclose(out);
  fclose(in);
  return (0);

tidy_1:
  fclose(in);
tidy_0:
  fprintf(stderr,"%s\n",lasterr);
  exit(1);
}
