version 1.4.44b
[polyglot.git] / ini.c
1 // includes
2
3 #include <stdio.h>
4 #include "option.h"
5 #include "ini.h"
6 #include "string.h"
7 #include "util.h"
8 #include "errno.h"
9
10 // macros
11
12 #define StringSize ((int) 4096)
13
14 // types
15
16 typedef enum {
17     START               =0,
18     SECTION_NAME        =1,
19     NAME                =2,
20     NAME_SPACE          =3,
21     START_VALUE         =4,
22     VALUE               =5,
23     VALUE_SPACE         =6,
24     QUOTE_SPACE         =7,
25     FINISHED            =8,
26 } parse_state_t;
27
28 // functions
29
30 // ini_line_parse()
31
32 line_type_t ini_line_parse(const char *line,
33                                   char *section,
34                                   char *name,
35                                   char *value){
36     int i;
37     parse_state_t state=START;
38     int name_index=0;
39     int value_index=0;
40     int section_index=0;
41     int index=0;
42     char c;
43     int type=SYNTAX_ERROR;
44     int spaces=0;
45     bool outer_quote=FALSE;
46     while(state!=FINISHED){
47         c=line[index++];
48 //        printf("STATE=%d c=[%c]\n",state,c);
49         switch(state){
50             case START:
51                 if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
52                     type=EMPTY_LINE;
53                     state=FINISHED;
54                 }else if(c=='['){
55                     state=SECTION_NAME;
56                 }else if(c!=' '){
57                     name[name_index++]=c;
58                     state=NAME;
59                 }
60                 goto next;
61                 break;
62             case NAME:
63                 if(c=='='){
64                     state=START_VALUE;
65                 }else if(c==' '){
66                     state=NAME_SPACE;
67                     spaces=1;
68                 }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
69                     type=SYNTAX_ERROR;
70                     state=FINISHED;
71                 }else{
72                     name[name_index++]=c;
73                 }
74                 goto next;
75                 break;    // we don't get here
76             case NAME_SPACE:
77                 if(c==' '){
78                     spaces++;
79                 }else if(c=='='){
80                     state=START_VALUE;
81                 }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
82                     type=SYNTAX_ERROR;
83                     state=FINISHED;
84                 }else{
85                     for(i=0;i<spaces;i++){
86                         name[name_index++]=' ';
87                     }
88                     spaces=0;
89                     name[name_index++]=c;
90                     state=NAME;
91                 }
92                 goto next;
93                 break;    // we don't get here
94             case START_VALUE:
95                 if(c=='"'){
96                     outer_quote=TRUE;
97                     spaces=0;
98                     state=VALUE_SPACE;
99                 }else if((c==';')||(c=='#')||(c=='\r')||(c=='\n')||(c=='\0')){
100                     type=EMPTY_VALUE;
101                     state=FINISHED;
102                 }else if(c!=' '){
103                     value[value_index++]=c;
104                     state=VALUE;
105                 }
106                 goto next;
107                 break;    // we don't get here
108             case VALUE:
109                 if(c=='"'){
110                     state=QUOTE_SPACE;
111                     spaces=0;
112                 }else if(c==' '){
113                     state=VALUE_SPACE;
114                     spaces=1;
115                 }else if((c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0'))&&
116                          !outer_quote){
117                     type=NAME_VALUE;
118                     state=FINISHED;
119                 }else{
120                     value[value_index++]=c;
121                 }
122                 goto next;
123                 break;    // we don't get here
124             case VALUE_SPACE:
125                 if(c==' '){
126                     spaces++;
127                 }else if((c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0'))&&
128                          !outer_quote){
129                     type=NAME_VALUE;
130                     state=FINISHED;
131                 }else{
132                     for(i=0;i<spaces;i++){
133                         value[value_index++]=' ';
134                     }
135                     spaces=0;
136                     if(c!='"'){
137                         value[value_index++]=c;
138                         state=VALUE;
139                     }else{
140                         state=QUOTE_SPACE;
141                     }
142                 }
143                 goto next;
144                 break;    // we don't get here
145             case QUOTE_SPACE:
146                 if(c==' '){
147                     spaces++;
148                 }else if(c=='\r' || c=='\n' || c==';' || c=='#'||(c=='\0')){
149                     type=NAME_VALUE;
150                     state=FINISHED;
151                 }else{
152                     value[value_index++]='"';
153                     for(i=0;i<spaces;i++){
154                         value[value_index++]=' ';
155                     }
156                     spaces=0;
157                     if(c!='"'){
158                         value[value_index++]=c;
159                         state=VALUE;
160                     }else{
161                         state=QUOTE_SPACE;
162                     }
163                 }
164                 goto next;
165                 break;    // we don't get here
166             case SECTION_NAME:
167                 if(c==']'){
168                     type=SECTION;
169                     state=FINISHED;
170                 }else{
171                     section[section_index++]=c;
172                 }
173                 goto next;
174                 break;    // we don't get here
175             default:
176                 break;
177         }
178       next:        if(!c) break;
179     }
180     section[section_index]='\0';
181     name[name_index]='\0';
182     value[value_index]='\0';
183     return type;
184 }
185
186 // ini_init()
187
188 void ini_init(ini_t *ini){
189     memset(ini,0,sizeof(ini_t));
190 }
191
192 // ini_clear()
193
194 void ini_clear(ini_t *ini){
195     int i;
196     ini_entry_t * entry;
197     for(i=0; i< ini->index; i++){
198         entry=ini->entries+i;
199         if(entry->name!=NULL){
200             my_string_clear(&entry->name);
201         }
202         if(entry->value!=NULL){
203             my_string_clear(&entry->value);
204         }
205         if(entry->comment!=NULL){
206             my_string_clear(&entry->comment);
207         }
208     }
209     ini->index=0;
210 }
211
212 // ini_copy()
213
214 void ini_copy(ini_t *dst, ini_t *src){
215   int i;
216   dst->index=src->index;
217   dst->iter=src->iter;
218   for(i=0;i<src->index;i++){
219     my_string_set(&dst->entries[i].section,src->entries[i].section);
220     my_string_set(&dst->entries[i].name,src->entries[i].name);
221     my_string_set(&dst->entries[i].value,src->entries[i].value);
222   }
223 }
224
225 // ini_find()
226
227 ini_entry_t *ini_find(ini_t *ini, const char *section, const char* name){
228     int i;
229     ini_entry_t * entry;
230     for(i=0; i< ini->index; i++){
231         entry=ini->entries+i;
232         if(my_string_case_equal(entry->name,name) &&
233            my_string_case_equal(entry->section,section)){
234             return entry;
235         }
236     }
237     return NULL;
238 }
239
240 // ini_insert()
241
242 void ini_insert(ini_t *ini, ini_entry_t *entry){
243     ini_entry_t * ini_entry;
244     ini_entry=ini_find(ini,entry->section,entry->name);
245     if(ini_entry!=NULL){
246         my_string_set(&ini_entry->value,entry->value);
247     }else{
248         if(ini->index>=IniEntriesNb){
249             my_fatal("ini_insert(): too many options\n");
250         }
251         ini_entry=ini->entries+(ini->index++);
252         my_string_set(&ini_entry->value,entry->value);
253         my_string_set(&ini_entry->name,entry->name);
254         my_string_set(&ini_entry->section,entry->section);
255     }
256 }
257
258 // ini_insert_ex()
259
260 void ini_insert_ex(ini_t *ini,
261                    const char *section,
262                    const char *name,
263                    const char *value){
264     ini_entry_t entry[1];
265     memset(entry,0,sizeof(ini_entry_t));
266     my_string_set(&entry->section,section);
267     my_string_set(&entry->name,name);
268     my_string_set(&entry->value,value);
269     ini_insert(ini,entry);
270     my_string_clear(&entry->section);
271     my_string_clear(&entry->name);
272     my_string_clear(&entry->value);
273 }
274
275 // ini_parse()
276
277 int ini_parse(ini_t *ini, const char *filename){
278     char name[StringSize];
279     char value[StringSize];
280     char section[StringSize];
281     char line[StringSize];
282     ini_entry_t entry[1];
283     line_type_t result;
284     const char *current_section=NULL;
285     FILE *f;
286     int line_nr=0;
287     my_string_set(&current_section,"Main");
288     memset(entry,0,sizeof(ini_entry_t));
289     f=fopen(filename,"r");
290     if(!f) {
291             //    my_fatal("ini_parse(): Can't open file \"%s\": %s\n",
292             //     filename,
293             //     strerror(errno));
294             // For now fail silently
295         return -1;
296     }
297     while(TRUE){
298         if(!fgets(line,StringSize,f)){
299             break;
300         }
301         line_nr++;
302         result=ini_line_parse(line,section,name,value);
303         if(result==SECTION){
304             my_string_set(&current_section,section);
305         }else if(result==NAME_VALUE){
306             ini_insert_ex(ini,current_section,name,value);
307         }else if(result==SYNTAX_ERROR){
308             my_fatal("ini_parse(): Syntax error in \"%s\": line %d\n",
309                      filename,
310                      line_nr);
311             
312         }else {  // empty line
313         }
314         
315     }
316     fclose(f);
317     return 0;
318
319 }
320
321 // ini_disp()
322
323 void ini_disp(ini_t *ini){
324     int i;
325     for(i=0;i<ini->index;i++){
326         my_log("POLYGLOT [%s] %s=\"%s\"\n",
327                (ini->entries)[i].section,
328                (ini->entries)[i].name,
329                (ini->entries)[i].value);
330     }
331 }
332
333 // ini_start_iter()
334
335 void ini_start_iter(ini_t *ini){
336     ini->iter=0;
337 }
338
339 // ini_next()
340
341 ini_entry_t * ini_next(ini_t *ini){
342     ASSERT(ini->iter<=ini->index);
343     if(ini->iter==ini->index){
344         return NULL;
345     }
346     return &ini->entries[ini->iter++];
347 }
348
349