mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 14:50:34 +00:00 
			
		
		
		
	 3779776a39
			
		
	
	
		3779776a39
		
	
	
	
	
		
			
			clippy can't process #ifdef or similar bits inside of an argument list (e.g. within the braces of a DEFUN or DEFPY statement.) Improve error reporting to catch these cases instead of generating broken C code. Fixes: #3840 Signed-off-by: David Lamparter <equinox@diac24.net>
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /*
 | |
|  * clippy (CLI preparator in python) C pseudo-lexer
 | |
|  * Copyright (C) 2016-2017  David Lamparter for NetDEF, Inc.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public License as published by the Free
 | |
|  * Software Foundation; either version 2 of the License, or (at your option)
 | |
|  * any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| /* This is just enough of a lexer to make rough sense of a C source file.
 | |
|  * It handles C preprocessor directives, strings, and looks for FRR-specific
 | |
|  * idioms (aka DEFUN).
 | |
|  *
 | |
|  * There is some preliminary support for documentation comments for DEFUNs.
 | |
|  * They would look like this (note the ~):  (replace \ by /)
 | |
|  *
 | |
|  * \*~  documentation for foobar_cmd
 | |
|  *  *   parameter does xyz
 | |
|  *  *\
 | |
|  * DEFUN(foobar_cmd, ...)
 | |
|  *
 | |
|  * This is intended for user documentation / command reference.  Don't put
 | |
|  * code documentation in it.
 | |
|  */
 | |
| 
 | |
| %top{
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| }
 | |
| %{
 | |
| /* ignore harmless bugs in old versions of flex */
 | |
| #pragma GCC diagnostic ignored "-Wsign-compare"
 | |
| #pragma GCC diagnostic ignored "-Wunused-value"
 | |
| 
 | |
| #include "config.h"
 | |
| #include <Python.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "command_graph.h"
 | |
| #include "clippy.h"
 | |
| 
 | |
| #define ID		258
 | |
| #define PREPROC		259
 | |
| #define OPERATOR	260
 | |
| #define STRING		261
 | |
| #define COMMENT		262
 | |
| #define SPECIAL		263
 | |
| 
 | |
| #define DEFUNNY		270
 | |
| #define INSTALL		271
 | |
| #define AUXILIARY	272
 | |
| 
 | |
| int comment_link;
 | |
| char string_end;
 | |
| 
 | |
| char *value;
 | |
| 
 | |
| static void extendbuf(char **what, const char *arg)
 | |
| {
 | |
| 	if (!*what)
 | |
| 		*what = strdup(arg);
 | |
| 	else {
 | |
| 		size_t vall = strlen(*what), argl = strlen(arg);
 | |
| 		*what = realloc(*what, vall + argl + 1);
 | |
| 		memcpy(*what + vall, arg, argl);
 | |
| 		(*what)[vall + argl] = '\0';
 | |
| 	}
 | |
| }
 | |
| #define extend(x) extendbuf(&value, x)
 | |
| 
 | |
| %}
 | |
| 
 | |
| ID		[A-Za-z0-9_]+
 | |
