
/*
**
** todo: MORE_STRINGS, MORE_PARAMS (sets!)   --> EXIT,REXX,RUN
**       optional parameters --> MAKEASSIGN,PROTECT,DATABASE,GETDISKSPACE
**
** dynamic typing with SET,IF
**
*/


#include <exec/types.h>

#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>

#include <clib/debuglib_protos.h>
#include <clib/savage_protos.h>

#include <libraries/installerngmodule.h>

#include <installerngmodule_base.h>

#include <savage/macros.h>

#include <string.h>

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

// these types are supported by the installerNG
// (note: TYPE_MORE_? = TYPE_? + TYPE_MORE_THRESHOLD)
#define TYPE_UNKNOWN          TYPE_BUILTIN_UNKNOWN
#define TYPE_NUMBER           (TYPE_BUILTIN_NUMBER - 400)
#define TYPE_STRING           (TYPE_BUILTIN_STRING - 400)
#define TYPE_PARAMETER        (TYPE_BUILTIN_PARAMETER - 400)  // a parameter function
#define TYPE_ANYTHING         4  // both, NUMBER/STRING is allowed (crappy)

// for describing the types of the builtin functions, we need
// to introduce some more defines to mark a undefined number
// of args of a certain type or any type
#define TYPE_MORE_THRESHOLD   10
#define TYPE_MORE_NUMBERS     11
#define TYPE_MORE_STRINGS     12
#define TYPE_MORE_PARAMETERS  13
#define TYPE_MORE_ANYTHING    14

// not yet defined...
#define TYPE_NODEFINE         20

// mark an optional parameter by setting the highest bit
#define TYPE_OPTIONAL         0 // TO DO: (1<<7)

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

