00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #define _UTILITY_SOURCE_
00032
00033 #include "setup.h"
00034
00035 #include <ctype.h>
00036 #include <stdlib.h>
00037
00038 #include <stdio.h>
00039 #define _STDIO_INCLUDED_
00040 #include <string.h>
00041
00042 #include "envrnmnt.h"
00043 #include "evaluatn.h"
00044 #include "facthsh.h"
00045 #include "memalloc.h"
00046 #include "multifld.h"
00047 #include "prntutil.h"
00048 #include "sysdep.h"
00049
00050 #include "utility.h"
00051
00052 #define MAX_EPHEMERAL_COUNT 1000L
00053 #define MAX_EPHEMERAL_SIZE 10240L
00054 #define COUNT_INCREMENT 1000L
00055 #define SIZE_INCREMENT 10240L
00056
00057
00058
00059
00060
00061 static void DeallocateUtilityData(void *);
00062
00063
00064
00065
00066
00067 globle void InitializeUtilityData(
00068 void *theEnv)
00069 {
00070 AllocateEnvironmentData(theEnv,UTILITY_DATA,sizeof(struct utilityData),DeallocateUtilityData);
00071
00072 UtilityData(theEnv)->GarbageCollectionLocks = 0;
00073 UtilityData(theEnv)->GarbageCollectionHeuristicsEnabled = TRUE;
00074 UtilityData(theEnv)->PeriodicFunctionsEnabled = TRUE;
00075 UtilityData(theEnv)->YieldFunctionEnabled = TRUE;
00076
00077 UtilityData(theEnv)->CurrentEphemeralCountMax = MAX_EPHEMERAL_COUNT;
00078 UtilityData(theEnv)->CurrentEphemeralSizeMax = MAX_EPHEMERAL_SIZE;
00079 UtilityData(theEnv)->LastEvaluationDepth = -1;
00080 }
00081
00082
00083
00084
00085
00086 static void DeallocateUtilityData(
00087 void *theEnv)
00088 {
00089 struct callFunctionItem *tmpPtr, *nextPtr;
00090 struct trackedMemory *tmpTM, *nextTM;
00091
00092 tmpTM = UtilityData(theEnv)->trackList;
00093 while (tmpTM != NULL)
00094 {
00095 nextTM = tmpTM->next;
00096 genfree(theEnv,tmpTM->theMemory,tmpTM->memSize);
00097 rtn_struct(theEnv,trackedMemory,tmpTM);
00098 tmpTM = nextTM;
00099 }
00100
00101 tmpPtr = UtilityData(theEnv)->ListOfPeriodicFunctions;
00102 while (tmpPtr != NULL)
00103 {
00104 nextPtr = tmpPtr->next;
00105 rtn_struct(theEnv,callFunctionItem,tmpPtr);
00106 tmpPtr = nextPtr;
00107 }
00108
00109 tmpPtr = UtilityData(theEnv)->ListOfCleanupFunctions;
00110 while (tmpPtr != NULL)
00111 {
00112 nextPtr = tmpPtr->next;
00113 rtn_struct(theEnv,callFunctionItem,tmpPtr);
00114 tmpPtr = nextPtr;
00115 }
00116 }
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 globle void PeriodicCleanup(
00127 void *theEnv,
00128 intBool cleanupAllDepths,
00129 intBool useHeuristics)
00130 {
00131 int oldDepth = -1;
00132 struct callFunctionItem *cleanupPtr,*periodPtr;
00133
00134
00135
00136
00137
00138 if (! UtilityData(theEnv)->GarbageCollectionHeuristicsEnabled)
00139 { useHeuristics = FALSE; }
00140
00141
00142
00143
00144
00145 if (UtilityData(theEnv)->PeriodicFunctionsEnabled)
00146 {
00147 for (periodPtr = UtilityData(theEnv)->ListOfPeriodicFunctions;
00148 periodPtr != NULL;
00149 periodPtr = periodPtr->next)
00150 {
00151 if (periodPtr->environmentAware)
00152 { (*periodPtr->func)(theEnv); }
00153 else
00154 { (* (void (*)(void)) periodPtr->func)(); }
00155 }
00156 }
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 if (UtilityData(theEnv)->LastEvaluationDepth > EvaluationData(theEnv)->CurrentEvaluationDepth)
00170 {
00171 UtilityData(theEnv)->LastEvaluationDepth = EvaluationData(theEnv)->CurrentEvaluationDepth;
00172 UtilityData(theEnv)->CurrentEphemeralCountMax = MAX_EPHEMERAL_COUNT;
00173 UtilityData(theEnv)->CurrentEphemeralSizeMax = MAX_EPHEMERAL_SIZE;
00174 }
00175
00176
00177
00178
00179
00180
00181
00182 if (UtilityData(theEnv)->GarbageCollectionLocks > 0) return;
00183
00184 if (useHeuristics &&
00185 (UtilityData(theEnv)->EphemeralItemCount < UtilityData(theEnv)->CurrentEphemeralCountMax) &&
00186 (UtilityData(theEnv)->EphemeralItemSize < UtilityData(theEnv)->CurrentEphemeralSizeMax))
00187 { return; }
00188
00189
00190
00191
00192
00193
00194
00195
00196 if (cleanupAllDepths)
00197 {
00198 oldDepth = EvaluationData(theEnv)->CurrentEvaluationDepth;
00199 EvaluationData(theEnv)->CurrentEvaluationDepth = -1;
00200 }
00201
00202
00203
00204
00205
00206 FlushMultifields(theEnv);
00207
00208
00209
00210
00211
00212 for (cleanupPtr = UtilityData(theEnv)->ListOfCleanupFunctions;
00213 cleanupPtr != NULL;
00214 cleanupPtr = cleanupPtr->next)
00215 {
00216 if (cleanupPtr->environmentAware)
00217 { (*cleanupPtr->func)(theEnv); }
00218 else
00219 { (* (void (*)(void)) cleanupPtr->func)(); }
00220 }
00221
00222
00223
00224
00225
00226 RemoveEphemeralAtoms(theEnv);
00227
00228
00229
00230
00231
00232
00233 if (cleanupAllDepths) EvaluationData(theEnv)->CurrentEvaluationDepth = oldDepth;
00234
00235
00236
00237
00238
00239
00240
00241 if ((UtilityData(theEnv)->EphemeralItemCount + COUNT_INCREMENT) > UtilityData(theEnv)->CurrentEphemeralCountMax)
00242 { UtilityData(theEnv)->CurrentEphemeralCountMax = UtilityData(theEnv)->EphemeralItemCount + COUNT_INCREMENT; }
00243
00244 if ((UtilityData(theEnv)->EphemeralItemSize + SIZE_INCREMENT) > UtilityData(theEnv)->CurrentEphemeralSizeMax)
00245 { UtilityData(theEnv)->CurrentEphemeralSizeMax = UtilityData(theEnv)->EphemeralItemSize + SIZE_INCREMENT; }
00246
00247
00248
00249
00250
00251
00252
00253 UtilityData(theEnv)->LastEvaluationDepth = EvaluationData(theEnv)->CurrentEvaluationDepth;
00254 }
00255
00256
00257
00258
00259
00260
00261 globle intBool AddCleanupFunction(
00262 void *theEnv,
00263 char *name,
00264 void (*theFunction)(void *),
00265 int priority)
00266 {
00267 UtilityData(theEnv)->ListOfCleanupFunctions =
00268 AddFunctionToCallList(theEnv,name,priority,
00269 (void (*)(void *)) theFunction,
00270 UtilityData(theEnv)->ListOfCleanupFunctions,TRUE);
00271 return(1);
00272 }
00273
00274 #if ALLOW_ENVIRONMENT_GLOBALS
00275
00276
00277
00278
00279 globle intBool AddPeriodicFunction(
00280 char *name,
00281 void (*theFunction)(void),
00282 int priority)
00283 {
00284 void *theEnv;
00285
00286 theEnv = GetCurrentEnvironment();
00287
00288 UtilityData(theEnv)->ListOfPeriodicFunctions =
00289 AddFunctionToCallList(theEnv,name,priority,
00290 (void (*)(void *)) theFunction,
00291 UtilityData(theEnv)->ListOfPeriodicFunctions,FALSE);
00292
00293 return(1);
00294 }
00295 #endif
00296
00297
00298
00299
00300
00301 globle intBool EnvAddPeriodicFunction(
00302 void *theEnv,
00303 char *name,
00304 void (*theFunction)(void *),
00305 int priority)
00306 {
00307 UtilityData(theEnv)->ListOfPeriodicFunctions =
00308 AddFunctionToCallList(theEnv,name,priority,
00309 (void (*)(void *)) theFunction,
00310 UtilityData(theEnv)->ListOfPeriodicFunctions,TRUE);
00311 return(1);
00312 }
00313
00314
00315
00316
00317
00318
00319 globle intBool RemoveCleanupFunction(
00320 void *theEnv,
00321 char *name)
00322 {
00323 intBool found;
00324
00325 UtilityData(theEnv)->ListOfCleanupFunctions =
00326 RemoveFunctionFromCallList(theEnv,name,UtilityData(theEnv)->ListOfCleanupFunctions,&found);
00327
00328 return found;
00329 }
00330
00331
00332
00333
00334
00335 globle intBool EnvRemovePeriodicFunction(
00336 void *theEnv,
00337 char *name)
00338 {
00339 intBool found;
00340
00341 UtilityData(theEnv)->ListOfPeriodicFunctions =
00342 RemoveFunctionFromCallList(theEnv,name,UtilityData(theEnv)->ListOfPeriodicFunctions,&found);
00343
00344 return found;
00345 }
00346
00347
00348
00349
00350
00351 globle char *StringPrintForm(
00352 void *theEnv,
00353 char *str)
00354 {
00355 int i = 0;
00356 size_t pos = 0;
00357 size_t max = 0;
00358 char *theString = NULL;
00359 void *thePtr;
00360
00361 theString = ExpandStringWithChar(theEnv,'"',theString,&pos,&max,max+80);
00362 while (str[i] != EOS)
00363 {
00364 if ((str[i] == '"') || (str[i] == '\\'))
00365 {
00366 theString = ExpandStringWithChar(theEnv,'\\',theString,&pos,&max,max+80);
00367 theString = ExpandStringWithChar(theEnv,str[i],theString,&pos,&max,max+80);
00368 }
00369 else
00370 { theString = ExpandStringWithChar(theEnv,str[i],theString,&pos,&max,max+80); }
00371 i++;
00372 }
00373
00374 theString = ExpandStringWithChar(theEnv,'"',theString,&pos,&max,max+80);
00375
00376 thePtr = EnvAddSymbol(theEnv,theString);
00377 rm(theEnv,theString,max);
00378 return(ValueToString(thePtr));
00379 }
00380
00381
00382
00383
00384
00385
00386 globle char *AppendStrings(
00387 void *theEnv,
00388 char *str1,
00389 char *str2)
00390 {
00391 size_t pos = 0;
00392 size_t max = 0;
00393 char *theString = NULL;
00394 void *thePtr;
00395
00396 theString = AppendToString(theEnv,str1,theString,&pos,&max);
00397 theString = AppendToString(theEnv,str2,theString,&pos,&max);
00398
00399 thePtr = EnvAddSymbol(theEnv,theString);
00400 rm(theEnv,theString,max);
00401 return(ValueToString(thePtr));
00402 }
00403
00404
00405
00406
00407
00408 globle char *AppendToString(
00409 void *theEnv,
00410 char *appendStr,
00411 char *oldStr,
00412 size_t *oldPos,
00413 size_t *oldMax)
00414 {
00415 size_t length;
00416
00417
00418
00419
00420
00421
00422 length = strlen(appendStr);
00423
00424
00425
00426
00427
00428 if ((oldStr = EnlargeString(theEnv,length,oldStr,oldPos,oldMax)) == NULL) { return(NULL); }
00429
00430
00431
00432
00433
00434 genstrcpy(&oldStr[*oldPos],appendStr);
00435 *oldPos += (int) length;
00436
00437
00438
00439
00440
00441 return(oldStr);
00442 }
00443
00444
00445
00446
00447
00448 globle char *InsertInString(
00449 void *theEnv,
00450 char *insertStr,
00451 size_t position,
00452 char *oldStr,
00453 size_t *oldPos,
00454 size_t *oldMax)
00455 {
00456 size_t length;
00457
00458
00459
00460
00461
00462
00463 length = strlen(insertStr);
00464
00465
00466
00467
00468
00469 if ((oldStr = EnlargeString(theEnv,length,oldStr,oldPos,oldMax)) == NULL) { return(NULL); }
00470
00471
00472
00473
00474
00475
00476 memmove(&oldStr[position],&oldStr[position+length],*oldPos - position);
00477
00478
00479
00480
00481
00482 genstrncpy(&oldStr[*oldPos],insertStr,length);
00483 *oldPos += (int) length;
00484
00485
00486
00487
00488
00489 return(oldStr);
00490 }
00491
00492
00493
00494
00495 globle char *EnlargeString(
00496 void *theEnv,
00497 size_t length,
00498 char *oldStr,
00499 size_t *oldPos,
00500 size_t *oldMax)
00501 {
00502
00503
00504
00505
00506
00507 if (length + *oldPos + 1 > *oldMax)
00508 {
00509 oldStr = (char *) genrealloc(theEnv,oldStr,*oldMax,length + *oldPos + 1);
00510 *oldMax = length + *oldPos + 1;
00511 }
00512
00513
00514
00515
00516
00517 if (oldStr == NULL) { return(NULL); }
00518
00519 return(oldStr);
00520 }
00521
00522
00523
00524
00525
00526
00527
00528 globle char *AppendNToString(
00529 void *theEnv,
00530 char *appendStr,
00531 char *oldStr,
00532 size_t length,
00533 size_t *oldPos,
00534 size_t *oldMax)
00535 {
00536 size_t lengthWithEOS;
00537
00538
00539
00540
00541
00542
00543 if (appendStr[length-1] != '\0') lengthWithEOS = length + 1;
00544 else lengthWithEOS = length;
00545
00546
00547
00548
00549
00550
00551 if (lengthWithEOS + *oldPos > *oldMax)
00552 {
00553 oldStr = (char *) genrealloc(theEnv,oldStr,*oldMax,*oldPos + lengthWithEOS);
00554 *oldMax = *oldPos + lengthWithEOS;
00555 }
00556
00557
00558
00559
00560
00561 if (oldStr == NULL) { return(NULL); }
00562
00563
00564
00565
00566
00567
00568 genstrncpy(&oldStr[*oldPos],appendStr,length);
00569 *oldPos += (lengthWithEOS - 1);
00570 oldStr[*oldPos] = '\0';
00571
00572
00573
00574
00575
00576 return(oldStr);
00577 }
00578
00579
00580
00581
00582
00583
00584
00585
00586 globle char *ExpandStringWithChar(
00587 void *theEnv,
00588 int inchar,
00589 char *str,
00590 size_t *pos,
00591 size_t *max,
00592 size_t newSize)
00593 {
00594 if ((*pos + 1) >= *max)
00595 {
00596 str = (char *) genrealloc(theEnv,str,*max,newSize);
00597 *max = newSize;
00598 }
00599
00600 if (inchar != '\b')
00601 {
00602 str[*pos] = (char) inchar;
00603 (*pos)++;
00604 str[*pos] = '\0';
00605 }
00606 else
00607 {
00608
00609
00610
00611
00612 while ((*pos > 1) && IsUTF8MultiByteContinuation(str[*pos - 1]))
00613 { (*pos)--; }
00614
00615
00616
00617
00618
00619 if (*pos > 0) (*pos)--;
00620 str[*pos] = '\0';
00621 }
00622
00623 return(str);
00624 }
00625
00626
00627
00628
00629
00630
00631 globle struct callFunctionItem *AddFunctionToCallList(
00632 void *theEnv,
00633 char *name,
00634 int priority,
00635 void (*func)(void *),
00636 struct callFunctionItem *head,
00637 intBool environmentAware)
00638 {
00639 return AddFunctionToCallListWithContext(theEnv,name,priority,func,head,environmentAware,NULL);
00640 }
00641
00642
00643
00644
00645
00646
00647 globle struct callFunctionItem *AddFunctionToCallListWithContext(
00648 void *theEnv,
00649 char *name,
00650 int priority,
00651 void (*func)(void *),
00652 struct callFunctionItem *head,
00653 intBool environmentAware,
00654 void *context)
00655 {
00656 struct callFunctionItem *newPtr, *currentPtr, *lastPtr = NULL;
00657
00658 newPtr = get_struct(theEnv,callFunctionItem);
00659
00660 newPtr->name = name;
00661 newPtr->func = func;
00662 newPtr->priority = priority;
00663 newPtr->environmentAware = (short) environmentAware;
00664 newPtr->context = context;
00665
00666 if (head == NULL)
00667 {
00668 newPtr->next = NULL;
00669 return(newPtr);
00670 }
00671
00672 currentPtr = head;
00673 while ((currentPtr != NULL) ? (priority < currentPtr->priority) : FALSE)
00674 {
00675 lastPtr = currentPtr;
00676 currentPtr = currentPtr->next;
00677 }
00678
00679 if (lastPtr == NULL)
00680 {
00681 newPtr->next = head;
00682 head = newPtr;
00683 }
00684 else
00685 {
00686 newPtr->next = currentPtr;
00687 lastPtr->next = newPtr;
00688 }
00689
00690 return(head);
00691 }
00692
00693
00694
00695
00696
00697
00698 globle struct callFunctionItem *RemoveFunctionFromCallList(
00699 void *theEnv,
00700 char *name,
00701 struct callFunctionItem *head,
00702 int *found)
00703 {
00704 struct callFunctionItem *currentPtr, *lastPtr;
00705
00706 *found = FALSE;
00707 lastPtr = NULL;
00708 currentPtr = head;
00709
00710 while (currentPtr != NULL)
00711 {
00712 if (strcmp(name,currentPtr->name) == 0)
00713 {
00714 *found = TRUE;
00715 if (lastPtr == NULL)
00716 { head = currentPtr->next; }
00717 else
00718 { lastPtr->next = currentPtr->next; }
00719
00720 rtn_struct(theEnv,callFunctionItem,currentPtr);
00721 return(head);
00722 }
00723
00724 lastPtr = currentPtr;
00725 currentPtr = currentPtr->next;
00726 }
00727
00728 return(head);
00729 }
00730
00731
00732
00733
00734
00735
00736 globle void DeallocateCallList(
00737 void *theEnv,
00738 struct callFunctionItem *theList)
00739 {
00740 struct callFunctionItem *tmpPtr, *nextPtr;
00741
00742 tmpPtr = theList;
00743 while (tmpPtr != NULL)
00744 {
00745 nextPtr = tmpPtr->next;
00746 rtn_struct(theEnv,callFunctionItem,tmpPtr);
00747 tmpPtr = nextPtr;
00748 }
00749 }
00750
00751
00752
00753
00754
00755 globle unsigned long ItemHashValue(
00756 void *theEnv,
00757 unsigned short theType,
00758 void *theValue,
00759 unsigned long theRange)
00760 {
00761 union
00762 {
00763 void *vv;
00764 unsigned uv;
00765 } fis;
00766
00767 switch(theType)
00768 {
00769 case FLOAT:
00770 return(HashFloat(ValueToDouble(theValue),theRange));
00771
00772 case INTEGER:
00773 return(HashInteger(ValueToLong(theValue),theRange));
00774
00775 case SYMBOL:
00776 case STRING:
00777 #if OBJECT_SYSTEM
00778 case INSTANCE_NAME:
00779 #endif
00780 return(HashSymbol(ValueToString(theValue),theRange));
00781
00782 case MULTIFIELD:
00783 return(HashMultifield((struct multifield *) theValue,theRange));
00784
00785 #if DEFTEMPLATE_CONSTRUCT
00786 case FACT_ADDRESS:
00787 return(((struct fact *) theValue)->hashValue % theRange);
00788 #endif
00789
00790 case EXTERNAL_ADDRESS:
00791 return(HashExternalAddress(ValueToExternalAddress(theValue),theRange));
00792
00793 #if OBJECT_SYSTEM
00794 case INSTANCE_ADDRESS:
00795 #endif
00796 fis.uv = 0;
00797 fis.vv = theValue;
00798 return(fis.uv % theRange);
00799 }
00800
00801 SystemError(theEnv,"UTILITY",1);
00802 return(0);
00803 }
00804
00805
00806
00807
00808
00809
00810
00811 globle void YieldTime(
00812 void *theEnv)
00813 {
00814 if ((UtilityData(theEnv)->YieldTimeFunction != NULL) && UtilityData(theEnv)->YieldFunctionEnabled)
00815 { (*UtilityData(theEnv)->YieldTimeFunction)(); }
00816 }
00817
00818
00819
00820
00821 globle short SetGarbageCollectionHeuristics(
00822 void *theEnv,
00823 short newValue)
00824 {
00825 short oldValue;
00826
00827 oldValue = UtilityData(theEnv)->GarbageCollectionHeuristicsEnabled;
00828
00829 UtilityData(theEnv)->GarbageCollectionHeuristicsEnabled = newValue;
00830
00831 return(oldValue);
00832 }
00833
00834
00835
00836
00837
00838 globle void EnvIncrementGCLocks(
00839 void *theEnv)
00840 {
00841 UtilityData(theEnv)->GarbageCollectionLocks++;
00842 }
00843
00844
00845
00846
00847
00848 globle void EnvDecrementGCLocks(
00849 void *theEnv)
00850 {
00851 if (UtilityData(theEnv)->GarbageCollectionLocks > 0)
00852 { UtilityData(theEnv)->GarbageCollectionLocks--; }
00853 }
00854
00855
00856
00857
00858 globle short EnablePeriodicFunctions(
00859 void *theEnv,
00860 short value)
00861 {
00862 short oldValue;
00863
00864 oldValue = UtilityData(theEnv)->PeriodicFunctionsEnabled;
00865
00866 UtilityData(theEnv)->PeriodicFunctionsEnabled = value;
00867
00868 return(oldValue);
00869 }
00870
00871
00872
00873
00874 globle short EnableYieldFunction(
00875 void *theEnv,
00876 short value)
00877 {
00878 short oldValue;
00879
00880 oldValue = UtilityData(theEnv)->YieldFunctionEnabled;
00881
00882 UtilityData(theEnv)->YieldFunctionEnabled = value;
00883
00884 return(oldValue);
00885 }
00886
00887
00888
00889
00890 globle struct trackedMemory *AddTrackedMemory(
00891 void *theEnv,
00892 void *theMemory,
00893 size_t theSize)
00894 {
00895 struct trackedMemory *newPtr;
00896
00897 newPtr = get_struct(theEnv,trackedMemory);
00898
00899 newPtr->prev = NULL;
00900 newPtr->theMemory = theMemory;
00901 newPtr->memSize = theSize;
00902 newPtr->next = UtilityData(theEnv)->trackList;
00903 UtilityData(theEnv)->trackList = newPtr;
00904
00905 return newPtr;
00906 }
00907
00908
00909
00910
00911 globle void RemoveTrackedMemory(
00912 void *theEnv,
00913 struct trackedMemory *theTracker)
00914 {
00915 if (theTracker->prev == NULL)
00916 { UtilityData(theEnv)->trackList = theTracker->next; }
00917 else
00918 { theTracker->prev->next = theTracker->next; }
00919
00920 if (theTracker->next != NULL)
00921 { theTracker->next->prev = theTracker->prev; }
00922
00923 rtn_struct(theEnv,trackedMemory,theTracker);
00924 }
00925
00926
00927
00928
00929
00930 globle size_t UTF8Length(
00931 char *s)
00932 {
00933 size_t i = 0, length = 0;
00934
00935 while (s[i] != '\0')
00936 {
00937 UTF8Increment(s,&i);
00938 length++;
00939 }
00940
00941 return(length);
00942 }
00943
00944
00945
00946
00947
00948 globle void UTF8Increment(
00949 char *s,
00950 size_t *i)
00951 {
00952 (void) (IsUTF8Start(s[++(*i)]) ||
00953 IsUTF8Start(s[++(*i)]) ||
00954 IsUTF8Start(s[++(*i)]) ||
00955 ++(*i));
00956 }
00957
00958
00959
00960
00961
00962 globle size_t UTF8Offset(
00963 char *str,
00964 size_t charnum)
00965 {
00966 size_t offs = 0;
00967
00968 while ((charnum > 0) && (str[offs]))
00969 {
00970 (void) (IsUTF8Start(str[++offs]) ||
00971 IsUTF8Start(str[++offs]) ||
00972 IsUTF8Start(str[++offs]) ||
00973 ++offs);
00974
00975 charnum--;
00976 }
00977
00978 return offs;
00979 }
00980
00981
00982
00983
00984
00985 globle size_t UTF8CharNum(
00986 char *s,
00987 size_t offset)
00988 {
00989 size_t charnum = 0, offs=0;
00990
00991 while ((offs < offset) && (s[offs]))
00992 {
00993 (void) (IsUTF8Start(s[++offs]) ||
00994 IsUTF8Start(s[++offs]) ||
00995 IsUTF8Start(s[++offs]) ||
00996 ++offs);
00997
00998 charnum++;
00999 }
01000
01001 return charnum;
01002 }