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