]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_py.c
lib: detangle DEFUN macros
[mirror_frr.git] / lib / command_py.c
CommitLineData
29ad6f68
DL
1/*
2 * clippy (CLI preparator in python) wrapper for FRR command_graph
3 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
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 Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/* note: this wrapper is intended to be used as build-time helper. while
21 * it should be generally correct and proper, there may be the occasional
22 * memory leak or SEGV for things that haven't been well-tested.
23 */
24
2618a52e
DL
25/* This file is "exempt" from having
26#include "config.h"
27 * as the first include statement because Python.h also does environment
28 * setup & these trample over each other.
29 */
30
5b4f4e62
DL
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
29ad6f68
DL
34#include <Python.h>
35#include "structmember.h"
36#include <string.h>
37#include <stdlib.h>
38
39#include "command_graph.h"
40#include "clippy.h"
41
42struct wrap_graph;
d62a17ae 43static PyObject *graph_to_pyobj(struct wrap_graph *graph,
44 struct graph_node *gn);
29ad6f68
DL
45
46/*
47 * nodes are wrapped as follows:
48 * - instances can only be acquired from a graph
49 * - the same node will return the same wrapper object (they're buffered
50 * through "idx")
51 * - a reference is held onto the graph
52 * - fields are copied for easy access with PyMemberDef
53 */
54struct wrap_graph_node {
55 PyObject_HEAD
56
d62a17ae 57 bool allowrepeat;
29ad6f68
DL
58 const char *type;
59
60 bool deprecated;
61 bool hidden;
62 const char *text;
63 const char *desc;
64 const char *varname;
65 long long min, max;
66
67 struct graph_node *node;
68 struct wrap_graph *wgraph;
69 size_t idx;
70};
71
72/*
73 * graphs are wrapped as follows:
74 * - they can only be created by parsing a definition string
75 * - there's a table here for the wrapped nodes (nodewrappers), indexed
76 * by "idx" (corresponds to node's position in graph's table of nodes)
77 * - graphs do NOT hold references to nodes (would be circular)
78 */
79struct wrap_graph {
80 PyObject_HEAD
81
d62a17ae 82 char *definition;
29ad6f68
DL
83 struct graph *graph;
84 struct wrap_graph_node **nodewrappers;
85};
86
87static PyObject *refuse_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
88{
d62a17ae 89 PyErr_SetString(PyExc_ValueError,
90 "cannot create instances of this type");
29ad6f68
DL
91 return NULL;
92}
93
d62a17ae 94#define member(name, type) \
95 { \
96 (char *)#name, type, offsetof(struct wrap_graph_node, name), \
97 READONLY, (char *)#name " (" #type ")" \
98 }
29ad6f68 99static PyMemberDef members_graph_node[] = {
d62a17ae 100 member(allowrepeat, T_BOOL), member(type, T_STRING),
101 member(deprecated, T_BOOL), member(hidden, T_BOOL),
102 member(text, T_STRING), member(desc, T_STRING),
103 member(min, T_LONGLONG), member(max, T_LONGLONG),
3e300703 104 member(varname, T_STRING), {},
29ad6f68
DL
105};
106#undef member
107
108/*
109 * node.next() -- returns list of all "next" nodes.
110 * this will include circles if the graph has them.
111 */
112static PyObject *graph_node_next(PyObject *self, PyObject *args)
113{
114 struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
115 PyObject *pylist;
116
117 if (wrap->node->data
d62a17ae 118 && ((struct cmd_token *)wrap->node->data)->type == END_TKN)
29ad6f68
DL
119 return PyList_New(0);
120 pylist = PyList_New(vector_active(wrap->node->to));
121 for (size_t i = 0; i < vector_active(wrap->node->to); i++) {
122 struct graph_node *gn = vector_slot(wrap->node->to, i);
123 PyList_SetItem(pylist, i, graph_to_pyobj(wrap->wgraph, gn));
124 }
125 return pylist;
126};
127
128/*
129 * node.join() -- return FORK's JOIN node or None
130 */
131static PyObject *graph_node_join(PyObject *self, PyObject *args)
132{
133 struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
134
135 if (!wrap->node->data
d62a17ae 136 || ((struct cmd_token *)wrap->node->data)->type == END_TKN)
29ad6f68
DL
137 Py_RETURN_NONE;
138
139 struct cmd_token *tok = wrap->node->data;
140 if (tok->type != FORK_TKN)
141 Py_RETURN_NONE;
142
143 return graph_to_pyobj(wrap->wgraph, tok->forkjoin);
144};
145
146static PyMethodDef methods_graph_node[] = {
147 {"next", graph_node_next, METH_NOARGS, "outbound graph edge list"},
148 {"join", graph_node_join, METH_NOARGS, "outbound join node"},
3e300703 149 {}};
29ad6f68
DL
150
151static void graph_node_wrap_free(void *arg)
152{
153 struct wrap_graph_node *wrap = arg;
154 wrap->wgraph->nodewrappers[wrap->idx] = NULL;
155 Py_DECREF(wrap->wgraph);
156}
157
158static PyTypeObject typeobj_graph_node = {
d62a17ae 159 PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.GraphNode",
160 .tp_basicsize = sizeof(struct wrap_graph_node),
161 .tp_flags = Py_TPFLAGS_DEFAULT,
162 .tp_doc = "struct graph_node *",
163 .tp_new = refuse_new,
164 .tp_free = graph_node_wrap_free,
165 .tp_members = members_graph_node,
166 .tp_methods = methods_graph_node,
29ad6f68
DL
167};
168
d62a17ae 169static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
170 struct graph_node *gn)
29ad6f68
DL
171{
172 struct wrap_graph_node *wrap;
173 size_t i;
174
175 for (i = 0; i < vector_active(wgraph->graph->nodes); i++)
176 if (vector_slot(wgraph->graph->nodes, i) == gn)
177 break;
178 if (i == vector_active(wgraph->graph->nodes)) {
179 PyErr_SetString(PyExc_ValueError, "cannot find node in graph");
180 return NULL;
181 }
182 if (wgraph->nodewrappers[i]) {
183 PyObject *obj = (PyObject *)wgraph->nodewrappers[i];
184 Py_INCREF(obj);
185 return obj;
186 }
187
d62a17ae 188 wrap = (struct wrap_graph_node *)typeobj_graph_node.tp_alloc(
189 &typeobj_graph_node, 0);
29ad6f68
DL
190 if (!wrap)
191 return NULL;
192 wgraph->nodewrappers[i] = wrap;
193 Py_INCREF(wgraph);
194
195 wrap->idx = i;
196 wrap->wgraph = wgraph;
197 wrap->node = gn;
198 wrap->type = "NULL";
199 wrap->allowrepeat = false;
200 if (gn->data) {
201 struct cmd_token *tok = gn->data;
202 switch (tok->type) {
3be280bb
DL
203#define item(x) \
204 case x: \
205 wrap->type = #x; \
206 break /* no semicolon */
207
208 item(WORD_TKN); // words
209 item(VARIABLE_TKN); // almost anything
210 item(RANGE_TKN); // integer range
211 item(IPV4_TKN); // IPV4 addresses
212 item(IPV4_PREFIX_TKN); // IPV4 network prefixes
213 item(IPV6_TKN); // IPV6 prefixes
214 item(IPV6_PREFIX_TKN); // IPV6 network prefixes
215 item(MAC_TKN); // MAC address
216 item(MAC_PREFIX_TKN); // MAC address with mask
217
218 /* plumbing types */
219 item(FORK_TKN);
220 item(JOIN_TKN);
221 item(START_TKN);
222 item(END_TKN);
223 item(NEG_ONLY_TKN);
224#undef item
225 default:
226 wrap->type = "???";
29ad6f68
DL
227 }
228
9eebf97e
DL
229 wrap->deprecated = !!(tok->attr & CMD_ATTR_DEPRECATED);
230 wrap->hidden = !!(tok->attr & CMD_ATTR_HIDDEN);
29ad6f68
DL
231 wrap->text = tok->text;
232 wrap->desc = tok->desc;
233 wrap->varname = tok->varname;
234 wrap->min = tok->min;
235 wrap->max = tok->max;
236 wrap->allowrepeat = tok->allowrepeat;
237 }
238
239 return (PyObject *)wrap;
240}
241
d62a17ae 242#define member(name, type) \
243 { \
244 (char *)#name, type, offsetof(struct wrap_graph, name), \
245 READONLY, (char *)#name " (" #type ")" \
246 }
29ad6f68
DL
247static PyMemberDef members_graph[] = {
248 member(definition, T_STRING),
3e300703 249 {},
29ad6f68
DL
250};
251#undef member
252
253/* graph.first() - root node */
254static PyObject *graph_first(PyObject *self, PyObject *args)
255{
256 struct wrap_graph *gwrap = (struct wrap_graph *)self;
257 struct graph_node *gn = vector_slot(gwrap->graph->nodes, 0);
258 return graph_to_pyobj(gwrap, gn);
259};
260
261static PyMethodDef methods_graph[] = {
262 {"first", graph_first, METH_NOARGS, "first graph node"},
3e300703 263 {}};
29ad6f68 264
d62a17ae 265static PyObject *graph_parse(PyTypeObject *type, PyObject *args,
266 PyObject *kwds);
29ad6f68
DL
267
268static void graph_wrap_free(void *arg)
269{
270 struct wrap_graph *wgraph = arg;
e871b669
DS
271
272 graph_delete_graph(wgraph->graph);
29ad6f68
DL
273 free(wgraph->nodewrappers);
274 free(wgraph->definition);
275}
276
277static PyTypeObject typeobj_graph = {
d62a17ae 278 PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.Graph",
279 .tp_basicsize = sizeof(struct wrap_graph),
280 .tp_flags = Py_TPFLAGS_DEFAULT,
281 .tp_doc = "struct graph *",
282 .tp_new = graph_parse,
283 .tp_free = graph_wrap_free,
284 .tp_members = members_graph,
285 .tp_methods = methods_graph,
29ad6f68
DL
286};
287
288/* top call / entrypoint for python code */
289static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds)
290{
291 const char *def, *doc = NULL;
292 struct wrap_graph *gwrap;
d62a17ae 293 static const char *kwnames[] = {"cmddef", "doc", NULL};
29ad6f68
DL
294
295 gwrap = (struct wrap_graph *)typeobj_graph.tp_alloc(&typeobj_graph, 0);
296 if (!gwrap)
297 return NULL;
298
d62a17ae 299 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", (char **)kwnames,
300 &def, &doc))
29ad6f68
DL
301 return NULL;
302
d62a17ae 303 struct graph *graph = graph_new();
304 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
305 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
29ad6f68 306
d62a17ae 307 struct cmd_element cmd = {.string = def, .doc = doc};
308 cmd_graph_parse(graph, &cmd);
309 cmd_graph_names(graph);
29ad6f68
DL
310
311 gwrap->graph = graph;
312 gwrap->definition = strdup(def);
313 gwrap->nodewrappers = calloc(vector_active(graph->nodes),
d62a17ae 314 sizeof(gwrap->nodewrappers[0]));
29ad6f68
DL
315 return (PyObject *)gwrap;
316}
317
318static PyMethodDef clippy_methods[] = {
319 {"parse", clippy_parse, METH_VARARGS, "Parse a C file"},
d62a17ae 320 {NULL, NULL, 0, NULL}};
29ad6f68
DL
321
322#if PY_MAJOR_VERSION >= 3
323static struct PyModuleDef pymoddef_clippy = {
324 PyModuleDef_HEAD_INIT,
325 "_clippy",
326 NULL, /* docstring */
327 -1,
328 clippy_methods,
329};
330#define modcreate() PyModule_Create(&pymoddef_clippy)
331#define initret(val) return val;
332#else
333#define modcreate() Py_InitModule("_clippy", clippy_methods)
d62a17ae 334#define initret(val) \
335 do { \
336 if (!val) \
337 Py_FatalError("initialization failure"); \
338 return; \
339 } while (0)
29ad6f68
DL
340#endif
341
f54dd468 342#pragma GCC diagnostic ignored "-Wstrict-aliasing"
29ad6f68
DL
343PyMODINIT_FUNC command_py_init(void)
344{
d62a17ae 345 PyObject *pymod;
29ad6f68
DL
346
347 if (PyType_Ready(&typeobj_graph_node) < 0)
348 initret(NULL);
349 if (PyType_Ready(&typeobj_graph) < 0)
350 initret(NULL);
351
352 pymod = modcreate();
353 if (!pymod)
354 initret(NULL);
355
9eebf97e
DL
356 if (PyModule_AddIntMacro(pymod, CMD_ATTR_YANG)
357 || PyModule_AddIntMacro(pymod, CMD_ATTR_HIDDEN)
358 || PyModule_AddIntMacro(pymod, CMD_ATTR_DEPRECATED))
359 initret(NULL);
360
29ad6f68
DL
361 Py_INCREF(&typeobj_graph_node);
362 PyModule_AddObject(pymod, "GraphNode", (PyObject *)&typeobj_graph_node);
363 Py_INCREF(&typeobj_graph);
364 PyModule_AddObject(pymod, "Graph", (PyObject *)&typeobj_graph);
5609b3af
DL
365 if (!elf_py_init(pymod))
366 initret(NULL);
29ad6f68
DL
367 initret(pymod);
368}