1    /*******************************************************/
2    /*      "C" Language Integrated Production System      */
3    /*                                                     */
4    /*             CLIPS Version 6.30  01/26/15            */
5    /*                                                     */
6    /*                 FILE COMMANDS MODULE                */
7    /*******************************************************/
8 
9 /*************************************************************/
10 /* Purpose: Contains the code for file commands including    */
11 /*   batch, dribble-on, dribble-off, save, load, bsave, and  */
12 /*   bload.                                                  */
13 /*                                                           */
14 /* Principal Programmer(s):                                  */
15 /*      Gary D. Riley                                        */
16 /*      Brian L. Dantes                                      */
17 /*                                                           */
18 /* Contributing Programmer(s):                               */
19 /*      Bebe Ly                                              */
20 /*                                                           */
21 /* Revision History:                                         */
22 /*                                                           */
23 /*      6.24: Added environment parameter to GenClose.       */
24 /*            Added environment parameter to GenOpen.        */
25 /*                                                           */
26 /*            Renamed BOOLEAN macro type to intBool.         */
27 /*                                                           */
28 /*      6.30: Removed conditional code for unsupported       */
29 /*            compilers/operating systems (IBM_MCW,          */
30 /*            MAC_MCW, and IBM_TBC).                         */
31 /*                                                           */
32 /*            Added code for capturing errors/warnings.      */
33 /*                                                           */
34 /*            Added AwaitingInput flag.                      */
35 /*                                                           */
36 /*            Added const qualifiers to remove C++           */
37 /*            deprecation warnings.                          */
38 /*                                                           */
39 /*            Converted API macros to function calls.        */
40 /*                                                           */
41 /*            Fixed linkage issue when BLOAD_ONLY compiler   */
42 /*            flag is set to 1.                              */
43 /*                                                           */
44 /*            Added STDOUT and STDIN logical name            */
45 /*            definitions.                                   */
46 /*                                                           */
47 /*************************************************************/
48 
49 #define _FILECOM_SOURCE_
50 
51 #include <stdio.h>
52 
53 #define _STDIO_INCLUDED_
54 #include <string.h>
55 
56 #include "setup.h"
57 
58 #include "argacces.h"
59 #include "constrct.h"
60 #include "commline.h"
61 #include "cstrcpsr.h"
62 #include "envrnmnt.h"
63 #include "extnfunc.h"
64 #include "memalloc.h"
65 #include "prcdrfun.h"
66 #include "router.h"
67 #include "strngrtr.h"
68 #include "sysdep.h"
69 #include "utility.h"
70 
71 #include "filecom.h"
72 
73 #if BLOAD || BLOAD_ONLY || BLOAD_AND_BSAVE
74 #include "bsave.h"
75 #include "bload.h"
76 #endif
77 
78 /***************/
79 /* STRUCTURES  */
80 /***************/
81 
82 struct batchEntry
83   {
84    int batchType;
85    void *inputSource;
86    const char *theString;
87    const char *fileName;
88    long lineNumber;
89    struct batchEntry *next;
90   };
91 
92 /***************/
93 /* DEFINITIONS */
94 /***************/
95 
96 #define FILE_BATCH      0
97 #define STRING_BATCH    1
98 
99 #define BUFFER_SIZE   120
100 
101 #define FILECOM_DATA 14
102 
103 struct fileCommandData
104   {
105 #if DEBUGGING_FUNCTIONS
106    FILE *DribbleFP;
107    char *DribbleBuffer;
108    size_t DribbleCurrentPosition;
109    size_t DribbleMaximumPosition;
110    int (*DribbleStatusFunction)(void *,int);
111 #endif
112    int BatchType;
113    void *BatchSource;
114    char *BatchBuffer;
115    size_t BatchCurrentPosition;
116    size_t BatchMaximumPosition;
117    struct batchEntry *TopOfBatchList;
118    struct batchEntry *BottomOfBatchList;
119    char *batchPriorParsingFile;
120   };
121 
122 #define FileCommandData(theEnv) ((struct fileCommandData *) GetEnvironmentData(theEnv,FILECOM_DATA))
123 
124 /***************************************/
125 /* LOCAL INTERNAL FUNCTION DEFINITIONS */
126 /***************************************/
127 
128 #if DEBUGGING_FUNCTIONS
129    static int                     FindDribble(void *,const char *);
130    static int                     GetcDribble(void *,const char *);
131    static int                     UngetcDribble(void *,int,const char *);
132    static int                     ExitDribble(void *,int);
133    static int                     PrintDribble(void *,const char *,const char *);
134    static void                    PutcDribbleBuffer(void *,int);
135 #endif
136    static int                     FindBatch(void *,const char *);
137    static int                     GetcBatch(void *,const char *);
138    static int                     UngetcBatch(void *,int,const char *);
139    static int                     ExitBatch(void *,int);
140    static void                    AddBatch(void *,int,void *,int,const char *,const char *);
141    static void                    DeallocateFileCommandData(void *);
142 
143 /***************************************/
144 /* FileCommandDefinitions: Initializes */
145 /*   file commands.                    */
146 /***************************************/
FileCommandDefinitions(void * theEnv)147 globle void FileCommandDefinitions(
148   void *theEnv)
149   {
150    AllocateEnvironmentData(theEnv,FILECOM_DATA,sizeof(struct fileCommandData),DeallocateFileCommandData);
151 
152 #if ! RUN_TIME
153 #if DEBUGGING_FUNCTIONS
154    EnvDefineFunction2(theEnv,"batch",'b',PTIEF BatchCommand,"BatchCommand","11k");
155    EnvDefineFunction2(theEnv,"batch*",'b',PTIEF BatchStarCommand,"BatchStarCommand","11k");
156    EnvDefineFunction2(theEnv,"dribble-on",'b',PTIEF DribbleOnCommand,"DribbleOnCommand","11k");
157    EnvDefineFunction2(theEnv,"dribble-off",'b',PTIEF DribbleOffCommand,"DribbleOffCommand","00");
158    EnvDefineFunction2(theEnv,"save",'b',PTIEF SaveCommand,"SaveCommand","11k");
159 #endif
160    EnvDefineFunction2(theEnv,"load",'b',PTIEF LoadCommand,"LoadCommand","11k");
161    EnvDefineFunction2(theEnv,"load*",'b',PTIEF LoadStarCommand,"LoadStarCommand","11k");
162 #if BLOAD_AND_BSAVE
163    EnvDefineFunction2(theEnv,"bsave",'b', PTIEF BsaveCommand,"BsaveCommand","11k");
164 #endif
165 #if BLOAD || BLOAD_ONLY || BLOAD_AND_BSAVE
166    InitializeBsaveData(theEnv);
167    InitializeBloadData(theEnv);
168    EnvDefineFunction2(theEnv,"bload",'b',PTIEF BloadCommand,"BloadCommand","11k");
169 #endif
170 #endif
171   }
172 
173 /******************************************************/
174 /* DeallocateFileCommandData: Deallocates environment */
175 /*    data for file commands.                         */
176 /******************************************************/
DeallocateFileCommandData(void * theEnv)177 static void DeallocateFileCommandData(
178   void *theEnv)
179   {
180    struct batchEntry *theEntry, *nextEntry;
181 
182    theEntry = FileCommandData(theEnv)->TopOfBatchList;
183    while (theEntry != NULL)
184      {
185       nextEntry = theEntry->next;
186 
187       if (theEntry->batchType == FILE_BATCH)
188         { GenClose(theEnv,(FILE *) FileCommandData(theEnv)->TopOfBatchList->inputSource); }
189       else
190         { rm(theEnv,(void *) theEntry->theString,strlen(theEntry->theString) + 1); }
191 
192       rtn_struct(theEnv,batchEntry,theEntry);
193 
194       theEntry = nextEntry;
195      }
196 
197    if (FileCommandData(theEnv)->BatchBuffer != NULL)
198      { rm(theEnv,FileCommandData(theEnv)->BatchBuffer,FileCommandData(theEnv)->BatchMaximumPosition); }
199 
200    DeleteString(theEnv,FileCommandData(theEnv)->batchPriorParsingFile);
201    FileCommandData(theEnv)->batchPriorParsingFile = NULL;
202 
203 #if DEBUGGING_FUNCTIONS
204    if (FileCommandData(theEnv)->DribbleBuffer != NULL)
205      { rm(theEnv,FileCommandData(theEnv)->DribbleBuffer,FileCommandData(theEnv)->DribbleMaximumPosition); }
206 
207    if (FileCommandData(theEnv)->DribbleFP != NULL)
208      { GenClose(theEnv,FileCommandData(theEnv)->DribbleFP); }
209 #endif
210   }
211 
212 #if DEBUGGING_FUNCTIONS
213 /*****************************************************/
214 /* FindDribble: Find routine for the dribble router. */
215 /*****************************************************/
FindDribble(void * theEnv,const char * logicalName)216 static int FindDribble(
217   void *theEnv,
218   const char *logicalName)
219   {
220 #if MAC_XCD
221 #pragma unused(theEnv)
222 #endif
223 
224    if ( (strcmp(logicalName,STDOUT) == 0) ||
225         (strcmp(logicalName,STDIN) == 0) ||
226         (strcmp(logicalName,WPROMPT) == 0) ||
227         (strcmp(logicalName,WTRACE) == 0) ||
228         (strcmp(logicalName,WERROR) == 0) ||
229         (strcmp(logicalName,WWARNING) == 0) ||
230         (strcmp(logicalName,WDISPLAY) == 0) ||
231         (strcmp(logicalName,WDIALOG) == 0) )
232      { return(TRUE); }
233 
234     return(FALSE);
235   }
236 
237 /*******************************************************/
238 /* PrintDribble: Print routine for the dribble router. */
239 /*******************************************************/
PrintDribble(void * theEnv,const char * logicalName,const char * str)240 static int PrintDribble(
241   void *theEnv,
242   const char *logicalName,
243   const char *str)
244   {
245    int i;
246 
247    /*======================================*/
248    /* Send the output to the dribble file. */
249    /*======================================*/
250 
251    for (i = 0 ; str[i] != EOS ; i++)
252      { PutcDribbleBuffer(theEnv,str[i]); }
253 
254    /*===========================================================*/
255    /* Send the output to any routers interested in printing it. */
256    /*===========================================================*/
257 
258    EnvDeactivateRouter(theEnv,"dribble");
259    EnvPrintRouter(theEnv,logicalName,str);
260    EnvActivateRouter(theEnv,"dribble");
261 
262    return(1);
263   }
264 
265 /*****************************************************/
266 /* GetcDribble: Getc routine for the dribble router. */
267 /*****************************************************/
GetcDribble(void * theEnv,const char * logicalName)268 static int GetcDribble(
269   void *theEnv,
270   const char *logicalName)
271   {
272    int rv;
273 
274    /*===========================================*/
275    /* Deactivate the dribble router and get the */
276    /* character from another active router.     */
277    /*===========================================*/
278 
279    EnvDeactivateRouter(theEnv,"dribble");
280    rv = EnvGetcRouter(theEnv,logicalName);
281    EnvActivateRouter(theEnv,"dribble");
282 
283    /*==========================================*/
284    /* Put the character retrieved from another */
285    /* router into the dribble buffer.          */
286    /*==========================================*/
287 
288    PutcDribbleBuffer(theEnv,rv);
289 
290    /*=======================*/
291    /* Return the character. */
292    /*=======================*/
293 
294    return(rv);
295   }
296 
297 /***********************************************************/
298 /* PutcDribbleBuffer: Putc routine for the dribble router. */
299 /***********************************************************/
PutcDribbleBuffer(void * theEnv,int rv)300 static void PutcDribbleBuffer(
301   void *theEnv,
302   int rv)
303   {
304    /*===================================================*/
305    /* Receiving an end-of-file character will cause the */
306    /* contents of the dribble buffer to be flushed.     */
307    /*===================================================*/
308 
309    if (rv == EOF)
310      {
311       if (FileCommandData(theEnv)->DribbleCurrentPosition > 0)
312         {
313          fprintf(FileCommandData(theEnv)->DribbleFP,"%s",FileCommandData(theEnv)->DribbleBuffer);
314          FileCommandData(theEnv)->DribbleCurrentPosition = 0;
315          FileCommandData(theEnv)->DribbleBuffer[0] = EOS;
316         }
317      }
318 
319    /*===========================================================*/
320    /* If we aren't receiving command input, then the character  */
321    /* just received doesn't need to be placed in the dribble    */
322    /* buffer--It can be written directly to the file. This will */
323    /* occur for example when the command prompt is being        */
324    /* printed (the AwaitingInput variable will be FALSE because */
325    /* command input has not been receivied yet). Before writing */
326    /* the character to the file, the dribble buffer is flushed. */
327    /*===========================================================*/
328 
329    else if (RouterData(theEnv)->AwaitingInput == FALSE)
330      {
331       if (FileCommandData(theEnv)->DribbleCurrentPosition > 0)
332         {
333          fprintf(FileCommandData(theEnv)->DribbleFP,"%s",FileCommandData(theEnv)->DribbleBuffer);
334          FileCommandData(theEnv)->DribbleCurrentPosition = 0;
335          FileCommandData(theEnv)->DribbleBuffer[0] = EOS;
336         }
337 
338       fputc(rv,FileCommandData(theEnv)->DribbleFP);
339      }
340 
341    /*=====================================================*/
342    /* Otherwise, add the character to the dribble buffer. */
343    /*=====================================================*/
344 
345    else
346      {
347       FileCommandData(theEnv)->DribbleBuffer = ExpandStringWithChar(theEnv,rv,FileCommandData(theEnv)->DribbleBuffer,
348                                            &FileCommandData(theEnv)->DribbleCurrentPosition,
349                                            &FileCommandData(theEnv)->DribbleMaximumPosition,
350                                            FileCommandData(theEnv)->DribbleMaximumPosition+BUFFER_SIZE);
351      }
352   }
353 
354 /*********************************************************/
355 /* UngetcDribble: Ungetc routine for the dribble router. */
356 /*********************************************************/
UngetcDribble(void * theEnv,int ch,const char * logicalName)357 static int UngetcDribble(
358   void *theEnv,
359   int ch,
360   const char *logicalName)
361   {
362    int rv;
363 
364    /*===============================================*/
365    /* Remove the character from the dribble buffer. */
366    /*===============================================*/
367 
368    if (FileCommandData(theEnv)->DribbleCurrentPosition > 0) FileCommandData(theEnv)->DribbleCurrentPosition--;
369    FileCommandData(theEnv)->DribbleBuffer[FileCommandData(theEnv)->DribbleCurrentPosition] = EOS;
370 
371    /*=============================================*/
372    /* Deactivate the dribble router and pass the  */
373    /* ungetc request to the other active routers. */
374    /*=============================================*/
375 
376    EnvDeactivateRouter(theEnv,"dribble");
377    rv = EnvUngetcRouter(theEnv,ch,logicalName);
378    EnvActivateRouter(theEnv,"dribble");
379 
380    /*==========================================*/
381    /* Return the result of the ungetc request. */
382    /*==========================================*/
383 
384    return(rv);
385   }
386 
387 /*****************************************************/
388 /* ExitDribble: Exit routine for the dribble router. */
389 /*****************************************************/
ExitDribble(void * theEnv,int num)390 static int ExitDribble(
391   void *theEnv,
392   int num)
393   {
394 #if MAC_XCD
395 #pragma unused(num)
396 #endif
397 
398    if (FileCommandData(theEnv)->DribbleCurrentPosition > 0)
399      { fprintf(FileCommandData(theEnv)->DribbleFP,"%s",FileCommandData(theEnv)->DribbleBuffer); }
400 
401    if (FileCommandData(theEnv)->DribbleFP != NULL) GenClose(theEnv,FileCommandData(theEnv)->DribbleFP);
402    return(1);
403   }
404 
405 /******************************************/
406 /* DribbleOnCommand: H/L access routine   */
407 /*   for the dribble-on command.          */
408 /******************************************/
DribbleOnCommand(void * theEnv)409 globle int DribbleOnCommand(
410   void *theEnv)
411   {
412    const char *fileName;
413 
414    if (EnvArgCountCheck(theEnv,"dribble-on",EXACTLY,1) == -1) return(FALSE);
415    if ((fileName = GetFileName(theEnv,"dribble-on",1)) == NULL) return(FALSE);
416 
417    return (EnvDribbleOn(theEnv,fileName));
418   }
419 
420 /**********************************/
421 /* EnvDribbleOn: C access routine */
422 /*   for the dribble-on command.  */
423 /**********************************/
EnvDribbleOn(void * theEnv,const char * fileName)424 globle intBool EnvDribbleOn(
425   void *theEnv,
426   const char *fileName)
427   {
428    /*==============================*/
429    /* If a dribble file is already */
430    /* open, then close it.         */
431    /*==============================*/
432 
433    if (FileCommandData(theEnv)->DribbleFP != NULL)
434      { EnvDribbleOff(theEnv); }
435 
436    /*========================*/
437    /* Open the dribble file. */
438    /*========================*/
439 
440    FileCommandData(theEnv)->DribbleFP = GenOpen(theEnv,fileName,"w");
441    if (FileCommandData(theEnv)->DribbleFP == NULL)
442      {
443       OpenErrorMessage(theEnv,"dribble-on",fileName);
444       return(0);
445      }
446 
447    /*============================*/
448    /* Create the dribble router. */
449    /*============================*/
450 
451    EnvAddRouter(theEnv,"dribble", 40,
452              FindDribble, PrintDribble,
453              GetcDribble, UngetcDribble,
454              ExitDribble);
455 
456    FileCommandData(theEnv)->DribbleCurrentPosition = 0;
457 
458    /*================================================*/
459    /* Call the dribble status function. This is used */
460    /* by some of the machine specific interfaces to  */
461    /* do things such as changing the wording of menu */
462    /* items from "Turn Dribble On..." to             */
463    /* "Turn Dribble Off..."                          */
464    /*================================================*/
465 
466    if (FileCommandData(theEnv)->DribbleStatusFunction != NULL)
467      { (*FileCommandData(theEnv)->DribbleStatusFunction)(theEnv,TRUE); }
468 
469    /*=====================================*/
470    /* Return TRUE to indicate the dribble */
471    /* file was successfully opened.       */
472    /*=====================================*/
473 
474    return(TRUE);
475   }
476 
477 /*************************************************/
478 /* EnvDribbleActive: Returns TRUE if the dribble */
479 /*   router is active, otherwise FALSE>          */
480 /*************************************************/
EnvDribbleActive(void * theEnv)481 globle intBool EnvDribbleActive(
482   void *theEnv)
483   {
484    if (FileCommandData(theEnv)->DribbleFP != NULL) return(TRUE);
485 
486    return(FALSE);
487   }
488 
489 /*******************************************/
490 /* DribbleOffCommand: H/L access  routine  */
491 /*   for the dribble-off command.          */
492 /*******************************************/
DribbleOffCommand(void * theEnv)493 globle int DribbleOffCommand(
494   void *theEnv)
495   {
496    if (EnvArgCountCheck(theEnv,"dribble-off",EXACTLY,0) == -1) return(FALSE);
497    return(EnvDribbleOff(theEnv));
498   }
499 
500 /***********************************/
501 /* EnvDribbleOff: C access routine */
502 /*   for the dribble-off command.  */
503 /***********************************/
EnvDribbleOff(void * theEnv)504 globle intBool EnvDribbleOff(
505   void *theEnv)
506   {
507    int rv = 0;
508 
509    /*================================================*/
510    /* Call the dribble status function. This is used */
511    /* by some of the machine specific interfaces to  */
512    /* do things such as changing the wording of menu */
513    /* items from "Turn Dribble On..." to             */
514    /* "Turn Dribble Off..."                          */
515    /*================================================*/
516 
517    if (FileCommandData(theEnv)->DribbleStatusFunction != NULL)
518      { (*FileCommandData(theEnv)->DribbleStatusFunction)(theEnv,FALSE); }
519 
520    /*=======================================*/
521    /* Close the dribble file and deactivate */
522    /* the dribble router.                   */
523    /*=======================================*/
524 
525    if (FileCommandData(theEnv)->DribbleFP != NULL)
526      {
527       if (FileCommandData(theEnv)->DribbleCurrentPosition > 0)
528         { fprintf(FileCommandData(theEnv)->DribbleFP,"%s",FileCommandData(theEnv)->DribbleBuffer); }
529       EnvDeleteRouter(theEnv,"dribble");
530       if (GenClose(theEnv,FileCommandData(theEnv)->DribbleFP) == 0) rv = 1;
531      }
532    else
533      { rv = 1; }
534 
535    FileCommandData(theEnv)->DribbleFP = NULL;
536 
537    /*============================================*/
538    /* Free the space used by the dribble buffer. */
539    /*============================================*/
540 
541    if (FileCommandData(theEnv)->DribbleBuffer != NULL)
542      {
543       rm(theEnv,FileCommandData(theEnv)->DribbleBuffer,FileCommandData(theEnv)->DribbleMaximumPosition);
544       FileCommandData(theEnv)->DribbleBuffer = NULL;
545      }
546 
547    FileCommandData(theEnv)->DribbleCurrentPosition = 0;
548    FileCommandData(theEnv)->DribbleMaximumPosition = 0;
549 
550    /*============================================*/
551    /* Return TRUE if the dribble file was closed */
552    /* without error, otherwise return FALSE.     */
553    /*============================================*/
554 
555    return(rv);
556   }
557 
558 /*****************************************************/
559 /* SetDribbleStatusFunction: Sets the function which */
560 /*   is called whenever the dribble router is turned */
561 /*   on or off.                                      */
562 /*****************************************************/
SetDribbleStatusFunction(void * theEnv,int (* fnptr)(void *,int))563 globle void SetDribbleStatusFunction(
564   void *theEnv,
565   int (*fnptr)(void *,int))
566   {
567    FileCommandData(theEnv)->DribbleStatusFunction = fnptr;
568   }
569 
570 #endif /* DEBUGGING_FUNCTIONS */
571 
572 /*************************************************/
573 /* FindBatch: Find routine for the batch router. */
574 /*************************************************/
FindBatch(void * theEnv,const char * logicalName)575 static int FindBatch(
576   void *theEnv,
577   const char *logicalName)
578   {
579 #if MAC_XCD
580 #pragma unused(theEnv)
581 #endif
582 
583    if (strcmp(logicalName,STDIN) == 0)
584      { return(TRUE); }
585 
586    return(FALSE);
587   }
588 
589 /*************************************************/
590 /* GetcBatch: Getc routine for the batch router. */
591 /*************************************************/
GetcBatch(void * theEnv,const char * logicalName)592 static int GetcBatch(
593   void *theEnv,
594   const char *logicalName)
595   {
596    return(LLGetcBatch(theEnv,logicalName,FALSE));
597   }
598 
599 /***************************************************/
600 /* LLGetcBatch: Lower level routine for retrieving */
601 /*   a character when a batch file is active.      */
602 /***************************************************/
LLGetcBatch(void * theEnv,const char * logicalName,int returnOnEOF)603 globle int LLGetcBatch(
604   void *theEnv,
605   const char *logicalName,
606   int returnOnEOF)
607   {
608    int rv = EOF, flag = 1;
609 
610    /*=================================================*/
611    /* Get a character until a valid character appears */
612    /* or no more batch files are left.                */
613    /*=================================================*/
614 
615    while ((rv == EOF) && (flag == 1))
616      {
617       if (FileCommandData(theEnv)->BatchType == FILE_BATCH)
618         { rv = getc((FILE *) FileCommandData(theEnv)->BatchSource); }
619       else
620         { rv = EnvGetcRouter(theEnv,(char *) FileCommandData(theEnv)->BatchSource); }
621 
622       if (rv == EOF)
623         {
624          if (FileCommandData(theEnv)->BatchCurrentPosition > 0) EnvPrintRouter(theEnv,STDOUT,(char *) FileCommandData(theEnv)->BatchBuffer);
625          flag = RemoveBatch(theEnv);
626         }
627      }
628 
629    /*=========================================================*/
630    /* If the character retrieved is an end-of-file character, */
631    /* then there are no batch files with character input      */
632    /* remaining. Remove the batch router.                     */
633    /*=========================================================*/
634 
635    if (rv == EOF)
636      {
637       if (FileCommandData(theEnv)->BatchCurrentPosition > 0) EnvPrintRouter(theEnv,STDOUT,(char *) FileCommandData(theEnv)->BatchBuffer);
638       EnvDeleteRouter(theEnv,"batch");
639       RemoveBatch(theEnv);
640       if (returnOnEOF == TRUE)
641         { return (EOF); }
642       else
643         { return(EnvGetcRouter(theEnv,logicalName)); }
644      }
645 
646    /*========================================*/
647    /* Add the character to the batch buffer. */
648    /*========================================*/
649 
650    FileCommandData(theEnv)->BatchBuffer = ExpandStringWithChar(theEnv,(char) rv,FileCommandData(theEnv)->BatchBuffer,&FileCommandData(theEnv)->BatchCurrentPosition,
651                                       &FileCommandData(theEnv)->BatchMaximumPosition,FileCommandData(theEnv)->BatchMaximumPosition+BUFFER_SIZE);
652 
653    /*======================================*/
654    /* If a carriage return is encountered, */
655    /* then flush the batch buffer.         */
656    /*======================================*/
657 
658    if ((char) rv == '\n')
659      {
660       EnvPrintRouter(theEnv,STDOUT,(char *) FileCommandData(theEnv)->BatchBuffer);
661       FileCommandData(theEnv)->BatchCurrentPosition = 0;
662       if ((FileCommandData(theEnv)->BatchBuffer != NULL) && (FileCommandData(theEnv)->BatchMaximumPosition > BUFFER_SIZE))
663         {
664          rm(theEnv,FileCommandData(theEnv)->BatchBuffer,FileCommandData(theEnv)->BatchMaximumPosition);
665          FileCommandData(theEnv)->BatchMaximumPosition = 0;
666          FileCommandData(theEnv)->BatchBuffer = NULL;
667         }
668      }
669 
670    /*=============================*/
671    /* Increment the line counter. */
672    /*=============================*/
673 
674    if (((char) rv == '\r') || ((char) rv == '\n'))
675      { IncrementLineCount(theEnv); }
676 
677    /*=====================================================*/
678    /* Return the character retrieved from the batch file. */
679    /*=====================================================*/
680 
681    return(rv);
682   }
683 
684 /*****************************************************/
685 /* UngetcBatch: Ungetc routine for the batch router. */
686 /*****************************************************/
UngetcBatch(void * theEnv,int ch,const char * logicalName)687 static int UngetcBatch(
688   void *theEnv,
689   int ch,
690   const char *logicalName)
691   {
692 #if MAC_XCD
693 #pragma unused(logicalName)
694 #endif
695 
696    if (FileCommandData(theEnv)->BatchCurrentPosition > 0) FileCommandData(theEnv)->BatchCurrentPosition--;
697    if (FileCommandData(theEnv)->BatchBuffer != NULL) FileCommandData(theEnv)->BatchBuffer[FileCommandData(theEnv)->BatchCurrentPosition] = EOS;
698    if (FileCommandData(theEnv)->BatchType == FILE_BATCH)
699      { return(ungetc(ch,(FILE *) FileCommandData(theEnv)->BatchSource)); }
700 
701    return(EnvUngetcRouter(theEnv,ch,(char *) FileCommandData(theEnv)->BatchSource));
702   }
703 
704 /*************************************************/
705 /* ExitBatch: Exit routine for the batch router. */
706 /*************************************************/
ExitBatch(void * theEnv,int num)707 static int ExitBatch(
708   void *theEnv,
709   int num)
710   {
711 #if MAC_XCD
712 #pragma unused(num)
713 #endif
714    CloseAllBatchSources(theEnv);
715    return(1);
716   }
717 
718 /**************************************/
719 /* BatchCommand: H/L access routine   */
720 /*   for the batch command.           */
721 /**************************************/
BatchCommand(void * theEnv)722 globle int BatchCommand(
723   void *theEnv)
724   {
725    const char *fileName;
726 
727    if (EnvArgCountCheck(theEnv,"batch",EXACTLY,1) == -1) return(FALSE);
728    if ((fileName = GetFileName(theEnv,"batch",1)) == NULL) return(FALSE);
729 
730    return(OpenBatch(theEnv,fileName,FALSE));
731   }
732 
733 /**************************************************/
734 /* Batch: C access routine for the batch command. */
735 /**************************************************/
Batch(void * theEnv,const char * fileName)736 globle int Batch(
737   void *theEnv,
738   const char *fileName)
739   { return(OpenBatch(theEnv,fileName,FALSE)); }
740 
741 /***********************************************/
742 /* OpenBatch: Adds a file to the list of files */
743 /*   opened with the batch command.            */
744 /***********************************************/
OpenBatch(void * theEnv,const char * fileName,int placeAtEnd)745 globle int OpenBatch(
746   void *theEnv,
747   const char *fileName,
748   int placeAtEnd)
749   {
750    FILE *theFile;
751 
752    /*======================*/
753    /* Open the batch file. */
754    /*======================*/
755 
756    theFile = GenOpen(theEnv,fileName,"r");
757 
758    if (theFile == NULL)
759      {
760       OpenErrorMessage(theEnv,"batch",fileName);
761       return(FALSE);
762      }
763 
764    /*============================*/
765    /* Create the batch router if */
766    /* it doesn't already exist.  */
767    /*============================*/
768 
769    if (FileCommandData(theEnv)->TopOfBatchList == NULL)
770      {
771       EnvAddRouter(theEnv,"batch", 20,
772                  FindBatch, NULL,
773                  GetcBatch, UngetcBatch,
774                  ExitBatch);
775      }
776 
777    /*===============================================================*/
778    /* If a batch file is already open, save its current line count. */
779    /*===============================================================*/
780 
781    if (FileCommandData(theEnv)->TopOfBatchList != NULL)
782      { FileCommandData(theEnv)->TopOfBatchList->lineNumber = GetLineCount(theEnv); }
783 
784 #if (! RUN_TIME) && (! BLOAD_ONLY)
785 
786    /*========================================================================*/
787    /* If this is the first batch file, remember the prior parsing file name. */
788    /*========================================================================*/
789 
790    if (FileCommandData(theEnv)->TopOfBatchList == NULL)
791      { FileCommandData(theEnv)->batchPriorParsingFile = CopyString(theEnv,EnvGetParsingFileName(theEnv)); }
792 
793    /*=======================================================*/
794    /* Create the error capture router if it does not exist. */
795    /*=======================================================*/
796 
797    EnvSetParsingFileName(theEnv,fileName);
798    SetLineCount(theEnv,0);
799 
800    CreateErrorCaptureRouter(theEnv);
801 #endif
802 
803    /*====================================*/
804    /* Add the newly opened batch file to */
805    /* the list of batch files opened.    */
806    /*====================================*/
807 
808    AddBatch(theEnv,placeAtEnd,(void *) theFile,FILE_BATCH,NULL,fileName);
809 
810    /*===================================*/
811    /* Return TRUE to indicate the batch */
812    /* file was successfully opened.     */
813    /*===================================*/
814 
815    return(TRUE);
816   }
817 
818 /*****************************************************************/
819 /* OpenStringBatch: Opens a string source for batch processing.  */
820 /*   The memory allocated for the argument stringName must be    */
821 /*   deallocated by the user. The memory allocated for theString */
822 /*   will be deallocated by the batch routines when batch        */
823 /*   processing for the  string is completed.                    */
824 /*****************************************************************/
OpenStringBatch(void * theEnv,const char * stringName,const char * theString,int placeAtEnd)825 globle int OpenStringBatch(
826   void *theEnv,
827   const char *stringName,
828   const char *theString,
829   int placeAtEnd)
830   {
831    if (OpenStringSource(theEnv,stringName,theString,0) == 0)
832      { return(0); }
833 
834    if (FileCommandData(theEnv)->TopOfBatchList == NULL)
835      {
836       EnvAddRouter(theEnv,"batch", 20,
837                  FindBatch, NULL,
838                  GetcBatch, UngetcBatch,
839                  ExitBatch);
840      }
841 
842    AddBatch(theEnv,placeAtEnd,(void *) stringName,STRING_BATCH,theString,NULL);
843 
844    return(1);
845   }
846 
847 /*******************************************************/
848 /* AddBatch: Creates the batch file data structure and */
849 /*   adds it to the list of opened batch files.        */
850 /*******************************************************/
AddBatch(void * theEnv,int placeAtEnd,void * theSource,int type,const char * theString,const char * theFileName)851 static void AddBatch(
852   void *theEnv,
853   int placeAtEnd,
854   void *theSource,
855   int type,
856   const char *theString,
857   const char *theFileName)
858   {
859    struct batchEntry *bptr;
860 
861    /*=========================*/
862    /* Create the batch entry. */
863    /*=========================*/
864 
865    bptr = get_struct(theEnv,batchEntry);
866    bptr->batchType = type;
867    bptr->inputSource = theSource;
868    bptr->theString = theString;
869    bptr->fileName = CopyString(theEnv,theFileName);
870    bptr->lineNumber = 0;
871    bptr->next = NULL;
872 
873    /*============================*/
874    /* Add the entry to the list. */
875    /*============================*/
876 
877    if (FileCommandData(theEnv)->TopOfBatchList == NULL)
878      {
879       FileCommandData(theEnv)->TopOfBatchList = bptr;
880       FileCommandData(theEnv)->BottomOfBatchList = bptr;
881       FileCommandData(theEnv)->BatchType = type;
882       FileCommandData(theEnv)->BatchSource = theSource;
883       FileCommandData(theEnv)->BatchCurrentPosition = 0;
884      }
885    else if (placeAtEnd == FALSE)
886      {
887       bptr->next = FileCommandData(theEnv)->TopOfBatchList;
888       FileCommandData(theEnv)->TopOfBatchList = bptr;
889       FileCommandData(theEnv)->BatchType = type;
890       FileCommandData(theEnv)->BatchSource = theSource;
891       FileCommandData(theEnv)->BatchCurrentPosition = 0;
892      }
893    else
894      {
895       FileCommandData(theEnv)->BottomOfBatchList->next = bptr;
896       FileCommandData(theEnv)->BottomOfBatchList = bptr;
897      }
898   }
899 
900 /******************************************************************/
901 /* RemoveBatch: Removes the top entry on the list of batch files. */
902 /******************************************************************/
RemoveBatch(void * theEnv)903 globle int RemoveBatch(
904   void *theEnv)
905   {
906    struct batchEntry *bptr;
907    int rv, fileBatch = FALSE;
908 
909    if (FileCommandData(theEnv)->TopOfBatchList == NULL) return(FALSE);
910 
911    /*==================================================*/
912    /* Close the source from which batch input is read. */
913    /*==================================================*/
914 
915    if (FileCommandData(theEnv)->TopOfBatchList->batchType == FILE_BATCH)
916      {
917       fileBatch = TRUE;
918       GenClose(theEnv,(FILE *) FileCommandData(theEnv)->TopOfBatchList->inputSource);
919 #if (! RUN_TIME) && (! BLOAD_ONLY)
920       FlushParsingMessages(theEnv);
921       DeleteErrorCaptureRouter(theEnv);
922 #endif
923      }
924    else
925      {
926       CloseStringSource(theEnv,(char *) FileCommandData(theEnv)->TopOfBatchList->inputSource);
927       rm(theEnv,(void *) FileCommandData(theEnv)->TopOfBatchList->theString,
928          strlen(FileCommandData(theEnv)->TopOfBatchList->theString) + 1);
929      }
930 
931    /*=================================*/
932    /* Remove the entry from the list. */
933    /*=================================*/
934 
935    DeleteString(theEnv,(char *) FileCommandData(theEnv)->TopOfBatchList->fileName);
936    bptr = FileCommandData(theEnv)->TopOfBatchList;
937    FileCommandData(theEnv)->TopOfBatchList = FileCommandData(theEnv)->TopOfBatchList->next;
938 
939    rtn_struct(theEnv,batchEntry,bptr);
940 
941    /*========================================================*/
942    /* If there are no batch files remaining to be processed, */
943    /* then free the space used by the batch buffer.          */
944    /*========================================================*/
945 
946    if (FileCommandData(theEnv)->TopOfBatchList == NULL)
947      {
948       FileCommandData(theEnv)->BottomOfBatchList = NULL;
949       FileCommandData(theEnv)->BatchSource = NULL;
950       if (FileCommandData(theEnv)->BatchBuffer != NULL)
951         {
952          rm(theEnv,FileCommandData(theEnv)->BatchBuffer,FileCommandData(theEnv)->BatchMaximumPosition);
953          FileCommandData(theEnv)->BatchBuffer = NULL;
954         }
955       FileCommandData(theEnv)->BatchCurrentPosition = 0;
956       FileCommandData(theEnv)->BatchMaximumPosition = 0;
957       rv = 0;
958 
959 #if (! RUN_TIME) && (! BLOAD_ONLY)
960       if (fileBatch)
961         {
962          EnvSetParsingFileName(theEnv,FileCommandData(theEnv)->batchPriorParsingFile);
963          DeleteString(theEnv,FileCommandData(theEnv)->batchPriorParsingFile);
964          FileCommandData(theEnv)->batchPriorParsingFile = NULL;
965         }
966 #endif
967      }
968 
969    /*===========================================*/
970    /* Otherwise move on to the next batch file. */
971    /*===========================================*/
972 
973    else
974      {
975       FileCommandData(theEnv)->BatchType = FileCommandData(theEnv)->TopOfBatchList->batchType;
976       FileCommandData(theEnv)->BatchSource = FileCommandData(theEnv)->TopOfBatchList->inputSource;
977       FileCommandData(theEnv)->BatchCurrentPosition = 0;
978       rv = 1;
979 #if (! RUN_TIME) && (! BLOAD_ONLY)
980       if (FileCommandData(theEnv)->TopOfBatchList->batchType == FILE_BATCH)
981         { EnvSetParsingFileName(theEnv,FileCommandData(theEnv)->TopOfBatchList->fileName); }
982 
983       SetLineCount(theEnv,FileCommandData(theEnv)->TopOfBatchList->lineNumber);
984 #endif
985      }
986 
987    /*====================================================*/
988    /* Return TRUE if a batch file if there are remaining */
989    /* batch files to be processed, otherwise FALSE.      */
990    /*====================================================*/
991 
992    return(rv);
993   }
994 
995 /****************************************/
996 /* BatchActive: Returns TRUE if a batch */
997 /*   file is open, otherwise FALSE.     */
998 /****************************************/
BatchActive(void * theEnv)999 globle intBool BatchActive(
1000   void *theEnv)
1001   {
1002    if (FileCommandData(theEnv)->TopOfBatchList != NULL) return(TRUE);
1003 
1004    return(FALSE);
1005   }
1006 
1007 /******************************************************/
1008 /* CloseAllBatchSources: Closes all open batch files. */
1009 /******************************************************/
CloseAllBatchSources(void * theEnv)1010 globle void CloseAllBatchSources(
1011   void *theEnv)
1012   {
1013    /*================================================*/
1014    /* Free the batch buffer if it contains anything. */
1015    /*================================================*/
1016 
1017    if (FileCommandData(theEnv)->BatchBuffer != NULL)
1018      {
1019       if (FileCommandData(theEnv)->BatchCurrentPosition > 0) EnvPrintRouter(theEnv,STDOUT,(char *) FileCommandData(theEnv)->BatchBuffer);
1020       rm(theEnv,FileCommandData(theEnv)->BatchBuffer,FileCommandData(theEnv)->BatchMaximumPosition);
1021       FileCommandData(theEnv)->BatchBuffer = NULL;
1022       FileCommandData(theEnv)->BatchCurrentPosition = 0;
1023       FileCommandData(theEnv)->BatchMaximumPosition = 0;
1024      }
1025 
1026    /*==========================*/
1027    /* Delete the batch router. */
1028    /*==========================*/
1029 
1030    EnvDeleteRouter(theEnv,"batch");
1031 
1032    /*=====================================*/
1033    /* Close each of the open batch files. */
1034    /*=====================================*/
1035 
1036    while (RemoveBatch(theEnv))
1037      { /* Do Nothing */ }
1038   }
1039 
1040 /******************************************/
1041 /* BatchStarCommand: H/L access routine   */
1042 /*   for the batch* command.              */
1043 /******************************************/
BatchStarCommand(void * theEnv)1044 globle int BatchStarCommand(
1045   void *theEnv)
1046   {
1047    const char *fileName;
1048 
1049    if (EnvArgCountCheck(theEnv,"batch*",EXACTLY,1) == -1) return(FALSE);
1050    if ((fileName = GetFileName(theEnv,"batch*",1)) == NULL) return(FALSE);
1051 
1052    return(EnvBatchStar(theEnv,fileName));
1053   }
1054 
1055 #if ! RUN_TIME
1056 
1057 /**********************************************************/
1058 /* EnvBatchStar: C access routine for the batch* command. */
1059 /**********************************************************/
EnvBatchStar(void * theEnv,const char * fileName)1060 globle int EnvBatchStar(
1061   void *theEnv,
1062   const char *fileName)
1063   {
1064    int inchar;
1065    FILE *theFile;
1066    char *theString = NULL;
1067    size_t position = 0;
1068    size_t maxChars = 0;
1069 #if (! RUN_TIME) && (! BLOAD_ONLY)
1070    char *oldParsingFileName;
1071    long oldLineCountValue;
1072 #endif
1073    /*======================*/
1074    /* Open the batch file. */
1075    /*======================*/
1076 
1077    theFile = GenOpen(theEnv,fileName,"r");
1078 
1079    if (theFile == NULL)
1080      {
1081       OpenErrorMessage(theEnv,"batch",fileName);
1082       return(FALSE);
1083      }
1084 
1085    /*======================================*/
1086    /* Setup for capturing errors/warnings. */
1087    /*======================================*/
1088 
1089 #if (! RUN_TIME) && (! BLOAD_ONLY)
1090    oldParsingFileName = CopyString(theEnv,EnvGetParsingFileName(theEnv));
1091    EnvSetParsingFileName(theEnv,fileName);
1092 
1093    CreateErrorCaptureRouter(theEnv);
1094 
1095    oldLineCountValue = SetLineCount(theEnv,1);
1096 #endif
1097 
1098    /*========================*/
1099    /* Reset the error state. */
1100    /*========================*/
1101 
1102    SetHaltExecution(theEnv,FALSE);
1103    SetEvaluationError(theEnv,FALSE);
1104 
1105    /*=============================================*/
1106    /* Evaluate commands from the file one by one. */
1107    /*=============================================*/
1108 
1109    while ((inchar = getc(theFile)) != EOF)
1110      {
1111       theString = ExpandStringWithChar(theEnv,inchar,theString,&position,
1112                                        &maxChars,maxChars+80);
1113 
1114       if (CompleteCommand(theString) != 0)
1115         {
1116          FlushPPBuffer(theEnv);
1117          SetPPBufferStatus(theEnv,OFF);
1118          RouteCommand(theEnv,theString,FALSE);
1119          FlushPPBuffer(theEnv);
1120          SetHaltExecution(theEnv,FALSE);
1121          SetEvaluationError(theEnv,FALSE);
1122          FlushBindList(theEnv);
1123          genfree(theEnv,theString,(unsigned) maxChars);
1124          theString = NULL;
1125          maxChars = 0;
1126          position = 0;
1127 #if (! RUN_TIME) && (! BLOAD_ONLY)
1128          FlushParsingMessages(theEnv);
1129 #endif
1130         }
1131 
1132       if ((inchar == '\r') || (inchar == '\n'))
1133         { IncrementLineCount(theEnv); }
1134      }
1135 
1136    if (theString != NULL)
1137      { genfree(theEnv,theString,(unsigned) maxChars); }
1138 
1139    /*=======================*/
1140    /* Close the batch file. */
1141    /*=======================*/
1142 
1143    GenClose(theEnv,theFile);
1144 
1145    /*========================================*/
1146    /* Cleanup for capturing errors/warnings. */
1147    /*========================================*/
1148 
1149 #if (! RUN_TIME) && (! BLOAD_ONLY)
1150    FlushParsingMessages(theEnv);
1151    DeleteErrorCaptureRouter(theEnv);
1152 
1153    SetLineCount(theEnv,oldLineCountValue);
1154 
1155    EnvSetParsingFileName(theEnv,oldParsingFileName);
1156    DeleteString(theEnv,oldParsingFileName);
1157 #endif
1158 
1159    return(TRUE);
1160   }
1161 
1162 #else
1163 
1164 /**************************************************/
1165 /* EnvBatchStar: This is the non-functional stub  */
1166 /*   provided for use with a run-time version.    */
1167 /**************************************************/
EnvBatchStar(void * theEnv,const char * fileName)1168 globle int EnvBatchStar(
1169   void *theEnv,
1170   const char *fileName)
1171   {
1172    PrintErrorID(theEnv,"FILECOM",1,FALSE);
1173    EnvPrintRouter(theEnv,WERROR,"Function batch* does not work in run time modules.\n");
1174    return(FALSE);
1175   }
1176 
1177 #endif
1178 
1179 /***********************************************************/
1180 /* LoadCommand: H/L access routine for the load command.   */
1181 /***********************************************************/
LoadCommand(void * theEnv)1182 globle int LoadCommand(
1183   void *theEnv)
1184   {
1185 #if (! BLOAD_ONLY) && (! RUN_TIME)
1186    const char *theFileName;
1187    int rv;
1188 
1189    if (EnvArgCountCheck(theEnv,"load",EXACTLY,1) == -1) return(FALSE);
1190    if ((theFileName = GetFileName(theEnv,"load",1)) == NULL) return(FALSE);
1191 
1192    SetPrintWhileLoading(theEnv,TRUE);
1193 
1194    if ((rv = EnvLoad(theEnv,theFileName)) == FALSE)
1195      {
1196       SetPrintWhileLoading(theEnv,FALSE);
1197       OpenErrorMessage(theEnv,"load",theFileName);
1198       return(FALSE);
1199      }
1200 
1201    SetPrintWhileLoading(theEnv,FALSE);
1202    if (rv == -1) return(FALSE);
1203    return(TRUE);
1204 #else
1205    EnvPrintRouter(theEnv,WDIALOG,"Load is not available in this environment\n");
1206    return(FALSE);
1207 #endif
1208   }
1209 
1210 /****************************************************************/
1211 /* LoadStarCommand: H/L access routine for the load* command.   */
1212 /****************************************************************/
LoadStarCommand(void * theEnv)1213 globle int LoadStarCommand(
1214   void *theEnv)
1215   {
1216 #if (! BLOAD_ONLY) && (! RUN_TIME)
1217    const char *theFileName;
1218    int rv;
1219 
1220    if (EnvArgCountCheck(theEnv,"load*",EXACTLY,1) == -1) return(FALSE);
1221    if ((theFileName = GetFileName(theEnv,"load*",1)) == NULL) return(FALSE);
1222 
1223    if ((rv = EnvLoad(theEnv,theFileName)) == FALSE)
1224      {
1225       OpenErrorMessage(theEnv,"load*",theFileName);
1226       return(FALSE);
1227      }
1228 
1229    if (rv == -1) return(FALSE);
1230    return(TRUE);
1231 #else
1232    EnvPrintRouter(theEnv,WDIALOG,"Load* is not available in this environment\n");
1233    return(FALSE);
1234 #endif
1235   }
1236 
1237 #if DEBUGGING_FUNCTIONS
1238 /***********************************************************/
1239 /* SaveCommand: H/L access routine for the save command.   */
1240 /***********************************************************/
SaveCommand(void * theEnv)1241 globle int SaveCommand(
1242   void *theEnv)
1243   {
1244 #if (! BLOAD_ONLY) && (! RUN_TIME)
1245    const char *theFileName;
1246 
1247    if (EnvArgCountCheck(theEnv,"save",EXACTLY,1) == -1) return(FALSE);
1248    if ((theFileName = GetFileName(theEnv,"save",1)) == NULL) return(FALSE);
1249 
1250    if (EnvSave(theEnv,theFileName) == FALSE)
1251      {
1252       OpenErrorMessage(theEnv,"save",theFileName);
1253       return(FALSE);
1254      }
1255 
1256    return(TRUE);
1257 #else
1258    EnvPrintRouter(theEnv,WDIALOG,"Save is not available in this environment\n");
1259    return(FALSE);
1260 #endif
1261   }
1262 #endif
1263 
1264 /*#####################################*/
1265 /* ALLOW_ENVIRONMENT_GLOBALS Functions */
1266 /*#####################################*/
1267 
1268 #if ALLOW_ENVIRONMENT_GLOBALS
1269 
1270 #if DEBUGGING_FUNCTIONS
1271 
DribbleActive()1272 globle intBool DribbleActive()
1273   {
1274    return EnvDribbleActive(GetCurrentEnvironment());
1275   }
1276 
DribbleOn(const char * fileName)1277 globle intBool DribbleOn(
1278   const char *fileName)
1279   {
1280    return EnvDribbleOn(GetCurrentEnvironment(),fileName);
1281   }
1282 
DribbleOff()1283 globle intBool DribbleOff()
1284   {
1285    return EnvDribbleOff(GetCurrentEnvironment());
1286   }
1287 
1288 #endif /* DEBUGGING_FUNCTIONS */
1289 
BatchStar(const char * fileName)1290 globle int BatchStar(
1291   const char *fileName)
1292   {
1293    return EnvBatchStar(GetCurrentEnvironment(),fileName);
1294   }
1295 
1296 #endif /* ALLOW_ENVIRONMENT_GLOBALS */
1297 
1298 
1299