// this is the type description for the functions; it describes the return
// type of every function (first entry) and then the types of the functions
// arguments; note, a function can have an unfixed number of arguments!
static unsigned short functionTypes[][] =

  //  return type,    argument types....

  { { TYPE_NUMBER,    TYPE_MORE_ANYTHING },  // abort
    { TYPE_NUMBER,    TYPE_NUMBER },  // complete
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // copyfiles
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // copylib
    { TYPE_STRING,    TYPE_NUMBER },  // debug
    { TYPE_NUMBER,    TYPE_STRING, TYPE_MORE_PARAMETERS },  // delete
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // execute
    { TYPE_NUMBER,    TYPE_MORE_ANYTHING },  // exit
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING, TYPE_STRING },  // foreach
    { TYPE_NUMBER,    TYPE_NODEFINE },  // makeassign
    { TYPE_NUMBER,    TYPE_STRING, TYPE_MORE_PARAMETERS },  // makedir
    { TYPE_STRING,    TYPE_MORE_STRINGS },  // message
    { TYPE_NUMBER,    TYPE_MORE_ANYTHING },  // onerror
    { TYPE_NODEFINE,  TYPE_NODEFINE },  // procedure
    { TYPE_NUMBER,    TYPE_NODEFINE },  // protect
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING, TYPE_MORE_PARAMETERS },  // rename
    { TYPE_NUMBER,    TYPE_NODEFINE },  // rexx
    { TYPE_NUMBER,    TYPE_NODEFINE },  // run
    { TYPE_NODEFINE,  TYPE_MORE_ANYTHING },  // set
    { TYPE_NUMBER,    TYPE_STRING, TYPE_MORE_PARAMETERS },  // startup
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // textfile
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // tooltype
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_MORE_ANYTHING },  // trap
    { TYPE_NUMBER,    TYPE_NUMBER },  // user
    { TYPE_STRING,    TYPE_MORE_STRINGS },  // welcome
    { TYPE_STRING,    TYPE_MORE_STRINGS },  // working
    { TYPE_NODEFINE,  TYPE_ANYTHING, TYPE_ANYTHING, TYPE_ANYTHING },  // if
    { TYPE_NODEFINE,  TYPE_ANYTHING, TYPE_MORE_ANYTHING },  // until
    { TYPE_NODEFINE,  TYPE_ANYTHING, TYPE_MORE_ANYTHING },  // while
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // eq
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // ne
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // gt
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // ge
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // lt
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // le
    { TYPE_NUMBER,    TYPE_MORE_NUMBERS },  // add
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // sub
    { TYPE_NUMBER,    TYPE_MORE_NUMBERS },  // mul
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // div
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // and
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // or
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // xor
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // not
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // bitand
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // bitor
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // bitxor
    { TYPE_NUMBER,    TYPE_NUMBER },  // bitnot
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // shiftleft
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // shiftright
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_MORE_NUMBERS },  // in
    { TYPE_STRING,    TYPE_MORE_PARAMETERS },  // askdir
    { TYPE_STRING,    TYPE_MORE_PARAMETERS },  // askfile
    { TYPE_STRING,    TYPE_MORE_PARAMETERS },  // askstring
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // asknumber
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // askchoice
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // askoptions
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // askbool
    { TYPE_NUMBER,    TYPE_MORE_PARAMETERS },  // askdisk
    { TYPE_STRING,    TYPE_MORE_STRINGS },  // cat
    { TYPE_NODEFINE,  TYPE_STRING, TYPE_STRING | TYPE_OPTIONAL },  // database
    { TYPE_NUMBER,    TYPE_STRING, TYPE_PARAMETER },  // exists
    { TYPE_STRING,    TYPE_STRING },  // expandpath
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING },  // earlier
    { TYPE_STRING,    TYPE_STRING },  // fileonly
    { TYPE_STRING,    TYPE_STRING, TYPE_STRING },  // getassign
    { TYPE_STRING,    TYPE_STRING },  // getdevice
    { TYPE_NODEFINE,  TYPE_NODEFINE },  // getdiskspace
    { TYPE_STRING,    TYPE_STRING },  // getenv
    { TYPE_NUMBER,    TYPE_STRING },  // getsize
    { TYPE_NUMBER,    TYPE_STRING },  // getsum
    { TYPE_NUMBER,    TYPE_STRING, TYPE_PARAMETER },  // getversion
    { TYPE_STRING,    TYPE_STRING },  // pathonly
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING },  // patmatch
    { TYPE_NODEFINE,  TYPE_NUMBER, TYPE_MORE_ANYTHING },  // select
    { TYPE_NUMBER,    TYPE_STRING },  // strlen
    { TYPE_STRING,    TYPE_STRING, TYPE_STRING, TYPE_NUMBER | TYPE_OPTIONAL },  // substr
    { TYPE_STRING,    TYPE_MORE_STRINGS },  // transcript
    { TYPE_STRING,    TYPE_STRING, TYPE_STRING },  // tackon
    { TYPE_PARAMETER, TYPE_NODEFINE },  // all
    { TYPE_PARAMETER, TYPE_STRING },  // append
    { TYPE_PARAMETER, TYPE_NODEFINE },  // assigns
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // choices
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // command
    { TYPE_PARAMETER, TYPE_ANYTHING },  // confirm
    { TYPE_PARAMETER, TYPE_ANYTHING },  // default
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // delopts
    { TYPE_PARAMETER, TYPE_STRING },  // dest
    { TYPE_PARAMETER, TYPE_NODEFINE },  // disk
    { TYPE_PARAMETER, TYPE_NODEFINE },  // files
    { TYPE_PARAMETER, TYPE_NODEFINE },  // fonts
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // help
    { TYPE_PARAMETER, TYPE_STRING },  // include
    { TYPE_PARAMETER, TYPE_NODEFINE },  // infos
    { TYPE_PARAMETER, TYPE_STRING },  // newname
    { TYPE_PARAMETER, TYPE_STRING },  // newpath
    { TYPE_PARAMETER, TYPE_NODEFINE },  // nogauge
    { TYPE_PARAMETER, TYPE_NODEFINE },  // noposition
    { TYPE_PARAMETER, TYPE_NODEFINE },  // noreq
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // optional
    { TYPE_PARAMETER, TYPE_STRING },  // pattern
    { TYPE_PARAMETER, TYPE_MORE_STRINGS },  // prompt
    { TYPE_PARAMETER, TYPE_NODEFINE },  // quiet
    { TYPE_PARAMETER, TYPE_NUMBER, TYPE_NUMBER },  // range
    { TYPE_PARAMETER, TYPE_NODEFINE },  // resident
    { TYPE_PARAMETER, TYPE_NODEFINE },  // safe
    { TYPE_PARAMETER, TYPE_STRING },  // setdefaulttool
    { TYPE_PARAMETER, TYPE_NUMBER },  // setstack
    { TYPE_PARAMETER, TYPE_STRING, TYPE_ANYTHING },  // settooltype
    { TYPE_PARAMETER, TYPE_STRING },  // source
    { TYPE_PARAMETER, TYPE_NODEFINE },  // swapcolors
    { TYPE_NODEFINE,  TYPE_NODEFINE },  // symbolset
    { TYPE_NODEFINE,  TYPE_STRING },  // symbolval
    { TYPE_NODEFINE,  TYPE_MORE_PARAMETERS },  // iconinfo
    { TYPE_PARAMETER, TYPE_NUMBER, TYPE_NUMBER },  // setposition
    { TYPE_PARAMETER, TYPE_STRING, TYPE_STRING },  // gettooltype
    { TYPE_PARAMETER, TYPE_STRING },  // getdefaulttool
    { TYPE_PARAMETER, TYPE_STRING },  // getstack
    { TYPE_PARAMETER, TYPE_STRING, TYPE_STRING },  // getposition
    { TYPE_NUMBER,    TYPE_NUMBER },  // simulate-error
    { TYPE_NUMBER,    TYPE_NODEFINE },  // beep
    { TYPE_NUMBER,    TYPE_ANYTHING, TYPE_ANYTHING },  // compare
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // findboard
    { TYPE_NODEFINE,  TYPE_NODEFINE, TYPE_NODEFINE },  // let
    { TYPE_NUMBER,    TYPE_PARAMETER | TYPE_OPTIONAL },  // reboot
    { TYPE_NUMBER,    TYPE_NUMBER },  // delay
    { TYPE_NUMBER,    TYPE_MORE_ANYTHING },  // swing
    { TYPE_NUMBER,    TYPE_NUMBER, TYPE_NUMBER },  // random
    { TYPE_NODEFINE,  TYPE_STRING, TYPE_STRING, TYPE_ANYTHING },  // put-property
    { TYPE_NODEFINE,  TYPE_STRING, TYPE_STRING },  // get-property
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING },  // remove-property
    { TYPE_NUMBER,    TYPE_STRING },  // save-property-object
    { TYPE_NUMBER,    TYPE_STRING },  // read-property-object
    { TYPE_NUMBER,    TYPE_ANYTHING },  // cast-int
    { TYPE_STRING,    TYPE_ANYTHING },  // cast-string
    { TYPE_NODEFINE,  TYPE_ANYTHING | TYPE_OPTIONAL },  // nop
    { TYPE_NUMBER,    TYPE_NODEFINE },  // flushlibs

    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER },  // effect
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_MORE_STRINGS },  // showmedia
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING, TYPE_STRING },  // setmedia
    { TYPE_NUMBER,    TYPE_STRING },  // closemedia
    { TYPE_NUMBER,    TYPE_NODEFINE },  // trace
    { TYPE_NUMBER,    TYPE_NODEFINE },  // retrace
    { TYPE_NUMBER,    TYPE_STRING, TYPE_STRING },  // querydisplay
    { TYPE_PARAMETER, TYPE_NODEFINE },  // back
    { TYPE_NUMBER,    TYPE_STRING, TYPE_MORE_PARAMETERS },  // openwbobject
    { TYPE_NUMBER,    TYPE_STRING },  // showwbobject
    { TYPE_NUMBER,    TYPE_STRING },  // closewbobject
    { TYPE_STRING,    TYPE_STRING | TYPE_OPTIONAL },  // currentdir

    { TYPE_STRING,    TYPE_STRING, TYPE_STRING },  // setenv

  };

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

