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