// ---------------------------------------------------------------------------
// - ccnf.cpp                                                                -
// - standard platform library - config file generator                       -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

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

// supported platforms
#define AFNIX_PLATFORM_UNKNOWN   0
#define AFNIX_PLATFORM_LINUX     1
#define AFNIX_PLATFORM_SOLARIS   2
#define AFNIX_PLATFORM_FREEBSD   3
#define AFNIX_PLATFORM_DARWIN    4
#define AFNIX_PLATFORM_GNUKBSD   5

// recognized platform name
#define AFNIX_PLATNAME_LINUX     "linux"
#define AFNIX_PLATNAME_SOLARIS   "solaris"
#define AFNIX_PLATNAME_FREEBSD   "freebsd"
#define AFNIX_PLATNAME_DARWIN    "darwin"
#define AFNIX_PLATNAME_GNUKBSD   "gnukbsd"

// supported processors
#define AFNIX_PROCTYPE_UNKNOWN    0
#define AFNIX_PROCTYPE_IA32       1
#define AFNIX_PROCTYPE_SPARC      2
#define AFNIX_PROCTYPE_ALPHA      3
#define AFNIX_PROCTYPE_ARM        4
#define AFNIX_PROCTYPE_PPC        5
#define AFNIX_PROCTYPE_M68K       6
#define AFNIX_PROCTYPE_MIPS       7
#define AFNIX_PROCTYPE_MIPSEL     8
#define AFNIX_PROCTYPE_PA64       9
#define AFNIX_PROCTYPE_IA64      10
#define AFNIX_PROCTYPE_S390      11
#define AFNIX_PROCTYPE_X64       12

// recognized processor name
#define AFNIX_PROCNAME_UNKNOWN   "unknown"
#define AFNIX_PROCNAME_IA32      "ia32"
#define AFNIX_PROCNAME_SPARC     "sparc"
#define AFNIX_PROCNAME_ALPHA     "alpha"
#define AFNIX_PROCNAME_ARM       "arm"
#define AFNIX_PROCNAME_PPC       "ppc"
#define AFNIX_PROCNAME_M68K      "m68k"
#define AFNIX_PROCNAME_MIPS      "mips"
#define AFNIX_PROCNAME_MIPSEL    "mipsel"
#define AFNIX_PROCNAME_PA64      "pa64"
#define AFNIX_PROCNAME_IA64      "ia64"
#define AFNIX_PROCNAME_S390      "s390"
#define AFNIX_PROCNAME_X64       "x64"

// force size type with S390
#if defined(__s390__)
const bool AFNIX_FORCE_LONG      = true;
#else
const bool AFNIX_FORCE_LONG      = false;
#endif

// special darwin flag
#if defined(__APPLE__)
const bool AFNIX_FORCE_DARWIN    = true;
#else
const bool AFNIX_FORCE_DARWIN    = false;
#endif

// compute the platform id
static int get_platid (const char* plat) {
  if (!plat || (strlen (plat) == 0)) return AFNIX_PLATFORM_UNKNOWN;
  // linux
  if (strcmp (plat, AFNIX_PLATNAME_LINUX) == 0) 
    return AFNIX_PLATFORM_LINUX;
  // solaris
  if (strcmp (plat, AFNIX_PLATNAME_SOLARIS) == 0) 
    return AFNIX_PLATFORM_SOLARIS;
  // freebsd
  if (strcmp (plat, AFNIX_PLATNAME_FREEBSD) == 0) 
    return AFNIX_PLATFORM_FREEBSD;
  // darwin
  if (strcmp (plat, AFNIX_PLATNAME_DARWIN) == 0) 
    return AFNIX_PLATFORM_DARWIN;
  // gnu/freebsd
  if (strcmp (plat, AFNIX_PLATNAME_GNUKBSD) == 0) 
    return AFNIX_PLATFORM_GNUKBSD;
  // unknown
  return AFNIX_PLATFORM_UNKNOWN; 
}

