initial import (incomplete)
[project/uci.git] / parse.c
1 /*
2  * libuci - Library for the Unified Configuration Interface
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * this program is free software; you can redistribute it and/or modify
6  * it under the terms of the gnu lesser general public license version 2.1
7  * as published by the free software foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14  
15 /*
16  * This file contains the code for parsing uci config files
17  */
18
19 #include <ctype.h>
20
21 #define LINEBUF 128
22 #define LINEBUF_MAX     4096
23
24 /*
25  * Fetch a new line from the input stream and resize buffer if necessary
26  */
27 static void uci_getln(struct uci_context *ctx)
28 {
29         struct uci_parse_context *pctx = ctx->pctx;
30         char *p;
31         int ofs;
32
33         if (pctx->buf == NULL) {
34                 pctx->buf = uci_malloc(ctx, LINEBUF);
35                 pctx->bufsz = LINEBUF;
36         }
37         
38         ofs = 0;
39         do {
40                 p = &pctx->buf[ofs];
41                 p[ofs] = 0;
42
43                 p = fgets(p, pctx->bufsz - ofs, pctx->file);
44                 if (!p || !p[ofs])
45                         return;
46                 
47                 ofs += strlen(p);
48                 if (pctx->buf[ofs - 1] == '\n') {
49                         pctx->line++;
50                         pctx->buf[ofs - 1] = 0;
51                         return;
52                 }
53
54                 if (pctx->bufsz > LINEBUF_MAX/2) {
55                         pctx->byte = LINEBUF_MAX;
56                         UCI_THROW(ctx, UCI_ERR_PARSE);
57                 }
58
59                 pctx->bufsz *= 2;
60                 pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
61         } while (1);
62 }
63
64 /*
65  * Clean up all extra memory used by the parser
66  */
67 static void uci_parse_cleanup(struct uci_context *ctx)
68 {
69         struct uci_parse_context *pctx;
70         
71         pctx = ctx->pctx;
72         if (!pctx)
73                 return;
74
75         if (pctx->buf)
76                 free(pctx->buf);
77         if (pctx->file)
78                 fclose(pctx->file);
79
80         free(pctx);
81 }
82
83 static void skip_whitespace(char **str)
84 {
85         while (**str && isspace(**str))
86                 *str += 1;
87 }
88
89 static char *parse_double_quote(char **str)
90 {
91         char *val;
92         
93         *str += 1;
94         val = *str;
95         while (**str) {
96                         
97                 /* skip escaped characters */
98                 if (**str == '\\') {
99                         *str += 2;
100                         continue;
101                 }
102                 
103                 /* check for the end of the quoted string */
104                 if (**str == '"') {
105                         **str = 0;
106                         *str += 1;
107                         return val;
108                 }
109                 *str += 1;
110         }
111
112         return NULL;
113 }
114
115 static char *parse_single_quote(char **str)
116 {
117         /* TODO: implement */
118         return NULL;
119 }
120
121 static char *parse_unquoted(char **str)
122 {
123         char *val;
124         
125         val = *str;
126
127         while (**str && !isspace(**str))
128                 *str += 1;
129         **str = 0;
130         *str += 1;
131
132         return val;
133 }
134
135 static char *next_arg(char **str)
136 {
137         skip_whitespace(str);
138
139         switch (**str) {
140                 case '"':
141                         return parse_double_quote(str);
142                 case '\'':
143                         return parse_single_quote(str);
144                 case 0:
145                         return NULL;
146                 default:
147                         return parse_unquoted(str);
148         }
149 }
150
151 static void uci_parse_config(struct uci_context *ctx, char **str)
152 {
153         char *type, *name;
154         
155         *str += strlen(*str) + 1;
156         
157         if (!*str) {
158                 ctx->pctx->byte = *str - ctx->pctx->buf;
159                 UCI_THROW(ctx, UCI_ERR_PARSE);
160         }
161
162         type = next_arg(str);
163         if (!type) {
164                 ctx->pctx->byte = *str - ctx->pctx->buf;
165                 UCI_THROW(ctx, UCI_ERR_PARSE);
166         }
167                 
168         name = next_arg(str);
169         DPRINTF("Section<%s>: %s\n", type, name);
170 }
171
172 static void uci_parse_option(struct uci_context *ctx, char **str)
173 {
174         char *name, *value;
175         
176         *str += strlen(*str) + 1;
177         
178         name = next_arg(str);
179         if (!name) {
180                 ctx->pctx->byte = *str - ctx->pctx->buf;
181                 UCI_THROW(ctx, UCI_ERR_PARSE);
182         }
183
184         value = next_arg(str);
185         if (!value) {
186                 ctx->pctx->byte = *str - ctx->pctx->buf;
187                 UCI_THROW(ctx, UCI_ERR_PARSE);
188         }
189
190         DPRINTF("\tOption: %s=\"%s\"\n", name, value);
191 }
192
193 static void uci_parse_line(struct uci_context *ctx)
194 {
195         struct uci_parse_context *pctx = ctx->pctx;
196         char *word, *brk;
197         
198         for (word = strtok_r(pctx->buf, ";", &brk);
199                  word;
200                  word = strtok_r(NULL, ";", &brk)) {
201                         
202                 char *pbrk;
203                 word = strtok_r(word, " \t", &pbrk);
204                 
205                 switch(word[0]) {
206                         case 'c':
207                                 if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
208                                         uci_parse_config(ctx, &word);
209                                 break;
210                         case 'o':
211                                 if ((word[1] == 0) || !strcmp(word + 1, "ption"))
212                                         uci_parse_option(ctx, &word);
213                                 break;
214                         default:
215                                 pctx->byte = word - pctx->buf;
216                                 UCI_THROW(ctx, UCI_ERR_PARSE);
217                                 break;
218                 }
219         }
220 }
221
222 int uci_parse(struct uci_context *ctx, const char *name)
223 {
224         struct uci_parse_context *pctx;
225         
226         UCI_HANDLE_ERR(ctx);
227         UCI_ASSERT(ctx, name != NULL);
228
229         /* make sure no memory from previous parse attempts is leaked */
230         uci_parse_cleanup(ctx);
231
232         pctx = (struct uci_parse_context *) uci_malloc(ctx, sizeof(struct uci_parse_context));
233         ctx->pctx = pctx;
234         
235         /* TODO: use /etc/config/ */
236         pctx->file = fopen(name, "r");
237         if (!pctx->file)
238                 UCI_THROW(ctx, UCI_ERR_NOTFOUND);
239
240         while (!feof(pctx->file)) {
241                 uci_getln(ctx);
242                 if (*(pctx->buf))
243                         uci_parse_line(ctx);
244         }
245
246         /* if no error happened, we can get rid of the parser context now */
247         uci_parse_cleanup(ctx);
248
249         return 0;
250 }
251
252