403cca92ba1ba671f70c08734c73547b30113ab9
[strongswan.git] / src / libstrongswan / utils / parser_helper.c
1 /*
2 * Copyright (C) 2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <limits.h>
17 #include <ctype.h>
18 #include <stdarg.h>
19
20 #include "parser_helper.h"
21
22 #include <collections/array.h>
23
24 typedef struct private_parser_helper_t private_parser_helper_t;
25 typedef struct private_parser_helper_file_t private_parser_helper_file_t;
26
27 struct private_parser_helper_t {
28
29 /**
30 * Public interface.
31 */
32 parser_helper_t public;
33
34 /**
35 * Stack of included files, as private_parser_helper_file_t.
36 */
37 array_t *files;
38
39 /**
40 * Helper for parsing strings.
41 */
42 bio_writer_t *writer;
43 };
44
45 struct private_parser_helper_file_t {
46
47 /**
48 * File data.
49 */
50 parser_helper_file_t public;
51
52 /**
53 * Enumerator of paths matching the most recent inclusion pattern.
54 */
55 enumerator_t *matches;
56 };
57
58 /**
59 * Destroy the given file data.
60 */
61 static void parser_helper_file_destroy(private_parser_helper_file_t *this)
62 {
63 if (this->public.file)
64 {
65 fclose(this->public.file);
66 }
67 free(this->public.name);
68 DESTROY_IF(this->matches);
69 free(this);
70 }
71
72 METHOD(parser_helper_t, file_current, parser_helper_file_t*,
73 private_parser_helper_t *this)
74 {
75 private_parser_helper_file_t *file;
76
77 array_get(this->files, ARRAY_TAIL, &file);
78 if (file->public.name)
79 {
80 return &file->public;
81 }
82 return NULL;
83 }
84
85 METHOD(parser_helper_t, file_next, parser_helper_file_t*,
86 private_parser_helper_t *this)
87 {
88 private_parser_helper_file_t *file, *next;
89 char *name;
90
91 array_get(this->files, ARRAY_TAIL, &file);
92 if (!file->matches)
93 {
94 array_remove(this->files, ARRAY_TAIL, NULL);
95 parser_helper_file_destroy(file);
96 /* continue with previous includes, if any */
97 array_get(this->files, ARRAY_TAIL, &file);
98 }
99 if (file->matches)
100 {
101 while (file->matches->enumerate(file->matches, &name, NULL))
102 {
103 INIT(next,
104 .public = {
105 .name = strdup(name),
106 .file = fopen(name, "r"),
107 },
108 );
109
110 if (next->public.file)
111 {
112 array_insert(this->files, ARRAY_TAIL, next);
113 return &next->public;
114 }
115 PARSER_DBG2(&this->public, "unable to open '%s'", name);
116 parser_helper_file_destroy(next);
117 }
118 file->matches->destroy(file->matches);
119 file->matches = NULL;
120 }
121 return NULL;
122 }
123
124 METHOD(parser_helper_t, file_include, void,
125 private_parser_helper_t *this, char *pattern)
126 {
127 private_parser_helper_file_t *file;
128 char pat[PATH_MAX];
129
130 array_get(this->files, ARRAY_TAIL, &file);
131 if (!pattern || !*pattern)
132 {
133 PARSER_DBG1(&this->public, "no include pattern specified, ignored");
134 file->matches = enumerator_create_empty();
135 return;
136 }
137
138 if (!file->public.name || pattern[0] == '/')
139 { /* absolute path */
140 if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
141 {
142 PARSER_DBG1(&this->public, "include pattern too long, ignored");
143 file->matches = enumerator_create_empty();
144 return;
145 }
146 }
147 else
148 { /* base relative paths to the directory of the current file */
149 char *dir = path_dirname(file->public.name);
150 if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat))
151 {
152 PARSER_DBG1(&this->public, "include pattern too long, ignored");
153 free(dir);
154 file->matches = enumerator_create_empty();
155 return;
156 }
157 free(dir);
158 }
159
160 file->matches = enumerator_create_glob(pat);
161 if (!file->matches)
162 { /* if glob(3) is not available, try to load pattern directly */
163 file->matches = enumerator_create_single(strdup(pat), free);
164 }
165 }
166
167 METHOD(parser_helper_t, string_init, void,
168 private_parser_helper_t *this)
169 {
170 chunk_t data;
171
172 data = this->writer->extract_buf(this->writer);
173 chunk_free(&data);
174 }
175
176 METHOD(parser_helper_t, string_add, void,
177 private_parser_helper_t *this, char *str)
178 {
179 this->writer->write_data(this->writer, chunk_from_str(str));
180 }
181
182 METHOD(parser_helper_t, string_get, char*,
183 private_parser_helper_t *this)
184 {
185 chunk_t data;
186
187 this->writer->write_data(this->writer, chunk_from_chars('\0'));
188 data = this->writer->extract_buf(this->writer);
189 return data.ptr;
190 }
191
192 METHOD(parser_helper_t, destroy, void,
193 private_parser_helper_t *this)
194 {
195 array_destroy_function(this->files, (void*)parser_helper_file_destroy, NULL);
196 this->writer->destroy(this->writer);
197 free(this);
198 }
199
200 /**
201 * Described in header
202 */
203 void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...)
204 {
205 parser_helper_file_t *file;
206 char msg[8192];
207 va_list args;
208 int line;
209
210 va_start(args, fmt);
211 vsnprintf(msg, sizeof(msg), fmt, args);
212 va_end(args);
213
214 file = ctx->file_current(ctx);
215 line = ctx->get_lineno ? ctx->get_lineno(ctx->scanner) : 0;
216 if (file)
217 {
218 dbg(DBG_CFG, level, "%s:%d: %s", file->name, line, msg);
219 }
220 else
221 {
222 dbg(DBG_CFG, level, "%s", msg);
223 }
224 }
225
226 /**
227 * Described in header
228 */
229 parser_helper_t *parser_helper_create(void *context)
230 {
231 private_parser_helper_t *this;
232 private_parser_helper_file_t *sentinel;
233
234 INIT(this,
235 .public = {
236 .context = context,
237 .file_current = _file_current,
238 .file_include = _file_include,
239 .file_next = _file_next,
240 .string_init = _string_init,
241 .string_add = _string_add,
242 .string_get = _string_get,
243 .destroy = _destroy,
244 },
245 .files = array_create(0, 0),
246 .writer = bio_writer_create(0),
247 );
248
249 INIT(sentinel,
250 .public = {
251 .name = NULL,
252 },
253 );
254 array_insert(this->files, ARRAY_TAIL, sentinel);
255
256 return &this->public;
257 }