G:/ScriptBasic/source/conftree.c

Go to the documentation of this file.
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   }

Generated on Sun Mar 12 23:56:27 2006 for ScriptBasic by  doxygen 1.4.6-NO