/* class comment */!!

inherit(Object, #CLIExecEngine, #(aliasTable /* alias (name, value) dictionary */
serverHandle  /* cli server who's driving us */
systemVariableTable /* system variables */
expressionEcho /* true if expr values shown */
argumentTupleTable /* holds arg tuples */
argumentTupleIndex /* index of active tuple */
workDir  /* work dir name for include, log */
stopWatch /* CLIStopWatch instance */), 2, nil)!!

setClassVars(CLIExecEngine, #(execEngine /* used for class method to return handle */
$systemAliasTable  /* holds system alias values */))!!

now(class(CLIExecEngine))!!

/* return system alias table */
Def $systemAliasTable(self)
{
  ^$systemAliasTable;
}
!!

/* add system alias */
Def addSystemAlias(self, key, value)
{
  if at($systemAliasTable, asLowerCase(key))
    displayFormattedError(ErrorTextLibClass$Inst,
        ER_CLI_INTERNAL, FORCE_POPUP, "Duplicated System Alias:",
        " " + asLowerCase(key), nil);
    ^nil;
  endif;
  add($systemAliasTable, asLowerCase(key), asLowerCase(value));
  ^0;
}
!!

/* return handle */
Def getExecutionEngine(self)
{
  ^execEngine;
}
!!

/* return system alias */
Def getSystemAlias(self, key)
{
  ^at($systemAliasTable, asLowerCase(key));
}
!!

/* return system alias table */
Def getSystemAliasTable(self)
{
  ^$systemAliasTable;
}
!!

/* create, initialize execution engine object. */
Def open(self | temp)
{
  temp := new(CLIExecEngine);
  initialize(temp);
  ^temp;
}
!!

now(CLIExecEngine)!!

/* set the log file to overwrite. */
Def overwriteLog(self)
{
  setAppendOff(presenter(serverHandle));
  ^0;
}
!!

/* set the log file to append. */
Def appendLog(self)
{
  setAppendOn(presenter(serverHandle));
  ^0;
}
!!

/* perform lap function on the stopwatch */
Def lapTimer(self | temp)
{
  temp := lap(stopWatch);
  ^temp;
}
!!

/* stop the stopwatch */
Def stopTimer(self)
{
  ^stop(stopWatch)
}
!!

/* start the stopwatch */
Def startTimer(self)
{
  ^start(stopWatch)
}
!!

