ca6db69ad2c0f5f1f5171d3e9c246616e2527608
[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 "doregex.h"
14 #include <regex.h>
15 #include <string.h>
16 #include <ctype.h>
17
18 #define MAX_LINE_LEN    128
19
20 // === TYPES ===
21 typedef struct sConfigValue     tConfigValue;
22 typedef struct sConfigKey       tConfigKey;
23
24 // === STRUCTURES ===
25 struct sConfigValue
26 {
27         tConfigValue    *Next;
28         char    Data[];
29 };
30
31 struct sConfigKey
32 {
33         tConfigKey      *NextKey;
34         tConfigValue    *FirstValue;
35         tConfigValue    *LastValue;
36          int    ValueCount;
37         char    KeyName[];
38 };
39
40 // === PROTOTYPES ===
41 //int   Config_ParseFile(const char *Filename);
42 void    Config_AddValue(const char *Key, const char *Value);
43 void    Config_int_AddValueToKey(tConfigKey *Key, const char *Value);
44 tConfigKey      *Config_int_GetKey(const char *KeyName, int bCreate);
45  int    Config_GetValueCount(const char *KeyName);
46 const char      *Config_GetValue(const char *KeyName, int Index);
47
48 // === GLOBALS ===
49 tConfigKey      *gConfig;
50
51 // === CODE ===
52 int Config_ParseFile(const char *Filename)
53 {
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         FILE *fp = fopen(Filename, "r");
62         if(!fp) {
63                 fprintf(stderr, "Unable to open config file '%s'\n", Filename);
64                 perror("Config_ParseFile");
65                 return 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         return 0;
108 }
109
110 void Config_AddValue(const char *Key, const char *Value)
111 {
112         tConfigKey      *key;
113         
114         // Find key (creating if needed)
115         key = Config_int_GetKey(Key, 1);
116
117         Config_int_AddValueToKey(key, Value);   
118 }
119
120 void Config_int_AddValueToKey(tConfigKey *Key, const char *Value)
121 {
122         tConfigValue    *newVal;
123         // Create value
124         newVal = malloc(sizeof(tConfigValue) + strlen(Value) + 1);
125         newVal->Next = NULL;
126         strcpy(newVal->Data, Value);
127         
128         #if 1
129         // Add to the end of the key's list
130         if(Key->LastValue)
131                 Key->LastValue->Next = newVal;
132         else
133                 Key->FirstValue = newVal;
134         Key->LastValue = newVal;
135         #else
136         // Add to the start of the key's list
137         if(Key->LastValue == NULL)
138                 Key->LastValue = newVal;
139         newVal->Next = Key->FirstValue;
140         Key->FirstValue = newVal;
141         #endif
142         Key->ValueCount ++;
143 }
144
145 /**
146  * \brief 
147  */
148 tConfigKey *Config_int_GetKey(const char *KeyName, int bCreate)
149 {
150         tConfigKey      *key, *prev = NULL;
151         
152         // Search the sorted list of keys
153         for( key = gConfig; key; prev = key, key = key->NextKey )
154         {
155                 int cmp = strcmp(key->KeyName, KeyName);
156                 if(cmp == 0)    return key;     // Equal, return
157                 if(cmp > 0)     break;  // Greater, fast exit
158         }
159         
160         if( bCreate )
161         {
162                 // Create new key
163                 key = malloc(sizeof(tConfigKey) + strlen(KeyName) + 1);
164                 key->FirstValue = NULL;
165                 key->LastValue = NULL;
166                 key->ValueCount = 0;
167                 strcpy(key->KeyName, KeyName);
168                 
169                 // Append
170                 if(prev) {
171                         key->NextKey = prev->NextKey;
172                         prev->NextKey = key;
173                 }
174                 else {
175                         key->NextKey = gConfig;
176                         gConfig = key;
177                 }
178         }
179         else
180         {
181                 key = NULL;
182         }
183         
184         return key;
185 }
186
187 int Config_GetValueCount(const char *KeyName)
188 {
189         tConfigKey      *key = Config_int_GetKey(KeyName, 0);
190         if(!key)        return 0;
191         
192         return key->ValueCount;
193 }
194
195 const tConfigValue *Config_int_GetValue(const char *KeyName, int Index)
196 {
197         tConfigValue    *val;   
198
199         tConfigKey *key = Config_int_GetKey(KeyName, 0);
200         if(!key) {
201                 fprintf(stderr, "Unknown key '%s'\n", KeyName);
202                 exit(1);
203                 return NULL;
204         }
205         
206         if(Index < 0 || Index >= key->ValueCount)       return NULL;
207         
208         for( val = key->FirstValue; Index && val; val = val->Next, Index -- );
209
210         ASSERT(val != NULL);
211         return val;
212 }
213
214 const char *Config_GetValue_Idx(const char *KeyName, int Index)
215 {
216         const tConfigValue* val = Config_int_GetValue(KeyName, Index);
217         if( !val )      return NULL;
218         return val->Data;
219 }
220
221 bool Config_GetValue_Str(const char *KeyName, const char** Value)
222 {
223         const tConfigValue* val = Config_int_GetValue(KeyName, 0);
224         if(!val)        return false;
225         *Value = val->Data;
226         return true;
227 }
228 bool str_to_bool(const char *val)
229 {
230         if( atoi(val) == 1 )    return 1;
231         if( val[0] == '0' && val[1] == '\0' )   return 0;
232         
233         if( strcasecmp(val, "true") == 0 )      return 1;
234         if( strcasecmp(val, "false") == 0 )     return 0;
235         
236         if( strcasecmp(val, "yes") == 0 )       return 1;
237         if( strcasecmp(val, "no") == 0 )        return 0;
238
239         // INVALID, TODO: Error message
240         return 0;
241 }
242 bool Config_GetValue_Bool(const char *KeyName, bool* Value)
243 {
244         const tConfigValue* val = Config_int_GetValue(KeyName, 0);
245         if(!val)        return false;
246         
247         *Value = str_to_bool(val->Data);
248         
249         return true;
250 }
251
252 bool Config_GetValue_Int(const char *KeyName, int* Value)
253 {
254         const tConfigValue* val = Config_int_GetValue(KeyName, 0);
255         if(!val)        return false;
256         
257         char* end;
258          int    value = strtol(val->Data, &end, 0);
259         if( *end != '\0' ) {
260                 return false;
261         }
262         *Value = value;
263         
264         return true;
265 }
266

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