#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "setup.h"
#include "constant.h"
#include "argacces.h"
#include "constrct.h"
#include "cstrcpsr.h"
#include "envrnmnt.h"
#include "exprnpsr.h"
#include "fileutil.h"
#include "memalloc.h"
#include "multifld.h"
#include "pprint.h"
#include "prcdrfun.h"
#include "prcdrpsr.h"
#include "prntutil.h"
#include "router.h"
#include "scanner.h"
#include "strngrtr.h"
#include "symbol.h"
#include "sysdep.h"
#include "utility.h"
#include "commline.h"
#define NO_SWITCH 0
#define BATCH_SWITCH 1
#define BATCH_STAR_SWITCH 2
#define LOAD_SWITCH 3
#if ! RUN_TIME
static int DoString(const char *,int,bool *);
static int DoComment(const char *,int);
static int DoWhiteSpace(const char *,int);
static void DefaultGetNextEvent(Environment *);
#endif
static void DeallocateCommandLineData(Environment *);
void InitializeCommandLineData(
Environment *theEnv)
{
AllocateEnvironmentData(theEnv,COMMANDLINE_DATA,sizeof(struct commandLineData),DeallocateCommandLineData);
#if ! RUN_TIME
CommandLineData(theEnv)->BannerString = BANNER_STRING;
CommandLineData(theEnv)->EventCallback = DefaultGetNextEvent;
#endif
}
static void DeallocateCommandLineData(
Environment *theEnv)
{
#if ! RUN_TIME
if (CommandLineData(theEnv)->CommandString != NULL)
{ rm(theEnv,CommandLineData(theEnv)->CommandString,CommandLineData(theEnv)->MaximumCharacters); }
if (CommandLineData(theEnv)->CurrentCommand != NULL)
{ ReturnExpression(theEnv,CommandLineData(theEnv)->CurrentCommand); }
#else
#if MAC_XCD
#pragma unused(theEnv)
#endif
#endif
}
void RerouteStdin(
Environment *theEnv,
int argc,
char *argv[])
{
int i;
int theSwitch = NO_SWITCH;
if (argc < 3)
{ return; }
if (argv == NULL) return;
for (i = 1 ; i < argc ; i++)
{
if (strcmp(argv[i],"-f") == 0) theSwitch = BATCH_SWITCH;
#if ! RUN_TIME
else if (strcmp(argv[i],"-f2") == 0) theSwitch = BATCH_STAR_SWITCH;
else if (strcmp(argv[i],"-l") == 0) theSwitch = LOAD_SWITCH;
#endif
else if (theSwitch == NO_SWITCH)
{
PrintErrorID(theEnv,"SYSDEP",2,false);
WriteString(theEnv,STDERR,"Invalid option '");
WriteString(theEnv,STDERR,argv[i]);
WriteString(theEnv,STDERR,"'.\n");
}
if (i > (argc-1))
{
PrintErrorID(theEnv,"SYSDEP",1,false);
WriteString(theEnv,STDERR,"No file found for '");
switch(theSwitch)
{
case BATCH_SWITCH:
WriteString(theEnv,STDERR,"-f");
break;
case BATCH_STAR_SWITCH:
WriteString(theEnv,STDERR,"-f2");
break;
case LOAD_SWITCH:
WriteString(theEnv,STDERR,"-l");
}
WriteString(theEnv,STDERR,"' option.\n");
return;
}
switch(theSwitch)
{
case BATCH_SWITCH:
OpenBatch(theEnv,argv[++i],true);
break;
#if (! RUN_TIME) && (! BLOAD_ONLY)
case BATCH_STAR_SWITCH:
BatchStar(theEnv,argv[++i]);
break;
case LOAD_SWITCH:
Load(theEnv,argv[++i]);
break;
#endif
}
}
}
#if ! RUN_TIME
bool ExpandCommandString(
Environment *theEnv,
int inchar)
{
size_t k;
k = RouterData(theEnv)->CommandBufferInputCount;
CommandLineData(theEnv)->CommandString = ExpandStringWithChar(theEnv,inchar,CommandLineData(theEnv)->CommandString,&RouterData(theEnv)->CommandBufferInputCount,
&CommandLineData(theEnv)->MaximumCharacters,CommandLineData(theEnv)->MaximumCharacters+80);
return((RouterData(theEnv)->CommandBufferInputCount != k) ? true : false);
}
void FlushCommandString(
Environment *theEnv)
{
if (CommandLineData(theEnv)->CommandString != NULL) rm(theEnv,CommandLineData(theEnv)->CommandString,CommandLineData(theEnv)->MaximumCharacters);
CommandLineData(theEnv)->CommandString = NULL;
CommandLineData(theEnv)->MaximumCharacters = 0;
RouterData(theEnv)->CommandBufferInputCount = 0;
RouterData(theEnv)->InputUngets = 0;
RouterData(theEnv)->AwaitingInput = true;
}
void SetCommandString(
Environment *theEnv,
const char *str)
{
size_t length;
FlushCommandString(theEnv);
length = strlen(str);
CommandLineData(theEnv)->CommandString = (char *)
genrealloc(theEnv,CommandLineData(theEnv)->CommandString,
CommandLineData(theEnv)->MaximumCharacters,
CommandLineData(theEnv)->MaximumCharacters + length + 1);
genstrcpy(CommandLineData(theEnv)->CommandString,str);
CommandLineData(theEnv)->MaximumCharacters += (length + 1);
RouterData(theEnv)->CommandBufferInputCount += length;
}
void SetNCommandString(
Environment *theEnv,
const char *str,
unsigned length)
{
FlushCommandString(theEnv);
CommandLineData(theEnv)->CommandString = (char *)
genrealloc(theEnv,CommandLineData(theEnv)->CommandString,
CommandLineData(theEnv)->MaximumCharacters,
CommandLineData(theEnv)->MaximumCharacters + length + 1);
genstrncpy(CommandLineData(theEnv)->CommandString,str,length);
CommandLineData(theEnv)->CommandString[CommandLineData(theEnv)->MaximumCharacters + length] = 0;
CommandLineData(theEnv)->MaximumCharacters += (length + 1);
RouterData(theEnv)->CommandBufferInputCount += length;
}
void AppendCommandString(
Environment *theEnv,
const char *str)
{
CommandLineData(theEnv)->CommandString = AppendToString(theEnv,str,CommandLineData(theEnv)->CommandString,&RouterData(theEnv)->CommandBufferInputCount,&CommandLineData(theEnv)->MaximumCharacters);
}
void InsertCommandString(
Environment *theEnv,
const char *str,
unsigned int position)
{
CommandLineData(theEnv)->CommandString =
InsertInString(theEnv,str,position,CommandLineData(theEnv)->CommandString,
&RouterData(theEnv)->CommandBufferInputCount,&CommandLineData(theEnv)->MaximumCharacters);
}
void AppendNCommandString(
Environment *theEnv,
const char *str,
unsigned length)
{
CommandLineData(theEnv)->CommandString = AppendNToString(theEnv,str,CommandLineData(theEnv)->CommandString,length,&RouterData(theEnv)->CommandBufferInputCount,&CommandLineData(theEnv)->MaximumCharacters);
}
char *GetCommandString(
Environment *theEnv)
{
return(CommandLineData(theEnv)->CommandString);
}
int CompleteCommand(
const char *mstring)
{
int i;
char inchar;
int depth = 0;
bool moreThanZero = false;
bool complete;
bool error = false;
if (mstring == NULL) return 0;
i = 0;
while ((inchar = mstring[i++]) != EOS)
{
switch(inchar)
{
case '\n' :
case '\r' :
if (error) return(-1);
if (moreThanZero && (depth == 0)) return 1;
i = DoWhiteSpace(mstring,i);
break;
case ' ' :
case '\f' :
case '\t' :
i = DoWhiteSpace(mstring,i);
break;
case '"' :
i = DoString(mstring,i,&complete);
if ((depth == 0) && complete) moreThanZero = true;
break;
case ';' :
i = DoComment(mstring,i);
if (moreThanZero && (depth == 0) && (mstring[i] != EOS))
{
if (error) return -1;
else return 1;
}
else if (mstring[i] != EOS) i++;
break;
case '(' :
if ((depth > 0) || (moreThanZero == false))
{
depth++;
moreThanZero = true;
}
break;
case ')' :
if (depth > 0) depth--;
else if (moreThanZero == false) error = true;
break;
default:
if (depth == 0)
{
if (IsUTF8MultiByteStart(inchar) || isprint(inchar))
{
while ((inchar = mstring[i++]) != EOS)
{
if ((inchar == '\n') || (inchar == '\r'))
{
if (error) return -1;
else return 1;
}
}
return 0;
}
}
break;
}
}
return 0;
}
static int DoString(
const char *str,
int pos,
bool *complete)
{
int inchar;
inchar = str[pos];
while (inchar != '"')
{
if (inchar == '\\')
{
pos++;
inchar = str[pos];
}
if (inchar == EOS)
{
*complete = false;
return(pos);
}
pos++;
inchar = str[pos];
}
pos++;
*complete = true;
return(pos);
}
static int DoComment(
const char *str,
int pos)
{
int inchar;
inchar = str[pos];
while ((inchar != '\n') && (inchar != '\r'))
{
if (inchar == EOS)
{ return(pos); }
pos++;
inchar = str[pos];
}
return(pos);
}
static int DoWhiteSpace(
const char *str,
int pos)
{
int inchar;
inchar = str[pos];
while ((inchar == ' ') || (inchar == '\f') || (inchar == '\t'))
{
pos++;
inchar = str[pos];
}
return(pos);
}
void CommandLoop(
Environment *theEnv)
{
int inchar;
WriteString(theEnv,STDOUT,CommandLineData(theEnv)->BannerString);
SetHaltExecution(theEnv,false);
SetEvaluationError(theEnv,false);
CleanCurrentGarbageFrame(theEnv,NULL);
CallPeriodicTasks(theEnv);
PrintPrompt(theEnv);
RouterData(theEnv)->CommandBufferInputCount = 0;
RouterData(theEnv)->InputUngets = 0;
RouterData(theEnv)->AwaitingInput = true;
while (true)
{
if (BatchActive(theEnv) == true)
{
inchar = LLGetcBatch(theEnv,STDIN,true);
if (inchar == EOF)
{ (*CommandLineData(theEnv)->EventCallback)(theEnv); }
else
{ ExpandCommandString(theEnv,(char) inchar); }
}
else
{ (*CommandLineData(theEnv)->EventCallback)(theEnv); }
if (GetHaltExecution(theEnv) == true)
{
SetHaltExecution(theEnv,false);
SetEvaluationError(theEnv,false);
FlushCommandString(theEnv);
WriteString(theEnv,STDOUT,"\n");
PrintPrompt(theEnv);
}
ExecuteIfCommandComplete(theEnv);
}
}
void CommandLoopBatch(
Environment *theEnv)
{
SetHaltExecution(theEnv,false);
SetEvaluationError(theEnv,false);
CleanCurrentGarbageFrame(theEnv,NULL);
CallPeriodicTasks(theEnv);
PrintPrompt(theEnv);
RouterData(theEnv)->CommandBufferInputCount = 0;
RouterData(theEnv)->InputUngets = 0;
RouterData(theEnv)->AwaitingInput = true;
CommandLoopBatchDriver(theEnv);
}
void CommandLoopOnceThenBatch(
Environment *theEnv)
{
if (! ExecuteIfCommandComplete(theEnv)) return;
CommandLoopBatchDriver(theEnv);
}
void CommandLoopBatchDriver(
Environment *theEnv)
{
int inchar;
while (true)
{
if (GetHaltCommandLoopBatch(theEnv) == true)
{
CloseAllBatchSources(theEnv);
SetHaltCommandLoopBatch(theEnv,false);
}
if (BatchActive(theEnv) == true)
{
inchar = LLGetcBatch(theEnv,STDIN,true);
if (inchar == EOF)
{ return; }
else
{ ExpandCommandString(theEnv,(char) inchar); }
}
else
{ return; }
if (GetHaltExecution(theEnv) == true)
{
SetHaltExecution(theEnv,false);
SetEvaluationError(theEnv,false);
FlushCommandString(theEnv);
WriteString(theEnv,STDOUT,"\n");
PrintPrompt(theEnv);
}
ExecuteIfCommandComplete(theEnv);
}
}
bool ExecuteIfCommandComplete(
Environment *theEnv)
{
if ((CompleteCommand(CommandLineData(theEnv)->CommandString) == 0) ||
(RouterData(theEnv)->CommandBufferInputCount == 0) ||
(RouterData(theEnv)->AwaitingInput == false))
{ return false; }
if (CommandLineData(theEnv)->BeforeCommandExecutionCallback != NULL)
{
if (! (*CommandLineData(theEnv)->BeforeCommandExecutionCallback)(theEnv))
{ return false; }
}
FlushPPBuffer(theEnv);
SetPPBufferStatus(theEnv,false);
RouterData(theEnv)->CommandBufferInputCount = 0;
RouterData(theEnv)->InputUngets = 0;
RouterData(theEnv)->AwaitingInput = false;
RouteCommand(theEnv,CommandLineData(theEnv)->CommandString,true);
FlushPPBuffer(theEnv);
#if (! BLOAD_ONLY)
FlushParsingMessages(theEnv);
#endif
SetHaltExecution(theEnv,false);
SetEvaluationError(theEnv,false);
FlushCommandString(theEnv);
CleanCurrentGarbageFrame(theEnv,NULL);
CallPeriodicTasks(theEnv);
PrintPrompt(theEnv);
return true;
}
bool CommandCompleteAndNotEmpty(
Environment *theEnv)
{
if ((CompleteCommand(CommandLineData(theEnv)->CommandString) == 0) ||
(RouterData(theEnv)->CommandBufferInputCount == 0) ||
(RouterData(theEnv)->AwaitingInput == false))
{ return false; }
return true;
}
void PrintPrompt(
Environment *theEnv)
{
WriteString(theEnv,STDOUT,COMMAND_PROMPT);
if (CommandLineData(theEnv)->AfterPromptCallback != NULL)
{ (*CommandLineData(theEnv)->AfterPromptCallback)(theEnv); }
}
void PrintBanner(
Environment *theEnv)
{
WriteString(theEnv,STDOUT,CommandLineData(theEnv)->BannerString);
}
void SetAfterPromptFunction(
Environment *theEnv,
AfterPromptFunction *funptr)
{
CommandLineData(theEnv)->AfterPromptCallback = funptr;
}
void SetBeforeCommandExecutionFunction(
Environment *theEnv,
BeforeCommandExecutionFunction *funptr)
{
CommandLineData(theEnv)->BeforeCommandExecutionCallback = funptr;
}
bool RouteCommand(
Environment *theEnv,
const char *command,
bool printResult)
{
UDFValue returnValue;
struct expr *top;
const char *commandName;
struct token theToken;
int danglingConstructs;
if (command == NULL)
{ return false; }
OpenStringSource(theEnv,"command",command,0);
GetToken(theEnv,"command",&theToken);
if ((theToken.tknType == SYMBOL_TOKEN) || (theToken.tknType == STRING_TOKEN) ||
(theToken.tknType == FLOAT_TOKEN) || (theToken.tknType == INTEGER_TOKEN) ||
(theToken.tknType == INSTANCE_NAME_TOKEN))
{
CloseStringSource(theEnv,"command");
if (printResult)
{
PrintAtom(theEnv,STDOUT,TokenTypeToType(theToken.tknType),theToken.value);
WriteString(theEnv,STDOUT,"\n");
}
return true;
}
if ((theToken.tknType == GBL_VARIABLE_TOKEN) ||
(theToken.tknType == MF_GBL_VARIABLE_TOKEN) ||
(theToken.tknType == SF_VARIABLE_TOKEN) ||
(theToken.tknType == MF_VARIABLE_TOKEN))
{
CloseStringSource(theEnv,"command");
top = GenConstant(theEnv,TokenTypeToType(theToken.tknType),theToken.value);
EvaluateExpression(theEnv,top,&returnValue);
rtn_struct(theEnv,expr,top);
if (printResult)
{
WriteUDFValue(theEnv,STDOUT,&returnValue);
WriteString(theEnv,STDOUT,"\n");
}
return true;
}
if (theToken.tknType != LEFT_PARENTHESIS_TOKEN)
{
PrintErrorID(theEnv,"COMMLINE",1,false);
WriteString(theEnv,STDERR,"Expected a '(', constant, or variable.\n");
CloseStringSource(theEnv,"command");
return false;
}
GetToken(theEnv,"command",&theToken);
if (theToken.tknType != SYMBOL_TOKEN)
{
PrintErrorID(theEnv,"COMMLINE",2,false);
WriteString(theEnv,STDERR,"Expected a command.\n");
CloseStringSource(theEnv,"command");
return false;
}
commandName = theToken.lexemeValue->contents;
#if (! RUN_TIME) && (! BLOAD_ONLY)
{
BuildError errorFlag;
errorFlag = ParseConstruct(theEnv,commandName,"command");
if (errorFlag != BE_CONSTRUCT_NOT_FOUND_ERROR)
{
CloseStringSource(theEnv,"command");
if (errorFlag == BE_PARSING_ERROR)
{
WriteString(theEnv,STDERR,"\nERROR:\n");
WriteString(theEnv,STDERR,GetPPBuffer(theEnv));
WriteString(theEnv,STDERR,"\n");
}
DestroyPPBuffer(theEnv);
SetWarningFileName(theEnv,NULL);
SetErrorFileName(theEnv,NULL);
if (errorFlag == BE_NO_ERROR) return true;
else return false;
}
}
#endif
danglingConstructs = ConstructData(theEnv)->DanglingConstructs;
CommandLineData(theEnv)->ParsingTopLevelCommand = true;
top = Function2Parse(theEnv,"command",commandName);
CommandLineData(theEnv)->ParsingTopLevelCommand = false;
ClearParsedBindNames(theEnv);
CloseStringSource(theEnv,"command");
if (top == NULL)
{
#if (! RUN_TIME) && (! BLOAD_ONLY)
SetWarningFileName(theEnv,NULL);
SetErrorFileName(theEnv,NULL);
#endif
ConstructData(theEnv)->DanglingConstructs = danglingConstructs;
return false;
}
ExpressionInstall(theEnv,top);
CommandLineData(theEnv)->EvaluatingTopLevelCommand = true;
CommandLineData(theEnv)->CurrentCommand = top;
EvaluateExpression(theEnv,top,&returnValue);
CommandLineData(theEnv)->CurrentCommand = NULL;
CommandLineData(theEnv)->EvaluatingTopLevelCommand = false;
ExpressionDeinstall(theEnv,top);
ReturnExpression(theEnv,top);
ConstructData(theEnv)->DanglingConstructs = danglingConstructs;
#if (! RUN_TIME) && (! BLOAD_ONLY)
SetWarningFileName(theEnv,NULL);
SetErrorFileName(theEnv,NULL);
#endif
if ((returnValue.header->type != VOID_TYPE) && printResult)
{
WriteUDFValue(theEnv,STDOUT,&returnValue);
WriteString(theEnv,STDOUT,"\n");
}
return true;
}
static void DefaultGetNextEvent(
Environment *theEnv)
{
int inchar;
inchar = ReadRouter(theEnv,STDIN);
if (inchar == EOF) inchar = '\n';
ExpandCommandString(theEnv,(char) inchar);
}
EventFunction *SetEventFunction(
Environment *theEnv,
EventFunction *theFunction)
{
EventFunction *tmp_ptr;
tmp_ptr = CommandLineData(theEnv)->EventCallback;
CommandLineData(theEnv)->EventCallback = theFunction;
return tmp_ptr;
}
bool TopLevelCommand(
Environment *theEnv)
{
return(CommandLineData(theEnv)->ParsingTopLevelCommand);
}
const char *GetCommandCompletionString(
Environment *theEnv,
const char *theString,
size_t maxPosition)
{
struct token lastToken;
struct token theToken;
char lastChar;
const char *rs;
size_t length;
if (theString == NULL) return("");
lastChar = theString[maxPosition - 1];
if ((lastChar == ' ') || (lastChar == '"') ||
(lastChar == '\t') || (lastChar == '\f') ||
(lastChar == '\n') || (lastChar == '\r'))
{ return(""); }
OpenTextSource(theEnv,"CommandCompletion",theString,0,maxPosition);
ScannerData(theEnv)->IgnoreCompletionErrors = true;
GetToken(theEnv,"CommandCompletion",&theToken);
CopyToken(&lastToken,&theToken);
while (theToken.tknType != STOP_TOKEN)
{
CopyToken(&lastToken,&theToken);
GetToken(theEnv,"CommandCompletion",&theToken);
}
CloseStringSource(theEnv,"CommandCompletion");
ScannerData(theEnv)->IgnoreCompletionErrors = false;
if (lastToken.tknType == SYMBOL_TOKEN)
{
rs = lastToken.lexemeValue->contents;
if (rs[0] == '[') return (&rs[1]);
return lastToken.lexemeValue->contents;
}
else if (lastToken.tknType == SF_VARIABLE_TOKEN)
{ return lastToken.lexemeValue->contents; }
else if (lastToken.tknType == MF_VARIABLE_TOKEN)
{ return lastToken.lexemeValue->contents; }
else if ((lastToken.tknType == GBL_VARIABLE_TOKEN) ||
(lastToken.tknType == MF_GBL_VARIABLE_TOKEN) ||
(lastToken.tknType == INSTANCE_NAME_TOKEN))
{ return NULL; }
else if (lastToken.tknType == STRING_TOKEN)
{
length = strlen(lastToken.lexemeValue->contents);
return GetCommandCompletionString(theEnv,lastToken.lexemeValue->contents,length);
}
else if ((lastToken.tknType == FLOAT_TOKEN) ||
(lastToken.tknType == INTEGER_TOKEN))
{ return NULL; }
return("");
}
void SetHaltCommandLoopBatch(
Environment *theEnv,
bool value)
{
CommandLineData(theEnv)->HaltCommandLoopBatch = value;
}
bool GetHaltCommandLoopBatch(
Environment *theEnv)
{
return(CommandLineData(theEnv)->HaltCommandLoopBatch);
}
#endif