/* apply workDir to incoming filename. */
Def applyWorkDir(self, string | lastChar temp)
{
  /* if no work dir, or if this already is a path name, nothing to apply */
  if not(workDir) cor (size(workDir) = 0) cor not(isFilename?(self, string))
    ^string;
  endif;

  /* otherwise, create a pathname */
  lastChar := workDir[size(workDir) - 1];
  if (lastChar = '/') cor (lastChar = '\')
    temp := workDir + string;
  else
    temp := workDir + "\" + string;
  endif;
  ^temp;
}
!!

/* add an argument to the currently open arg tuple */
Def argTupleAddArg(self, varFlag, value)
{
  if not(argumentTupleIndex)
    displayFormattedError(ErrorTextLibClass$Inst,
        ER_CLI_INTERNAL, FORCE_POPUP, "argument table not currently open",
        "CLIExecEngine:argTupleAddArg", nil);
  else
    add(argumentTupleTable[argumentTupleIndex], tuple(varFlag, value));
  endif;
}
!!

/* close (inactivate) the arg tuple entry */
Def argTupleCloseTuple(self)
{
  argumentTupleIndex := nil;
}
!!

/* get an arg tuple from the table */
Def argTupleGetArgTuple(self, index)
{
  if index < 0 cor index > (size(argumentTupleTable) - 1)
    displayFormattedError(ErrorTextLibClass$Inst,
        ER_CLI_INTERNAL, FORCE_POPUP, "index out of range",
        "CLIExecEngine:argTupleGetArgTuple", nil);
    ^nil
  else
    ^argumentTupleTable[index];
  endif;
}
!!

/* initialize the arg tuple structure */
Def argTupleInitTable(self)
{
  argumentTupleTable := new(OrderedCollection, 4);
  argumentTupleIndex := 0;
}
!!

/* open a new arg tuple entry */
Def argTupleOpenTuple(self)
{
  add(argumentTupleTable, new(OrderedCollection, 4));
  argumentTupleIndex := size(argumentTupleTable) - 1;
  ^argumentTupleIndex;
}
!!

/* perform assignment operation, return value. */
Def assignValue(self, key, value)
{
  setLValue(self, key, value);
  ^value;
}
!!

/* map logical from actor to C: nil -> 0, !nil -> 1 */
Def aTrue(self, expr)
{
  if expr
    ^1;
  else
    ^0
  endif;
}
!!

/* call chkAbort; minimize name to minimize actor source volume. */
Def cA(self | temp)
{
  /* cA is only called at the end of a CLI stmt, so clear flag */
  serverCallFlagClear(serverHandle);
  ^chkAbort(self);
}
!!

/* abort command if true returned from queryAbort */
Def chkAbort(self)
{
  /* check for abort if this is not a server call, or if it is a server
     call that has not already aborted */
  if (serverCallFlagClear?(serverHandle) cor
     serverCallFlagSet?(serverHandle))
  then
    if (TaskLibClass$Inst cand queryAbort(TaskLibClass$Inst) )
      syntaxError(self, nil, getFormattedError(ErrorTextLibClass$Inst,
         ER_CLI_USER_ABORT, nil, nil, nil));
      /* syntaxError unravels call stack, and does not return */
    endif;
  endif;
  ^nil;
}
!!

/* return single string equivalent of source line, double quotes cleaned up. */
Def cleanString(self, istring | index)
{
  index := 0;
  loop
  while (index := indexOf(istring, '"', index))
  begin
    /* beware: this is actually inserting a literal actor call into string */
    istring := insert(istring, doubleQuoteString(CLIUtilities) +
      "+doubleQuoteString(CLIUtilities)+", index);
    /*          1         2         3    */
    /* 123456789012345678901234567890123 */
    index := index + 35;
        /* increment count:
           1 for double quote.
          33 for "+doubleQuoteString(CLIUtilities)+"
           1 to get beyond last char
          --
          35 */
  endLoop;
  ^istring;
}!!

/* clear transcript window */
Def clearTranscript(self)
{
  clearTranscript(presenter(serverHandle));
  ^0;
}
!!

/* comment */
Def close(self)
{
  execEngine := nil;
}
!!

/* convert incoming value to printable form. */
Def convertToString(self, value | u16 u10 s10 u8)
{
  select
    case testString(self, value)
    is
      ^asString(value);
    endCase
    case testInteger(self, value)
    is
      u16 := "0x" + asUnsignedStringRadix(asLong(value), 16);
      u10 := asUnsignedStringRadix(asLong(value), 10);
      s10 := asStringRadix(asLong(value), 10);
      u8  := "0" + asUnsignedStringRadix(asLong(value), 8);
      if u10 = s10
        ^(u16 + " " + u10 + " " + u8);
      else
        ^(u16 + "  " + u10 + " (" + s10 + ")  " + u8);
      endif;
    endCase
    default
      ^"";
  endSelect;

}
!!

/* map logical from C to actor: 0 -> nil, !0 -> 0 */
Def cTrue(self, expr)
{
  if expr = 0
    ^nil;
  else
    ^0
  endif;
}
!!

/* delete an alias from the table.
   NOTE:  calling remove directly on a absent key results in a
          (YUK) error box. */
Def deleteAlias(self, key)
{
  if at(aliasTable, key)
  then
    remove(aliasTable, key)
  endif;
  ^0;
}
!!

/* remove the value from the global variable table. */
Def deleteGlobalVariable(self, key)
{
  ^removeUsing(globalVariableTable, key, {});
}
!!

/* delete the variable. */
Def deleteVariable(self, key | val)
{
  if not(deleteGlobalVariable(self, key))
    listVariableLine(self, key, "<undefined>", nil);
  endif;
  ^0;
}
!!

/* display error text, return value. */
Def displayErrorText(self, value)
{
  /* just return the value; the error has already been displayed by
     the CLI server call */
  ^value;

  /* obsolete code...
  displayError(ErrorTextLibClass$Inst, value, FORCE_CLI);
  ^0;
  */
}
!!

/* display logical value, return value */
Def displayLogical(self, value)
{
  displayValue(self, aTrue(self, value));
  ^value;
}
!!

/* display value, return value */
Def displayValue(self, value | signString)
{
  outputResults(self, convertToString(self, value));
  ^value;
}
!!

/* true if expressionEcho is 0 */
Def expressionEcho(self)
{
  ^(expressionEcho = 0);
}
!!

/* increment number of times echo has been turned off */
Def expressionEchoOff(self)
{
  expressionEcho := expressionEcho + 1;
}
!!

/* decrement number of times echo has been turned off */
Def expressionEchoOn(self)
{
  expressionEcho := expressionEcho - 1;
}
!!

/* set expression echo to on. */
Def expressionEchoReset(self)
{
  expressionEcho := 0;
}
!!

/* extract command name from servername_commandname */
Def extractCommandName(self, string | index)
{ if (not(string) cor (size(string) = 0))
    ^nil;
  endif;
  index := indexOf(string, '_', 0);
  /* error if missing separator */
  if (not(index) cor ((index+1) = (size(string))))
  then
    ^nil;
  endif;
  ^subString(string, index+1, size(string));
}!!

/* extract servername from servername_commandname string */
Def extractServerName(self, string | index)
{ if (not(string) cor (size(string) = 0))
    ^nil;
  endif;
  index := indexOf(string, '_', 0);
  if (not(index) cor (index = 0))   /* error if missing separator */
  then
    ^nil;
  endif;
  ^subString(string, 0, index);
}!!

/* retrieve alias value from table. */
Def getAlias(self, key, system | temp)
{
  /* check system aliases first; only check if requested */
  if system
    if (temp := getSystemAlias(CLIExecEngine, key))
      ^temp;
    endif;
  endif;
  /* return user alias otherwise */
  ^at(aliasTable, asLowerCase(key));
}
!!

/* return the value of a variable from the global variable table. */
Def getGlobalVariableValue(self, key)
{
  ^at(globalVariableTable, key);
}
!!

/* return the value of a variable.  If variable doesn't exist,
   call error routine which immediately halts execution. */
Def getRValue(self, key | val errorMessage)
{
  if (val := getVariableValue(self, key))
    ^val[0];
  endif;

  /* if not found, error */
  errorMessage := getFormattedError(ErrorTextLibClass$Inst,
     ER_CLI_UNDECL_VAR, asciiz(key), nil, nil);
  syntaxError(self, nil, errorMessage);
  /* syntaxError unravels call stack, and does not return */
}
!!

/* return the value of a variable from the system variable table. */
Def getSystemVariableValue(self, key)
{
  ^at(systemVariableTable, key);
}
!!

/* return the value of a variable, and whether it comes from the
   global (nil) or system (0) table. */
Def getVariableValue(self, key | val)
{
  /* check system variable table first */
  if (val := getSystemVariableValue(self, key))
    ^tuple(val, 0);
  endif;

  /* then check global variable table */
  if (val := getGlobalVariableValue(self, key))
    ^tuple(val, nil);
  endif;

  ^nil
}
!!

/* return workDir. */
Def getWorkDir(self)
{
  ^workDir;
}
!!

/* help command */
Def help(self, topic, internal)
{
  if topic
    helpOnOne(serverHandle, topic, internal);
  else
    helpOnAll(serverHandle, internal);
  endif;
  ^0;
}
!!

/* help command */
Def helpSave(self, topic, internal)
{
  if topic
  then
    helpOnOne(serverHandle, topic, internal);
  else
    helpOnAll(serverHandle, internal);
  endif;
  ^0;
}
!!

/* initialize */
Def initialize(self)
{
  execEngine := self;
  aliasTable := new(OrderedDictionary, 4);
  setOrderClass(aliasTable, SortedCollection);
  globalVariableTable := new(OrderedDictionary, 4);
  setOrderClass(globalVariableTable, SortedCollection);
  systemVariableTable := new(OrderedDictionary, 4);
  setOrderClass(systemVariableTable, SortedCollection);
  setSystemVariableValue(self, "SHELL_STATUS", 0);
  argTupleInitTable(self);
  stopWatch := new(CLIStopWatch);
}
!!

/* return true if candidate filename contains neither "/" nor "\" */
Def isFilename?(self, string)
{
  if string
    ^not( find(string, "/", 0) cor find(string, "\", 0) );
  endif;
  ^0;
}
!!

/* List one or all aliases and values */
Def listAlias(self, key | text value)
{
  if key
  then
    /* process a single alias */
    if (value := getAlias(self, key, nil))
    then
      text := listAliasEntry(self, key, value);
    else
      text := listAliasEntry(self, key, "<not defined>");
    endif;
    outputResults(presenter(serverHandle), text);
  else
    /* list all of the aliases in sorted order */
    assocsDo(aliasTable,
      {using(assoc)
        outputResults(presenter(serverHandle),
          listAliasEntry(self, key(assoc), value(assoc)));
      });
  endif;
  ^0;
}
!!

/* format and return an alias entry. */
Def listAliasEntry(self, key, value)
{
  ^(key + ":  " + doubleQuoteString(CLIUtilities) +
    value + doubleQuoteString(CLIUtilities));
}
!!

/* list the variable and the value for the key; if key is null, list all. */
Def listVariable(self, key | val)
{
  /* list specific var */
  if key
    val := getVariableValue(self, key);
    if val
      listVariableLine(self, key, asString(val[0]), val[1]);
    else
      listVariableLine(self, key, "<undefined>", nil);
    endif;
  else

    /* list all system vars first */
    if (size(systemVariableTable) > 0)
      assocsDo(systemVariableTable,
        {using(assoc)
          listVariableLine(self, key(assoc), asString(value(assoc)), 0);
        });
    endif;

    /* list all global vars next */
    if (size(globalVariableTable) > 0)
      assocsDo(globalVariableTable,
        {using(assoc)
          listVariableLine(self, key(assoc), asString(value(assoc)), nil);
        });
    endif;
  endif;
  ^0;
}
!!

/* format and output variable line. */
Def listVariableLine(self, key, value, system)
{
  if system
    outputResults(self, "(system) $" + key + " = " + asString(value));
  else
    outputResults(self, "         $" + key + " = " + asString(value));
  endif;
}
!!

/* return C value of logical and operation (C "&&") */
Def logicalAnd(self, arg1, arg2)
{
  if cTrue(self, arg1) cand cTrue(self, arg2)
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical equal to operation (C "==") */
Def logicalEqual(self, arg1, arg2)
{
  if arg1 = arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical greater than operation (C ">") */
Def logicalGreater(self, arg1, arg2)
{
  if arg1 > arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical greater or equal operation (C ">=") */
Def logicalGreaterEq(self, arg1, arg2)
{
  if arg1 >= arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical less than operation (C "<") */
Def logicalLess(self, arg1, arg2)
{
  if arg1 < arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical less or equal operation (C "<=") */
Def logicalLessEq(self, arg1, arg2)
{
  if arg1 <= arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical not operation (C "!") */
Def logicalNot(self, arg)
{
  if arg = 0
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical not equal to operation (C "!=") */
Def logicalNotEqual(self, arg1, arg2)
{
  if arg1 <> arg2
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* return C value of logical or operation (C "||") */
Def logicalOr(self, arg1, arg2)
{
  if cTrue(self, arg1) cor cTrue(self, arg2)
  then
    ^1
  else
    ^0;
  endif;
}
!!

/* output the results line if show results selected. */
Def outputResults(self, text)
{
  outputResults(presenter(serverHandle), text);
  ^0;
}
!!

/* respond to CLI "exit" command. */
Def presenterExit(self)
{
  exitFlagSet(presenter(serverHandle));  /* we want to exit */
  abortCommand(serverHandle);  /* abort command processing immediately */
}
!!

/* placeholder for primitive bitwise negation function */
Def primBitwiseNegation(self, arg | value)
{
  if not(value := bitwiseNegation(CLIULibraryClass$Inst, arg))
  then
    ^0;  /* error reporting done by library */
  endif;
  ^value;
}!!

/* left shift function. */
Def primLeftShift(self, arg1, arg2 | value)
{
  if not(value := leftShift(CLIULibraryClass$Inst, arg1, arg2))
  then
    ^0;  /* error reporting done by library */
  endif;
  ^value;
}!!

/* right shift function. */
Def primRightShift(self, arg1, arg2 | value)
{
  if not(value := rightShift(CLIULibraryClass$Inst, arg1, arg2))
  then
    ^0;  /* error reporting done by library */
  endif;
  ^value;
}!!

/* register the server with the parser; parser occasionally needs access. */
Def registerServerHandle(self, s)
{
  serverHandle := s;
}
!!

/* re-initialize after a break in processing */
Def reInit(self)
{
}
!!

Def restoreSetup(self, filename)
{
  restoreSetup(presenter(serverHandle), filename, nil);
  ^0;
}
!!

/* comment */
Def saveSetup(self, filename)
{
  saveSetup(presenter(serverHandle), filename, nil);
  ^0;
}
!!

/* collect and format arguments prior to making a server call */
Def serverCall(self, fullCommandName, argTupleNumber |
  serverName commandName index argv argumentString argTuple arg value valueString)
{
  serverName := extractServerName(self, fullCommandName);
  commandName := extractCommandName(self, fullCommandName);
  if not(serverName) cor not(commandName)
  then
    syntaxError(self, nil, getFormattedError(ErrorTextLibClass$Inst,
       ER_CLI_BAD_SERVER_COMMAND, asciiz(serverName), asciiz(commandName),
       nil));
    /* syntaxError unravels call stack, and does not return */
  endif;
  /* get argument tuple */
  argTuple := argTupleGetArgTuple(self, argTupleNumber);
  if not(argTuple)
    displayFormattedError(ErrorTextLibClass$Inst,
        ER_CLI_INTERNAL, FORCE_POPUP, "Argument tuple not found",
        "CLIExecEngine:serverCall", nil);
  endif;
  /* construct arguments */
  argumentString := asciiz(commandName);
  argv := new(OrderedCollection, size(argTuple)+1);  /* holds start positions */
  add(argv, 0);  /* start position of commandName */
  /* place each argument in argv */
  index := 0;
  loop
  while index < size(argTuple)
  begin
    arg := argTuple[index];
    /* arg[0] -> variable, not(arg[0]) -> string */
    if arg[0]
      value := getRValue(self, arg[1]);
      if not(value)
        displayFormattedError(ErrorTextLibClass$Inst,
            ER_CLI_INTERNAL, FORCE_POPUP, "Value not found",
            "CLIExecEngine:serverCall", nil);
      endif;
      /* if its a string variable, or its negative, just convert to string */
      if (value = asString(value)) cor (negative(value))
         valueString := asString(value);
      else
         /* make it hex if possible */
         valueString := "0x" + asUnsignedStringRadix(asLong(value), 16);
      endif;      
    else
      valueString := asString(arg[1]);
    endif;
    add(argv, size(argumentString));  /* start index of this element */
    argumentString := argumentString + asciiz(valueString);
    index := index + 1;
  endLoop;
  /* pass to server */
  ^callCServer(serverHandle, serverName, commandName,
    argumentString, (index+1) /* argc */, argv);
 }
!!

/* create a server call command string suitable to be passed to
   CliServer:processCliCommand.
   NOTE:  This is inefficient.  Since the command has already been fully
          parsed, it would be better to call a lower-level interface
          rather than have processCliCommand re-parse the string. */
Def serverCmdString(self, args | string)
{
  string := "";
  do(args,
    {using(el)
      if(el)
      then
        string := string + el + " ";
      endif;
    }
  );
  ^string;
}!!

/* return server. */
Def serverHandle(self)
{
  ^serverHandle;
}!!

/* add an alias to the alias table; overwrite if already there. */
Def setAlias(self, key, value)
{
  /* put replaces value if key exists, or inserts if key doesn't */
  put(aliasTable, value, key);
  ^0;
}
!!

/* set SHELL_STATUS to the C logical equivalent, and return the orig. value */
Def setCliStatusLogical(self, value)
{
  setSystemVariableValue(self, "SHELL_STATUS", aTrue(self, value));
  ^value;
}
!!

/* set the SHELL_STATUS user variable, and return value. */
Def setCliStatusValue(self, value)
{
  setSystemVariableValue(self, "SHELL_STATUS", value);
  ^value;
}
!!

/* comment */
Def setCommandOff(self)
{
  setCommandOff(presenter(serverHandle));
  ^0;
}
!!

Def setCommandOn(self)
{
  setCommandOn(presenter(serverHandle));
  ^0;
}
!!

/* If value already exists, set its value; otherwise, ignore the request. */
Def setCondLValue(self, key, value)
{
  if variableExists(self, key)
  then
    setLValue(self, key, value);
  endif;
  ^0;
}
!!

/* update a value in the global variable table. */
Def setGlobalVariableValue(self, key, value)
{
  put(globalVariableTable, value, key);
  ^0;
}
!!

/* set max history size */
Def setLogFileName(self, filename)
{
  setLogFileName(presenter(serverHandle), filename, nil);
  ^0;
}
!!

Def setLoggingOff(self)
{
  setLoggingOff(presenter(serverHandle), nil);
  ^0;
}
!!

Def setLoggingOn(self)
{
  setLoggingOn(presenter(serverHandle), nil);
  ^0;
}
!!

/* add or update a variable to the global variable table. */
Def setLValue(self, key, value)
{
  setGlobalVariableValue(self, key, value);
  ^0;
}
!!

/* set max history size */
Def setMaxHistorySize(self, historySize)
{
  setMaxHistorySize(presenter(serverHandle), historySize, nil);
  ^0;
}
!!

Def setResultsOff(self)
{
  setResultsOff(presenter(serverHandle));
  ^0;
}
!!

Def setResultsOn(self)
{
  setResultsOn(presenter(serverHandle));
  ^0;
}
!!

/* update a value in the system variable table. */
Def setSystemVariableValue(self, key, value)
{
  put(systemVariableTable, value, key);
  ^0;
}
!!

/* set workDir. */
Def setWorkDir(self, string)
{
  workDir := string;
  ^0;
}
!!

/* show command display status */
Def showCommand(self)
{
  if showCommand(presenter(serverHandle))
  then
    outputResults(self, "command echo ON");
  else
    outputResults(self, "command echo OFF");
  endif;
  ^0;
}
!!

/* this is called when the Yacc Machine detects a parsing error */
Def showError(self, loc0 | errorMessage)
{
  errorMessage := getFormattedError(ErrorTextLibClass$Inst,
     ER_CLI_SYNTAX, nil, nil, nil);
  syntaxError(self, nil, errorMessage);
}!!

/* show log file name */
Def showLogFileName(self | text temp)
{
  text := "log file name: ";
  if temp := logFileName(presenter(serverHandle))
  then
    text := text + asString(temp);
  else
    text := text + "<not set>";
  endif;
  outputResults(self, text);
  ^0;
}
!!

/* display logging state */
Def showLogging(self | text temp)
{
  if logging(presenter(serverHandle))
  then
    outputResults(self, "logging ON");
  else
    outputResults(self, "logging OFF");
  endif;
  ^0;
}
!!

/* show history size */
Def showMaxHistorySize(self | text temp)
{
  text := "history size: ";
  if temp := getMaxHistorySize(presenter(serverHandle))
  then
    text := text + asString(temp);
  else
    text := text + "<not set>";
  endif;
  outputResults(self, text);
  ^0;
}
!!

/* show results display status */
Def showResults(self)
{
  if showResults(presenter(serverHandle))
  then
    outputResults(self, "results display ON");
  else
    outputResults(self, "results display OFF");
  endif;
  ^0;
}
!!

/* show workDir. */
Def showWorkDir(self)
{
  if not(workDir) cor (workDir = "")
    outputResults(self, "work directory: <empty>");
  else
    outputResults(self, "work directory: " + workDir);
  endif;
  ^0;
}
!!

Def syntaxError(self, bp, str)
{
  ^syntaxError(serverHandle, bp, str)
}!!

/* return true if val is integer, false if not. */
Def testInteger(self, val)
{
  if not(val)
  then
    ^nil;
  endif;
  /* test for string first avoids syntax err if calling asInt(string) */
  if testString(self, val)
  then
    ^nil;
  endif;
  if (val = asLong(val)) cor (val = asInt(val))
  then
    ^0;
  else
    ^nil;
  endif;
}
!!

/* return 0 if val is string, false if not. */
Def testString(self, val)
{
  if not(val)
  then
    ^nil;
  endif;
  if val = asString(val)
  then
    ^0;
  else
    ^nil;
  endif;
}
!!

/* output timestamp */
Def timeStamp(self)
{
  outputResults(self, asLongString(current(Date)) + "  " +
    asString(current(Time)));
}
!!

/* return true if value exists, and false otherwise. */
Def variableExists(self, key)
{
  if getGlobalVariableValue(self, key)
  then
    ^0;
  endif;
  ^nil;
}
!!

/* CLIExecEngine Class Initialization Code */
/* define system alias table, add system aliases */
$systemAliasTable := new(OrderedDictionary, 20);
setOrderClass($systemAliasTable, SortedCollection);
