1/***
2  This file is part of avahi.
3
4  avahi is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Lesser General Public License as
6  published by the Free Software Foundation; either version 2.1 of the
7  License, or (at your option) any later version.
8
9  avahi is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12  Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public
15  License along with avahi; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  USA.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25#include <string.h>
26#include <errno.h>
27#include <ctype.h>
28
29#include "avahi-common/avahi-malloc.h"
30#include <avahi-core/log.h>
31
32#include "ini-file-parser.h"
33
34AvahiIniFile* avahi_ini_file_load(const char *fname) {
35    AvahiIniFile *f;
36    FILE *fo;
37    AvahiIniFileGroup *group = NULL;
38    unsigned line;
39
40    assert(fname);
41
42    if (!(fo = fopen(fname, "r"))) {
43        avahi_log_error("Failed to open file '%s': %s", fname, strerror(errno));
44        return NULL;
45    }
46
47    f = avahi_new(AvahiIniFile, 1);
48    AVAHI_LLIST_HEAD_INIT(AvahiIniFileGroup, f->groups);
49    f->n_groups = 0;
50
51    line = 0;
52    while (!feof(fo)) {
53        char ln[256], *s, *e;
54        AvahiIniFilePair *pair;
55
56        if (!(fgets(ln, sizeof(ln), fo)))
57            break;
58
59        line++;
60
61        s = ln + strspn(ln, " \t");
62        s[strcspn(s, "\r\n")] = 0;
63
64        /* Skip comments and empty lines */
65        if (*s == '#' || *s == '%' || *s == 0)
66            continue;
67
68        if (*s == '[') {
69            /* new group */
70
71            if (!(e = strchr(s, ']'))) {
72                avahi_log_error("Unclosed group header in %s:%u: <%s>", fname, line, s);
73                goto fail;
74            }
75
76            *e = 0;
77
78            group = avahi_new(AvahiIniFileGroup, 1);
79            group->name = avahi_strdup(s+1);
80            group->n_pairs = 0;
81            AVAHI_LLIST_HEAD_INIT(AvahiIniFilePair, group->pairs);
82
83            AVAHI_LLIST_PREPEND(AvahiIniFileGroup, groups, f->groups, group);
84            f->n_groups++;
85        } else {
86
87            /* Normal assignment */
88            if (!(e = strchr(s, '='))) {
89                avahi_log_error("Missing assignment in %s:%u: <%s>", fname, line, s);
90                goto fail;
91            }
92
93            if (!group) {
94                avahi_log_error("Assignment outside group in %s:%u <%s>", fname, line, s);
95                goto fail;
96            }
97
98            /* Split the key and the value */
99            *(e++) = 0;
100
101            pair = avahi_new(AvahiIniFilePair, 1);
102            pair->key = avahi_strdup(s);
103            pair->value = avahi_strdup(e);
104
105            AVAHI_LLIST_PREPEND(AvahiIniFilePair, pairs, group->pairs, pair);
106            group->n_pairs++;
107        }
108    }
109
110    fclose(fo);
111
112    return f;
113
114fail:
115
116    if (fo)
117        fclose(fo);
118
119    if (f)
120        avahi_ini_file_free(f);
121
122    return NULL;
123}
124
125void avahi_ini_file_free(AvahiIniFile *f) {
126    AvahiIniFileGroup *g;
127    assert(f);
128
129    while ((g = f->groups)) {
130        AvahiIniFilePair *p;
131
132        while ((p = g->pairs)) {
133            avahi_free(p->key);
134            avahi_free(p->value);
135
136            AVAHI_LLIST_REMOVE(AvahiIniFilePair, pairs, g->pairs, p);
137            avahi_free(p);
138        }
139
140        avahi_free(g->name);
141
142        AVAHI_LLIST_REMOVE(AvahiIniFileGroup, groups, f->groups, g);
143        avahi_free(g);
144    }
145
146    avahi_free(f);
147}
148
149char** avahi_split_csv(const char *t) {
150    unsigned n_comma = 0;
151    const char *p;
152    char **r, **i;
153
154    for (p = t; *p; p++)
155        if (*p == ',')
156            n_comma++;
157
158    i = r = avahi_new(char*, n_comma+2);
159
160    for (;;) {
161        size_t n, l = strcspn(t, ",");
162        const char *c;
163
164        /* Ignore leading blanks */
165        for (c = t, n = l; isblank(*c); c++, n--);
166
167        /* Ignore trailing blanks */
168        for (; n > 0 && isblank(c[n-1]); n--);
169
170        *(i++) = avahi_strndup(c, n);
171
172        t += l;
173
174        if (*t == 0)
175            break;
176
177        assert(*t == ',');
178        t++;
179    }
180
181    *i = NULL;
182
183    return r;
184}
185
186void avahi_strfreev(char **p) {
187    char **i;
188
189    if (!p)
190        return;
191
192    for (i = p; *i; i++)
193        avahi_free(*i);
194
195    avahi_free(p);
196}
197