/* rescomp.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Compiler fuer Message-Dateien                                             */
/*                                                                           */
/*    17. 5.1998  Symbol gegen Mehrfachinklusion eingebaut                   */
/*     5. 7.1998  zusaetzliche Sonderzeichen                                 */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>
#include <ctype.h>

#include "be_le.h"
#include "strutil.h"
#include "bpemu.h"

/*****************************************************************************/

typedef struct _TMsgList
{
  struct _TMsgList *Next;
  LongInt Position;
  char *Contents;
} TMsgList,*PMsgList;

typedef struct
{
  PMsgList Messages,LastMessage;
  char *CtryName;
  LongInt *CtryCodes,CtryCodeCnt;
  LongInt FilePos,HeadLength,TotLength;
} TMsgCat,*PMsgCat;

typedef struct
{
  const char *AbbString,*Character;
} TransRec;

#define IO_RETCODE_SRC 2
#define IO_RETCODE_RSC 3
#define IO_RETCODE_MSG 4
#define IO_RETCODE_MSH 5

/*****************************************************************************/

#ifdef __TURBOC__
unsigned _stklen = 16384;
#endif

static const char *IdentString = "AS Message Catalog - not readable\n\032\004";

static LongInt MsgCounter;
static PMsgCat MsgCats;
static LongInt CatCount, DefCat;
static const char *p_msg_file_name = NULL, *p_msh_file_name = NULL;
static FILE *p_src_file, *p_msg_file, *p_rsc_file, *p_msh_file;
static size_t bytes_written;

static TransRec TransRecs[] =
{
  { "\\n"     , "\n"      },
  { NULL      , NULL      }
};

/*****************************************************************************/

typedef struct _TIncList
{
  struct _TIncList *Next;
  FILE *Contents;
} TIncList,*PIncList;

PIncList IncList = NULL;

void WrError(Word Num)
{
  (void)Num;
}

static void fwritechk(const char *pArg, int retcode,
                      const void *pBuffer, size_t size, size_t nmemb, FILE *pFile)
{
  size_t res;

  res = fwrite(pBuffer, size, nmemb, pFile);
  if (res != nmemb)
  {
    perror(pArg);
    exit(retcode);
  }
}

static FILE *fopenchk(const char *pName, int retcode, const char *pOpenMode)
{
  FILE *pRes;

  pRes = fopen(pName, pOpenMode);
  if (!pRes)
  {
    perror(pName);
    exit(retcode);
  }
  return pRes;
}

static void GetLine(char *Dest)
{
  PIncList OneFile;

  ReadLn(p_src_file, Dest);
  if (!as_strncasecmp(Dest, "INCLUDE", 7))
  {
    OneFile = (PIncList) malloc(sizeof(TIncList));
    OneFile->Next = IncList; OneFile->Contents = p_src_file;
    IncList = OneFile;
    strmov(Dest, Dest + 7); KillPrefBlanks(Dest); KillPrefBlanks(Dest);
    p_src_file = fopenchk(Dest, 2, "r");
    GetLine(Dest);
  }
  if (feof(p_src_file) && IncList)
  {
    fclose(p_src_file);
    OneFile = IncList; IncList = OneFile->Next;
    p_src_file = OneFile->Contents; free(OneFile);
  }
}

/*****************************************************************************/

static void SynError(const char *LinePart)
{
  fprintf(stderr, "syntax error : %s\n", LinePart); exit(10);
}

/*---------------------------------------------------------------------------*/