/*
extern "C" INIT_1_Module(struct InstallerNGModuleBase *modulebase)
{
  modulebase->ingm_Magic = INSTALLERNGMODULE_MAGIC;
  return 0;
}

extern "C" EXIT_1_Module(struct InstallerNGModuleBase *modulebase)
{
  return 0;
}
*/

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

// get the string for a givent type
static char *ti_TypeToString(ULONG type);
static char *ti_TypeToString(ULONG type)
{
  char *typetext;

  switch (type)
  {
    case TYPE_MORE_STRINGS:
    case TYPE_STRING:          { typetext = "STRING"; break; }

    case TYPE_MORE_NUMBERS:
    case TYPE_NUMBER:          { typetext = "NUMBER"; break; }

    case TYPE_MORE_PARAMETERS:
    case TYPE_PARAMETER:       { typetext = "PARAMETER"; break; }

    case TYPE_MORE_ANYTHING:
    case TYPE_ANYTHING:        { typetext = "STRING/NUMBER"; break; }

    case TYPE_UNKNOWN:
    case TYPE_MORE_THRESHOLD:
    default:                   { typetext = "«UNKNOWN»"; break; }
  }

  return typetext;
}

// finally ... print something
static void ti_Print(char *text);
static void ti_Print(char *text)
{
  KPrintF(text, 0);
}

