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