static void Process_LANGS(const char *Line)
{
  char NLine[1024], *p, *p2, *p3, *end, z, Part[1024];
  PMsgCat PCat;
  int num;

  strmaxcpy(NLine, Line, 1024);
  KillPrefBlanks(NLine); KillPostBlanks(NLine);

  p = NLine; z = 0;
  while (*p)
  {
    for (; *p && !as_isspace(*p); p++);
    for (; *p && as_isspace(*p); p++);
    z++;
  }

  MsgCats = (PMsgCat) malloc(sizeof(TMsgCat) * (CatCount = z)); p = NLine;
  for (z = 0, PCat = MsgCats; z < CatCount; z++, PCat++)
  {
    PCat->Messages = NULL;
    PCat->TotLength = 0;
    PCat->CtryCodeCnt = 0;
    for (p2 = p; *p2 && !as_isspace(*p2); p2++);
    if (*p2 == '\0')
      strcpy(Part, p);
    else
    {
      *p2 = '\0'; strcpy(Part, p);
      for (p = p2 + 1; *p && as_isspace(*p2); p++); /* TODO: p instead of p2? */
    }
    if ((!*Part) || (Part[strlen(Part)-1] != ')')) SynError(Part);
    Part[strlen(Part) - 1] = '\0';
    p2 = strchr(Part, '(');
    if (!p2)
      SynError(Part);
    *p2 = '\0';
    PCat->CtryName = as_strdup(Part); p2++;
    do
    {
      for (p3 = p2; ((*p3) && (*p3 != ',')); p3++);
      switch (*p3)
      {
        case '\0':
          num = strtol(p2, &end, 10); p2 = p3;
          break;
        case ',':
          *p3 = '\0'; num = strtol(p2, &end, 10); p2 = p3 + 1;
          break;
        default:
          num = 0;
          break;
      }
      if (*end) SynError("numeric format");
      PCat->CtryCodes=(PCat->CtryCodeCnt == 0) ?
                      (LongInt *) malloc(sizeof(LongInt)) :
                      (LongInt *) realloc(PCat->CtryCodes, sizeof(LongInt) * (PCat->CtryCodeCnt + 1));
      PCat->CtryCodes[PCat->CtryCodeCnt++] = num;
    }
    while (*p2!='\0');
  }
}

static void Process_DEFAULT(char *Line)
{
  PMsgCat z;

  if (!CatCount) SynError("DEFAULT before LANGS");
  KillPrefBlanks(Line); KillPostBlanks(Line);
  for (z = MsgCats; z < MsgCats + CatCount; z++)
    if (!as_strcasecmp(z->CtryName,Line))
      break;
  if (z >= MsgCats + CatCount)
    SynError("unknown country name in DEFAULT");
  DefCat = z - MsgCats;
}

static void Process_MESSAGE(char *Line)
{
  char Msg[4096];
  String OneLine;
  int l;
  PMsgCat z;
  TransRec *PRec;
  PMsgList List;
  Boolean Cont;

  KillPrefBlanks(Line); KillPostBlanks(Line);
  if (p_rsc_file) fprintf(p_rsc_file, "#define Num_%s %d\n", Line, MsgCounter);
  MsgCounter++;

  for (z = MsgCats; z < MsgCats + CatCount; z++)
  {
    *Msg = '\0';
    do
    {
      GetLine(OneLine); KillPrefBlanks(OneLine); KillPostBlanks(OneLine);
      l = strlen(OneLine);
      Cont = OneLine[l - 1] == '\\';
      if (Cont)
      {
        OneLine[l - 1] = '\0'; KillPostBlanks(OneLine); l = strlen(OneLine);
      }
      if (*OneLine != '"') SynError(OneLine);
      if (OneLine[l - 1] != '"')
        SynError(OneLine);
      OneLine[l - 1] = '\0';
      strmaxcat(Msg, OneLine + 1, 4095);
    }
    while (Cont);
    for (PRec = TransRecs; PRec->AbbString; PRec++)
      strreplace(Msg, PRec->AbbString, PRec->Character, -1, sizeof(Msg));
    List = (PMsgList) malloc(sizeof(TMsgList));
    List->Next = NULL;
    List->Position = z->TotLength;
    List->Contents = as_strdup(Msg);
    if (!z->Messages) z->Messages = List;
    else z->LastMessage->Next = List;
    z->LastMessage = List;
    z->TotLength += strlen(Msg) + 1;
  }
}

static char *make_sym_name(const char *p_file_name, int include_guard)
{
  char *p_ret, *p_run;
  const char *p_base;
  size_t l;
  Boolean no_replace;

  p_base = strrchr(p_file_name, PATHSEP);
  if (p_base)
    p_file_name = p_base + 1;
  l = strlen(p_file_name);
  p_run = p_ret = (char*)malloc(!!include_guard + l + 1);
  if (include_guard)
    *p_run++ = '_';
  for (; *p_file_name; p_file_name++)
  {
    no_replace = (p_run == p_ret) ? as_isalpha(*p_file_name) : as_isalnum(*p_file_name);
    *p_run++ = no_replace
             ? (include_guard ? as_toupper(*p_file_name) : as_tolower(*p_file_name))
             : '_';
  }
  *p_run++ = '\0';
  return p_ret;
}

