]> git.proxmox.com Git - mirror_frr.git/blob - lib/defun_lex.l
lib/clippy: don't SEGV on invalid tokens in DEFPY
[mirror_frr.git] / lib / defun_lex.l
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * clippy (CLI preparator in python) C pseudo-lexer
4 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
5 */
6
7 /* This is just enough of a lexer to make rough sense of a C source file.
8 * It handles C preprocessor directives, strings, and looks for FRR-specific
9 * idioms (aka DEFUN).
10 *
11 * There is some preliminary support for documentation comments for DEFUNs.
12 * They would look like this (note the ~): (replace \ by /)
13 *
14 * \*~ documentation for foobar_cmd
15 * * parameter does xyz
16 * *\
17 * DEFUN(foobar_cmd, ...)
18 *
19 * This is intended for user documentation / command reference. Don't put
20 * code documentation in it.
21 */
22
23 %top{
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 }
28 %{
29 /* ignore harmless bugs in old versions of flex */
30 #pragma GCC diagnostic ignored "-Wsign-compare"
31 #pragma GCC diagnostic ignored "-Wunused-value"
32
33 #include "config.h"
34 #include <Python.h>
35 #include <string.h>
36 #include <stdlib.h>
37
38 #include "command_graph.h"
39 #include "clippy.h"
40
41 #define ID 258
42 #define PREPROC 259
43 #define OPERATOR 260
44 #define STRING 261
45 #define COMMENT 262
46 #define SPECIAL 263
47
48 #define DEFUNNY 270
49 #define INSTALL 271
50 #define AUXILIARY 272
51
52 int comment_link;
53 char string_end;
54
55 char *value;
56
57 static void extendbuf(char **what, const char *arg)
58 {
59 if (!*what)
60 *what = strdup(arg);
61 else {
62 size_t vall = strlen(*what), argl = strlen(arg);
63 *what = realloc(*what, vall + argl + 1);
64 memcpy(*what + vall, arg, argl);
65 (*what)[vall + argl] = '\0';
66 }
67 }
68 #define extend(x) extendbuf(&value, x)
69
70 #ifndef __clang_analyzer__
71
72 %}
73
74 ID [A-Za-z0-9_]+
75 OPERATOR [!%&/\[\]{}=?:^|\*.;><~'\\+-]
76 SPECIAL [(),]
77
78 %pointer
79 %option yylineno
80 %option noyywrap
81 %option noinput
82 %option nounput
83 %option outfile="lib/defun_lex.c"
84 %option prefix="def_yy"
85 %option 8bit
86
87 %s linestart
88 %x comment
89 %x linecomment
90 %x preproc
91 %x rstring
92 %%
93 BEGIN(linestart);
94
95 \n BEGIN(linestart);
96
97 <INITIAL,linestart,preproc>"/*" comment_link = YY_START; extend(yytext); BEGIN(comment);
98 <comment>[^*\n]* extend(yytext);
99 <comment>"*"+[^*/\n]* extend(yytext);
100 <comment>\n extend(yytext);
101 <comment>"*"+"/" extend(yytext); BEGIN(comment_link); return COMMENT;
102
103 <INITIAL,linestart,preproc>"//" comment_link = YY_START; extend(yytext); BEGIN(linecomment);
104 <linecomment>[^\n]* extend(yytext);
105 <linecomment>\n BEGIN((comment_link == INITIAL) ? linestart : comment_link); return COMMENT;
106
107 <linestart># BEGIN(preproc);
108 <preproc>\n BEGIN(INITIAL); return PREPROC;
109 <preproc>[^\n\\]+ extend(yytext);
110 <preproc>\\\n extend(yytext);
111 <preproc>\\+[^\n] extend(yytext);
112
113 [\"\'] string_end = yytext[0]; extend(yytext); BEGIN(rstring);
114 <rstring>[\"\'] {
115 extend(yytext);
116 if (yytext[0] == string_end) {
117 BEGIN(INITIAL);
118 return STRING;
119 }
120 }
121 <rstring>\\\n /* ignore */
122 <rstring>\\. extend(yytext);
123 <rstring>[^\\\"\']+ extend(yytext);
124
125 "DEFUN" value = strdup(yytext); return DEFUNNY;
126 "DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
127 "DEFUN_HIDDEN" value = strdup(yytext); return DEFUNNY;
128 "DEFPY" value = strdup(yytext); return DEFUNNY;
129 "DEFPY_NOSH" value = strdup(yytext); return DEFUNNY;
130 "DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
131 "DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
132 "DEFPY_YANG" value = strdup(yytext); return DEFUNNY;
133 "DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY;
134 "ALIAS" value = strdup(yytext); return DEFUNNY;
135 "ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
136 "install_element" value = strdup(yytext); return INSTALL;
137 "VTYSH_TARGETS" value = strdup(yytext); return AUXILIARY;
138 "VTYSH_NODESWITCH" value = strdup(yytext); return AUXILIARY;
139
140 [ \t\n]+ /* ignore */
141 \\ /* ignore */
142 {ID} BEGIN(INITIAL); value = strdup(yytext); return ID;
143 {OPERATOR} BEGIN(INITIAL); value = strdup(yytext); return OPERATOR;
144 {SPECIAL} BEGIN(INITIAL); value = strdup(yytext); return SPECIAL;
145 . /* printf("-- '%s' in init\n", yytext); */ BEGIN(INITIAL); return yytext[0];
146
147 %%
148
149 #endif /* __clang_analyzer__ */
150
151 static int yylex_clr(char **retbuf)
152 {
153 int rv = def_yylex();
154 *retbuf = value;
155 value = NULL;
156 return rv;
157 }
158
159 static PyObject *get_args(const char *filename, int lineno)
160 {
161 PyObject *pyObj = PyList_New(0);
162 PyObject *pyArg = NULL;
163
164 char *tval;
165 int depth = 1;
166 int token;
167
168 while ((token = yylex_clr(&tval)) != YY_NULL) {
169 if (token == SPECIAL && tval[0] == '(') {
170 free(tval);
171 break;
172 }
173 if (token == COMMENT) {
174 free(tval);
175 continue;
176 }
177 fprintf(stderr, "invalid input!\n");
178 exit(1);
179 }
180
181 while ((token = yylex_clr(&tval)) != YY_NULL) {
182 if (token == COMMENT) {
183 free(tval);
184 continue;
185 }
186 if (token == PREPROC) {
187 free(tval);
188 Py_DECREF(pyObj);
189 return PyErr_Format(PyExc_ValueError,
190 "%s:%d: cannot process CPP directive within argument list",
191 filename, lineno);
192 }
193 if (token == SPECIAL) {
194 if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) {
195 if (pyArg)
196 PyList_Append(pyObj, pyArg);
197 pyArg = NULL;
198 if (tval[0] == ')') {
199 free(tval);
200 break;
201 }
202 free(tval);
203 continue;
204 }
205 if (tval[0] == '(')
206 depth++;
207 if (tval[0] == ')')
208 depth--;
209 }
210 if (!tval)
211 return PyErr_Format(PyExc_ValueError,
212 "%s:%d: invalid token in DEFPY parameters",
213 filename, lineno);
214 if (!pyArg)
215 pyArg = PyList_New(0);
216 PyList_Append(pyArg, PyUnicode_FromString(tval));
217 free(tval);
218 }
219 return pyObj;
220 }
221
222 /* _clippy.parse() -- read a C file, returning a list of interesting bits.
223 * note this ditches most of the actual C code. */
224 PyObject *clippy_parse(PyObject *self, PyObject *args)
225 {
226 const char *filename;
227 if (!PyArg_ParseTuple(args, "s", &filename))
228 return NULL;
229
230 FILE *fd = fopen(filename, "r");
231 if (!fd)
232 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
233
234 char *tval;
235 int token;
236 yyin = fd;
237 value = NULL;
238
239 PyObject *pyCont = PyDict_New();
240 PyObject *pyObj = PyList_New(0);
241 PyDict_SetItemString(pyCont, "filename", PyUnicode_FromString(filename));
242 PyDict_SetItemString(pyCont, "data", pyObj);
243
244 while ((token = yylex_clr(&tval)) != YY_NULL) {
245 int lineno = yylineno;
246 PyObject *pyItem = NULL, *pyArgs;
247 switch (token) {
248 case DEFUNNY:
249 case INSTALL:
250 case AUXILIARY:
251 pyArgs = get_args(filename, lineno);
252 if (!pyArgs) {
253 free(tval);
254 Py_DECREF(pyCont);
255 return NULL;
256 }
257 pyItem = PyDict_New();
258 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval));
259 PyDict_SetItemString(pyItem, "args", pyArgs);
260 break;
261 case COMMENT:
262 if (strncmp(tval, "//~", 3) && strncmp(tval, "/*~", 3))
263 break;
264 pyItem = PyDict_New();
265 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("COMMENT"));
266 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
267 break;
268 case PREPROC:
269 pyItem = PyDict_New();
270 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC"));
271 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
272 lineno--;
273 break;
274 }
275 if (pyItem) {
276 PyDict_SetItemString(pyItem, "lineno", PyLong_FromLong(lineno));
277 PyList_Append(pyObj, pyItem);
278 }
279 free(tval);
280 }
281 def_yylex_destroy();
282 fclose(fd);
283 return pyCont;
284 }