00001 /* FILE: conftree.c 00002 HEADER: conftree.h 00003 00004 --GNU LGPL 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 00019 This file contains the main definitions that are needed to store 00020 configuration information. 00021 00022 A configuration information of a program is generally string values associated 00023 with symbols. The values and the symbols are zero terminated strings. 00024 00025 In this implementation the symbols are hierachically ordered just like in 00026 the Windows NT registry or the UNIX file hierarchy. 00027 00028 The program using this module can 00029 00030 retrieve information knowing the name of the symbol, 00031 can traverse the tree of the symbols. 00032 00033 The configuration file is stored in text file or in binary file to speed up 00034 reading. There is API to read text file, to save the read information in 00035 binary format and to read binary format. 00036 00037 There is no API to modify the structure in memory. Configuration information 00038 is changed modifying the text file and then recompiling it. The binary format 00039 is an intermediary compiled format. 00040 00041 00042 00043 TO_HEADER: 00044 00045 typedef long CFT_NODE; 00046 00047 #define CFT_NODE_LEAF 0x00 00048 #define CFT_NODE_BRANCH 0x01 00049 #define CFT_NODE_MASK 0x01 00050 00051 #define CFT_TYPE_STRING 0x02 00052 #define CFT_TYPE_INTEGER 0x04 00053 #define CFT_TYPE_REAL 0x06 00054 #define CFT_TYPE_MASK 0x06 00055 00056 #define CFT_ERROR_FILE 0x00000001 00057 #define CFT_ERROR_SYNTAX 0x00000002 00058 #define CFT_ERROR_MEMORY 0x00000003 00059 #define CFT_ERROR_EMPTY 0x00000004 00060 #define CFT_ERROR_NOTYPE 0x00000005 00061 00062 // this is the node of the compiled structure 00063 // the nodes are stored in an array and index values 00064 // point to each other 00065 typedef struct _tConfigNode { 00066 long lKey; // offset of the key string in the string table 00067 long lNext; // the next element on the same level or zero if this is the last one 00068 union { 00069 long lVal; // the element under the node or 00070 // the offset of the value string in the string table 00071 // the value of the long number 00072 double dVal;// the value of the float number 00073 }Val; 00074 unsigned char fFlag; //type of the node 00075 } tConfigNode, *tpConfigNode; 00076 00077 typedef struct _tConfigTree { 00078 tpConfigNode Root; 00079 long cNode; // the number of nodes in the config tree 00080 char *StringTable; 00081 unsigned long cbStringTable; // the size of the string table 00082 void *(*memory_allocating_function)(size_t, void *); 00083 void (*memory_releasing_function)(void *, void *); 00084 void *pMemorySegment; 00085 char *pszConfigFileName; 00086 char TC; 00087 } tConfigTree, *ptConfigTree; 00088 00089 #define CFT_ROOT_NODE 1 00090 00091 #define CFT_ERROR_SUCCESS 0x00000000 00092 #define CFT_ERROR_NOT_FOUND 0x00000001 00093 00094 */ 00095 #if _WIN32 00096 #include <windows.h> 00097 #if BCC32 || CYGWIN 00098 extern char *_pgmptr; 00099 #endif 00100 #endif 00101 #ifdef _DEBUG 00102 #include "testalloc.h" 00103 #endif 00104 00105 static char MAGIC[4] = { 0x43, 0x46, 0x47, 0x1A }; 00106 00107 #include <stdio.h> 00108 #include <stdlib.h> 00109 #include <string.h> 00110 #include <ctype.h> 00111 00112 #include "conftree.h" 00113 00114 #define ALLOC(X) (pCT->memory_allocating_function((X),pCT->pMemorySegment)) 00115 #define FREE(X) (pCT->memory_releasing_function((X),pCT->pMemorySegment)) 00116 00117 static void * _mya(size_t x,void *y){ 00118 return malloc(x); 00119 } 00120 static void _myf(void *x, void *y){ 00121 free(x); 00122 } 00123 00124 /*POD 00125 =H cft_init() 00126 00127 Before calling any other configuration handling function the caller has to prepare a T<tConfigTree> 00128 structure. To do this it has to call this function. 00129 00130 The first argument has to point to an allocated and uninitialized T<tConfigTree> structure. The second 00131 argument has to point to a memory allocating function. The third argument has to point to the memory releasing 00132 function that is capable releasing the memory allocated by the memory allocating function. 00133 00134 The argument T<pMemorySegment> should be the segment pointer to the memory handling functions. All memory allocation 00135 will be performed calling the T<memory_allocating_function> and passing the T<pMemorySegment> pointer as second argument 00136 to it. All memory releasing will be done via the function T<memory_releasing_function> passing 00137 T<pMemorySegment> pointer as second argument. This lets the caller to use sophisticated memory handling architecture. 00138 00139 B<On the other hand for the simple use> all these three arguments can be T<NULL>. In this case the configuration 00140 management system will use its own memory allocating and releasing function that simply uses T<malloc> and T<free>. 00141 In this case T<pMemorySegment> is ignored. 00142 00143 For a ready made module that delivers more features see the alloc module of the ScriptBasic project at 00144 T<http://scriptbasic.com> 00145 00146 /*FUNCTION*/ 00147 int cft_init(ptConfigTree pCT, 00148 void *(*memory_allocating_function)(size_t, void *), 00149 void (*memory_releasing_function)(void *, void *), 00150 void *pMemorySegment 00151 ){ 00152 /*noverbatim 00153 Note that suggested convention is to use the 'T<.>' character as separator for hierarchical key structures, but 00154 this is only a suggestion. In other words the module writers advice is to use T<key.subkey.subsubkey> as key string 00155 for hierarchical strings. On the other hand you can use any character as separator except the zero character and 00156 except the characters that are used as key characters. You can write 00157 00158 =verbatim 00159 key\subkey\subsubkey 00160 =noverbatim 00161 00162 if you are a windows geek. To do this you have to change the character saying 00163 00164 =verbatim 00165 pCT->TC = '\\'; 00166 =noverbatim 00167 00168 after calling the initialization function. You can change this character any time, this character is not 00169 used in the configuration structure. The only point is that you have to use the actual character when you have 00170 changed it. The best practice is to use the dot ever. 00171 CUT*/ 00172 pCT->memory_allocating_function = memory_allocating_function ? 00173 memory_allocating_function 00174 : 00175 _mya; 00176 pCT->memory_releasing_function = memory_releasing_function ? 00177 memory_releasing_function 00178 : 00179 _myf; 00180 pCT->pMemorySegment = pMemorySegment; 00181 pCT->Root = NULL; 00182 pCT->TC = '.'; 00183 return 0; 00184 } 00185 00186 /*POD 00187 =H cft_GetConfigFileName() 00188 00189 This function tries to locate the configuration file. The working of this function 00190 is system dependant. There are two different implementations: one for UNIX and one for Win32. 00191 00192 B<WIN32> 00193 00194 On Win32 systems the function tries to read the system registry. The value of the key given in the argument 00195 T<env> is used and returned as the config file name. For example if the argument T<env> is 00196 T<Software\myprog\conf> then the registry value of the key T<HKEY_LOCAL_MACHINE\Software\myprog\conf> will 00197 be returned as configuration file name. The program does not check that the file really exists. It only 00198 checks that the registry key exists, it is a string and has some value. 00199 00200 If the registry key does not exists the program tries to locate the system directory getting the environment 00201 variable T<windir>, then T<systemroot> and finally taking T<c:\WINDOWS>. The argument T<DefaultFileName> is 00202 appended to the directory name and is returned. 00203 00204 B<UNIX> 00205 00206 On UNIX it is more simple. The environment variable T<env> is used as a file name. 00207 If this does not exists the T<DefaultFileName> is used and returned. 00208 00209 B<BOTH> 00210 00211 The return value of the function is zero if no error has happened. A pointer to the resulting file name 00212 is returned in the variable T<ppszConfigFile>. The space to hold the resulting file name is allocated 00213 via the allocation function given by the T<tConfigTree> structure pointed by T<pCT>. 00214 00215 /*FUNCTION*/ 00216 int cft_GetConfigFileName(ptConfigTree pCT, 00217 char **ppszConfigFile, 00218 char *env,/* environment variable or registry key on win32 */ 00219 char *DefaultFileName 00220 ){ 00221 /*noverbatim 00222 This function is T<static> and can not be called from outside of this module. 00223 CUT*/ 00224 00225 #if _WIN32 00226 #define STRING_BUFFER_LENGTH 256 00227 HKEY hKey ; 00228 long Ret; 00229 unsigned int i; 00230 char *s; 00231 CHAR ValueName[STRING_BUFFER_LENGTH]; 00232 DWORD cbValueName = STRING_BUFFER_LENGTH; 00233 DWORD dwType; 00234 char *regkey,*svname; 00235 00236 char sData[STRING_BUFFER_LENGTH]; 00237 char xData[STRING_BUFFER_LENGTH]; 00238 DWORD cbData; 00239 FILE *fp; 00240 00241 s = _pgmptr; 00242 if( strlen(s) < STRING_BUFFER_LENGTH ){ 00243 strcpy(ValueName,s); 00244 s = ValueName; 00245 while( *s && ! isspace(*s) )s++; 00246 *s = (char)0; 00247 i = GetFullPathName(ValueName, 00248 STRING_BUFFER_LENGTH, 00249 sData, 00250 &s); 00251 if( i < STRING_BUFFER_LENGTH && /* result is OK and we have*/ 00252 STRING_BUFFER_LENGTH - i + strlen(s) > 12 ){/* space for 'scriba.conf' */ 00253 strcpy(s,"scriba.conf"); 00254 } 00255 if( fp = fopen(sData,"r") ){ 00256 fclose(fp); 00257 *ppszConfigFile = ALLOC(strlen(sData)+1); 00258 if( *ppszConfigFile == NULL )return CFT_ERROR_MEMORY; 00259 strcpy(*ppszConfigFile,sData); 00260 return 0; 00261 } 00262 } 00263 00264 /* if env is specified we try to use the file name given in the registry key specified by env */ 00265 svname = NULL; 00266 if( env ){ 00267 regkey = ALLOC(strlen(env)+1); 00268 if( regkey == NULL )return CFT_ERROR_MEMORY; 00269 strcpy(regkey,env); 00270 for( s = regkey + strlen(env); s > regkey ; s-- ){ 00271 if( *s == '\\' ){ 00272 *s++ = (char)0; 00273 svname = s; 00274 break; 00275 } 00276 } 00277 } 00278 00279 if( svname != NULL ){ 00280 Ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,regkey,0,KEY_EXECUTE,&hKey); 00281 if( Ret == ERROR_SUCCESS ){ 00282 for( i=0 ; 1 ; i++ ){ 00283 cbValueName = STRING_BUFFER_LENGTH; 00284 cbData = STRING_BUFFER_LENGTH; 00285 Ret = RegEnumValue (hKey, 00286 i, // Value index, taken from listbox. 00287 ValueName, // Name of value. 00288 &cbValueName,// Size of value name. 00289 NULL, // Reserved, dword = NULL. 00290 &dwType, // Type of data. 00291 sData, // Data buffer. 00292 &cbData); // Size of data buffer. 00293 if( Ret != ERROR_SUCCESS )break; 00294 if( dwType == REG_EXPAND_SZ ){ 00295 ExpandEnvironmentStrings(sData,xData,cbData); 00296 strcpy(sData,xData); 00297 dwType = REG_SZ; 00298 } 00299 if( dwType == REG_MULTI_SZ )dwType = REG_SZ; 00300 00301 if( dwType != REG_SZ )continue; 00302 00303 if( !strcmp(ValueName,svname) ){ 00304 *ppszConfigFile = ALLOC(strlen(sData)+1); 00305 if( *ppszConfigFile == NULL ){ 00306 RegCloseKey(hKey); 00307 return CFT_ERROR_MEMORY; 00308 } 00309 strcpy(*ppszConfigFile,sData); 00310 RegCloseKey(hKey);/* */ 00311 return 0; 00312 } 00313 } 00314 } 00315 RegCloseKey(hKey);/* Gavin Jenkins recognized it missing */ 00316 } 00317 s = getenv("windir"); 00318 if( s == NULL )s = getenv("systemroot"); 00319 if( s == NULL )s = "c:\\WINDOWS"; 00320 *ppszConfigFile = ALLOC(strlen(s)+strlen(DefaultFileName)+2); 00321 if( *ppszConfigFile == NULL )return CFT_ERROR_MEMORY; 00322 strcpy(*ppszConfigFile,s); 00323 strcat(*ppszConfigFile,"\\"); 00324 strcat(*ppszConfigFile,DefaultFileName); 00325 return 0; 00326 #else 00327 char *s; 00328 00329 s = getenv(env); 00330 00331 if( s == NULL ){ 00332 *ppszConfigFile = ALLOC(strlen(DefaultFileName)+1); 00333 if( *ppszConfigFile == NULL )return CFT_ERROR_MEMORY; 00334 strcpy(*ppszConfigFile,DefaultFileName); 00335 return 0; 00336 } 00337 *ppszConfigFile = ALLOC(strlen(s)+1); 00338 if( *ppszConfigFile == NULL )return CFT_ERROR_MEMORY; 00339 strcpy(*ppszConfigFile,s); 00340 return 0; 00341 #endif 00342 } 00343 00344 /*POD 00345 =H cft_start() 00346 00347 When writing real applications you usually want to call this function. This function initializes the 00348 T<tConfigTree> structure pointed by T<pCT>, searches for the configuration file and reads it. 00349 00350 When trying to allocate the configuration file the static internal function R<GetConfigFileName> is used. 00351 00352 The argument T<Envir> is the registry key under T<HKLM>, eg T<Software\Myprog\conf> under Win32 or 00353 the environment variable to look for the configuration file name. The argument T<pszDefaultFileName> 00354 is the file name searched on WIN32 in the system directories or the full path to the default configuration 00355 file nam eunder UNIX. The argument T<pszForcedFileName> can overrride the file name search or 00356 has to be T<NULL> to let the reader search the environment and registry for file name. 00357 /*FUNCTION*/ 00358 int cft_start(ptConfigTree pCT, 00359 void *(*memory_allocating_function)(size_t, void *), 00360 void (*memory_releasing_function)(void *, void *), 00361 void *pMemorySegment, 00362 char *Envir, 00363 char *pszDefaultFileName, 00364 char *pszForcedFileName 00365 ){ 00366 /*noverbatim 00367 CUT*/ 00368 int iError; 00369 00370 /* First of all we have to initialize the structure */ 00371 if( iError = cft_init(pCT,memory_allocating_function,memory_releasing_function,pMemorySegment) ) 00372 return iError; 00373 00374 #if _DEBUG 00375 testa_Assert0x80(); 00376 #endif 00377 if( pszForcedFileName == NULL ) 00378 if( iError = cft_GetConfigFileName(pCT,&pszForcedFileName,Envir,pszDefaultFileName) )return iError; 00379 #if _DEBUG 00380 testa_Assert0x80(); 00381 #endif 00382 if( strlen(pszForcedFileName) >0 ){ 00383 pCT->pszConfigFileName = ALLOC(strlen(pszForcedFileName)+1); 00384 if( pCT->pszConfigFileName ) 00385 strcpy(pCT->pszConfigFileName,pszForcedFileName); 00386 }else pCT->pszConfigFileName = NULL; 00387 iError = cft_ReadConfig(pCT,pszForcedFileName); 00388 #if _DEBUG 00389 testa_Assert0x80(); 00390 #endif 00391 return iError; 00392 } 00393 00394 00395 /*POD 00396 =H strmyeq() 00397 00398 This is an internal T<static> function that compares two strings and returns true iff they 00399 are equal. The string terminator is the usual zero character or the dot. Both are legal terminators 00400 for this functions and their difference in the compared strings is not treated as difference in the result. 00401 If one string is terminated by zero character and the other is terminated by a dot but they are the same in any 00402 other character then the return value is true. 00403 00404 This function is used find a sub-key when the caller has specified a dot separated hierarchical key. 00405 00406 Note that the dot is only a convention and the default value for the separator and the caller has 00407 =verbatim 00408 /**/ 00409 static int strmyeq(ptConfigTree pCT,char *a, char *b){ 00410 /*noverbatim 00411 This function is T<static> and can not be called from outside of this module. 00412 CUT*/ 00413 while( *a && *a != pCT->TC && *b && *b != pCT->TC ){ 00414 if( *a != *b )return 0; 00415 a++; b++; 00416 } 00417 return *a == *b || 00418 ( *a == pCT->TC && ! *b ) || 00419 ( *b == pCT->TC && ! *a ); 00420 } 00421 00422 /*POD 00423 =H cft_FindNode() 00424 00425 Find a node starting from the start node T<lStartNode> 00426 and searching for the T<key>. 00427 00428 The function returns zero if the key is not found in the configuration 00429 information tree T<pCT> or returns the node id of the key. This node 00430 can either be an internal node or leaf. 00431 00432 Note that the string T<key> may contain dot characters. In this case the 00433 key is searched down in the configuration tree. (You can set the separator character 00434 different from the dot character.) 00435 00436 /*FUNCTION*/ 00437 CFT_NODE cft_FindNode(ptConfigTree pCT, 00438 CFT_NODE lStartNode, 00439 char *key 00440 ){ 00441 /*noverbatim 00442 You need this function when you want to iterate over the sub-keys of a node. You get the 00443 node id for the key and then you can call R<cft_EnumFirst> to start the loop and then R<cft_EnumNext> to 00444 iterate the loop over the sub-keys. 00445 00446 If you just want to get the value of a single key you can call the function R<cft_GetEx> that 00447 uses this function. 00448 CUT*/ 00449 long i; 00450 00451 /* lasy programmers like I use config tree after failed config read. */ 00452 if( pCT == NULL || pCT->Root == NULL )return 0; 00453 00454 RestartFindNode: 00455 for( i = lStartNode ; i ; i = pCT->Root[i-1].lNext ){ 00456 /* if the key of the node is the same as the first part of the key */ 00457 if( strmyeq(pCT,key,pCT->StringTable+pCT->Root[i-1].lKey) ){ 00458 /* step key to point to the end of the first part */ /*pCT->TC is usually the dot character */ 00459 while( *key && *key != pCT->TC )key++; 00460 /* if there are no more parts then we have found the node */ 00461 if( !*key )return i; 00462 /* step over the dot to the next part of the key */ 00463 key++; 00464 /* the key has sub parts but this is not a brach node. For example you want to 00465 get the node of key="a.b.c.d" but the node for key="a.b" is already a value 00466 and not a sub-configuration. */ 00467 if( (pCT->Root[i-1].fFlag&CFT_NODE_MASK) != CFT_NODE_BRANCH )return 0; 00468 lStartNode = pCT->Root[i-1].Val.lVal; 00469 /* use a nasty goto instead of tail recursion */ 00470 goto RestartFindNode; 00471 } 00472 } 00473 /* if we went through the list and could not find the key then 00474 there is no node for the given key */ 00475 return 0; 00476 } 00477 00478 /*POD 00479 =H cft_GetEx() 00480 00481 Get the value associated with the key T<key> from the configuration 00482 structure T<pCT>, or get the values of a node. 00483 00484 The arguments: 00485 00486 =itemize 00487 =item T<pCT> the configuration information searched. 00488 =item T<key> the key that we search the value for, or NULL if we already 00489 know the node id where the needed information is. 00490 =item T<plNodeId> the id of the node that we need information from. If the 00491 key argumentum is not NULL then this argument is overwritten with the 00492 node id associated with the key. If the argument key is NULL this 00493 argument should specify the id of the node we need information from. 00494 If the node id is not needed upon return this argument may point to NULL. 00495 =item T<ppszValue> will return a pointer to a constant ZCHAR string if 00496 the value associated with T<key> is string. If the argument is T<NULL> 00497 then the function ignore this argument. 00498 =item T<plValue> will return a T<long> if the value associated with 00499 T<key> is integer. If the argument is T<NULL> 00500 then the function ignore this argument. 00501 =item T<pdValue> will return a T<double> if the value associated with 00502 T<key> is a real number. If the argument is T<NULL> 00503 then the function ignore this argument. 00504 =item T<type> will return the type of the key. This can be 00505 =itemize 00506 =item T<CFT_NODE_BRANCH> if the key is associated with a subtree. 00507 =item T<CFT_TYPE_STRING> if the key is associated with a string 00508 =item T<CFT_TYPE_INTEGER> if the key is associated with an integer number 00509 =item T<CFT_TYPE_REAL> if the key is associated with a real number 00510 =noitemize 00511 This argument can also be NULL if the caller is not interested in the 00512 type of the value. 00513 =noitemize 00514 00515 Note that any of T<ppszValue>, T<plValue>, T<pdValue> can point to a 00516 variable or to T<NULL> in case the caller does not need the actual value. 00517 00518 /*FUNCTION*/ 00519 int cft_GetEx(ptConfigTree pCT, 00520 char *key, 00521 CFT_NODE *plNodeId, 00522 char **ppszValue, 00523 long *plValue, 00524 double *pdValue, 00525 int *type 00526 ){ 00527 /*noverbatim 00528 00529 The function returns T<CFT_ERROR_SUCCESS> if no error happens. 00530 The value T<CFT_ERROR_SUCCESS> is zero. 00531 00532 If an error happens the error code is returned. These error codes are: 00533 =itemize 00534 =item T<CFT_ERROR_NOT_FOUND> the key is not present in the table, and 00535 T<*plNodeId> will also be set to zero. 00536 =item T<CFT_ERROR_NOTYPE> the key is found but has a type that can not 00537 be returned, because the caller passed NULL as storage location. 00538 In this case the type of the configuration information is probably 00539 wrong. 00540 =noitemize 00541 00542 CUT*/ 00543 CFT_NODE lNodeId; 00544 00545 if( plNodeId )lNodeId = *plNodeId; 00546 if( key ) 00547 lNodeId = cft_FindNode(pCT,1,key); 00548 if( plNodeId )*plNodeId = lNodeId; 00549 00550 if( lNodeId == 0 )return CFT_ERROR_NOT_FOUND; 00551 if( (pCT->Root[lNodeId-1].fFlag&CFT_NODE_MASK) == CFT_NODE_BRANCH ){ 00552 if( type ) 00553 *type = CFT_NODE_BRANCH; 00554 return CFT_ERROR_SUCCESS; 00555 } 00556 if( type ) 00557 *type = pCT->Root[lNodeId-1].fFlag & CFT_TYPE_MASK; 00558 switch( pCT->Root[lNodeId-1].fFlag & CFT_TYPE_MASK ){ 00559 default: return CFT_ERROR_NOT_FOUND; 00560 case CFT_TYPE_STRING: 00561 if( ppszValue ) 00562 *ppszValue = pCT->StringTable + pCT->Root[lNodeId-1].Val.lVal; 00563 else 00564 return CFT_ERROR_NOTYPE; 00565 break; 00566 case CFT_TYPE_INTEGER: 00567 if( plValue ) 00568 *plValue = pCT->Root[lNodeId-1].Val.lVal; 00569 else 00570 return CFT_ERROR_NOTYPE; 00571 break; 00572 case CFT_TYPE_REAL: 00573 if( pdValue ) 00574 *pdValue = pCT->Root[lNodeId-1].Val.dVal; 00575 else 00576 return CFT_ERROR_NOTYPE; 00577 break; 00578 } 00579 return CFT_ERROR_SUCCESS; 00580 } 00581 00582 /*POD 00583 =H cft_GetString() 00584 00585 This is the simplest interface function to retrieve a configuration 00586 string. This assumes that you exactly know the name of the key and 00587 you are sure that the value is a string. The function returns the pointer 00588 to the constant string or returns NULL if the configuration key is not 00589 present in the tree or the value is not a string. 00590 00591 The use of this function is not recommended. This function is present 00592 in this package to ease porting of programs that use simpler configuration 00593 information management software. 00594 /*FUNCTION*/ 00595 char *cft_GetString(ptConfigTree pCT, 00596 char *key 00597 ){ 00598 /*noverbatim 00599 This function calls R<cft_GetEx>. 00600 00601 CUT*/ 00602 char *pszReturn; 00603 int type; 00604 long dummy;/* Store the node id, though we do not need it. */ 00605 00606 if( cft_GetEx(pCT,key,&dummy,&pszReturn,NULL,NULL,&type) )return NULL; 00607 if( type != CFT_TYPE_STRING )return NULL; 00608 return pszReturn; 00609 } 00610 00611 /*POD 00612 =H cft_EnumFirst() 00613 00614 Whenever you need to enumerate the sub-keys of a key you have to 00615 get the node associated with the key (see R<cft_GetEx> or R<cft_FindNode>). 00616 When you have the node associated with the key you can get the node of the 00617 first sub-key calling this function. 00618 00619 The function needs the node id T<lNodeId> of the key for which 00620 we need to enumerate the sub keys and returns the node id of the 00621 first sub key. 00622 00623 If the key is associated with a leaf node the function returns zero. 00624 00625 If the key is associated with a branch node that has no sub-keys the 00626 function returns zero. 00627 /*FUNCTION*/ 00628 CFT_NODE cft_EnumFirst(ptConfigTree pCT, 00629 CFT_NODE lNodeId 00630 ){ 00631 /*noverbatim 00632 CUT*/ 00633 00634 if( lNodeId == 0 )return 1; 00635 if( (pCT->Root[lNodeId-1].fFlag&CFT_NODE_MASK) != CFT_NODE_BRANCH )return 0; 00636 return pCT->Root[lNodeId-1].Val.lVal; 00637 } 00638 00639 /*POD 00640 =H cft_EnumNext() 00641 00642 Whenever you need to enumerate the sub-keys of a key you have to 00643 get the node associated with the key (see R<cft_GetEx> or R<cft_FindNode>). 00644 When you have the node associated with the key you can get the node of the 00645 first sub-key calling the function R<cft_EnumFirst>. Later on you can enumerate 00646 the sub keys stepping from node to node calling this function. 00647 00648 The function needs the node id T<lNodeId> returned by R<cft_EnumFirst> or 00649 by previous call of this function. 00650 00651 The function returns the node id of the next sub key. 00652 00653 If the enumeration has ended, in other words there is no next sub-key the 00654 function returns zero. 00655 /*FUNCTION*/ 00656 long cft_EnumNext(ptConfigTree pCT, 00657 long lNodeId 00658 ){ 00659 /*noverbatim 00660 CUT*/ 00661 return pCT->Root[lNodeId-1].lNext; 00662 } 00663 00664 /*POD 00665 =H cft_GetKey() 00666 00667 This function returns a pointer to the constant zchar string that 00668 holds the key of the node defined by the id T<lNodeId>. 00669 /*FUNCTION*/ 00670 char *cft_GetKey(ptConfigTree pCT, 00671 CFT_NODE lNodeId 00672 ){ 00673 /*noverbatim 00674 CUT*/ 00675 if( lNodeId == 0 )return NULL; 00676 return pCT->StringTable + pCT->Root[lNodeId-1].lKey; 00677 } 00678 00679 /*POD 00680 =H cft_ReadConfig() 00681 00682 /*FUNCTION*/ 00683 int cft_ReadConfig(ptConfigTree pCT, 00684 char *pszFileName 00685 ){ 00686 /*noverbatim 00687 CUT*/ 00688 FILE *fp; 00689 size_t cbRead; 00690 char magic[4]; 00691 unsigned long lNodeSize; 00692 00693 fp = fopen(pszFileName,"rb"); 00694 if( fp == NULL )return CFT_ERROR_FILE; 00695 cbRead = fread(magic,1,4,fp); 00696 if( cbRead != 4 || memcmp(magic,MAGIC,4) ){ 00697 fclose(fp); 00698 return CFT_ERROR_FILE; 00699 } 00700 00701 cbRead = fread(&lNodeSize,1,sizeof(long),fp); 00702 if( cbRead != sizeof(long) || lNodeSize != sizeof(tConfigNode) ){ 00703 fclose(fp); 00704 return CFT_ERROR_FILE; 00705 } 00706 00707 cbRead = fread((void *)&(pCT->cNode),1,sizeof(long),fp); 00708 if( cbRead != sizeof(long) ){ 00709 fclose(fp); 00710 return CFT_ERROR_FILE; 00711 } 00712 cbRead = fread((void *)&(pCT->cbStringTable),1,sizeof(long),fp); 00713 if( cbRead != sizeof(long) ){ 00714 fclose(fp); 00715 return CFT_ERROR_FILE; 00716 } 00717 if( pCT->cNode == 0 ){ 00718 fclose(fp); 00719 return CFT_ERROR_EMPTY; 00720 } 00721 pCT->Root = ALLOC(pCT->cNode*sizeof(tConfigNode)); 00722 if( pCT->Root == NULL ){ 00723 fclose(fp); 00724 return CFT_ERROR_MEMORY; 00725 } 00726 pCT->StringTable = ALLOC(pCT->cbStringTable); 00727 if( pCT->StringTable == NULL ){ 00728 fclose(fp); 00729 FREE(pCT->Root); 00730 return CFT_ERROR_MEMORY; 00731 } 00732 cbRead = fread((void *)pCT->Root,1,pCT->cNode*sizeof(tConfigNode),fp); 00733 if( cbRead != pCT->cNode*sizeof(tConfigNode) ){ 00734 fclose(fp); 00735 return CFT_ERROR_FILE; 00736 } 00737 cbRead = fread((void *)pCT->StringTable,1,pCT->cbStringTable,fp); 00738 fclose(fp); 00739 if( cbRead != pCT->cbStringTable )return CFT_ERROR_FILE; 00740 return 0; 00741 } 00742 00743 /*POD 00744 =H cft_WriteConfig() 00745 00746 /*FUNCTION*/ 00747 int cft_WriteConfig(ptConfigTree pCT, 00748 char *pszFileName 00749 ){ 00750 /*noverbatim 00751 CUT*/ 00752 FILE *fp; 00753 size_t cbWrite; 00754 unsigned long lNodeSize; 00755 00756 if( pCT->cNode == 0 )return CFT_ERROR_EMPTY; 00757 00758 fp = fopen(pszFileName,"wb"); 00759 if( fp == NULL )return CFT_ERROR_FILE; 00760 cbWrite = fwrite(MAGIC,1,4,fp); 00761 if( cbWrite != 4 ){ 00762 fclose(fp); 00763 return CFT_ERROR_FILE; 00764 } 00765 00766 lNodeSize = sizeof(tConfigNode); 00767 cbWrite = fwrite(&lNodeSize,1,sizeof(long),fp); 00768 if( cbWrite != sizeof(long) ){ 00769 fclose(fp); 00770 return CFT_ERROR_FILE; 00771 } 00772 00773 cbWrite = fwrite((void *)&(pCT->cNode),1,sizeof(long),fp); 00774 if( cbWrite != sizeof(long) ){ 00775 fclose(fp); 00776 return CFT_ERROR_FILE; 00777 } 00778 00779 cbWrite = fwrite((void *)&(pCT->cbStringTable),1,sizeof(long),fp); 00780 if( cbWrite != sizeof(long) ){ 00781 fclose(fp); 00782 return CFT_ERROR_FILE; 00783 } 00784 00785 cbWrite = fwrite((void *)pCT->Root,1,pCT->cNode*sizeof(tConfigNode),fp); 00786 if( cbWrite != pCT->cNode*sizeof(tConfigNode) ){ 00787 fclose(fp); 00788 return CFT_ERROR_FILE; 00789 } 00790 cbWrite = fwrite((void *)pCT->StringTable,1,pCT->cbStringTable,fp); 00791 fclose(fp); 00792 if( cbWrite != pCT->cbStringTable )return CFT_ERROR_FILE; 00793 return 0; 00794 } 00795 00796 /*POD 00797 =H cft_DropConfig() 00798 00799 /*FUNCTION*/ 00800 void cft_DropConfig(ptConfigTree pCT 00801 ){ 00802 /*noverbatim 00803 CUT*/ 00804 00805 FREE(pCT->Root); 00806 pCT->Root = NULL; 00807 FREE(pCT->StringTable); 00808 pCT->StringTable = NULL; 00809 pCT->cNode = 0; 00810 pCT->cbStringTable = 0; 00811 }