static void write_dual(const void *p_v_data, size_t data_len)
{
  if (p_msg_file)
    fwritechk(p_msg_file_name, IO_RETCODE_MSG, p_v_data, 1, data_len, p_msg_file);

  if (p_msh_file)
  {
    const unsigned char *p_data = (const unsigned char*)p_v_data;
    size_t z;

    for (z = 0; z < data_len; z++)
    {
      if (!(bytes_written & 15))
      {
        if (bytes_written > 0)
          fprintf(p_msh_file, ",");
        fprintf(p_msh_file, "\n  ");
      }
      else
        fprintf(p_msh_file, ",");
      fprintf(p_msh_file, "0x%02x", p_data[z]);
      bytes_written++;
    }
  }
  else
    bytes_written += data_len;
}

static void write_dual_4_le(LongInt *p_lword)
{
  if (HostBigEndian)
    DSwap(p_lword, 4);
  write_dual(p_lword, 4);
  if (HostBigEndian)
    DSwap(p_lword, 4);
}

/*---------------------------------------------------------------------------*/

int main(int argc, char **argv)
{
  char Line[1024], Cmd[1024], *p, Save;
  PMsgCat z;
  TMsgCat TmpCat;
  PMsgList List;
  int c, argz, nextidx, curridx;
  time_t stamp;
  LongInt Id1, Id2;
  LongInt RunPos, StrPos;
  const char *pSrcName = NULL, *p_rsc_file_name = NULL;
  char *p_rsc_file_incl_guard_sym = NULL, *p_msh_file_incl_guard_sym = NULL;

  be_le_init(); strutil_init();

  curridx = 0;
  nextidx = -1;
  for (argz = 1; argz < argc; argz++)
  {
    if (!strcmp(argv[argz], "-h"))
      nextidx = 2;
    else if (!strcmp(argv[argz], "-m"))
      nextidx = 1;
    else if (!strcmp(argv[argz], "-i"))
      nextidx = 3;
    else
    {
      int thisidx;

      if (nextidx >= 0)
      {
        thisidx = nextidx;
        nextidx = -1;
      }
      else
        thisidx = curridx++;
      switch (thisidx)
      {
        case 0:
          pSrcName = argv[argz]; break;
        case 1:
          p_msg_file_name = argv[argz]; break;
        case 2:
          p_rsc_file_name = argv[argz]; break;
        case 3:
          p_msh_file_name = argv[argz]; break;
      }
    }
  }

  if (!pSrcName)
  {
    fprintf(stderr, "usage: %s <input resource file> <[-m] output msg file> <[-i] output include file> [[-h ]header file]\n", *argv);
    exit(1);
  }

  p_src_file = fopenchk(pSrcName, IO_RETCODE_SRC, "r");

  if (p_rsc_file_name)
  {
    p_rsc_file = fopenchk(p_rsc_file_name, IO_RETCODE_RSC, "w");
    p_rsc_file_incl_guard_sym = make_sym_name(p_rsc_file_name, 1);
  }
  else
    p_rsc_file = NULL;

  stamp = MyGetFileTime(argv[1]); Id1 = stamp & 0x7fffffff;
  Id2 = 0;
  for (c = 0; c < min((int)strlen(argv[1]), 4); c++)
   Id2 = (Id2 << 8) + ((Byte) argv[1][c]);
  if (p_rsc_file)
  {
    fprintf(p_rsc_file, "#ifndef %s\n", p_rsc_file_incl_guard_sym);
    fprintf(p_rsc_file, "#define %s\n", p_rsc_file_incl_guard_sym);
    fprintf(p_rsc_file, "#define MsgId1 %ld\n", (long) Id1);
    fprintf(p_rsc_file, "#define MsgId2 %ld\n", (long) Id2);
  }

  MsgCounter = CatCount = 0; MsgCats = NULL; DefCat = -1;
  while (!feof(p_src_file))
  {
    GetLine(Line); KillPrefBlanks(Line); KillPostBlanks(Line);
    if ((*Line != ';') && (*Line != '#') && (*Line != '\0'))
    {
      for (p = Line; !as_isspace(*p) && *p; p++);
      Save = *p; *p = '\0'; strmaxcpy(Cmd, Line, 1024); *p = Save; strmov(Line, p);
      if (!as_strcasecmp(Cmd, "LANGS")) Process_LANGS(Line);
      else if (!as_strcasecmp(Cmd, "DEFAULT")) Process_DEFAULT(Line);
      else if (!as_strcasecmp(Cmd, "MESSAGE")) Process_MESSAGE(Line);
    }
  }
  fclose(p_src_file);

  if (p_rsc_file)
  {
    fprintf(p_rsc_file, "#endif /* #ifndef %s */\n", p_rsc_file_incl_guard_sym);
    free(p_rsc_file_incl_guard_sym); p_rsc_file_incl_guard_sym = NULL;
    fclose(p_rsc_file);
  }

  if (p_msg_file_name || p_msh_file_name)
  {
    if (p_msg_file_name)
      p_msg_file = fopenchk(p_msg_file_name, IO_RETCODE_MSG, OPENWRMODE);
    if (p_msh_file_name)
    {
      char *p_array_name = make_sym_name(p_msh_file_name, 0);

      p_msh_file = fopenchk(p_msh_file_name, IO_RETCODE_MSH, "w");
      p_msh_file_incl_guard_sym = make_sym_name(p_msh_file_name, 1);
      fprintf(p_msh_file, "#ifndef %s\n", p_msh_file_incl_guard_sym);
      fprintf(p_msh_file, "#define %s\n", p_msh_file_incl_guard_sym);
      fprintf(p_msh_file, "static const unsigned char %s_data[] = {", p_array_name);
      free(p_array_name); p_array_name = NULL;
    }
    bytes_written = 0;

    /* Magic-String */

    write_dual(IdentString, strlen(IdentString));
    write_dual_4_le(&Id1); write_dual_4_le(&Id2);

    /* Default nach vorne */

    if (DefCat > 0)
    {
      TmpCat = MsgCats[0]; MsgCats[0] = MsgCats[DefCat]; MsgCats[DefCat] = TmpCat;
    }

    /* Startadressen String-Kataloge berechnen */

    RunPos = bytes_written + 1;
    for (z = MsgCats; z < MsgCats + CatCount; z++)
      RunPos += (z->HeadLength = strlen(z->CtryName) + 1 + 4 + 4 + (4 * z->CtryCodeCnt) + 4);
    for (z = MsgCats; z < MsgCats + CatCount; z++)
    {
      z->FilePos = RunPos; RunPos += z->TotLength + (4 * MsgCounter);
    }

    /* Country-Records schreiben */

    for (z = MsgCats; z < MsgCats + CatCount; z++)
    {
      write_dual(z->CtryName, strlen(z->CtryName) + 1);
      write_dual_4_le(&(z->TotLength));
      write_dual_4_le(&(z->CtryCodeCnt));
      for (c = 0; c < z->CtryCodeCnt; c++) write_dual_4_le(z->CtryCodes + c);
      write_dual_4_le(&(z->FilePos));
    }
    Save = '\0'; write_dual(&Save, 1);

    /* Stringtabellen schreiben */

    for (z = MsgCats; z < MsgCats + CatCount; z++)
    {
      for (List = z->Messages; List; List = List->Next)
      {
        StrPos = z->FilePos + (4 * MsgCounter) + List->Position;
        write_dual_4_le(&StrPos);
      }
      for (List = z->Messages; List; List = List->Next)
        write_dual(List->Contents, strlen(List->Contents) + 1);
    }

    /* faeaedisch... */

    if (p_msg_file)
    {
      fclose(p_msg_file); p_msg_file = NULL;
    }
    if (p_msh_file)
    {
      fprintf(p_msh_file, "\n};\n");
      fprintf(p_msh_file, "#endif /* %s */\n", p_msh_file_incl_guard_sym);
      free(p_msh_file_incl_guard_sym); p_msh_file_incl_guard_sym = NULL;
      fclose(p_msh_file);
    }

  }

  return 0;
}

#ifdef CKMALLOC
#undef malloc
#undef realloc

void *ckmalloc(size_t s)
{
  void *tmp = malloc(s);

  if (!tmp)
  {
    fprintf(stderr, "allocation error(malloc): out of memory");
    exit(255);
  }
  return tmp;
}

void *ckrealloc(void *p, size_t s)
{
  void *tmp = realloc(p, s);
  if (!tmp)
  {
    fprintf(stderr, "allocation error(realloc): out of memory");
    exit(255);
  }
  return tmp;
}
#endif