| OPERATOR	[!%&/\[\]{}=?:^|\*.;><~'\\+-]
 | |
| SPECIAL		[(),]
 | |
| 
 | |
| %pointer
 | |
| %option yylineno
 | |
| %option noyywrap
 | |
| %option noinput
 | |
| %option nounput
 | |
| %option outfile="lib/defun_lex.c"
 | |
| %option prefix="def_yy"
 | |
| %option 8bit
 | |
| 
 | |
| %s linestart
 | |
| %x comment
 | |
| %x linecomment
 | |
| %x preproc
 | |
| %x rstring
 | |
| %%
 | |
| 				BEGIN(linestart);
 | |
| 
 | |
| \n				BEGIN(linestart);
 | |
| 
 | |
| <INITIAL,linestart,preproc>"/*"	comment_link = YY_START; extend(yytext); BEGIN(comment);
 | |
| <comment>[^*\n]*		extend(yytext);
 | |
| <comment>"*"+[^*/\n]*		extend(yytext);
 | |
| <comment>\n			extend(yytext);
 | |
| <comment>"*"+"/"		extend(yytext); BEGIN(comment_link); return COMMENT;
 | |
| 
 | |
| <INITIAL,linestart,preproc>"//"	comment_link = YY_START; extend(yytext); BEGIN(linecomment);
 | |
| <linecomment>[^\n]*		extend(yytext);
 | |
| <linecomment>\n			BEGIN((comment_link == INITIAL) ? linestart : comment_link); return COMMENT;
 | |
| 
 | |
| <linestart>#			BEGIN(preproc);
 | |
| <preproc>\n			BEGIN(INITIAL); return PREPROC;
 | |
| <preproc>[^\n\\]+		extend(yytext);
 | |
| <preproc>\\\n			extend(yytext);
 | |
| <preproc>\\+[^\n]		extend(yytext);
 | |
| 
 | |
| [\"\']				string_end = yytext[0]; extend(yytext); BEGIN(rstring);
 | |
| <rstring>[\"\']			{
 | |
| 					extend(yytext);
 | |
| 					if (yytext[0] == string_end) {
 | |
| 						BEGIN(INITIAL);
 | |
| 						return STRING;
 | |
| 					}
 | |
| 				}
 | |
| <rstring>\\\n			/* ignore */
 | |
| <rstring>\\.			extend(yytext);
 | |
| <rstring>[^\\\"\']+		extend(yytext);
 | |
| 
 | |
| "DEFUN"				value = strdup(yytext); return DEFUNNY;
 | |
| "DEFUN_NOSH"			value = strdup(yytext); return DEFUNNY;
 | |
| "DEFUN_HIDDEN"			value = strdup(yytext); return DEFUNNY;
 | |
| "DEFPY"				value = strdup(yytext); return DEFUNNY;
 | |
| "DEFPY_NOSH"			value = strdup(yytext); return DEFUNNY;
 | |
| "DEFPY_ATTR"			value = strdup(yytext); return DEFUNNY;
 | |
| "DEFPY_HIDDEN"			value = strdup(yytext); return DEFUNNY;
 | |
| "ALIAS"				value = strdup(yytext); return DEFUNNY;
 | |
| "ALIAS_HIDDEN"			value = strdup(yytext); return DEFUNNY;
 | |
| "install_element"		value = strdup(yytext); return INSTALL;
 | |
| "VTYSH_TARGETS"			value = strdup(yytext); return AUXILIARY;
 | |
| "VTYSH_NODESWITCH"		value = strdup(yytext); return AUXILIARY;
 | |
| 
 | |
| [ \t\n]+			/* ignore */
 | |
| \\				/* ignore */
 | |
| {ID}				BEGIN(INITIAL); value = strdup(yytext); return ID;
 | |
| {OPERATOR}			BEGIN(INITIAL); value = strdup(yytext); return OPERATOR;
 | |
| {SPECIAL}			BEGIN(INITIAL); value = strdup(yytext); return SPECIAL;
 | |
| .				/* printf("-- '%s' in init\n", yytext); */ BEGIN(INITIAL); return yytext[0];
 | |
| 
 | |
| %%
 | |
| 
 | |
| static int yylex_clr(char **retbuf)
 | |
| {
 | |
| 	int rv = def_yylex();
 | |
| 	*retbuf = value;
 | |
| 	value = NULL;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| static PyObject *get_args(const char *filename, int lineno)
 | |
| {
 | |
| 	PyObject *pyObj = PyList_New(0);
 | |
| 	PyObject *pyArg = NULL;
 | |
| 
 | |
| 	char *tval;
 | |
| 	int depth = 1;
 | |
| 	int token;
 | |
| 
 | |
| 	while ((token = yylex_clr(&tval)) != YY_NULL) {
 | |
| 		if (token == SPECIAL && tval[0] == '(') {
 | |
| 			free(tval);
 | |
| 			break;
 | |
| 		}
 | |
| 		if (token == COMMENT) {
 | |
| 			free(tval);
 | |
| 			continue;
 | |
| 		}
 | |
| 		fprintf(stderr, "invalid input!\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	while ((token = yylex_clr(&tval)) != YY_NULL) {
 | |
| 		if (token == COMMENT) {
 | |
| 			free(tval);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (token == PREPROC) {
 | |
| 			free(tval);
 | |
| 			Py_DECREF(pyObj);
 | |
| 			return PyErr_Format(PyExc_ValueError,
 | |
| 					"%s:%d: cannot process CPP directive within argument list",
 | |
| 					filename, lineno);
 | |
| 		}
 | |
| 		if (token == SPECIAL) {
 | |
| 			if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) {
 | |
| 				if (pyArg)
 | |
| 					PyList_Append(pyObj, pyArg);
 | |
| 				pyArg = NULL;
 | |
| 				if (tval[0] == ')') {
 | |
| 					free(tval);
 | |
| 					break;
 | |
| 				}
 | |
| 				free(tval);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (tval[0] == '(')
 | |
| 				depth++;
 | |
| 			if (tval[0] == ')')
 | |
| 				depth--;
 | |
| 		}
 | |
| 		if (!pyArg)
 | |
| 			pyArg = PyList_New(0);
 | |
| 		PyList_Append(pyArg, PyUnicode_FromString(tval));
 | |
| 		free(tval);
 | |
| 	}
 | |
| 	return pyObj;
 | |
| }
 | |
| 
 | |
| /* _clippy.parse() -- read a C file, returning a list of interesting bits.
 | |
|  * note this ditches most of the actual C code. */
 | |
| PyObject *clippy_parse(PyObject *self, PyObject *args)
 | |
| {
 | |
| 	const char *filename;
 | |
| 	if (!PyArg_ParseTuple(args, "s", &filename))
 | |
| 		return NULL;
 | |
| 	
 | |
| 	FILE *fd = fopen(filename, "r");
 | |
| 	if (!fd)
 | |
| 		return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
 | |
| 
 | |
| 	char *tval;
 | |
| 	int token;
 | |
| 	yyin = fd;
 | |
| 	value = NULL;
 | |
| 
 | |
| 	PyObject *pyCont = PyDict_New();
 | |
| 	PyObject *pyObj = PyList_New(0);
 | |
| 	PyDict_SetItemString(pyCont, "filename", PyUnicode_FromString(filename));
 | |
| 	PyDict_SetItemString(pyCont, "data", pyObj);
 | |
| 
 | |
| 	while ((token = yylex_clr(&tval)) != YY_NULL) {
 | |
|                 int lineno = yylineno;
 | |
| 		PyObject *pyItem = NULL, *pyArgs;
 | |
| 		switch (token) {
 | |
| 		case DEFUNNY:
 | |
| 		case INSTALL:
 | |
| 		case AUXILIARY:
 | |
| 			pyArgs = get_args(filename, lineno);
 | |
| 			if (!pyArgs) {
 | |
| 				free(tval);
 | |
| 				Py_DECREF(pyCont);
 | |
| 				return NULL;
 | |
| 			}
 | |
| 			pyItem = PyDict_New();
 | |
| 			PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval));
 | |
| 			PyDict_SetItemString(pyItem, "args", pyArgs);
 | |
| 			break;
 | |
| 		case COMMENT:
 | |
|                         if (strncmp(tval, "//~", 3) && strncmp(tval, "/*~", 3))
 | |
|                                 break;
 | |
| 			pyItem = PyDict_New();
 | |
| 			PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("COMMENT"));
 | |
| 			PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
 | |
| 			break;
 | |
| 		case PREPROC:
 | |
| 			pyItem = PyDict_New();
 | |
| 			PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC"));
 | |
| 			PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
 | |
| 			lineno--;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (pyItem) {
 | |
| 			PyDict_SetItemString(pyItem, "lineno", PyLong_FromLong(lineno));
 | |
| 			PyList_Append(pyObj, pyItem);
 | |
| 		}
 | |
| 		free(tval);
 | |
| 	}
 | |
| 	def_yylex_destroy();
 | |
| 	fclose(fd);
 | |
| 	return pyCont;
 | |
| }
 |