Initial move to common config code directory
[tpg/opendispense2.git] / src / common / config.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * config.c - Configuration file parser
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "config.h"
13 #include <regex.h>
14 #include <string.h>
15 #include <ctype.h>
16
17 #define MAX_LINE_LEN    128
18
19 // === TYPES ===
20 typedef struct sConfigValue     tConfigValue;
21 typedef struct sConfigKey       tConfigKey;
22
23 // === STRUCTURES ===
24 struct sConfigValue
25 {
26         tConfigValue    *Next;
27         char    Data[];
28 };
29
30 struct sConfigKey
31 {
32         tConfigKey      *NextKey;
33         tConfigValue    *FirstValue;
34         tConfigValue    *LastValue;
35          int    ValueCount;
36         char    KeyName[];
37 };
38
39 // === PROTOTYPES ===
40 void    Config_ParseFile(const char *Filename);
41 void    Config_AddValue(const char *Key, const char *Value);
42 void    Config_int_AddValueToKey(tConfigKey *Key, const char *Value);
43 tConfigKey      *Config_int_GetKey(const char *KeyName, int bCreate);
44  int    Config_GetValueCount(const char *KeyName);
45 const char      *Config_GetValue(const char *KeyName, int Index);
46
47 // === GLOBALS ===
48 tConfigKey      *gConfig;
49
50 // === CODE ===
51 void Config_ParseFile(const char *Filename)
52 {
53         FILE    *fp;
54         char    line[MAX_LINE_LEN];
55         regex_t regexp_option;
56         regex_t regexp_empty;
57
58         CompileRegex(&regexp_option, "^\\s*([^ \t]+)\\s+(.+)$", REG_EXTENDED);  //
59         CompileRegex(&regexp_empty, "^\\s*$", REG_EXTENDED);    //
60         
61         fp = fopen(Filename, "r");
62         if(!fp) {
63                 fprintf(stderr, "Unable to open config file '%s'\n", Filename);
64                 perror("Config_ParseFile");
65                 exit(-1);
66         }
67         
68         while( fgets(line, sizeof(line), fp) )
69         {
70                 regmatch_t      matches[3];
71
72                 // Trim and clean up
73                 {
74                          int    i;
75                         for( i = 0; line[i]; i ++ )
76                         {
77                                 if( line[i] == '#' || line[i] == ';' ) {
78                                         line[i] = '\0';
79                                         break;
80                                 }
81                         }
82                         
83                         while( i --, isspace(line[i]) )
84                                 line[i] = 0;
85                 }
86                 
87                                 
88                 if( regexec(&regexp_empty, line, 1, matches, 0) == 0 )
89                         continue ;
90
91                 if( RunRegex(&regexp_option, line, 3, matches, "Parsing configuration file") )
92                 {
93                         fprintf(stderr, "Syntax error\n- %s", line);
94                         continue ;
95                 }
96                 
97                 line[matches[1].rm_eo] = 0;
98                 line[matches[2].rm_eo] = 0;
99         
100                 Config_AddValue(line + matches[1].rm_so, line + matches[2].rm_so);
101         }
102         
103         fclose(fp);
104         regfree(&regexp_option);
105         regfree(&regexp_empty);
106 }
107
108 void Config_AddValue(const char *Key, const char *Value)
109 {
110         tConfigKey      *key;
111         
112         // Find key (creating if needed)
113         key = Config_int_GetKey(Key, 1);
114
115         Config_int_AddValueToKey(key, Value);   
116 }
117
118 void Config_int_AddValueToKey(tConfigKey *Key, const char *Value)
119 {
120         tConfigValue    *newVal;
121         // Create value
122         newVal = malloc(sizeof(tConfigValue) + strlen(Value) + 1);
123         newVal->Next = NULL;
124         strcpy(newVal->Data, Value);
125         
126         #if 1
127         // Add to the end of the key's list
128         if(Key->LastValue)
129                 Key->LastValue->Next = newVal;
130         else
131                 Key->FirstValue = newVal;
132         Key->LastValue = newVal;
133         #else
134         // Add to the start of the key's list
135         if(Key->LastValue == NULL)
136                 Key->LastValue = newVal;
137         newVal->Next = Key->FirstValue;
138         Key->FirstValue = newVal;
139         #endif
140         Key->ValueCount ++;
141 }
142
143 /**
144  * \brief 
145  */
146 tConfigKey *Config_int_GetKey(const char *KeyName, int bCreate)
147 {
148         tConfigKey      *key, *prev = NULL;
149         
150         // Search the sorted list of keys
151         for( key = gConfig; key; prev = key, key = key->NextKey )
152         {
153                 int cmp = strcmp(key->KeyName, KeyName);
154                 if(cmp == 0)    return key;     // Equal, return
155                 if(cmp > 0)     break;  // Greater, fast exit
156         }
157         
158         if( bCreate )
159         {
160                 // Create new key
161                 key = malloc(sizeof(tConfigKey) + strlen(KeyName) + 1);
162                 key->FirstValue = NULL;
163                 key->LastValue = NULL;
164                 key->ValueCount = 0;
165                 strcpy(key->KeyName, KeyName);
166                 
167                 // Append
168                 if(prev) {
169                         key->NextKey = prev->NextKey;
170                         prev->NextKey = key;
171                 }
172                 else {
173                         key->NextKey = gConfig;
174                         gConfig = key;
175                 }
176         }
177         else
178         {
179                 key = NULL;
180         }
181         
182         return key;
183 }
184
185 int Config_GetValueCount(const char *KeyName)
186 {
187         tConfigKey      *key = Config_int_GetKey(KeyName, 0);
188         if(!key)        return 0;
189         
190         return key->ValueCount;
191 }
192
193 const char *Config_GetValue(const char *KeyName, int Index)
194 {
195         tConfigKey      *key;
196         tConfigValue    *val;   
197
198         key = Config_int_GetKey(KeyName, 0);
199         if(!key) {
200                 fprintf(stderr, "Unknown key '%s'\n", KeyName);
201                 exit(1);
202                 return NULL;
203         }
204         
205         if(Index < 0 || Index >= key->ValueCount)       return NULL;
206         
207         for( val = key->FirstValue; Index && val; val = val->Next, Index -- );
208
209         ASSERT(val != NULL);
210         
211         return val->Data;
212 }
213
214 int Config_GetValue_Bool(const char *KeyName, int Index)
215 {
216         const char *val = Config_GetValue(KeyName, Index);
217         if(!val)        return -1;
218         
219         if( atoi(val) == 1 )    return 1;
220         if( val[0] == '0' && val[1] == '\0' )   return 0;
221         
222         if( strcasecmp(val, "true") == 0 )      return 1;
223         if( strcasecmp(val, "false") == 0 )     return 0;
224         
225         if( strcasecmp(val, "yes") == 0 )       return 1;
226         if( strcasecmp(val, "no") == 0 )        return 0;
227         
228         return -1;
229 }
230
231 int Config_GetValue_Int(const char *KeyName, int Index)
232 {
233          int    tmp;
234         const char *val = Config_GetValue(KeyName, Index);
235         if(!val)        return -1;
236         
237         if( (tmp = atoi(val)) ) return tmp;
238         if( val[0] == '0' && val[1] == '\0' )   return 0;
239         
240         return -1;
241 }
242

UCC git Repository :: git.ucc.asn.au