17307e92cab2afbd73e7c1431485715cf1b3b3f2
[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 parser_helper_file_t 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 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 parser_helper_file_t {
46
47 /**
48 * File name
49 */
50 char *name;
51
52 /**
53 * File stream
54 */
55 FILE *file;
56
57 /**
58 * Enumerator of paths matching the most recent inclusion pattern.
59 */
60 enumerator_t *matches;
61 };
62
63 /**
64 * Destroy the given file data.
65 */
66 static void parser_helper_file_destroy(parser_helper_file_t *this)
67 {
68 if (this->file)
69 {
70 fclose(this->file);
71 }
72 free(this->name);
73 DESTROY_IF(this->matches);
74 free(this);
75 }
76
77 /**
78 * Returns the current file, if any.
79 */
80 static parser_helper_file_t *current_file(private_parser_helper_t *this)
81 {
82 parser_helper_file_t *file;
83
84 array_get(this->files, ARRAY_TAIL, &file);
85 if (file->name)
86 {
87 return file;
88 }
89 return NULL;
90 }
91
92 METHOD(parser_helper_t, file_next, FILE*,
93 private_parser_helper_t *this)
94 {
95 parser_helper_file_t *file, *next;
96 char *name;
97
98 array_get(this->files, ARRAY_TAIL, &file);
99 if (!file->matches)
100 {
101 array_remove(this->files, ARRAY_TAIL, NULL);
102 parser_helper_file_destroy(file);
103 /* continue with previous includes, if any */
104 array_get(this->files, ARRAY_TAIL, &file);
105 }
106 if (file->matches)
107 {
108 while (file->matches->enumerate(file->matches, &name, NULL))
109 {
110 INIT(next,
111 .name = strdup(name),
112 .file = fopen(name, "r"),
113 );
114
115 if (next->file)
116 {
117 array_insert(this->files, ARRAY_TAIL, next);
118 return next->file;
119 }
120 PARSER_DBG2(&this->public, "unable to open '%s'", name);
121 parser_helper_file_destroy(next);
122 }
123 file->matches->destroy(file->matches);
124 file->matches = NULL;
125 }
126 return NULL;
127 }
128
129 METHOD(parser_helper_t, file_include, void,
130 private_parser_helper_t *this, char *pattern)
131 {
132 parser_helper_file_t *file;
133 char pat[PATH_MAX];
134
135 array_get(this->files, ARRAY_TAIL, &file);
136 if (!pattern || !*pattern)
137 {
138 PARSER_DBG1(&this->public, "no include pattern specified, ignored");
139 file->matches = enumerator_create_empty();
140 return;
141 }
142
143 if (!file->name || path_absolute(pattern))
144 { /* absolute path */
145 if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
146 {
147 PARSER_DBG1(&this->public, "include pattern too long, ignored");
148 file->matches = enumerator_create_empty();
149 return;
150 }
151 }
152 else
153 { /* base relative paths to the directory of the current file */
154 char *dir = path_dirname(file->name);
155 if (snprintf(pat, sizeof(pat), "%s%s%s", dir, DIRECTORY_SEPARATOR,
156 pattern) >= sizeof(pat))
157 {
158 PARSER_DBG1(&this->public, "include pattern too long, ignored");
159 free(dir);
160 file->matches = enumerator_create_empty();
161 return;
162 }
163 free(dir);
164 }
165
166 file->matches = enumerator_create_glob(pat);
167 if (!file->matches)
168 { /* if glob(3) is not available, try to load pattern directly */
169 file->matches = enumerator_create_single(strdup(pat), free);
170 }
171 }
172
173 METHOD(parser_helper_t, string_init, void,
174 private_parser_helper_t *this)
175 {
176 chunk_t data;
177
178 data = this->writer->extract_buf(this->writer);
179 chunk_free(&data);
180 }
181
182 METHOD(parser_helper_t, string_add, void,
183 private_parser_helper_t *this, char *str)
184 {
185 this->writer->write_data(this->writer, chunk_from_str(str));
186 }
187
188 METHOD(parser_helper_t, string_get, char*,
189 private_parser_helper_t *this)
190 {
191 chunk_t data;
192
193 this->writer->write_data(this->writer, chunk_from_chars('\0'));
194 data = this->writer->extract_buf(this->writer);
195 return data.ptr;
196 }
197
198 METHOD(parser_helper_t, destroy, void,
199 private_parser_helper_t *this)
200 {
201 array_destroy_function(this->files, (void*)parser_helper_file_destroy, NULL);
202 this->writer->destroy(this->writer);
203 free(this);
204 }
205
206 /**
207 * Described in header
208 */
209 void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...)
210 {
211 private_parser_helper_t *this = (private_parser_helper_t*)ctx;
212 parser_helper_file_t *file;
213 char msg[8192];
214 va_list args;
215 int line;
216
217 va_start(args, fmt);
218 vsnprintf(msg, sizeof(msg), fmt, args);
219 va_end(args);
220
221 file = current_file(this);
222 line = ctx->get_lineno ? ctx->get_lineno(ctx->scanner) : 0;
223 if (file)
224 {
225 dbg(DBG_CFG, level, "%s:%d: %s", file->name, line, msg);
226 }
227 else
228 {
229 dbg(DBG_CFG, level, "%s", msg);
230 }
231 }
232
233 /**
234 * Described in header
235 */
236 parser_helper_t *parser_helper_create(void *context)
237 {
238 private_parser_helper_t *this;
239 parser_helper_file_t *sentinel;
240
241 INIT(this,
242 .public = {
243 .context = context,
244 .file_include = _file_include,
245 .file_next = _file_next,
246 .string_init = _string_init,
247 .string_add = _string_add,
248 .string_get = _string_get,
249 .destroy = _destroy,
250 },
251 .files = array_create(0, 0),
252 .writer = bio_writer_create(0),
253 );
254
255 INIT(sentinel,
256 .name = NULL,
257 );
258 array_insert(this->files, ARRAY_TAIL, sentinel);
259
260 return &this->public;
261 }