#include "setup.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "argacces.h"
#include "commline.h"
#include "envrnmnt.h"
#include "extnfunc.h"
#include "memalloc.h"
#include "prntutil.h"
#include "router.h"
#include "sysdep.h"
#include "utility.h"
#include "textpro.h"
#if TEXTPRO_FUNCTIONS
#define NAMESIZE 80
#define NULLCHAR '\0'
#define BLANK (' ')
#define TAB ('\t')
#define LNFEED ('\n')
#define NORMAL 0
#define NO_FILE -10
#define NEW_FILE -15
#define OLD_FILE -20
#define NO_TOPIC -25
#define EXIT -30
#define BRANCH_UP -35
#define BRANCH_DOWN -40
#define MENU -45
#define INFO -50
struct entries
{
int level;
int type;
char name[NAMESIZE];
long offset;
struct entries *child;
struct entries *parent;
struct entries *next;
};
struct lists
{
char file[NAMESIZE];
struct entries *topics;
struct entries *curr_menu;
struct lists *next;
};
#define BDELIM "BEGIN-ENTRY-"
#define BDLEN 12
#define EDELIM "END-ENTRY"
#define EDLEN 9
#define BFORMAT "%d%1s%12s%s"
#define LIT_DELIM ('$')
#define OPEN_READ "r"
#define OPEN_READ_BINARY "rb"
#define TEXTPRO_DATA 8
struct textProcessingData
{
struct lists *headings;
struct entries *parent;
};
#define TextProcessingData(theEnv) ((struct textProcessingData *) GetEnvironmentData(theEnv,TEXTPRO_DATA))
static int TextLookupFetch(Environment *,const char *);
static bool TextLookupToss(Environment *,const char *);
static FILE *GetEntries(Environment *,const char *,char **,char *,int *);
static FILE *GetCurrentMenu(Environment *,const char *,int *);
static char *GrabString(Environment *,FILE *,char *,int);
static int findstr(const char *,const char *);
static void upper(char *);
static struct lists *NewFetchFile(Environment *,const char *);
static struct entries *AllocateEntryNode(Environment *,FILE *,const char *,const char *,int);
static bool AttachLeaf(Environment *,struct lists *,struct entries *,FILE *,const char *,int);
static long LookupEntry(Environment *,const char *,char **,char *,int *);
static void TossFunction(Environment *,struct entries *);
static void DeallocateTextProcessingData(Environment *);
static int TextLookupFetch(
Environment *theEnv,
const char *file)
{
FILE *fp;
char str[256];
bool INFO_BEGIN, INFO_END;
struct lists *lnode;
struct entries *enode;
int line_ct;
int entries_ct;
fp = GenOpen(theEnv,file,OPEN_READ_BINARY);
if (fp == NULL)
{
PrintErrorID(theEnv,"TEXTPRO",1,false);
WriteString(theEnv,STDERR,"Could not open file '");
WriteString(theEnv,STDERR,file);
WriteString(theEnv,STDERR,"'.\n");
return -1;
}
if ((lnode = NewFetchFile(theEnv,file)) == NULL)
{
GenClose(theEnv,fp);
PrintErrorID(theEnv,"TEXTPRO",2,false);
WriteString(theEnv,STDERR,"File '");
WriteString(theEnv,STDERR,file);
WriteString(theEnv,STDERR,"' already loaded.\n");
return -1;
}
line_ct = 0;
entries_ct = 0;
INFO_BEGIN = false;
INFO_END = true;
while (fgets(str,256,fp) != NULL)
{
line_ct++;
if ((str[0] != LIT_DELIM) || (str[1] != LIT_DELIM))
{
if (findstr(str,EDELIM) >= 0)
{
if (INFO_BEGIN == true)
{
INFO_BEGIN = false;
INFO_END = true;
entries_ct++;
}
else
{
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",8,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Unmatched end marker.\n");
return(-1);
}
}
else if (findstr(str,BDELIM) >= 0)
{
if (INFO_END == true)
{
INFO_END = false;
INFO_BEGIN = true;
}
else
{
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",4,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Previous entry not closed.\n");
return(-1);
}
if ((enode=AllocateEntryNode(theEnv,fp,file,str,line_ct))==NULL)
return(-1);
if (AttachLeaf(theEnv,lnode,enode,fp,file,line_ct) == false)
return(-1);
}
}
}
GenClose(theEnv,fp);
if (INFO_END == false)
{
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",4,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Previous entry not closed.\n");
return(-1);
}
if (entries_ct == 0)
TextLookupToss(theEnv,file);
return(entries_ct);
}
static bool TextLookupToss(
Environment *theEnv,
const char *file)
{
struct lists *plptr, *clptr;
int l_flag;
clptr = TextProcessingData(theEnv)->headings;
plptr = clptr;
if (clptr != NULL)
if (strcmp(clptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
while (l_flag > 0)
{
plptr = clptr;
clptr = clptr->next;
if (clptr != NULL)
if (strcmp(clptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
}
if (clptr == NULL)
return false;
TossFunction(theEnv,clptr->topics);
if (plptr == clptr)
TextProcessingData(theEnv)->headings = clptr->next;
else
plptr->next = clptr->next;
rm(theEnv,clptr,sizeof(struct lists));
return true;
}
static FILE *GetEntries(
Environment *theEnv,
const char *file,
char **menu,
char *name,
int *code)
{
FILE *fp;
long offset;
offset = LookupEntry(theEnv,file,menu,name,code);
if (offset < 0)
return NULL;
fp = GenOpen(theEnv,file,OPEN_READ_BINARY);
if (fp == NULL)
{
*code = NO_FILE;
return NULL;
}
if (fseek(fp,offset,0) < 0)
{
GenClose(theEnv,fp);
*code = NO_FILE;
return NULL;
}
return(fp);
}
static FILE *GetCurrentMenu(
Environment *theEnv,
const char *file,
int *status)
{
struct lists *lptr;
FILE *fp;
int l_flag;
lptr = TextProcessingData(theEnv)->headings;
if (lptr != NULL)
if (strcmp(lptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
while (l_flag > 0)
{
lptr = lptr->next;
if (lptr != NULL)
if (strcmp(lptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
}
if (lptr == NULL)
{
*status = NO_FILE;
return NULL;
}
if (lptr->curr_menu == NULL)
{
*status = NO_TOPIC;
return NULL;
}
if ((fp = GenOpen(theEnv,file,OPEN_READ_BINARY)) == NULL)
{
*status = NO_FILE;
return NULL;
}
if (fseek(fp,lptr->curr_menu->offset,0) < 0)
{
GenClose(theEnv,fp);
*status = NO_FILE;
return NULL;
}
*status = NORMAL;
return(fp);
}
static char *GrabString(
Environment *theEnv,
FILE *fp,
char *buf,
int bufsize)
{
if (fgets(buf,bufsize,fp) == NULL)
{
GenClose(theEnv,fp);
return NULL;
}
if ((buf[0] == LIT_DELIM) && (buf[1] == LIT_DELIM))
{
buf[0] = BLANK;
buf[1] = BLANK;
}
else if (findstr(buf,EDELIM) >= 0)
{
buf = NULL;
GenClose(theEnv,fp);
}
return(buf);
}
static int findstr(
const char *s,
const char *t)
{
int i,j,k;
for (i = 0; s[i] != '\0'; i++)
{
for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++) ;
if ((t[k] == '\0') && (k != 0))
return(i);
}
return(-1);
}
static void upper(
char *str)
{
int theIndex;
for (theIndex = 0 ; str[theIndex] != NULLCHAR; theIndex++)
if (islower(str[theIndex])) str[theIndex] = (char) toupper(str[theIndex]);
}
static struct lists *NewFetchFile(
Environment *theEnv,
const char *file)
{
struct lists *lptr = NULL, *lnode;
if (TextProcessingData(theEnv)->headings != NULL)
{
lptr = TextProcessingData(theEnv)->headings;
while (lptr->next != NULL)
{
if (strcmp(lptr->file,file) == 0)
return NULL;
lptr = lptr->next;
}
if (strcmp(lptr->file,file) == 0)
return NULL;
}
lnode = (struct lists *) gm2(theEnv,sizeof(struct lists));
genstrcpy(lnode->file,file);
lnode->topics = NULL;
lnode->curr_menu = NULL;
lnode->next = NULL;
if (TextProcessingData(theEnv)->headings == NULL)
TextProcessingData(theEnv)->headings = lnode;
else
lptr->next = lnode;
return(lnode);
}
static struct entries *AllocateEntryNode(
Environment *theEnv,
FILE *fp,
const char *file,
const char *str,
int line_ct)
{
struct entries *enode;
char bmarker[BDLEN+1],
t_code[2];
enode = (struct entries *) gm2(theEnv,sizeof(struct entries));
if (sscanf(str,BFORMAT,
&enode->level,t_code,bmarker,enode->name) != 4)
{
rm(theEnv,enode,sizeof(struct entries));
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",5,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Invalid delimeter string.\n");
return NULL;
}
if (t_code[0] == 'M')
enode->type = MENU;
else if (t_code[0] == 'I')
enode->type = INFO;
else
{
rm(theEnv,enode,sizeof(struct entries));
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",6,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Invalid entry type.\n");
return NULL;
}
if (strcmp(bmarker,BDELIM) != 0)
{
rm(theEnv,enode,sizeof(struct entries));
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",5,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Invalid delimeter string.\n");
return NULL;
}
ungetc(getc(fp),fp);
enode->offset = ftell(fp);
enode->parent = NULL;
enode->child = NULL;
enode->next = NULL;
upper(enode->name);
return(enode);
}
static bool AttachLeaf(
Environment *theEnv,
struct lists *lnode,
struct entries *enode,
FILE *fp,
const char *file,
int line_ct)
{
int p_flag;
if (lnode->topics == NULL)
lnode->topics = enode;
else if (enode->level > TextProcessingData(theEnv)->parent->level)
if (TextProcessingData(theEnv)->parent->type == MENU)
{
enode->parent = TextProcessingData(theEnv)->parent;
TextProcessingData(theEnv)->parent->child = enode;
}
else
{
rm(theEnv,enode,sizeof(struct entries));
GenClose(theEnv,fp);
TextLookupToss(theEnv,file);
PrintErrorID(theEnv,"TEXTPRO",7,false);
WriteString(theEnv,STDERR,"Line ");
WriteInteger(theEnv,STDERR,line_ct);
WriteString(theEnv,STDERR," : Non-menu entries cannot have subtopics.\n");
return false;
}
else if (enode->level == TextProcessingData(theEnv)->parent->level)
{
enode->parent = TextProcessingData(theEnv)->parent->parent;
enode->next = TextProcessingData(theEnv)->parent->next;
TextProcessingData(theEnv)->parent->next = enode;
}
else
{
if (TextProcessingData(theEnv)->parent != NULL)
p_flag = 1;
else
p_flag = 0;
while (p_flag > 0)
{
TextProcessingData(theEnv)->parent = TextProcessingData(theEnv)->parent->parent;
if (TextProcessingData(theEnv)->parent != NULL)
if (enode->level < TextProcessingData(theEnv)->parent->level)
p_flag = 1;
else
p_flag = 0;
else
p_flag = 0;
}
if (TextProcessingData(theEnv)->parent != NULL)
if (TextProcessingData(theEnv)->parent->level < enode->level)
{
enode->parent = TextProcessingData(theEnv)->parent;
enode->next = TextProcessingData(theEnv)->parent->child;
TextProcessingData(theEnv)->parent->child = enode;
}
else
{
enode->parent = TextProcessingData(theEnv)->parent->parent;
enode->next = TextProcessingData(theEnv)->parent->next;
TextProcessingData(theEnv)->parent->next = enode;
}
else
{
enode->parent = NULL;
enode->next = lnode->topics;
lnode->topics = enode;
}
}
TextProcessingData(theEnv)->parent = enode;
return true;
}
static long LookupEntry(
Environment *theEnv,
const char *file,
char **menu,
char *name,
int *code)
{
struct lists *lptr;
struct entries *eptr;
int l_flag, e_flag;
lptr = TextProcessingData(theEnv)->headings;
if (lptr != NULL)
if (strcmp(lptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
while (l_flag > 0)
{
lptr = lptr->next;
if (lptr != NULL)
if (strcmp(lptr->file,file) != 0)
l_flag = 1;
else
l_flag = 0;
else
l_flag = 0;
}
if (lptr == NULL)
{
*code = NO_FILE;
return(-1);
}
if (name == NULL)
{
if (lptr->curr_menu == NULL)
{
*code = EXIT;
return(-1);
}
else
{
if (lptr->curr_menu->parent == NULL)
{
*code = EXIT;
lptr->curr_menu = NULL;
*menu = NULL;
return(-1);
}
lptr->curr_menu = lptr->curr_menu->parent;
*code = BRANCH_UP;
*menu = lptr->curr_menu->name;
return(lptr->curr_menu->offset);
}
}
upper(name);
if (lptr->curr_menu != NULL)
eptr = lptr->curr_menu->child;
else
eptr = lptr->topics;
if (eptr != NULL)
if (findstr(eptr->name,name) == 0)
e_flag = 0;
else
e_flag = 1;
else
e_flag = 0;
while (e_flag > 0)
{
eptr = eptr->next;
if (eptr != NULL)
if (findstr(eptr->name,name) == 0)
e_flag = 0;
else
e_flag = 1;
else
e_flag = 0;
}
if (eptr == NULL)
{
*code = NO_TOPIC;
if (lptr->curr_menu != NULL)
{
*menu = lptr->curr_menu->name;
return(lptr->curr_menu->offset);
}
return(-1);
}
if (eptr->type == MENU)
{
*code = BRANCH_DOWN;
lptr->curr_menu = eptr;
}
else
*code = NORMAL;
if (lptr->curr_menu != NULL)
*menu = lptr->curr_menu->name;
return(eptr->offset);
}
static void TossFunction(
Environment *theEnv,
struct entries *eptr)
{
struct entries *prev;
while (eptr != NULL)
{
if (eptr->child != NULL)
TossFunction(theEnv,eptr->child);
prev = eptr;
eptr = eptr->next;
rm(theEnv,prev,sizeof(struct entries));
}
}
#define SCREEN_LN 22
struct topics
{
char name[NAMESIZE];
struct topics *end_list;
struct topics *next;
};
static struct topics *GetCommandLineTopics(UDFContext *);
static FILE *FindTopicInEntries(Environment *,const char *,struct topics *,char **,int *);
#if TEXTPRO_FUNCTIONS
void FetchCommand(
Environment *theEnv,
UDFContext *context,
UDFValue *returnValue)
{
int load_ct;
UDFValue theArg;
returnValue->lexemeValue = FalseSymbol(theEnv);
if (! UDFFirstArgument(context,LEXEME_BITS,&theArg))
{ return; }
load_ct = TextLookupFetch(theEnv,theArg.lexemeValue->contents);
if (load_ct <= 0)
{
if (load_ct == 0)
{
PrintErrorID(theEnv,"TEXTPRO",3,false);
WriteString(theEnv,STDERR,"No entries found.\n");
}
return;
}
returnValue->integerValue = CreateInteger(theEnv,load_ct);
}
void PrintRegionCommand(
Environment *theEnv,
UDFContext *context,
UDFValue *returnValue)
{
struct topics *params,
*tptr;
char buf[256];
FILE *fp;
char *menu[1];
int status;
bool com_code;
params = GetCommandLineTopics(context);
fp = FindTopicInEntries(theEnv,params->next->name,params->next->next,menu,&status);
if ((status != NO_FILE) && (status != NO_TOPIC) && (status != EXIT))
{
if (strcmp(params->name,"t") == 0)
genstrcpy(params->name,STDOUT);
WriteString(theEnv,params->name,"\n");
while (GrabString(theEnv,fp,buf,256) != NULL)
WriteString(theEnv,params->name,buf);
com_code = true;
}
else
{
if (fp != NULL)
GenClose(theEnv,fp);
com_code = false;
}
while (params != NULL)
{
tptr = params;
params = params->next;
rm(theEnv,tptr,sizeof(struct topics));
}
returnValue->lexemeValue = CreateBoolean(theEnv,com_code);
}
void GetRegionCommand(
Environment *theEnv,
UDFContext *context,
UDFValue *returnValue)
{
struct topics *params,
*tptr;
char buf[256];
FILE *fp;
char *menu[1];
int status;
char *theString = NULL;
size_t oldPos = 0;
size_t oldMax = 0;
size_t sLength;
params = GetCommandLineTopics(context);
fp = FindTopicInEntries(theEnv,params->name,params->next,menu,&status);
if ((status != NO_FILE) && (status != NO_TOPIC) && (status != EXIT))
{
while (GrabString(theEnv,fp,buf,256) != NULL)
theString = AppendToString(theEnv,buf,theString,&oldPos,&oldMax);
}
else
{
if (fp != NULL)
GenClose(theEnv,fp);
}
while (params != NULL)
{
tptr = params;
params = params->next;
rm(theEnv,tptr,sizeof(struct topics));
}
if (theString == NULL)
{ returnValue->lexemeValue = CreateString(theEnv,""); }
else
{
sLength = strlen(theString);
if ((sLength > 0) &&
(((theString[sLength-1] == '\r') && (theString[sLength-2] == '\n'))
||
((theString[sLength-1] == '\n') && (theString[sLength-2] == '\r'))))
{ theString[sLength-2] = 0; }
returnValue->lexemeValue = CreateString(theEnv,theString);
}
if (theString != NULL)
{ genfree(theEnv,theString,oldMax); }
}
void TossCommand(
Environment *theEnv,
UDFContext *context,
UDFValue *returnValue)
{
const char *file;
UDFValue theArg;
if (! UDFFirstArgument(context,LEXEME_BITS,&theArg))
{ return; }
file = theArg.lexemeValue->contents;
returnValue->lexemeValue = CreateBoolean(theEnv,TextLookupToss(theEnv,file));
}
#endif
static struct topics *GetCommandLineTopics(
UDFContext *context)
{
struct topics *head,
*tnode,
*tptr;
UDFValue val;
Environment *theEnv = context->environment;
head = NULL;
while (UDFHasNextArgument(context))
{
tnode = (struct topics *) gm2(theEnv,sizeof(struct topics));
UDFNextArgument(context,ANY_TYPE_BITS,&val);
if ((val.header->type == SYMBOL_TYPE) || (val.header->type == STRING_TYPE))
genstrncpy(tnode->name,val.lexemeValue->contents,NAMESIZE-1);
else if (val.header->type == FLOAT_TYPE)
genstrncpy(tnode->name,FloatToString(theEnv,val.floatValue->contents),NAMESIZE-1);
else if (val.header->type == INTEGER_TYPE)
genstrncpy(tnode->name,LongIntegerToString(theEnv,val.integerValue->contents),NAMESIZE-1);
else
genstrncpy(tnode->name,"***ERROR***",NAMESIZE-1);
tnode->next = NULL;
tnode->end_list = NULL;
if (head == NULL)
head = tnode;
else
{
tptr = head;
while (tptr->next != NULL)
tptr = tptr->next;
tptr->next = tnode;
}
}
return(head);
}
static FILE *FindTopicInEntries(
Environment *theEnv,
const char *file,
struct topics *main_topic,
char **menu,
int *status)
{
FILE *fp = NULL;
struct topics *tptr,
*end_list;
if (main_topic != NULL)
end_list = main_topic->end_list;
else
end_list = NULL;
tptr = main_topic;
if (tptr != end_list)
do
{
if (fp != NULL)
GenClose(theEnv,fp);
if (strcmp(tptr->name,"^") == 0)
fp = GetEntries(theEnv,file,menu,NULL,status);
else if ((strcmp(tptr->name,"?") == 0) && (tptr->next == end_list))
fp = GetCurrentMenu(theEnv,file,status);
else
fp = GetEntries(theEnv,file,menu,tptr->name,status);
if ((*status == NO_FILE) || (*status == NO_TOPIC))
break;
tptr = tptr->next;
} while (tptr != end_list);
else
fp = GetEntries(theEnv,file,menu,NULL,status);
return(fp);
}
void HelpFunctionDefinitions(
Environment *theEnv)
{
AllocateEnvironmentData(theEnv,TEXTPRO_DATA,sizeof(struct textProcessingData),DeallocateTextProcessingData);
#if ! RUN_TIME
#if TEXTPRO_FUNCTIONS
AddUDF(theEnv,"fetch","bl",1,1,"sy",FetchCommand,"FetchCommand",NULL);
AddUDF(theEnv,"toss","b",1,1,"sy",TossCommand,"TossCommand",NULL);
AddUDF(theEnv,"print-region","b",2,UNBOUNDED,"*;y;sy",PrintRegionCommand,"PrintRegionCommand",NULL);
AddUDF(theEnv,"get-region","s",1,UNBOUNDED,"*;sy",GetRegionCommand,"GetRegionCommand", NULL);
#endif
#endif
}
static void DeallocateTextProcessingData(
Environment *theEnv)
{
struct lists *nextptr, *clptr;
clptr = TextProcessingData(theEnv)->headings;
while (clptr != NULL)
{
nextptr = clptr->next;
TossFunction(theEnv,clptr->topics);
rm(theEnv,clptr,sizeof(struct lists));
clptr = nextptr;
}
}
#endif