tizen 2.4 release
[external/clips.git] / src / utility.c
1    /*******************************************************/
2    /*      "C" Language Integrated Production System      */
3    /*                                                     */
4    /*             CLIPS Version 6.30  02/03/15            */
5    /*                                                     */
6    /*                   UTILITY MODULE                    */
7    /*******************************************************/
8
9 /*************************************************************/
10 /* Purpose: Provides a set of utility functions useful to    */
11 /*   other modules. Primarily these are the functions for    */
12 /*   handling periodic garbage collection and appending      */
13 /*   string data.                                            */
14 /*                                                           */
15 /* Principal Programmer(s):                                  */
16 /*      Gary D. Riley                                        */
17 /*                                                           */
18 /* Contributing Programmer(s):                               */
19 /*      Brian Dantes                                         */
20 /*      Jeff Bezanson                                        */
21 /*         www.cprogramming.com/tutorial/unicode.html        */
22 /*                                                           */
23 /* Revision History:                                         */
24 /*                                                           */
25 /*      6.24: Renamed BOOLEAN macro type to intBool.         */
26 /*                                                           */
27 /*      6.30: Changed integer type/precision.                */
28 /*                                                           */
29 /*            Changed garbage collection algorithm.          */
30 /*                                                           */
31 /*            Added CopyString, DeleteString,                */
32 /*            InsertInString,and EnlargeString functions.    */
33 /*                                                           */
34 /*            Used genstrncpy function instead of strncpy    */
35 /*            function.                                      */
36 /*                                                           */
37 /*            Support for typed EXTERNAL_ADDRESS.            */
38 /*                                                           */
39 /*            Support for tracked memory (allows memory to   */
40 /*            be freed if CLIPS is exited while executing).  */
41 /*                                                           */
42 /*            Added UTF-8 routines.                          */
43 /*                                                           */
44 /*            Added const qualifiers to remove C++           */
45 /*            deprecation warnings.                          */
46 /*                                                           */
47 /*            Converted API macros to function calls.        */
48 /*                                                           */
49 /*************************************************************/
50
51 #define _UTILITY_SOURCE_
52
53 #include "setup.h"
54
55 #include <ctype.h>
56 #include <stdlib.h>
57
58 #include <stdio.h>
59 #define _STDIO_INCLUDED_
60 #include <string.h>
61
62 #include "commline.h"
63 #include "envrnmnt.h"
64 #include "evaluatn.h"
65 #include "facthsh.h"
66 #include "memalloc.h"
67 #include "multifld.h"
68 #include "prntutil.h"
69 #include "sysdep.h"
70
71 #include "utility.h"
72
73 #define MAX_EPHEMERAL_COUNT 1000L
74 #define MAX_EPHEMERAL_SIZE 10240L
75 #define COUNT_INCREMENT 1000L
76 #define SIZE_INCREMENT 10240L
77
78 /***************************************/
79 /* LOCAL INTERNAL FUNCTION DEFINITIONS */
80 /***************************************/
81
82    static void                    DeallocateUtilityData(void *);
83
84 /************************************************/
85 /* InitializeUtilityData: Allocates environment */
86 /*    data for utility routines.                */
87 /************************************************/
88 globle void InitializeUtilityData(
89   void *theEnv)
90   {
91    AllocateEnvironmentData(theEnv,UTILITY_DATA,sizeof(struct utilityData),DeallocateUtilityData);
92
93    UtilityData(theEnv)->CurrentGarbageFrame = &UtilityData(theEnv)->MasterGarbageFrame;
94    UtilityData(theEnv)->CurrentGarbageFrame->topLevel = TRUE;
95
96    UtilityData(theEnv)->GarbageCollectionLocks = 0;
97    UtilityData(theEnv)->PeriodicFunctionsEnabled = TRUE;
98    UtilityData(theEnv)->YieldFunctionEnabled = TRUE;
99   }
100
101 /**************************************************/
102 /* DeallocateUtilityData: Deallocates environment */
103 /*    data for utility routines.                  */
104 /**************************************************/
105 static void DeallocateUtilityData(
106   void *theEnv)
107   {
108    struct callFunctionItem *tmpPtr, *nextPtr;
109    struct trackedMemory *tmpTM, *nextTM;
110    struct garbageFrame *theGarbageFrame;
111    struct ephemeron *edPtr, *nextEDPtr;
112    struct multifield *tmpMFPtr, *nextMFPtr;
113
114    /*======================*/
115    /* Free tracked memory. */
116    /*======================*/
117
118    tmpTM = UtilityData(theEnv)->trackList;
119    while (tmpTM != NULL)
120      {
121       nextTM = tmpTM->next;
122       genfree(theEnv,tmpTM->theMemory,tmpTM->memSize);
123       rtn_struct(theEnv,trackedMemory,tmpTM);
124       tmpTM = nextTM;
125      }
126
127    /*==========================*/
128    /* Free callback functions. */
129    /*==========================*/
130
131    tmpPtr = UtilityData(theEnv)->ListOfPeriodicFunctions;
132    while (tmpPtr != NULL)
133      {
134       nextPtr = tmpPtr->next;
135       rtn_struct(theEnv,callFunctionItem,tmpPtr);
136       tmpPtr = nextPtr;
137      }
138
139    tmpPtr = UtilityData(theEnv)->ListOfCleanupFunctions;
140    while (tmpPtr != NULL)
141      {
142       nextPtr = tmpPtr->next;
143       rtn_struct(theEnv,callFunctionItem,tmpPtr);
144       tmpPtr = nextPtr;
145      }
146
147    /*=========================================*/
148    /* Free the ephemerons tracking data which */
149    /* needs to be garbage collected.          */
150    /*=========================================*/
151
152    while (UtilityData(theEnv)->CurrentGarbageFrame != NULL)
153      {
154       theGarbageFrame = UtilityData(theEnv)->CurrentGarbageFrame;
155
156       edPtr = theGarbageFrame->ephemeralSymbolList;
157
158       while (edPtr != NULL)
159         {
160          nextEDPtr = edPtr->next;
161          rtn_struct(theEnv,ephemeron,edPtr);
162          edPtr = nextEDPtr;
163         }
164
165       edPtr = theGarbageFrame->ephemeralFloatList;
166
167       while (edPtr != NULL)
168         {
169          nextEDPtr = edPtr->next;
170          rtn_struct(theEnv,ephemeron,edPtr);
171          edPtr = nextEDPtr;
172         }
173
174       edPtr = theGarbageFrame->ephemeralIntegerList;
175
176       while (edPtr != NULL)
177         {
178          nextEDPtr = edPtr->next;
179          rtn_struct(theEnv,ephemeron,edPtr);
180          edPtr = nextEDPtr;
181         }
182
183       edPtr = theGarbageFrame->ephemeralBitMapList;
184
185       while (edPtr != NULL)
186         {
187          nextEDPtr = edPtr->next;
188          rtn_struct(theEnv,ephemeron,edPtr);
189          edPtr = nextEDPtr;
190         }
191
192       edPtr = theGarbageFrame->ephemeralExternalAddressList;
193
194       while (edPtr != NULL)
195         {
196          nextEDPtr = edPtr->next;
197          rtn_struct(theEnv,ephemeron,edPtr);
198          edPtr = nextEDPtr;
199         }
200
201       /*==========================*/
202       /* Free up multifield data. */
203       /*==========================*/
204
205       tmpMFPtr = theGarbageFrame->ListOfMultifields;
206       while (tmpMFPtr != NULL)
207         {
208          nextMFPtr = tmpMFPtr->next;
209          ReturnMultifield(theEnv,tmpMFPtr);
210          tmpMFPtr = nextMFPtr;
211         }
212
213       UtilityData(theEnv)->CurrentGarbageFrame = UtilityData(theEnv)->CurrentGarbageFrame->priorFrame;
214      }
215   }
216
217 /*****************************/
218 /* CleanCurrentGarbageFrame: */
219 /*****************************/
220 globle void CleanCurrentGarbageFrame(
221   void *theEnv,
222   DATA_OBJECT *returnValue)
223   {
224    struct garbageFrame *currentGarbageFrame;
225
226    currentGarbageFrame = UtilityData(theEnv)->CurrentGarbageFrame;
227
228    if (! currentGarbageFrame->dirty) return;
229
230    if (returnValue != NULL)
231      { ValueInstall(theEnv,returnValue); }
232
233    CallCleanupFunctions(theEnv);
234    RemoveEphemeralAtoms(theEnv);
235    FlushMultifields(theEnv);
236
237    if (returnValue != NULL)
238      { ValueDeinstall(theEnv,returnValue); }
239
240    if ((currentGarbageFrame->ephemeralFloatList == NULL) &&
241        (currentGarbageFrame->ephemeralIntegerList == NULL) &&
242        (currentGarbageFrame->ephemeralSymbolList == NULL) &&
243        (currentGarbageFrame->ephemeralBitMapList == NULL) &&
244        (currentGarbageFrame->ephemeralExternalAddressList == NULL) &&
245        (currentGarbageFrame->LastMultifield == NULL))
246      { currentGarbageFrame->dirty = FALSE; }
247   }
248
249 /*****************************/
250 /* RestorePriorGarbageFrame: */
251 /*****************************/
252 globle void RestorePriorGarbageFrame(
253   void *theEnv,
254   struct garbageFrame *newGarbageFrame,
255   struct garbageFrame *oldGarbageFrame,
256   DATA_OBJECT *returnValue)
257   {
258    if (newGarbageFrame->dirty)
259      {
260       if (returnValue != NULL) ValueInstall(theEnv,returnValue);
261       CallCleanupFunctions(theEnv);
262       RemoveEphemeralAtoms(theEnv);
263       FlushMultifields(theEnv);
264      }
265
266    UtilityData(theEnv)->CurrentGarbageFrame = oldGarbageFrame;
267
268    if (newGarbageFrame->dirty)
269      {
270       if (newGarbageFrame->ListOfMultifields != NULL)
271         {
272          if (oldGarbageFrame->ListOfMultifields == NULL)
273            { oldGarbageFrame->ListOfMultifields = newGarbageFrame->ListOfMultifields; }
274          else
275            { oldGarbageFrame->LastMultifield->next = newGarbageFrame->ListOfMultifields; }
276
277          oldGarbageFrame->LastMultifield = newGarbageFrame->LastMultifield;
278          oldGarbageFrame->dirty = TRUE;
279         }
280
281       if (returnValue != NULL) ValueDeinstall(theEnv,returnValue);
282      }
283
284    if (returnValue != NULL)
285      { EphemerateValue(theEnv,returnValue->type,returnValue->value); }
286   }
287
288 /*************************/
289 /* CallCleanupFunctions: */
290 /*************************/
291 globle void CallCleanupFunctions(
292   void *theEnv)
293   {
294    struct callFunctionItem *cleanupPtr;
295
296    for (cleanupPtr = UtilityData(theEnv)->ListOfCleanupFunctions;
297         cleanupPtr != NULL;
298         cleanupPtr = cleanupPtr->next)
299      {
300       if (cleanupPtr->environmentAware)
301         { (*cleanupPtr->func)(theEnv); }
302       else
303         { (* (void (*)(void)) cleanupPtr->func)(); }
304      }
305   }
306
307 /**************************************************/
308 /* CallPeriodicTasks: Calls the list of functions */
309 /*   for handling periodic tasks.                 */
310 /**************************************************/
311 globle void CallPeriodicTasks(
312   void *theEnv)
313   {
314    struct callFunctionItem *periodPtr;
315
316    if (UtilityData(theEnv)->PeriodicFunctionsEnabled)
317      {
318       for (periodPtr = UtilityData(theEnv)->ListOfPeriodicFunctions;
319            periodPtr != NULL;
320            periodPtr = periodPtr->next)
321         {
322          if (periodPtr->environmentAware)
323            { (*periodPtr->func)(theEnv); }
324          else
325            { (* (void (*)(void)) periodPtr->func)(); }
326         }
327      }
328   }
329
330 /***************************************************/
331 /* AddCleanupFunction: Adds a function to the list */
332 /*   of functions called to perform cleanup such   */
333 /*   as returning free memory to the memory pool.  */
334 /***************************************************/
335 globle intBool AddCleanupFunction(
336   void *theEnv,
337   const char *name,
338   void (*theFunction)(void *),
339   int priority)
340   {
341    UtilityData(theEnv)->ListOfCleanupFunctions =
342      AddFunctionToCallList(theEnv,name,priority,
343                            (void (*)(void *)) theFunction,
344                            UtilityData(theEnv)->ListOfCleanupFunctions,TRUE);
345    return(1);
346   }
347
348 #if ALLOW_ENVIRONMENT_GLOBALS
349 /****************************************************/
350 /* AddPeriodicFunction: Adds a function to the list */
351 /*   of functions called to handle periodic tasks.  */
352 /****************************************************/
353 globle intBool AddPeriodicFunction(
354   const char *name,
355   void (*theFunction)(void),
356   int priority)
357   {
358    void *theEnv;
359
360    theEnv = GetCurrentEnvironment();
361
362    UtilityData(theEnv)->ListOfPeriodicFunctions =
363      AddFunctionToCallList(theEnv,name,priority,
364                            (void (*)(void *)) theFunction,
365                            UtilityData(theEnv)->ListOfPeriodicFunctions,FALSE);
366
367    return(1);
368   }
369 #endif
370
371 /*******************************************************/
372 /* EnvAddPeriodicFunction: Adds a function to the list */
373 /*   of functions called to handle periodic tasks.     */
374 /*******************************************************/
375 globle intBool EnvAddPeriodicFunction(
376   void *theEnv,
377   const char *name,
378   void (*theFunction)(void *),
379   int priority)
380   {
381    UtilityData(theEnv)->ListOfPeriodicFunctions =
382      AddFunctionToCallList(theEnv,name,priority,
383                            (void (*)(void *)) theFunction,
384                            UtilityData(theEnv)->ListOfPeriodicFunctions,TRUE);
385    return(1);
386   }
387
388 /*******************************************************/
389 /* RemoveCleanupFunction: Removes a function from the  */
390 /*   list of functions called to perform cleanup such  */
391 /*   as returning free memory to the memory pool.      */
392 /*******************************************************/
393 globle intBool RemoveCleanupFunction(
394   void *theEnv,
395   const char *name)
396   {
397    intBool found;
398
399    UtilityData(theEnv)->ListOfCleanupFunctions =
400       RemoveFunctionFromCallList(theEnv,name,UtilityData(theEnv)->ListOfCleanupFunctions,&found);
401
402    return found;
403   }
404
405 /**********************************************************/
406 /* EnvRemovePeriodicFunction: Removes a function from the */
407 /*   list of functions called to handle periodic tasks.   */
408 /**********************************************************/
409 globle intBool EnvRemovePeriodicFunction(
410   void *theEnv,
411   const char *name)
412   {
413    intBool found;
414
415    UtilityData(theEnv)->ListOfPeriodicFunctions =
416       RemoveFunctionFromCallList(theEnv,name,UtilityData(theEnv)->ListOfPeriodicFunctions,&found);
417
418    return found;
419   }
420
421 /*****************************************************/
422 /* StringPrintForm: Generates printed representation */
423 /*   of a string. Replaces / with // and " with /".  */
424 /*****************************************************/
425 globle const char *StringPrintForm(
426   void *theEnv,
427   const char *str)
428   {
429    int i = 0;
430    size_t pos = 0;
431    size_t max = 0;
432    char *theString = NULL;
433    void *thePtr;
434
435    theString = ExpandStringWithChar(theEnv,'"',theString,&pos,&max,max+80);
436    while (str[i] != EOS)
437      {
438       if ((str[i] == '"') || (str[i] == '\\'))
439         {
440          theString = ExpandStringWithChar(theEnv,'\\',theString,&pos,&max,max+80);
441          theString = ExpandStringWithChar(theEnv,str[i],theString,&pos,&max,max+80);
442         }
443       else
444         { theString = ExpandStringWithChar(theEnv,str[i],theString,&pos,&max,max+80); }
445       i++;
446      }
447
448    theString = ExpandStringWithChar(theEnv,'"',theString,&pos,&max,max+80);
449
450    thePtr = EnvAddSymbol(theEnv,theString);
451    rm(theEnv,theString,max);
452    return(ValueToString(thePtr));
453   }
454
455 /**************************************************************/
456 /* CopyString: Copies a string using CLIPS memory management. */
457 /**************************************************************/
458 globle char *CopyString(
459   void *theEnv,
460   const char *theString)
461   {
462    char *stringCopy = NULL;
463
464    if (theString != NULL)
465      {
466       stringCopy = (char *) genalloc(theEnv,strlen(theString) + 1);
467       genstrcpy(stringCopy,theString);
468      }
469
470    return stringCopy;
471   }
472
473 /*****************************************************************/
474 /* DeleteString: Deletes a string using CLIPS memory management. */
475 /*****************************************************************/
476 globle void DeleteString(
477   void *theEnv,
478   char *theString)
479   {
480    if (theString != NULL)
481      { genfree(theEnv,theString,strlen(theString) + 1); }
482   }
483
484 /***********************************************************/
485 /* AppendStrings: Appends two strings together. The string */
486 /*   created is added to the SymbolTable, so it is not     */
487 /*   necessary to deallocate the string returned.          */
488 /***********************************************************/
489 globle const char *AppendStrings(
490   void *theEnv,
491   const char *str1,
492   const char *str2)
493   {
494    size_t pos = 0;
495    size_t max = 0;
496    char *theString = NULL;
497    void *thePtr;
498
499    theString = AppendToString(theEnv,str1,theString,&pos,&max);
500    theString = AppendToString(theEnv,str2,theString,&pos,&max);
501
502    thePtr = EnvAddSymbol(theEnv,theString);
503    rm(theEnv,theString,max);
504    return(ValueToString(thePtr));
505   }
506
507 /******************************************************/
508 /* AppendToString: Appends a string to another string */
509 /*   (expanding the other string if necessary).       */
510 /******************************************************/
511 globle char *AppendToString(
512   void *theEnv,
513   const char *appendStr,
514   char *oldStr,
515   size_t *oldPos,
516   size_t *oldMax)
517   {
518    size_t length;
519
520    /*=========================================*/
521    /* Expand the old string so it can contain */
522    /* the new string (if necessary).          */
523    /*=========================================*/
524
525    length = strlen(appendStr);
526
527    /*==============================================================*/
528    /* Return NULL if the old string was not successfully expanded. */
529    /*==============================================================*/
530
531    if ((oldStr = EnlargeString(theEnv,length,oldStr,oldPos,oldMax)) == NULL) { return(NULL); }
532
533    /*===============================================*/
534    /* Append the new string to the expanded string. */
535    /*===============================================*/
536
537    genstrcpy(&oldStr[*oldPos],appendStr);
538    *oldPos += (int) length;
539
540    /*============================================================*/
541    /* Return the expanded string containing the appended string. */
542    /*============================================================*/
543
544    return(oldStr);
545   }
546
547 /**********************************************************/
548 /* InsertInString: Inserts a string within another string */
549 /*   (expanding the other string if necessary).           */
550 /**********************************************************/
551 globle char *InsertInString(
552   void *theEnv,
553   const char *insertStr,
554   size_t position,
555   char *oldStr,
556   size_t *oldPos,
557   size_t *oldMax)
558   {
559    size_t length;
560
561    /*=========================================*/
562    /* Expand the old string so it can contain */
563    /* the new string (if necessary).          */
564    /*=========================================*/
565
566    length = strlen(insertStr);
567
568    /*==============================================================*/
569    /* Return NULL if the old string was not successfully expanded. */
570    /*==============================================================*/
571
572    if ((oldStr = EnlargeString(theEnv,length,oldStr,oldPos,oldMax)) == NULL) { return(NULL); }
573
574    /*================================================================*/
575    /* Shift the contents to the right of insertion point so that the */
576    /* new text does not overwrite what is currently in the string.   */
577    /*================================================================*/
578
579    memmove(&oldStr[position],&oldStr[position+length],*oldPos - position);
580
581    /*===============================================*/
582    /* Insert the new string in the expanded string. */
583    /*===============================================*/
584
585    genstrncpy(&oldStr[*oldPos],insertStr,length);
586    *oldPos += (int) length;
587
588    /*============================================================*/
589    /* Return the expanded string containing the appended string. */
590    /*============================================================*/
591
592    return(oldStr);
593   }
594
595 /*******************************************************************/
596 /* EnlargeString: Enlarges a string by the specified amount.       */
597 /*******************************************************************/
598 globle char *EnlargeString(
599   void *theEnv,
600   size_t length,
601   char *oldStr,
602   size_t *oldPos,
603   size_t *oldMax)
604   {
605    /*=========================================*/
606    /* Expand the old string so it can contain */
607    /* the new string (if necessary).          */
608    /*=========================================*/
609
610    if (length + *oldPos + 1 > *oldMax)
611      {
612       oldStr = (char *) genrealloc(theEnv,oldStr,*oldMax,length + *oldPos + 1);
613       *oldMax = length + *oldPos + 1;
614      }
615
616    /*==============================================================*/
617    /* Return NULL if the old string was not successfully expanded. */
618    /*==============================================================*/
619
620    if (oldStr == NULL) { return(NULL); }
621
622    return(oldStr);
623   }
624
625 /*******************************************************/
626 /* AppendNToString: Appends a string to another string */
627 /*   (expanding the other string if necessary). Only a */
628 /*   specified number of characters are appended from  */
629 /*   the string.                                       */
630 /*******************************************************/
631 globle char *AppendNToString(
632   void *theEnv,
633   const char *appendStr,
634   char *oldStr,
635   size_t length,
636   size_t *oldPos,
637   size_t *oldMax)
638   {
639    size_t lengthWithEOS;
640
641    /*====================================*/
642    /* Determine the number of characters */
643    /* to be appended from the string.    */
644    /*====================================*/
645
646    if (appendStr[length-1] != '\0') lengthWithEOS = length + 1;
647    else lengthWithEOS = length;
648
649    /*=========================================*/
650    /* Expand the old string so it can contain */
651    /* the new string (if necessary).          */
652    /*=========================================*/
653
654    if (lengthWithEOS + *oldPos > *oldMax)
655      {
656       oldStr = (char *) genrealloc(theEnv,oldStr,*oldMax,*oldPos + lengthWithEOS);
657       *oldMax = *oldPos + lengthWithEOS;
658      }
659
660    /*==============================================================*/
661    /* Return NULL if the old string was not successfully expanded. */
662    /*==============================================================*/
663
664    if (oldStr == NULL) { return(NULL); }
665
666    /*==================================*/
667    /* Append N characters from the new */
668    /* string to the expanded string.   */
669    /*==================================*/
670
671    genstrncpy(&oldStr[*oldPos],appendStr,length);
672    *oldPos += (lengthWithEOS - 1);
673    oldStr[*oldPos] = '\0';
674
675    /*============================================================*/
676    /* Return the expanded string containing the appended string. */
677    /*============================================================*/
678
679    return(oldStr);
680   }
681
682 /*******************************************************/
683 /* ExpandStringWithChar: Adds a character to a string, */
684 /*   reallocating space for the string if it needs to  */
685 /*   be enlarged. The backspace character causes the   */
686 /*   size of the string to reduced if it is "added" to */
687 /*   the string.                                       */
688 /*******************************************************/
689 globle char *ExpandStringWithChar(
690   void *theEnv,
691   int inchar,
692   char *str,
693   size_t *pos,
694   size_t *max,
695   size_t newSize)
696   {
697    if ((*pos + 1) >= *max)
698      {
699       str = (char *) genrealloc(theEnv,str,*max,newSize);
700       *max = newSize;
701      }
702
703   if (inchar != '\b')
704     {
705      str[*pos] = (char) inchar;
706      (*pos)++;
707      str[*pos] = '\0';
708     }
709   else
710     {
711      /*===========================================================*/
712      /* First delete any UTF-8 multibyte continuation characters. */
713      /*===========================================================*/
714
715      while ((*pos > 1) && IsUTF8MultiByteContinuation(str[*pos - 1]))
716        { (*pos)--; }
717
718      /*===================================================*/
719      /* Now delete the first byte of the UTF-8 character. */
720      /*===================================================*/
721
722      if (*pos > 0) (*pos)--;
723      str[*pos] = '\0';
724     }
725
726    return(str);
727   }
728
729 /*****************************************************************/
730 /* AddFunctionToCallList: Adds a function to a list of functions */
731 /*   which are called to perform certain operations (e.g. clear, */
732 /*   reset, and bload functions).                                */
733 /*****************************************************************/
734 globle struct callFunctionItem *AddFunctionToCallList(
735   void *theEnv,
736   const char *name,
737   int priority,
738   void (*func)(void *),
739   struct callFunctionItem *head,
740   intBool environmentAware)
741   {
742    return AddFunctionToCallListWithContext(theEnv,name,priority,func,head,environmentAware,NULL);
743   }
744
745 /***********************************************************/
746 /* AddFunctionToCallListWithContext: Adds a function to a  */
747 /*   list of functions which are called to perform certain */
748 /*   operations (e.g. clear, reset, and bload functions).  */
749 /***********************************************************/
750 globle struct callFunctionItem *AddFunctionToCallListWithContext(
751   void *theEnv,
752   const char *name,
753   int priority,
754   void (*func)(void *),
755   struct callFunctionItem *head,
756   intBool environmentAware,
757   void *context)
758   {
759    struct callFunctionItem *newPtr, *currentPtr, *lastPtr = NULL;
760
761    newPtr = get_struct(theEnv,callFunctionItem);
762
763    newPtr->name = name;
764    newPtr->func = func;
765    newPtr->priority = priority;
766    newPtr->environmentAware = (short) environmentAware;
767    newPtr->context = context;
768
769    if (head == NULL)
770      {
771       newPtr->next = NULL;
772       return(newPtr);
773      }
774
775    currentPtr = head;
776    while ((currentPtr != NULL) ? (priority < currentPtr->priority) : FALSE)
777      {
778       lastPtr = currentPtr;
779       currentPtr = currentPtr->next;
780      }
781
782    if (lastPtr == NULL)
783      {
784       newPtr->next = head;
785       head = newPtr;
786      }
787    else
788      {
789       newPtr->next = currentPtr;
790       lastPtr->next = newPtr;
791      }
792
793    return(head);
794   }
795
796 /*****************************************************************/
797 /* RemoveFunctionFromCallList: Removes a function from a list of */
798 /*   functions which are called to perform certain operations    */
799 /*   (e.g. clear, reset, and bload functions).                   */
800 /*****************************************************************/
801 globle struct callFunctionItem *RemoveFunctionFromCallList(
802   void *theEnv,
803   const char *name,
804   struct callFunctionItem *head,
805   int *found)
806   {
807    struct callFunctionItem *currentPtr, *lastPtr;
808
809    *found = FALSE;
810    lastPtr = NULL;
811    currentPtr = head;
812
813    while (currentPtr != NULL)
814      {
815       if (strcmp(name,currentPtr->name) == 0)
816         {
817          *found = TRUE;
818          if (lastPtr == NULL)
819            { head = currentPtr->next; }
820          else
821            { lastPtr->next = currentPtr->next; }
822
823          rtn_struct(theEnv,callFunctionItem,currentPtr);
824          return(head);
825         }
826
827       lastPtr = currentPtr;
828       currentPtr = currentPtr->next;
829      }
830
831    return(head);
832   }
833
834 /**************************************************************/
835 /* DeallocateCallList: Removes all functions from a list of   */
836 /*   functions which are called to perform certain operations */
837 /*   (e.g. clear, reset, and bload functions).                */
838 /**************************************************************/
839 globle void DeallocateCallList(
840   void *theEnv,
841   struct callFunctionItem *theList)
842   {
843    struct callFunctionItem *tmpPtr, *nextPtr;
844
845    tmpPtr = theList;
846    while (tmpPtr != NULL)
847      {
848       nextPtr = tmpPtr->next;
849       rtn_struct(theEnv,callFunctionItem,tmpPtr);
850       tmpPtr = nextPtr;
851      }
852   }
853
854 /***************************************************************/
855 /* AddFunctionToCallListWithArg: Adds a function to a list of  */
856 /*   functions which are called to perform certain operations  */
857 /*   (e.g. clear,reset, and bload functions).                  */
858 /***************************************************************/
859 globle struct callFunctionItemWithArg *AddFunctionToCallListWithArg(
860   void *theEnv,
861   const char *name,
862   int priority,
863   void (*func)(void *, void *),
864   struct callFunctionItemWithArg *head,
865   intBool environmentAware)
866   {
867    return AddFunctionToCallListWithArgWithContext(theEnv,name,priority,func,head,environmentAware,NULL);
868   }
869
870 /***************************************************************/
871 /* AddFunctionToCallListWithArgWithContext: Adds a function to */
872 /*   a list of functions which are called to perform certain   */
873 /*   operations (e.g. clear, reset, and bload functions).      */
874 /***************************************************************/
875 globle struct callFunctionItemWithArg *AddFunctionToCallListWithArgWithContext(
876   void *theEnv,
877   const char *name,
878   int priority,
879   void (*func)(void *, void *),
880   struct callFunctionItemWithArg *head,
881   intBool environmentAware,
882   void *context)
883   {
884    struct callFunctionItemWithArg *newPtr, *currentPtr, *lastPtr = NULL;
885
886    newPtr = get_struct(theEnv,callFunctionItemWithArg);
887
888    newPtr->name = name;
889    newPtr->func = func;
890    newPtr->priority = priority;
891    newPtr->environmentAware = (short) environmentAware;
892    newPtr->context = context;
893
894    if (head == NULL)
895      {
896       newPtr->next = NULL;
897       return(newPtr);
898      }
899
900    currentPtr = head;
901    while ((currentPtr != NULL) ? (priority < currentPtr->priority) : FALSE)
902      {
903       lastPtr = currentPtr;
904       currentPtr = currentPtr->next;
905      }
906
907    if (lastPtr == NULL)
908      {
909       newPtr->next = head;
910       head = newPtr;
911      }
912    else
913      {
914       newPtr->next = currentPtr;
915       lastPtr->next = newPtr;
916      }
917
918    return(head);
919   }
920
921 /**************************************************************/
922 /* RemoveFunctionFromCallListWithArg: Removes a function from */
923 /*   a list of functions which are called to perform certain  */
924 /*   operations (e.g. clear, reset, and bload functions).     */
925 /**************************************************************/
926 globle struct callFunctionItemWithArg *RemoveFunctionFromCallListWithArg(
927   void *theEnv,
928   const char *name,
929   struct callFunctionItemWithArg *head,
930   int *found)
931   {
932    struct callFunctionItemWithArg *currentPtr, *lastPtr;
933
934    *found = FALSE;
935    lastPtr = NULL;
936    currentPtr = head;
937
938    while (currentPtr != NULL)
939      {
940       if (strcmp(name,currentPtr->name) == 0)
941         {
942          *found = TRUE;
943          if (lastPtr == NULL)
944            { head = currentPtr->next; }
945          else
946            { lastPtr->next = currentPtr->next; }
947
948          rtn_struct(theEnv,callFunctionItemWithArg,currentPtr);
949          return(head);
950         }
951
952       lastPtr = currentPtr;
953       currentPtr = currentPtr->next;
954      }
955
956    return(head);
957   }
958
959 /**************************************************************/
960 /* DeallocateCallListWithArg: Removes all functions from a list of   */
961 /*   functions which are called to perform certain operations */
962 /*   (e.g. clear, reset, and bload functions).                */
963 /**************************************************************/
964 globle void DeallocateCallListWithArg(
965   void *theEnv,
966   struct callFunctionItemWithArg *theList)
967   {
968    struct callFunctionItemWithArg *tmpPtr, *nextPtr;
969
970    tmpPtr = theList;
971    while (tmpPtr != NULL)
972      {
973       nextPtr = tmpPtr->next;
974       rtn_struct(theEnv,callFunctionItemWithArg,tmpPtr);
975       tmpPtr = nextPtr;
976      }
977   }
978
979 /*****************************************/
980 /* ItemHashValue: Returns the hash value */
981 /*   for the specified value.            */
982 /*****************************************/
983 globle unsigned long ItemHashValue(
984   void *theEnv,
985   unsigned short theType,
986   void *theValue,
987   unsigned long theRange)
988   {
989    union
990      {
991       void *vv;
992       unsigned uv;
993      } fis;
994
995    switch(theType)
996      {
997       case FLOAT:
998         return(HashFloat(ValueToDouble(theValue),theRange));
999
1000       case INTEGER:
1001         return(HashInteger(ValueToLong(theValue),theRange));
1002
1003       case SYMBOL:
1004       case STRING:
1005 #if OBJECT_SYSTEM
1006       case INSTANCE_NAME:
1007 #endif
1008         return(HashSymbol(ValueToString(theValue),theRange));
1009
1010       case MULTIFIELD:
1011         return(HashMultifield((struct multifield *) theValue,theRange));
1012
1013 #if DEFTEMPLATE_CONSTRUCT
1014       case FACT_ADDRESS:
1015         return(((struct fact *) theValue)->hashValue % theRange);
1016 #endif
1017
1018       case EXTERNAL_ADDRESS:
1019         return(HashExternalAddress(ValueToExternalAddress(theValue),theRange));
1020
1021 #if OBJECT_SYSTEM
1022       case INSTANCE_ADDRESS:
1023 #endif
1024         fis.uv = 0;
1025         fis.vv = theValue;
1026         return(fis.uv % theRange);
1027      }
1028
1029    SystemError(theEnv,"UTILITY",1);
1030    return(0);
1031   }
1032
1033 /********************************************/
1034 /* YieldTime: Yields time to a user-defined */
1035 /*   function. Intended to allow foreground */
1036 /*   application responsiveness when CLIPS  */
1037 /*   is running in the background.          */
1038 /********************************************/
1039 globle void YieldTime(
1040   void *theEnv)
1041   {
1042    if ((UtilityData(theEnv)->YieldTimeFunction != NULL) && UtilityData(theEnv)->YieldFunctionEnabled)
1043      { (*UtilityData(theEnv)->YieldTimeFunction)(); }
1044   }
1045
1046 /**********************************************/
1047 /* EnvIncrementGCLocks: Increments the number */
1048 /*   of garbage collection locks.             */
1049 /**********************************************/
1050 globle void EnvIncrementGCLocks(
1051   void *theEnv)
1052   {
1053    UtilityData(theEnv)->GarbageCollectionLocks++;
1054   }
1055
1056 /**********************************************/
1057 /* EnvDecrementGCLocks: Decrements the number */
1058 /*   of garbage collection locks.             */
1059 /**********************************************/
1060 globle void EnvDecrementGCLocks(
1061   void *theEnv)
1062   {
1063    if (UtilityData(theEnv)->GarbageCollectionLocks > 0)
1064      { UtilityData(theEnv)->GarbageCollectionLocks--; }
1065
1066    if ((UtilityData(theEnv)->CurrentGarbageFrame->topLevel) && (! CommandLineData(theEnv)->EvaluatingTopLevelCommand) &&
1067        (EvaluationData(theEnv)->CurrentExpression == NULL) && (UtilityData(theEnv)->GarbageCollectionLocks == 0))
1068      {
1069       CleanCurrentGarbageFrame(theEnv,NULL);
1070       CallPeriodicTasks(theEnv);
1071      }
1072   }
1073
1074 /********************************************/
1075 /* EnablePeriodicFunctions:         */
1076 /********************************************/
1077 globle short EnablePeriodicFunctions(
1078   void *theEnv,
1079   short value)
1080   {
1081    short oldValue;
1082
1083    oldValue = UtilityData(theEnv)->PeriodicFunctionsEnabled;
1084
1085    UtilityData(theEnv)->PeriodicFunctionsEnabled = value;
1086
1087    return(oldValue);
1088   }
1089
1090 /************************/
1091 /* EnableYieldFunction: */
1092 /************************/
1093 globle short EnableYieldFunction(
1094   void *theEnv,
1095   short value)
1096   {
1097    short oldValue;
1098
1099    oldValue = UtilityData(theEnv)->YieldFunctionEnabled;
1100
1101    UtilityData(theEnv)->YieldFunctionEnabled = value;
1102
1103    return(oldValue);
1104   }
1105
1106 /*************************************************************************/
1107 /* AddTrackedMemory: Tracked memory is memory allocated by CLIPS that's  */
1108 /*   referenced by a variable on the stack, but not by any environment   */
1109 /*   data structure. An example would be the storage for local variables */
1110 /*   allocated when a deffunction is executed. Tracking this memory      */
1111 /*   allows it to be removed later when using longjmp as the code that   */
1112 /*   would normally deallocate the memory would be bypassed.             */
1113 /*************************************************************************/
1114 globle struct trackedMemory *AddTrackedMemory(
1115   void *theEnv,
1116   void *theMemory,
1117   size_t theSize)
1118   {
1119    struct trackedMemory *newPtr;
1120
1121    newPtr = get_struct(theEnv,trackedMemory);
1122
1123    newPtr->prev = NULL;
1124    newPtr->theMemory = theMemory;
1125    newPtr->memSize = theSize;
1126    newPtr->next = UtilityData(theEnv)->trackList;
1127    UtilityData(theEnv)->trackList = newPtr;
1128
1129    return newPtr;
1130   }
1131
1132 /************************/
1133 /* RemoveTrackedMemory: */
1134 /************************/
1135 globle void RemoveTrackedMemory(
1136   void *theEnv,
1137   struct trackedMemory *theTracker)
1138   {
1139    if (theTracker->prev == NULL)
1140      { UtilityData(theEnv)->trackList = theTracker->next; }
1141    else
1142      { theTracker->prev->next = theTracker->next; }
1143
1144    if (theTracker->next != NULL)
1145      { theTracker->next->prev = theTracker->prev; }
1146
1147    rtn_struct(theEnv,trackedMemory,theTracker);
1148   }
1149
1150 /******************************************/
1151 /* UTF8Length: Returns the logical number */
1152 /*   of characters in a UTF8 string.      */
1153 /******************************************/
1154 globle size_t UTF8Length(
1155   const char *s)
1156   {
1157    size_t i = 0, length = 0;
1158
1159    while (s[i] != '\0')
1160      {
1161       UTF8Increment(s,&i);
1162       length++;
1163      }
1164
1165    return(length);
1166   }
1167
1168 /*********************************************/
1169 /* UTF8Increment: Finds the beginning of the */
1170 /*   next character in a UTF8 string.        */
1171 /*********************************************/
1172 globle void UTF8Increment(
1173   const char *s,
1174   size_t *i)
1175   {
1176    (void) (IsUTF8Start(s[++(*i)]) ||
1177            IsUTF8Start(s[++(*i)]) ||
1178            IsUTF8Start(s[++(*i)]) ||
1179            ++(*i));
1180   }
1181
1182 /****************************************************/
1183 /* UTF8Offset: Converts the logical character index */
1184 /*   in a UTF8 string to the actual byte offset.    */
1185 /****************************************************/
1186 globle size_t UTF8Offset(
1187   const char *str,
1188   size_t charnum)
1189   {
1190    size_t offs = 0;
1191
1192    while ((charnum > 0) && (str[offs]))
1193      {
1194       (void) (IsUTF8Start(str[++offs]) ||
1195               IsUTF8Start(str[++offs]) ||
1196               IsUTF8Start(str[++offs]) ||
1197               ++offs);
1198
1199       charnum--;
1200      }
1201
1202    return offs;
1203   }
1204
1205 /*************************************************/
1206 /* UTF8CharNum: Converts the UTF8 character byte */
1207 /*   offset to the logical character index.      */
1208 /*************************************************/
1209 globle size_t UTF8CharNum(
1210   const char *s,
1211   size_t offset)
1212   {
1213    size_t charnum = 0, offs=0;
1214
1215    while ((offs < offset) && (s[offs]))
1216      {
1217       (void) (IsUTF8Start(s[++offs]) ||
1218               IsUTF8Start(s[++offs]) ||
1219               IsUTF8Start(s[++offs]) ||
1220               ++offs);
1221
1222       charnum++;
1223      }
1224
1225    return charnum;
1226   }
1227
1228 /*#####################################*/
1229 /* ALLOW_ENVIRONMENT_GLOBALS Functions */
1230 /*#####################################*/
1231
1232 #if ALLOW_ENVIRONMENT_GLOBALS
1233
1234 globle void IncrementGCLocks()
1235   {
1236    EnvIncrementGCLocks(GetCurrentEnvironment());
1237   }
1238
1239 globle void DecrementGCLocks()
1240   {
1241    EnvDecrementGCLocks(GetCurrentEnvironment());
1242   }
1243
1244 globle intBool RemovePeriodicFunction(
1245   const char *name)
1246   {
1247    return EnvRemovePeriodicFunction(GetCurrentEnvironment(),name);
1248   }
1249
1250 #endif /* ALLOW_ENVIRONMENT_GLOBALS */