Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include <cassert>
7 : #include <stdlib.h>
8 : #include <stdio.h>
9 : #include <stdarg.h>
10 : #include <time.h>
11 : #include <string.h>
12 : #include <algorithm>
13 : #include <sys/types.h>
14 : #include <sys/stat.h>
15 : #include <errno.h>
16 : #include <limits.h>
17 :
18 : // Careful: must include globals first for extern definitions
19 : #include "ruleparserl.h"
20 : #include "ruleutil.h"
21 : #include "t_ruleparser.h"
22 :
23 : std::string t_ruleaction::RuleActionEchoResult;
24 :
25 : /**
26 : * Globals
27 : */
28 : t_rulelist* g_rulelist;
29 :
30 : /**
31 : * Parsing pass
32 : */
33 : PARSE_MODE g_parse_mode;
34 :
35 : /**
36 : * Current directory of file being parsed
37 : */
38 : std::string g_curdir;
39 :
40 : /**
41 : * Current file being parsed
42 : */
43 : std::string g_curpath;
44 :
45 : /**
46 : * Search path for inclusions
47 : */
48 : std::vector<std::string> g_incl_searchpath;
49 :
50 : /**
51 : * Global debug state
52 : */
53 : int g_debug = 0;
54 :
55 : /**
56 : * Strictness level
57 : */
58 : int g_strict = 127;
59 :
60 : /**
61 : * Warning level
62 : */
63 : int g_warn = 1;
64 :
65 : /**
66 : * Verbose output
67 : */
68 : int g_verbose = 0;
69 :
70 : /**
71 : * The last parsed doctext comment.
72 : */
73 : char* g_doctext;
74 :
75 : /**
76 : * The location of the last parsed doctext comment.
77 : */
78 : int g_doctext_lineno;
79 :
80 : /**
81 : * Converts a string filename into a thrift program name
82 : */
83 0 : std::string program_name(std::string filename) {
84 0 : std::string::size_type slash = filename.rfind("/");
85 0 : if (slash != std::string::npos) {
86 0 : filename = filename.substr(slash+1);
87 : }
88 0 : std::string::size_type dot = filename.rfind(".");
89 0 : if (dot != std::string::npos) {
90 0 : filename = filename.substr(0, dot);
91 : }
92 0 : return filename;
93 : }
94 :
95 : /**
96 : * MinGW doesn't have realpath, so use fallback implementation in that case,
97 : * otherwise this just calls through to realpath
98 : */
99 0 : char *saferealpath(const char *path, char *resolved_path) {
100 0 : return realpath(path, resolved_path);
101 : }
102 :
103 :
104 : /**
105 : * Report an error to the user. This is called yyerror for historical
106 : * reasons (lex and yacc expect the error reporting routine to be called
107 : * this). Call this function to report any errors to the user.
108 : * yyerror takes printf style arguments.
109 : *
110 : * @param fmt C format string followed by additional arguments
111 : */
112 0 : void yyerror(const char* fmt, ...) {
113 : va_list args;
114 0 : fprintf(stderr,
115 : "[ERROR:%s:%d] (last token was '%s')\n",
116 : g_curpath.c_str(),
117 : yylineno,
118 : yytext);
119 :
120 0 : va_start(args, fmt);
121 0 : vfprintf(stderr, fmt, args);
122 0 : va_end(args);
123 :
124 0 : fprintf(stderr, "\n");
125 0 : }
126 :
127 : /**
128 : * Prints a debug message from the parser.
129 : *
130 : * @param fmt C format string followed by additional arguments
131 : */
132 0 : void pdebug(const char* fmt, ...) {
133 0 : if (g_debug == 0) {
134 0 : return;
135 : }
136 : va_list args;
137 0 : printf("[PARSE:%d] ", yylineno);
138 0 : va_start(args, fmt);
139 0 : vprintf(fmt, args);
140 0 : va_end(args);
141 0 : printf("\n");
142 : }
143 :
144 : /**
145 : * Prints a verbose output mode message
146 : *
147 : * @param fmt C format string followed by additional arguments
148 : */
149 0 : void pverbose(const char* fmt, ...) {
150 0 : if (g_verbose == 0) {
151 0 : return;
152 : }
153 : va_list args;
154 0 : va_start(args, fmt);
155 0 : vprintf(fmt, args);
156 0 : va_end(args);
157 : }
158 :
159 : /**
160 : * Prints a warning message
161 : *
162 : * @param fmt C format string followed by additional arguments
163 : */
164 0 : void pwarning(int level, const char* fmt, ...) {
165 0 : if (g_warn < level) {
166 0 : return;
167 : }
168 : va_list args;
169 0 : printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
170 0 : va_start(args, fmt);
171 0 : vprintf(fmt, args);
172 0 : va_end(args);
173 0 : printf("\n");
174 : }
175 :
176 : /**
177 : * Prints a failure message and exits
178 : *
179 : * @param fmt C format string followed by additional arguments
180 : */
181 0 : void failure(const char* fmt, ...) {
182 : va_list args;
183 0 : fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
184 0 : va_start(args, fmt);
185 0 : vfprintf(stderr, fmt, args);
186 0 : va_end(args);
187 0 : printf("\n");
188 : //exit(1);
189 0 : }
190 :
191 : /**
192 : * Gets the directory path of a filename
193 : */
194 0 : std::string directory_name(std::string filename) {
195 0 : std::string::size_type slash = filename.rfind("/");
196 : // No slash, just use the current directory
197 0 : if (slash == std::string::npos) {
198 0 : return ".";
199 : }
200 0 : return filename.substr(0, slash);
201 : }
202 :
203 : /**
204 : * Finds the appropriate file path for the given filename
205 : */
206 0 : std::string include_file(std::string filename) {
207 : // Absolute path? Just try that
208 0 : if (filename[0] == '/') {
209 : // Realpath!
210 : char rp[PATH_MAX];
211 0 : if (saferealpath(filename.c_str(), rp) == NULL) {
212 0 : pwarning(0, "Cannot open include file %s\n", filename.c_str());
213 0 : return std::string();
214 : }
215 :
216 : // Stat this file
217 : struct stat finfo;
218 0 : if (stat(rp, &finfo) == 0) {
219 0 : return rp;
220 : }
221 : } else { // relative path, start searching
222 : // new search path with current dir global
223 0 : std::vector<std::string> sp = g_incl_searchpath;
224 0 : sp.insert(sp.begin(), g_curdir);
225 :
226 : // iterate through paths
227 0 : std::vector<std::string>::iterator it;
228 0 : for (it = sp.begin(); it != sp.end(); it++) {
229 0 : std::string sfilename = *(it) + "/" + filename;
230 :
231 : // Realpath!
232 : char rp[PATH_MAX];
233 0 : if (saferealpath(sfilename.c_str(), rp) == NULL) {
234 0 : continue;
235 : }
236 :
237 : // Stat this files
238 : struct stat finfo;
239 0 : if (stat(rp, &finfo) == 0) {
240 0 : return rp;
241 : }
242 0 : }
243 0 : }
244 :
245 : // Uh oh
246 0 : pwarning(0, "Could not find include file %s\n", filename.c_str());
247 0 : return std::string();
248 : }
249 :
250 : /**
251 : * Clears any previously stored doctext string.
252 : * Also prints a warning if we are discarding information.
253 : */
254 0 : void clear_doctext() {
255 0 : if (g_doctext != NULL) {
256 0 : pwarning(2, "Uncaptured doctext at on line %d.", g_doctext_lineno);
257 : }
258 0 : free(g_doctext);
259 0 : g_doctext = NULL;
260 0 : }
261 :
262 : /**
263 : * Cleans up text commonly found in doxygen-like comments
264 : *
265 : * Warning: if you mix tabs and spaces in a non-uniform way,
266 : * you will get what you deserve.
267 : */
268 0 : char* clean_up_doctext(char* doctext) {
269 : // Convert to C++ string, and remove Windows's carriage returns.
270 0 : std::string docstring = doctext;
271 0 : docstring.erase(
272 0 : remove(docstring.begin(), docstring.end(), '\r'),
273 0 : docstring.end());
274 :
275 : // Separate into lines.
276 0 : std::vector<std::string> lines;
277 0 : std::string::size_type pos = std::string::npos;
278 : std::string::size_type last;
279 : while (true) {
280 0 : last = (pos == std::string::npos) ? 0 : pos+1;
281 0 : pos = docstring.find('\n', last);
282 0 : if (pos == std::string::npos) {
283 : // First bit of cleaning. If the last line is only whitespace, drop it.
284 0 : std::string::size_type nonwhite = docstring.find_first_not_of(" \t", last);
285 0 : if (nonwhite != std::string::npos) {
286 0 : lines.push_back(docstring.substr(last));
287 : }
288 0 : break;
289 : }
290 0 : lines.push_back(docstring.substr(last, pos-last));
291 0 : }
292 :
293 : // A very profound docstring.
294 0 : if (lines.empty()) {
295 0 : return NULL;
296 : }
297 :
298 : // Clear leading whitespace from the first line.
299 0 : pos = lines.front().find_first_not_of(" \t");
300 0 : lines.front().erase(0, pos);
301 :
302 : // If every nonblank line after the first has the same number of spaces/tabs,
303 : // then a star, remove them.
304 0 : bool have_prefix = true;
305 0 : bool found_prefix = false;
306 0 : std::string::size_type prefix_len = 0;
307 0 : std::vector<std::string>::iterator l_iter;
308 0 : for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
309 0 : if (l_iter->empty()) {
310 0 : continue;
311 : }
312 :
313 0 : pos = l_iter->find_first_not_of(" \t");
314 0 : if (!found_prefix) {
315 0 : if (pos != std::string::npos) {
316 0 : if (l_iter->at(pos) == '*') {
317 0 : found_prefix = true;
318 0 : prefix_len = pos;
319 : } else {
320 0 : have_prefix = false;
321 0 : break;
322 : }
323 : } else {
324 : // Whitespace-only line. Truncate it.
325 0 : l_iter->clear();
326 : }
327 0 : } else if (l_iter->size() > pos
328 0 : && l_iter->at(pos) == '*'
329 0 : && pos == prefix_len) {
330 : // Business as usual.
331 0 : } else if (pos == std::string::npos) {
332 : // Whitespace-only line. Let's truncate it for them.
333 0 : l_iter->clear();
334 : } else {
335 : // The pattern has been broken.
336 0 : have_prefix = false;
337 0 : break;
338 : }
339 : }
340 :
341 : // If our prefix survived, delete it from every line.
342 0 : if (have_prefix) {
343 : // Get the star too.
344 0 : prefix_len++;
345 0 : for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
346 0 : l_iter->erase(0, prefix_len);
347 : }
348 : }
349 :
350 : // Now delete the minimum amount of leading whitespace from each line.
351 0 : prefix_len = std::string::npos;
352 0 : for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
353 0 : if (l_iter->empty()) {
354 0 : continue;
355 : }
356 0 : pos = l_iter->find_first_not_of(" \t");
357 0 : if (pos != std::string::npos
358 0 : && (prefix_len == std::string::npos || pos < prefix_len)) {
359 0 : prefix_len = pos;
360 : }
361 : }
362 :
363 : // If our prefix survived, delete it from every line.
364 0 : if (prefix_len != std::string::npos) {
365 0 : for (l_iter = lines.begin()+1; l_iter != lines.end(); ++l_iter) {
366 0 : l_iter->erase(0, prefix_len);
367 : }
368 : }
369 :
370 : // Remove trailing whitespace from every line.
371 0 : for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
372 0 : pos = l_iter->find_last_not_of(" \t");
373 0 : if (pos != std::string::npos && pos != l_iter->length()-1) {
374 0 : l_iter->erase(pos+1);
375 : }
376 : }
377 :
378 : // If the first line is empty, remove it.
379 : // Don't do this earlier because a lot of steps skip the first line.
380 0 : if (lines.front().empty()) {
381 0 : lines.erase(lines.begin());
382 : }
383 :
384 : // Now rejoin the lines and copy them back into doctext.
385 0 : docstring.clear();
386 0 : for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
387 0 : docstring += *l_iter;
388 0 : docstring += '\n';
389 : }
390 :
391 0 : assert(docstring.length() <= strlen(doctext));
392 0 : strcpy(doctext, docstring.c_str());
393 0 : return doctext;
394 0 : }
395 :
396 0 : void post_parse_cleanup() {
397 0 : yylex_destroy();
398 0 : }
399 :
400 : /**
401 : * Parses a program
402 : */
403 0 : void parse(t_rulelist *rulelist, std::string file) {
404 0 : yyin = fopen(file.c_str(), "r");
405 0 : if (yyin == 0) {
406 0 : failure("Could not open input file: \"%s\"", file.c_str());
407 : }
408 0 : pverbose("Parsing %s for types\n", file.c_str());
409 0 : yylineno = 1;
410 0 : g_parse_mode = PROGRAM;
411 0 : g_rulelist = rulelist;
412 :
413 0 : if (yyparse() != 0) {
414 0 : failure("Parser error.");
415 : }
416 0 : fclose(yyin);
417 :
418 0 : post_parse_cleanup();
419 0 : }
420 :
421 : /**
422 : * Parses a program from memory in place with no copy
423 : */
424 0 : void parse(t_rulelist *rulelist, char *base, size_t sz) {
425 0 : if (yy_scan_buffer(base, sz) == NULL) {
426 0 : failure("yy_scan_buffer...");
427 : }
428 :
429 0 : yylineno = 1;
430 0 : g_parse_mode = PROGRAM;
431 0 : g_rulelist = rulelist;
432 :
433 0 : if (yyparse() != 0) {
434 0 : failure("Parser error.");
435 : }
436 0 : post_parse_cleanup();
437 0 : }
438 :
439 : /**
440 : * Parses a program from memory with copy
441 : */
442 0 : void parse(t_rulelist *rulelist, const char *bytes, int len) {
443 0 : if (yy_scan_bytes(bytes, len) == NULL) {
444 0 : failure("yy_scan_buffer...");
445 : }
446 :
447 0 : yylineno = 1;
448 0 : g_parse_mode = PROGRAM;
449 0 : g_rulelist = rulelist;
450 :
451 0 : if (yyparse() != 0) {
452 0 : failure("Parser error.");
453 : }
454 0 : post_parse_cleanup();
455 0 : }
456 :
457 : /**
458 : * parse function to verify the rulefile format
459 : */
460 0 : int check_rulebuf(const char *bytes, int len) {
461 0 : t_rulelist *rulelist = new t_rulelist;
462 :
463 0 : if (yy_scan_bytes(bytes, len) == NULL) {
464 0 : failure("yy_scan_buffer...");
465 0 : return -1;
466 : }
467 :
468 0 : yylineno = 1;
469 0 : g_parse_mode = PROGRAM;
470 0 : g_rulelist = rulelist;
471 :
472 0 : if (yyparse() != 0) {
473 0 : failure("Parser error.");
474 0 : return -1;
475 : }
476 :
477 0 : free(rulelist);
478 0 : post_parse_cleanup();
479 :
480 0 : return 0;
481 : }
|