// compute the processor id
static int get_procid (const char* proc) {
  if (!proc || (strlen (proc) == 0)) return AFNIX_PROCTYPE_UNKNOWN;
  // ia
  if (strcmp (proc, AFNIX_PROCNAME_IA32) == 0) 
    return AFNIX_PROCTYPE_IA32;
  // sparc
  if (strcmp (proc, AFNIX_PROCNAME_SPARC) == 0) 
    return AFNIX_PROCTYPE_SPARC;
  // alpha
  if (strcmp (proc, AFNIX_PROCNAME_ALPHA) == 0) 
    return AFNIX_PROCTYPE_ALPHA;
  // arm
  if (strcmp (proc, AFNIX_PROCNAME_ARM) == 0) 
    return AFNIX_PROCTYPE_ARM;
  // ppc
  if (strcmp (proc, AFNIX_PROCNAME_PPC) == 0) 
    return AFNIX_PROCTYPE_PPC;
  // m68k
  if (strcmp (proc, AFNIX_PROCNAME_M68K) == 0) 
    return AFNIX_PROCTYPE_M68K;
  // mips
  if (strcmp (proc, AFNIX_PROCNAME_MIPS) == 0)
    return AFNIX_PROCTYPE_MIPS;
  // mipsel
  if (strcmp (proc, AFNIX_PROCNAME_MIPSEL) == 0)
    return AFNIX_PROCTYPE_MIPSEL;
  // pa64
  if (strcmp (proc, AFNIX_PROCNAME_PA64) == 0)
    return AFNIX_PROCTYPE_PA64;
  // ia64
  if (strcmp (proc, AFNIX_PROCNAME_IA64) == 0)
    return AFNIX_PROCTYPE_IA64;
  // s390
  if (strcmp (proc, AFNIX_PROCNAME_S390) == 0)
    return AFNIX_PROCTYPE_S390;
  // x86-64
  if (strcmp (proc, AFNIX_PROCNAME_X64) == 0) {
    switch (sizeof(void*)) {
    case 4: 
      return AFNIX_PROCTYPE_IA32;
    case 8: 
      return AFNIX_PROCTYPE_X64;
    default:
      return AFNIX_PROCTYPE_UNKNOWN;
    }
  }
  // unknown
  return AFNIX_PROCTYPE_UNKNOWN;
}

// get the processor name by id - this is needed as the procid
// can remap the processor
static const char* get_procnm (const int id) {
  if (id ==  AFNIX_PROCTYPE_IA32)   return AFNIX_PROCNAME_IA32;
  if (id ==  AFNIX_PROCTYPE_SPARC)  return AFNIX_PROCNAME_SPARC;
  if (id ==  AFNIX_PROCTYPE_ALPHA)  return AFNIX_PROCNAME_ALPHA;
  if (id ==  AFNIX_PROCTYPE_ARM)    return AFNIX_PROCNAME_ARM;
  if (id ==  AFNIX_PROCTYPE_PPC)    return AFNIX_PROCNAME_PPC;
  if (id ==  AFNIX_PROCTYPE_M68K)   return AFNIX_PROCNAME_M68K;
  if (id ==  AFNIX_PROCTYPE_MIPS)   return AFNIX_PROCNAME_MIPS;
  if (id ==  AFNIX_PROCTYPE_MIPSEL) return AFNIX_PROCNAME_MIPSEL;
  if (id ==  AFNIX_PROCTYPE_PA64)   return AFNIX_PROCNAME_PA64;
  if (id ==  AFNIX_PROCTYPE_IA64)   return AFNIX_PROCNAME_IA64;
  if (id ==  AFNIX_PROCTYPE_S390)   return AFNIX_PROCNAME_S390;
  if (id ==  AFNIX_PROCTYPE_X64)    return AFNIX_PROCNAME_X64;
  return AFNIX_PROCNAME_UNKNOWN;
}

// the ccnf header
static const char* header = 
"// ------------------------------------------------------------------------\n"
"// - ccnf.hpp                                                             -\n"
"// - standard platform library - base type configuration                  -\n"
"// ------------------------------------------------------------------------\n"
"// - This program is free software; you can redistribute it and/or modify -\n"
"// - it provided that this copyright notice is kept intact.               -\n"
"// -                                                                      -\n"
"// - This program is distributed in the hope  that it will be useful, but -\n"
"// - without  any  warranty;  without  even  the  implied   warranty   of -\n"
"// - merchantability  or  fitness  for  a particular purpose. In no event -\n"
"// - shall the  copyright  holder  be  liable  for any  direct, indirect, -\n"
"// - incidental or special  damages  arising in any way out of the use of -\n"
"// - this software.                                                       -\n"
"// -                                                                      -\n"
"// - This file was automatically generated by ccnf                        -\n"
"// ------------------------------------------------------------------------\n"
"// - copyright (c) 1999-2011 amaury darsch                                -\n"
"// ------------------------------------------------------------------------";