// this function prints a formatted text like "<text> at <src-position>"
static void ti_PrintText(char *text, struct TreeNode *node);
static void ti_PrintText(char *text, struct TreeNode *node)
{
  ULONG params[] = { (ULONG) text,
                     (ULONG) node->tn_SrcLine,
                     (ULONG) node->tn_SrcChar
                   };

  char *fmttext = sav_StringF("%s at [%ld,%ld]\n", &params);
  if (fmttext)
  {
    ti_Print(fmttext);
    FreeVec(fmttext);
  }
}

// print out a type
static void ti_PrintType(ULONG type, struct TreeNode *node);
static void ti_PrintType(ULONG type, struct TreeNode *node)
{
  ti_PrintText(ti_TypeToString(type), node);
}

// notify a type conflict
static void ti_PrintTypeConflict(ULONG expectedtype, ULONG foundtype, struct TreeNode *node);
static void ti_PrintTypeConflict(ULONG expectedtype, ULONG foundtype, struct TreeNode *node)
{
  ULONG params[] = { (ULONG) ti_TypeToString(expectedtype),
                     (ULONG) ti_TypeToString(foundtype)
                   };

  char *fmttext = sav_StringF("Type conflict: expected %s, found %s", &params);
  if (fmttext)
  {
    ti_PrintText(fmttext, node);
    FreeVec(fmttext);
  }
}

// return the SymbolTableNode of an identifier
static struct SymbolTableNode *ti_GetSymNode(char *symname, struct ModuleEnvironment *me);
static struct SymbolTableNode *ti_GetSymNode(char *symname, struct ModuleEnvironment *me)
{
  // call the GetSymbolHook, which delivers the entry of the symtab for any given identifyer or NULL
  me->me_GetSymbolHook.h_Data = symname;
  return (struct SymbolTableNode *) CallHookPkt(&me->me_GetSymbolHook, NULL, NULL);
}

// return the PROCEDURE's tree for a given identifier
static struct TreeNode *ti_GetProcNode(char *procname, struct ModuleEnvironment *me);
static struct TreeNode *ti_GetProcNode(char *procname, struct ModuleEnvironment *me)
{
  struct TreeNode *procnode = (struct TreeNode *) me->me_Procedures;
  while (procnode = (struct TreeNode *) sav_GetSucc((struct Node *) procnode))
  {
    struct TreeNode *firstchild = (struct TreeNode *) sav_GetHead(&procnode->tn_Childs);
    if (!strcmp(procname, firstchild->tn_Value.tnv_IdentName)) { return procnode; }
  }

  return(NULL);
}

