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