static const char* nilptr =
  "// the base nilp pointer definition\n"
  "#ifndef nilp\n"
  "#define nilp 0\n"
  "#endif\n";

static const char* darwindefs =
  "// special darwin definitions\n"
  "#if defined (__APPLE__)\n"
  "#if defined (__ppc__)\n"
  "#define AFNIX_DARWIN_PROCID       AFNIX_PROCTYPE_PPC\n"
  "#define AFNIX_DARWIN_PROC         AFNIX_PROCNAME_PPC\n"
  "#elif defined (__ppc64__)\n"
  "#define AFNIX_DARWIN_PROCID       AFNIX_PROCTYPE_PPC\n"
  "#define AFNIX_DARWIN_PROC         AFNIX_PROCNAME_PPC\n"
  "#elif defined (__i386__)\n"
  "#define AFNIX_DARWIN_PROCID       AFNIX_PROCTYPE_IA32\n"
  "#define AFNIX_DARWIN_PROC         AFNIX_PROCNAME_IA32\n"
  "#else\n"
  "#error \"unsupported darwin architecture\"\n"
  "#endif\n"
  "#endif\n";

static const char* types = 
  "  // fundamental types of the afnix system\n"
  "  typedef unsigned char      t_byte;\n"
  "  typedef unsigned short     t_word;\n"
  "  typedef unsigned int       t_quad;\n"
  "  typedef unsigned long long t_octa;\n"
  "  typedef long long          t_long;\n"
  "  typedef double             t_real;\n";

static const char* constants =
  "  // fundamental characters of the afnix system\n"
  "  const   char     nilc      = 0x00;       // nil character\n"
  "  const   char     sohc      = 0x01;       // start of heading\n"
  "  const   char     stxc      = 0x02;       // start of text\n"
  "  const   char     etxc      = 0x03;       // end of text\n"
  "  const   char     eosc      = 0x04;       // end of stream\n"
  "  const   char     bspc      = 0x08;       // backspace\n"
  "  const   char     delc      = 0x7f;       // delete\n"
  "  const   char     eolc      = 0x0a;       // end of line\n"
  "  const   char     crlc      = 0x0d;       // carriage return\n"
  "  const   char     blkc      = 0x20;       // blank\n"
  "  const   char     tabc      = 0x09;       // tab\n"
  "\n"
  "  // fundamental quads of the afnix system\n"
  "  const   t_quad   nilq      = 0x00000000; // nil quad\n"
  "  const   t_quad   sohq      = 0x00000001; // start of heading\n"
  "  const   t_quad   stxq      = 0x00000002; // start of text\n"
  "  const   t_quad   etxq      = 0x00000003; // end of text\n"
  "  const   t_quad   eosq      = 0x00000004; // end of stream\n"
  "  const   t_quad   bspq      = 0x00000008; // backspace\n"
  "  const   t_quad   delq      = 0x0000007f; // delete\n"
  "  const   t_quad   eolq      = 0x0000000a; // end of line\n"
  "  const   t_quad   crlq      = 0x0000000d; // carriage return\n"
  "  const   t_quad   blkq      = 0x00000020; // blank\n"
  "  const   t_quad   tabq      = 0x00000009; // tab\n";

