add FOR_EACH_IN_ARRAY
[libjh.git] / ini.c
1 // Copyright (2013) Jann Horn <jann@thejh.net>
2
3 // Supports very simple ini files. Structure:
4 //  - data is in lines, key=value
5 //  - # or ; starts a comment line
6 //  - lines without = are ignored, too
7
8 HEADER #include <string.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14
15 HEADER struct inifile_entry {
16 HEADER   char *key;
17 HEADER   char *value;
18 HEADER   struct inifile_entry *next;
19 HEADER };
20 HEADER 
21 HEADER struct inifile {
22 HEADER   struct inifile_entry *first;
23 HEADER };
24
25 // Parses an inifile. When you're done, you can free the whole thing
26 // (including all the entries and the strings) by freeing the pointer
27 // that this function returns.
28 PUBLIC_FN struct inifile *parse_inifile(char *f) {
29   int n_of_lines = count_char_occurences(f, '\n')+1;
30   size_t worst_case_size = strlen(f) +
31                            n_of_lines*sizeof(struct inifile_entry) +
32                            sizeof(struct inifile);
33   
34   char *buf = malloc(worst_case_size);
35   if (buf == NULL) return NULL;
36   
37   struct inifile *head = (void*)buf;
38   void *buf_ = (void*)(head+1);
39   
40   struct inifile_entry **nextp = &head->first;
41   
42   while (*f) {
43     char *nl = strchr(f, '\n');
44     if (!nl) nl=f+strlen(f);
45     if (*f == '#' || *f == ';') goto next;
46     char *eqsign = strchr(f, '=');
47     if (!eqsign) break;
48     if (eqsign > nl) goto next;
49     
50     struct inifile_entry *e = buf_; buf_ = e+1;
51     *nextp = e; nextp = &e->next;
52     char *key = f;
53     int keylen = eqsign-key;
54     char *value = eqsign+1;
55     int valuelen = nl-value;
56     e->key = buf_; buf_+=keylen+1;
57     memcpyn(e->key, key, keylen);
58     trim_end(e->key, " \r");
59     e->value = buf_; buf_+=valuelen+1;
60     memcpyn(e->value, value, valuelen);
61     trim_end(e->value, " \r");
62     
63 next:
64     f = nl;
65     if (!*f) break;
66     f++;
67   }
68   
69   *nextp = NULL;
70   return head;
71 }
72
73 PUBLIC_FN struct inifile *fslurp_inifile(int fd) {
74   char *f = slurp_fd(fd, NULL, 0);
75   if (f == NULL) return NULL;
76   struct inifile *r = parse_inifile(f);
77   int errno_ = errno;
78   free(f);
79   errno = errno_;
80   return r;
81 }
82
83 PUBLIC_FN struct inifile *slurp_inifile(char *path) {
84   int fd = open(path, O_RDONLY);
85   if (fd == -1) return NULL;
86   struct inifile *r = fslurp_inifile(fd);
87   int errno_ = errno;
88   close(fd);
89   errno = errno_;
90   return r;
91 }
92
93 PUBLIC_FN char *inifile_lookup(struct inifile *inf, char *key) {
94   for (struct inifile_entry *e = inf->first; e; e=e->next) {
95     if (streq(e->key, key)) return e->value;
96   }
97   return NULL;
98 }