// more prototypes
static ULONG ti_CheckIdent(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckTree(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckFunction(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckExprList(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckProcedure(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckFmtString(struct TreeNode *node, struct ModuleEnvironment *me);
static ULONG ti_CheckTreeListTail(struct Node *node, struct ModuleEnvironment *me);

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

// the entry for this function call
ULONG ingm_PerformAction(register __a0 struct ModuleEnvironment *me)
{
  // now check the main tree
  ti_CheckTree(me->me_TreeRoot, me);

  //
  return 0L;
}

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

// starting with the given node's successor, check all the nodes
static ULONG ti_CheckTreeListTail(struct Node *node, struct ModuleEnvironment *me)
{
  ULONG type = TYPE_UNKNOWN;

  while (node = sav_GetSucc(node))
  {
    type = ti_CheckTree((struct TreeNode *) node, me);
  }

  return type;
}

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

static ULONG ti_CheckTree(struct TreeNode *node, struct ModuleEnvironment *me)
{
  ULONG type = TYPE_UNKNOWN;

  // if there is enough free stack space, we go on traversing the tree
  if (sav_StackLeft() < MINIMUM_FREE_STACKSPACE)
  {
    ti_Print("\n\n----> ERROR: NO FREE STACKSPACE LEFT <----\n\n");
  }

  // check for user break
  else if (SIGBREAKF_CTRL_C & CheckSignal(SIGBREAKF_CTRL_C))
  {
    ti_Print("\n\n----> USER BREAK BY CTRL-C <----\n\n");
  }

  // look at the node type and call the print function for one specific node
  else
  {
    switch (node->tn_Type)
    {
      // find the the type of an identifier
      case NODE_IDENT:      { type = ti_CheckIdent(node, me); break; }

      // find the type of an procedure
      case NODE_FUNIDENT:   { type = ti_CheckProcedure(node, me); break; }

      // find the type of a function
      case NODE_FUNCTION:   { type = ti_CheckFunction(node, me); break; }

      // basic types...
      case NODE_STRING:     { type = TYPE_STRING; break; }
      case NODE_NUMBER:     { type = TYPE_NUMBER; break; }

      // find the type of an expressionlist
      case NODE_EXPRLIST:   { type = ti_CheckExprList(node, me); break; }

      // find the type of the root (does not matter, but evaluate all the subtypes)
      case NODE_ROOT:       { type = ti_CheckTreeListTail((struct Node *) &node->tn_Childs, me); break; }

      // find the type of a format string function (trivial, but check the args!)
      case NODE_FORMATSTR:  { type = ti_CheckFmtString((struct TreeNode *) &node->tn_Childs, me); break; }

      // find the type
      case NODE_FIRSTEXPR:  { break; }

      // well....
      default:              { }
    }
  }

  // ti_PrintType(type, node);
  return type;
}

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

// return the hereby evaluated type! note: you MUST use the sn_UserData field
static ULONG ti_CheckIdent(struct TreeNode *node, struct ModuleEnvironment *me)
{
  // get the symtab entry for that identifier
  struct SymbolTableNode *stn = ti_GetSymNode(node->tn_Value.tnv_IdentName, me);

  // if the ident has a builtin type (ie its a builtin variable) we use that type,
  // otherwise we use our assigned type
  return stn ? (ULONG) ( stn->sn_Type != TYPE_UNKNOWN ? (ULONG) stn->sn_Type - 400 : (ULONG) stn->sn_UserData) : TYPE_UNKNOWN ;
}

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

// check an entire function call to builtin functions
static ULONG ti_CheckFunction(struct TreeNode *node, struct ModuleEnvironment *me)
{
  // dont forget a nodes childes
  if (0 == sav_ListLen((struct List *) &node->tn_Childs)) { }
  else
  {
    int i=1;
    struct TreeNode *argnode = (struct TreeNode *) &node->tn_Childs;

    // SET function nodes are special, cause they set types to identifiers!
    if (node->tn_Value.tnv_FunID == FUN_SET)
    {
      struct SymbolTableNode *stn;

      // there are always IDENT-VALUE pairs of nodes! we can assume this, otherwise
      // the script would be defect (fucking lazycompile option!)
      while (argnode = (struct TreeNode *) sav_GetSucc((struct Node *) argnode))
      {
        stn = ti_GetSymNode(argnode->tn_Value.tnv_IdentName, me);
        stn->sn_UserData = (APTR) ti_CheckTree(argnode = (struct TreeNode *) sav_GetSucc((struct Node *) argnode), me);
      }

      // the type of this SET depends on the type of the last expression!
      functionTypes[FUN_SET-1][0] = (unsigned char) stn->sn_UserData;
    }

    else
    {
      // first check all the arguments for their correct type
      while (argnode = (struct TreeNode *) sav_GetSucc((struct Node *) argnode))
      {
        // this is the type of the argument
        ULONG needed_argtype = functionTypes[node->tn_Value.tnv_FunID-1][i],
              argtype = ti_CheckTree(argnode, me);

        // before we modify the needed_argtype, lets increment i
        if (needed_argtype < TYPE_MORE_THRESHOLD) { i += 1; }
        else                                      { needed_argtype -= TYPE_MORE_THRESHOLD; }

        // ignore, if we did not define it (yet)
        if      (needed_argtype == TYPE_NODEFINE) { ti_PrintText("No type-specification yet", argnode); }
        else if (argtype == TYPE_NODEFINE)        { ti_PrintText("Warning: could not resolve static type", argnode); }
        else
        {
          // accepting "anything" means whether STRING or NUMBER
          if (needed_argtype == TYPE_ANYTHING)
          {
            switch (argtype)
            {
              case TYPE_NUMBER:
              case TYPE_STRING:    { break; }
              case TYPE_PARAMETER:
              default:             { ti_PrintTypeConflict(needed_argtype, argtype, argnode); break; }
            }
          }

          // check for the right type
          else if (argtype != needed_argtype) { ti_PrintTypeConflict(needed_argtype, argtype, argnode); }
        }
      }
    }
  }

  // after checking the arguments, we can return the type
  // of the function itself
  return functionTypes[node->tn_Value.tnv_FunID-1][0];
}

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

// yes, i know it is somehow weird, but thats Installer's language. if we find
// such an identifier, it can be either a procedure call or, if a procedure with
// this name does not exist, a string formatting.
static ULONG ti_CheckProcedure(struct TreeNode *node, struct ModuleEnvironment *me)
{
  ULONG type = TYPE_UNKNOWN;

  // try to find the procedures tree
  struct TreeNode *proc = ti_GetProcNode(node->tn_Value.tnv_IdentName, me);

  if (!proc) { type = ti_CheckFmtString(node, me); }
  else
  {
    ti_PrintText("Checking PROCEDURE call is not yet supported", node);
  }

  return type;
}

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

// result-type of an expressionlist is the type of the last evaluated in this list
static ULONG ti_CheckExprList(struct TreeNode *node, struct ModuleEnvironment *me)
{
  // go to end of list and now get the type of the last expression
  struct Node *child = sav_GetLastListNode((struct List *) &node->tn_Childs);
  return child ? ti_CheckTree((struct TreeNode *) child, me) : TYPE_UNKNOWN ;
}

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

// this delivers always a STRING but accepts only STRINGs and NUMBERs
static ULONG ti_CheckFmtString(struct TreeNode *node, struct ModuleEnvironment *me)
{
  struct TreeNode *argnode = (struct TreeNode *) &node->tn_Childs;
  while (argnode = (struct TreeNode *) sav_GetSucc((struct Node *) argnode))
  {
    // get the type of the argument
    ULONG argtype = ti_CheckTree(argnode, me);

    // check the type
    if (argtype != TYPE_STRING && argtype != TYPE_NUMBER) { ti_PrintTypeConflict(TYPE_ANYTHING, argtype, argnode); }
    else                                                  { }
  }

  // return STRING
  return TYPE_STRING;
}


