LIRC libraries
LinuxInfraredRemoteControl
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups Pages
ciniparser.c
Go to the documentation of this file.
1 /* Copyright (c) 2000-2007 by Nicolas Devillard.
2  * Copyright (x) 2009 by Tim Post <tinkertim@gmail.com>
3  * MIT License
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
35 #include <ctype.h>
36 #include <ciniparser.h>
37 
38 #define ASCIILINESZ (1024)
39 #define INI_INVALID_KEY ((char*)NULL)
40 
44 typedef enum _line_status_ {
45  LINE_UNPROCESSED,
46  LINE_ERROR,
47  LINE_EMPTY,
48  LINE_COMMENT,
49  LINE_SECTION,
50  LINE_VALUE
51 } line_status;
52 
53 
64 static char* strlwc(const char* s)
65 {
66  static char l[ASCIILINESZ + 1];
67  int i;
68 
69  if (s == NULL)
70  return NULL;
71 
72  for (i = 0; s[i] && i < ASCIILINESZ; i++)
73  l[i] = tolower(s[i]);
74  l[i] = '\0';
75  return l;
76 }
77 
90 static char* strstrip(const char* s)
91 {
92  static char l[ASCIILINESZ + 1];
93  unsigned int i, numspc;
94 
95  if (s == NULL)
96  return NULL;
97 
98  while (isspace(*s))
99  s++;
100 
101  for (i = numspc = 0; s[i] && i < ASCIILINESZ; i++) {
102  l[i] = s[i];
103  if (isspace(l[i]))
104  numspc++;
105  else
106  numspc = 0;
107  }
108  l[i - numspc] = '\0';
109  return l;
110 }
111 
120 static
121 line_status ciniparser_line(char* input_line, char* section,
122  char* key, char* value)
123 {
124  line_status sta = LINE_ERROR;
125  char line[ASCIILINESZ + 1];
126  int len;
127 
128  strcpy(line, strstrip(input_line));
129  len = (int)strlen(line);
130 
131  if (len < 1) {
132  /* Empty line */
133  sta = LINE_EMPTY;
134  } else if (line[0] == '#') {
135  /* Comment line */
136  sta = LINE_COMMENT;
137  } else if (line[0] == '[' && line[len - 1] == ']') {
138  /* Section name */
139  if (sscanf(line, "[%[^]]", section) == 1) {
140  strcpy(section, strstrip(section));
141  strcpy(section, strlwc(section));
142  sta = LINE_SECTION;
143  }
144  } else if (sscanf(line, "%[^=] = \"%[^\"]\"", key, value) == 2
145  || sscanf(line, "%[^=] = '%[^\']'", key, value) == 2
146  || sscanf(line, "%[^=] = %[^;#]", key, value) == 2) {
147  /* Usual key=value, with or without comments */
148  strcpy(key, strstrip(key));
149  strcpy(key, strlwc(key));
150  strcpy(value, strstrip(value));
151  /*
152  * sscanf cannot handle '' or "" as empty values
153  * this is done here
154  */
155  if (!strcmp(value, "\"\"") || (!strcmp(value, "''")))
156  value[0] = 0;
157  sta = LINE_VALUE;
158  } else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2
159  || sscanf(line, "%[^=] %[=]", key, value) == 2) {
160  /*
161  * Special cases:
162  * key=
163  * key=;
164  * key=#
165  */
166  strcpy(key, strstrip(key));
167  strcpy(key, strlwc(key));
168  value[0] = 0;
169  sta = LINE_VALUE;
170  } else {
171  /* Generate syntax error */
172  sta = LINE_ERROR;
173  }
174  return sta;
175 }
176 
177 /* The remaining public functions are documented in ciniparser.h */
178 
180 {
181  int i;
182  int nsec;
183 
184  if (d == NULL)
185  return -1;
186 
187  nsec = 0;
188  for (i = 0; i < d->size; i++) {
189  if (d->key[i] == NULL)
190  continue;
191  if (strchr(d->key[i], ':') == NULL)
192  nsec++;
193  }
194 
195  return nsec;
196 }
197 
198 const char* ciniparser_getsecname(dictionary* d, int n)
199 {
200  int i;
201  int foundsec;
202 
203  if (d == NULL || n < 0)
204  return NULL;
205 
206  if (n == 0)
207  n++;
208 
209  foundsec = 0;
210 
211  for (i = 0; i < d->size; i++) {
212  if (d->key[i] == NULL)
213  continue;
214  if (!strchr(d->key[i], ':')) {
215  foundsec++;
216  if (foundsec >= n)
217  break;
218  }
219  }
220 
221  if (foundsec == n)
222  return d->key[i];
223 
224  return (char*)NULL;
225 }
226 
227 void ciniparser_dump(dictionary* d, FILE* f)
228 {
229  int i;
230 
231  if (d == NULL || f == NULL)
232  return;
233 
234  for (i = 0; i < d->size; i++) {
235  if (d->key[i] == NULL)
236  continue;
237  if (d->val[i] != NULL)
238  fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
239  else
240  fprintf(f, "[%s]=UNDEF\n", d->key[i]);
241  }
242 }
243 
245 {
246  int i, j;
247  char keym[ASCIILINESZ + 1];
248  int nsec;
249  const char* secname;
250  int seclen;
251 
252  if (d == NULL || f == NULL)
253  return;
254 
255  memset(keym, 0, ASCIILINESZ + 1);
256 
257  nsec = ciniparser_getnsec(d);
258  if (nsec < 1) {
259  /* No section in file: dump all keys as they are */
260  for (i = 0; i < d->size; i++) {
261  if (d->key[i] == NULL)
262  continue;
263  fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
264  }
265  return;
266  }
267 
268  for (i = 0; i < nsec; i++) {
269  secname = ciniparser_getsecname(d, i);
270  seclen = (int)strlen(secname);
271  fprintf(f, "\n[%s]\n", secname);
272  snprintf(keym, ASCIILINESZ + 1, "%s:", secname);
273  for (j = 0; j < d->size; j++) {
274  if (d->key[j] == NULL)
275  continue;
276  if (!strncmp(d->key[j], keym, seclen + 1)) {
277  fprintf(f, "%-30s = %s\n",
278  d->key[j] + seclen + 1,
279  d->val[j] ? d->val[j] : "");
280  }
281  }
282  }
283  fprintf(f, "\n");
284 }
285 
286 const char* ciniparser_getstring(dictionary* d, const char* key, char* def)
287 {
288  char* lc_key;
289  const char* sval;
290 
291  if (d == NULL || key == NULL)
292  return def;
293 
294  lc_key = strlwc(key);
295  sval = dictionary_get(d, lc_key, def);
296 
297  return sval;
298 }
299 
300 int ciniparser_getint(dictionary* d, const char* key, int notfound)
301 {
302  const char* str;
303 
304  str = ciniparser_getstring(d, key, INI_INVALID_KEY);
305 
306  if (str == INI_INVALID_KEY)
307  return notfound;
308 
309  return (int)strtol(str, NULL, 10);
310 }
311 
312 double ciniparser_getdouble(dictionary* d, const char* key, double notfound)
313 {
314  const char* str;
315 
316  str = ciniparser_getstring(d, key, INI_INVALID_KEY);
317 
318  if (str == INI_INVALID_KEY)
319  return notfound;
320 
321  return atof(str);
322 }
323 
324 int ciniparser_getboolean(dictionary* d, const char* key, int notfound)
325 {
326  const char* c;
327  int ret;
328 
329  c = ciniparser_getstring(d, key, INI_INVALID_KEY);
330  if (c == INI_INVALID_KEY)
331  return notfound;
332 
333  switch (c[0]) {
334  case 'y': case 'Y': case '1': case 't': case 'T':
335  ret = 1;
336  break;
337  case 'n': case 'N': case '0': case 'f': case 'F':
338  ret = 0;
339  break;
340  default:
341  ret = notfound;
342  break;
343  }
344 
345  return ret;
346 }
347 
348 int ciniparser_find_entry(dictionary* ini, const char* entry)
349 {
350  int found = 0;
351 
352  if (ciniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY)
353  found = 1;
354 
355  return found;
356 }
357 
358 int ciniparser_set(dictionary* d, const char* entry, const char* val)
359 {
360  return dictionary_set(d, strlwc(entry), val);
361 }
362 
363 void ciniparser_unset(dictionary* ini, char* entry)
364 {
365  dictionary_unset(ini, strlwc(entry));
366 }
367 
368 dictionary* ciniparser_load(const char* ininame)
369 {
370  FILE* in;
371  char line[ASCIILINESZ + 1];
372  char section[ASCIILINESZ + 1];
373  char key[ASCIILINESZ + 1];
374  char tmp[ASCIILINESZ + 1];
375  char val[ASCIILINESZ + 1];
376  int last = 0, len, lineno = 0, errs = 0;
377  dictionary* dict;
378 
379  in = fopen(ininame, "r");
380  if (in == NULL) {
381  fprintf(stderr, "ciniparser: cannot open %s\n (ignored)",
382  ininame);
383  return NULL;
384  }
385 
386  dict = dictionary_new(0);
387  if (!dict) {
388  fclose(in);
389  return NULL;
390  }
391 
392  memset(line, 0, ASCIILINESZ + 1);
393  memset(section, 0, ASCIILINESZ + 1);
394  memset(key, 0, ASCIILINESZ + 1);
395  memset(val, 0, ASCIILINESZ + 1);
396  last = 0;
397 
398  while (fgets(line + last, ASCIILINESZ - last, in) != NULL) {
399  lineno++;
400  len = (int)strlen(line) - 1;
401  /* Safety check against buffer overflows */
402  if (line[len] != '\n') {
403  fprintf(stderr,
404  "ciniparser: input line too long in %s (%d)\n",
405  ininame,
406  lineno);
407  dictionary_del(dict);
408  fclose(in);
409  return NULL;
410  }
411 
412  /* Get rid of \n and spaces at end of line */
413  while ((len >= 0) &&
414  ((line[len] == '\n') || (isspace(line[len])))) {
415  line[len] = 0;
416  len--;
417  }
418 
419  /* Detect multi-line */
420  if (len >= 0 && line[len] == '\\') {
421  /* Multi-line value */
422  last = len;
423  continue;
424  }
425 
426  switch (ciniparser_line(line, section, key, val)) {
427  case LINE_EMPTY:
428  case LINE_COMMENT:
429  break;
430 
431  case LINE_SECTION:
432  errs = dictionary_set(dict, section, NULL);
433  break;
434 
435  case LINE_VALUE:
436  snprintf(tmp, ASCIILINESZ + 1, "%s:%s", section, key);
437  errs = dictionary_set(dict, tmp, val);
438  break;
439 
440  case LINE_ERROR:
441  fprintf(stderr, "ciniparser: syntax error in %s (%d):\n",
442  ininame, lineno);
443  fprintf(stderr, "-> %s\n", line);
444  errs++;
445  break;
446 
447  default:
448  break;
449  }
450  memset(line, 0, ASCIILINESZ);
451  last = 0;
452  if (errs < 0) {
453  fprintf(stderr, "ciniparser: memory allocation failure\n");
454  break;
455  }
456  }
457 
458  if (errs) {
459  dictionary_del(dict);
460  dict = NULL;
461  }
462 
463  fclose(in);
464 
465  return dict;
466 }
467 
469 {
470  dictionary_del(d);
471 }
472 
const char * ciniparser_getstring(dictionary *d, const char *key, char *def)
Get the string associated to a key.
Definition: ciniparser.c:286
int dictionary_set(dictionary *d, const char *key, const char *val)
Set a value in a dictionary.
Definition: dictionary.c:147
dictionary * ciniparser_load(const char *ininame)
Parse an ini file and return an allocated dictionary object.
Definition: ciniparser.c:368
void ciniparser_dump_ini(dictionary *d, FILE *f)
Save a dictionary to a loadable ini file.
Definition: ciniparser.c:244
int ciniparser_getint(dictionary *d, const char *key, int notfound)
Get the string associated to a key, convert to an int.
Definition: ciniparser.c:300
const char * dictionary_get(dictionary *d, const char *key, const char *def)
Get a value from a dictionary.
Definition: dictionary.c:128
dictionary * dictionary_new(int size)
Create a new dictionary object.
Definition: dictionary.c:92
void dictionary_del(dictionary *d)
Delete a dictionary object.
Definition: dictionary.c:109
void ciniparser_freedict(dictionary *d)
Free all memory associated to an ini dictionary.
Definition: ciniparser.c:468
int ciniparser_set(dictionary *d, const char *entry, const char *val)
Set an item in the dictionary.
Definition: ciniparser.c:358
int ciniparser_find_entry(dictionary *ini, const char *entry)
Finds out if a given entry exists in a dictionary.
Definition: ciniparser.c:348
void ciniparser_unset(dictionary *ini, char *entry)
Delete an entry in a dictionary.
Definition: ciniparser.c:363
double ciniparser_getdouble(dictionary *d, const char *key, double notfound)
Get the string associated to a key, convert to a double.
Definition: ciniparser.c:312
enum _line_status_ line_status
void dictionary_unset(dictionary *d, const char *key)
Delete a key in a dictionary.
Definition: dictionary.c:206
const char * ciniparser_getsecname(dictionary *d, int n)
Get name for section n in a dictionary.
Definition: ciniparser.c:198
void ciniparser_dump(dictionary *d, FILE *f)
Dump a dictionary to an opened file pointer.
Definition: ciniparser.c:227
_line_status_
Definition: ciniparser.c:44
Dictionary object.
Definition: dictionary.h:67
int ciniparser_getnsec(dictionary *d)
Get number of sections in a dictionary.
Definition: ciniparser.c:179
Parser for ini files.
int ciniparser_getboolean(dictionary *d, const char *key, int notfound)
Get the string associated to a key, convert to a boolean.
Definition: ciniparser.c:324