int main (int, char**) {
  // print the header
  fprintf (stdout, "%s\n\n", header);

  // install the control header
  fprintf (stdout, "#ifndef  AFNIX_CCNF_HPP\n");
  fprintf (stdout, "#define  AFNIX_CCNF_HPP\n\n");

  // define current revision
  fprintf (stdout, "// afnix revision\n");
  fprintf (stdout, "#define  AFNIX_VERSION_MAJOR      %s\n", PLTMAJOR);
  fprintf (stdout, "#define  AFNIX_VERSION_MINOR      %s\n", PLTMINOR);
  fprintf (stdout, "#define  AFNIX_VERSION_PATCH      %s\n", PLTPATCH);
  fprintf (stdout, "#define  AFNIX_VERSION_TOTAL      \"%s.%s.%s\"\n", 
	   PLTMAJOR, PLTMINOR, PLTPATCH);  
  fprintf (stdout, "\n");

  // define the platform list
  fprintf (stdout, "// supported platforms\n");
  fprintf (stdout, "#define  AFNIX_PLATFORM_UNKNOWN   %d\n",
	   AFNIX_PLATFORM_UNKNOWN);
  fprintf (stdout, "#define  AFNIX_PLATFORM_LINUX     %d\n",
	   AFNIX_PLATFORM_LINUX);
  fprintf (stdout, "#define  AFNIX_PLATFORM_SOLARIS   %d\n",
	   AFNIX_PLATFORM_SOLARIS);
  fprintf (stdout, "#define  AFNIX_PLATFORM_FREEBSD   %d\n",
	   AFNIX_PLATFORM_FREEBSD);
  fprintf (stdout, "#define  AFNIX_PLATFORM_DARWIN    %d\n",
	   AFNIX_PLATFORM_DARWIN);
  fprintf (stdout, "#define  AFNIX_PLATFORM_GNUKBSD   %d\n",
	   AFNIX_PLATFORM_GNUKBSD);
  fprintf (stdout, "\n");

  // define the processor list
  fprintf (stdout, "// supported processors\n");
  fprintf (stdout, "#define  AFNIX_PROCTYPE_UNKNOWN   %d\n",
	   AFNIX_PROCTYPE_UNKNOWN);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_IA32      %d\n",
	   AFNIX_PROCTYPE_IA32);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_SPARC     %d\n",
	   AFNIX_PROCTYPE_SPARC);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_ALPHA     %d\n",
	   AFNIX_PROCTYPE_ALPHA);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_ARM       %d\n",
	   AFNIX_PROCTYPE_ARM);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_PPC       %d\n",
	   AFNIX_PROCTYPE_PPC);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_M68K      %d\n",
	   AFNIX_PROCTYPE_M68K);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_MIPS      %d\n",
	   AFNIX_PROCTYPE_MIPS);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_MIPSEL    %d\n",
	   AFNIX_PROCTYPE_MIPSEL);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_PA64      %d\n",
	   AFNIX_PROCTYPE_PA64);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_IA64      %d\n",
	   AFNIX_PROCTYPE_IA64);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_S390      %d\n",
	   AFNIX_PROCTYPE_S390);
  fprintf (stdout, "#define  AFNIX_PROCTYPE_X64       %d\n",
	   AFNIX_PROCTYPE_X64);
  fprintf (stdout, "\n");

  // recognized processor names
  fprintf (stdout, "// recognized processor names\n");
  fprintf (stdout, "#define  AFNIX_PROCNAME_UNKNOWN   \"%s\"\n",
	   AFNIX_PROCNAME_UNKNOWN);
  fprintf (stdout, "#define  AFNIX_PROCNAME_IA32      \"%s\"\n",
	   AFNIX_PROCNAME_IA32);
  fprintf (stdout, "#define  AFNIX_PROCNAME_SPARC     \"%s\"\n",
	   AFNIX_PROCNAME_SPARC);
  fprintf (stdout, "#define  AFNIX_PROCNAME_ALPHA     \"%s\"\n",
	   AFNIX_PROCNAME_ALPHA);
  fprintf (stdout, "#define  AFNIX_PROCNAME_ARM       \"%s\"\n",
	   AFNIX_PROCNAME_ARM);
  fprintf (stdout, "#define  AFNIX_PROCNAME_PPC       \"%s\"\n",
	   AFNIX_PROCNAME_PPC);
  fprintf (stdout, "#define  AFNIX_PROCNAME_M68K      \"%s\"\n",
	   AFNIX_PROCNAME_M68K);
  fprintf (stdout, "#define  AFNIX_PROCNAME_MIPS      \"%s\"\n",
	   AFNIX_PROCNAME_MIPS);
  fprintf (stdout, "#define  AFNIX_PROCNAME_MIPSEL    \"%s\"\n",
	   AFNIX_PROCNAME_MIPSEL);
  fprintf (stdout, "#define  AFNIX_PROCNAME_PA64      \"%s\"\n",
	   AFNIX_PROCNAME_PA64);
  fprintf (stdout, "#define  AFNIX_PROCNAME_IA64      \"%s\"\n",
	   AFNIX_PROCNAME_IA64);
  fprintf (stdout, "#define  AFNIX_PROCNAME_S390      \"%s\"\n",
	   AFNIX_PROCNAME_S390);
  fprintf (stdout, "#define  AFNIX_PROCNAME_X64       \"%s\"\n",
	   AFNIX_PROCNAME_X64);
  fprintf (stdout, "\n");

  // install the darwin definition
  if (AFNIX_FORCE_DARWIN == true) {
    fprintf (stdout, "%s", darwindefs);
    fprintf (stdout, "\n");
  }

  // compute id and name
  const int   platid = get_platid (PLATNAME);
  const int   procid = get_procid (PROCNAME);
  const char* procnm = get_procnm (procid);
  // install the config names
  fprintf (stdout, "// platform definitions\n");
  fprintf (stdout, "#define  AFNIX_PLATFORM_PLATID    %d\n",     platid);
  fprintf (stdout, "#define  AFNIX_PLATFORM_VERSION   %s\n",     PLATVERS);
  fprintf (stdout, "#define  AFNIX_PLATFORM_MAJOR     %s\n",     PLATVMAJ);
  fprintf (stdout, "#define  AFNIX_PLATFORM_MINOR     %s\n",     PLATVMIN);
  fprintf (stdout, "#define  AFNIX_PLATFORM_NAME      \"%s\"\n", PLATNAME);
  fprintf (stdout, "#define  AFNIX_PLATFORM_VINFO     \"%s\"\n", PLATVERS);
  // special case for darwin
  if (AFNIX_FORCE_DARWIN == true) {
    fprintf (stdout, "#define  AFNIX_PLATFORM_PROCID    AFNIX_DARWIN_PROCID\n");
    fprintf (stdout, "#define  AFNIX_PLATFORM_PROC      AFNIX_DARWIN_PROC\n");
  } else {
    fprintf (stdout, "#define  AFNIX_PLATFORM_PROCID    %d\n",     procid);
    fprintf (stdout, "#define  AFNIX_PLATFORM_PROC      \"%s\"\n", procnm);
  }
  fprintf (stdout, "#define  AFNIX_PLATFORM_PTYPE     \"%s\"\n", PROCTYPE);
  fprintf (stdout, "\n");

  // install installation onfiguration
  fprintf (stdout, "// configuration definitions\n");
  if (strcmp (LINKTYPE, "static") == 0) {
    fprintf (stdout, "#define  AFNIX_REGISTER_STATIC\n");
    fprintf (stdout, "#define  AFNIX_LINKTYPE_STATIC    true\n");
  } else {
    fprintf (stdout, "#define  AFNIX_LINKTYPE_STATIC    false\n");
  }
  fprintf (stdout, "\n");

  // add the nil pointer definition
  fprintf (stdout, "%s", nilptr);
  fprintf (stdout, "\n");

  // start the namespace
  fprintf (stdout, "namespace afnix {\n");

  // install the base types
  fprintf (stdout, "%s", types);
  fprintf (stdout, "\n");

  // install the size type
  fprintf (stdout, "  // pointer size type\n");
  if (platid == AFNIX_PLATFORM_DARWIN)
    fprintf (stdout, "  typedef unsigned long int  t_size;\n");
  else if (AFNIX_FORCE_LONG == true)
    fprintf (stdout, "  typedef unsigned long      t_size;\n");
  else if (sizeof (void*) == 8 )
    fprintf (stdout, "  typedef unsigned long      t_size;\n");
  else
    fprintf (stdout, "  typedef unsigned int       t_size;\n");
  fprintf (stdout, "\n");
  
  // install the constants
  fprintf (stdout, "%s", constants);

  // install the footer
  fprintf (stdout, "}\n\n");
  fprintf (stdout, "#endif\n");

  return 0;
}
