]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_py.c
*: reindent
[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
25#include <Python.h>
26#include "structmember.h"
27#include <string.h>
28#include <stdlib.h>
29
30#include "command_graph.h"
31#include "clippy.h"
32
33struct wrap_graph;
d62a17ae 34static PyObject *graph_to_pyobj(struct wrap_graph *graph,
35 struct graph_node *gn);
29ad6f68
DL
36
37/*
38 * nodes are wrapped as follows:
39 * - instances can only be acquired from a graph
40 * - the same node will return the same wrapper object (they're buffered
41 * through "idx")
42 * - a reference is held onto the graph
43 * - fields are copied for easy access with PyMemberDef
44 */
45struct wrap_graph_node {
46 PyObject_HEAD
47
d62a17ae 48 bool allowrepeat;
29ad6f68
DL
49 const char *type;
50
51 bool deprecated;
52 bool hidden;
53 const char *text;
54 const char *desc;
55 const char *varname;
56 long long min, max;
57
58 struct graph_node *node;
59 struct wrap_graph *wgraph;
60 size_t idx;
61};
62
63/*
64 * graphs are wrapped as follows:
65 * - they can only be created by parsing a definition string
66 * - there's a table here for the wrapped nodes (nodewrappers), indexed
67 * by "idx" (corresponds to node's position in graph's table of nodes)
68 * - graphs do NOT hold references to nodes (would be circular)
69 */
70struct wrap_graph {
71 PyObject_HEAD
72
d62a17ae 73 char *definition;
29ad6f68
DL
74 struct graph *graph;
75 struct wrap_graph_node **nodewrappers;
76};
77
78static PyObject *refuse_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
79{
d62a17ae 80 PyErr_SetString(PyExc_ValueError,
81 "cannot create instances of this type");
29ad6f68
DL
82 return NULL;
83}
84
d62a17ae 85#define member(name, type) \
86 { \
87 (char *)#name, type, offsetof(struct wrap_graph_node, name), \
88 READONLY, (char *)#name " (" #type ")" \
89 }
29ad6f68 90static PyMemberDef members_graph_node[] = {
d62a17ae 91 member(allowrepeat, T_BOOL), member(type, T_STRING),
92 member(deprecated, T_BOOL), member(hidden, T_BOOL),
93 member(text, T_STRING), member(desc, T_STRING),
94 member(min, T_LONGLONG), member(max, T_LONGLONG),
95 member(varname, T_STRING), {},
29ad6f68
DL
96};
97#undef member
98
99/*
100 * node.next() -- returns list of all "next" nodes.
101 * this will include circles if the graph has them.
102 */
103static PyObject *graph_node_next(PyObject *self, PyObject *args)
104{
105 struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
106 PyObject *pylist;
107
108 if (wrap->node->data
d62a17ae 109 && ((struct cmd_token *)wrap->node->data)->type == END_TKN)
29ad6f68
DL
110 return PyList_New(0);
111 pylist = PyList_New(vector_active(wrap->node->to));
112 for (size_t i = 0; i < vector_active(wrap->node->to); i++) {
113 struct graph_node *gn = vector_slot(wrap->node->to, i);
114 PyList_SetItem(pylist, i, graph_to_pyobj(wrap->wgraph, gn));
115 }
116 return pylist;
117};
118
119/*
120 * node.join() -- return FORK's JOIN node or None
121 */
122static PyObject *graph_node_join(PyObject *self, PyObject *args)
123{
124 struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
125
126 if (!wrap->node->data
d62a17ae 127 || ((struct cmd_token *)wrap->node->data)->type == END_TKN)
29ad6f68
DL
128 Py_RETURN_NONE;
129
130 struct cmd_token *tok = wrap->node->data;
131 if (tok->type != FORK_TKN)
132 Py_RETURN_NONE;
133
134 return graph_to_pyobj(wrap->wgraph, tok->forkjoin);
135};
136
137static PyMethodDef methods_graph_node[] = {
138 {"next", graph_node_next, METH_NOARGS, "outbound graph edge list"},
139 {"join", graph_node_join, METH_NOARGS, "outbound join node"},
d62a17ae 140 {}};
29ad6f68
DL
141
142static void graph_node_wrap_free(void *arg)
143{
144 struct wrap_graph_node *wrap = arg;
145 wrap->wgraph->nodewrappers[wrap->idx] = NULL;
146 Py_DECREF(wrap->wgraph);
147}
148
149static PyTypeObject typeobj_graph_node = {
d62a17ae 150 PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.GraphNode",
151 .tp_basicsize = sizeof(struct wrap_graph_node),
152 .tp_flags = Py_TPFLAGS_DEFAULT,
153 .tp_doc = "struct graph_node *",
154 .tp_new = refuse_new,
155 .tp_free = graph_node_wrap_free,
156 .tp_members = members_graph_node,
157 .tp_methods = methods_graph_node,
29ad6f68
DL
158};
159
d62a17ae 160static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
161 struct graph_node *gn)
29ad6f68
DL
162{
163 struct wrap_graph_node *wrap;
164 size_t i;
165
166 for (i = 0; i < vector_active(wgraph->graph->nodes); i++)
167 if (vector_slot(wgraph->graph->nodes, i) == gn)
168 break;
169 if (i == vector_active(wgraph->graph->nodes)) {
170 PyErr_SetString(PyExc_ValueError, "cannot find node in graph");
171 return NULL;
172 }
173 if (wgraph->nodewrappers[i]) {
174 PyObject *obj = (PyObject *)wgraph->nodewrappers[i];
175 Py_INCREF(obj);
176 return obj;
177 }
178
d62a17ae 179 wrap = (struct wrap_graph_node *)typeobj_graph_node.tp_alloc(
180 &typeobj_graph_node, 0);
29ad6f68
DL
181 if (!wrap)
182 return NULL;
183 wgraph->nodewrappers[i] = wrap;
184 Py_INCREF(wgraph);
185
186 wrap->idx = i;
187 wrap->wgraph = wgraph;
188 wrap->node = gn;
189 wrap->type = "NULL";
190 wrap->allowrepeat = false;
191 if (gn->data) {
192 struct cmd_token *tok = gn->data;
193 switch (tok->type) {
194#define item(x) case x: wrap->type = #x; break;
d62a17ae 195 item(WORD_TKN) // words
196 item(VARIABLE_TKN) // almost anything
197 item(RANGE_TKN) // integer range
198 item(IPV4_TKN) // IPV4 addresses
199 item(IPV4_PREFIX_TKN) // IPV4 network prefixes
200 item(IPV6_TKN) // IPV6 prefixes
201 item(IPV6_PREFIX_TKN) // IPV6 network prefixes
202
203 /* plumbing types */
204 item(FORK_TKN) item(JOIN_TKN) item(START_TKN)
205 item(END_TKN) default
206 : wrap->type = "???";
29ad6f68
DL
207 }
208
209 wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED);
210 wrap->hidden = (tok->attr == CMD_ATTR_HIDDEN);
211 wrap->text = tok->text;
212 wrap->desc = tok->desc;
213 wrap->varname = tok->varname;
214 wrap->min = tok->min;
215 wrap->max = tok->max;
216 wrap->allowrepeat = tok->allowrepeat;
217 }
218
219 return (PyObject *)wrap;
220}
221
d62a17ae 222#define member(name, type) \
223 { \
224 (char *)#name, type, offsetof(struct wrap_graph, name), \
225 READONLY, (char *)#name " (" #type ")" \
226 }
29ad6f68
DL
227static PyMemberDef members_graph[] = {
228 member(definition, T_STRING),
229 {},
230};
231#undef member
232
233/* graph.first() - root node */
234static PyObject *graph_first(PyObject *self, PyObject *args)
235{
236 struct wrap_graph *gwrap = (struct wrap_graph *)self;
237 struct graph_node *gn = vector_slot(gwrap->graph->nodes, 0);
238 return graph_to_pyobj(gwrap, gn);
239};
240
241static PyMethodDef methods_graph[] = {
242 {"first", graph_first, METH_NOARGS, "first graph node"},
d62a17ae 243 {}};
29ad6f68 244
d62a17ae 245static PyObject *graph_parse(PyTypeObject *type, PyObject *args,
246 PyObject *kwds);
29ad6f68
DL
247
248static void graph_wrap_free(void *arg)
249{
250 struct wrap_graph *wgraph = arg;
251 free(wgraph->nodewrappers);
252 free(wgraph->definition);
253}
254
255static PyTypeObject typeobj_graph = {
d62a17ae 256 PyVarObject_HEAD_INIT(NULL, 0).tp_name = "_clippy.Graph",
257 .tp_basicsize = sizeof(struct wrap_graph),
258 .tp_flags = Py_TPFLAGS_DEFAULT,
259 .tp_doc = "struct graph *",
260 .tp_new = graph_parse,
261 .tp_free = graph_wrap_free,
262 .tp_members = members_graph,
263 .tp_methods = methods_graph,
29ad6f68
DL
264};
265
266/* top call / entrypoint for python code */
267static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds)
268{
269 const char *def, *doc = NULL;
270 struct wrap_graph *gwrap;
d62a17ae 271 static const char *kwnames[] = {"cmddef", "doc", NULL};
29ad6f68
DL
272
273 gwrap = (struct wrap_graph *)typeobj_graph.tp_alloc(&typeobj_graph, 0);
274 if (!gwrap)
275 return NULL;
276
d62a17ae 277 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", (char **)kwnames,
278 &def, &doc))
29ad6f68
DL
279 return NULL;
280
d62a17ae 281 struct graph *graph = graph_new();
282 struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
283 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
29ad6f68 284
d62a17ae 285 struct cmd_element cmd = {.string = def, .doc = doc};
286 cmd_graph_parse(graph, &cmd);
287 cmd_graph_names(graph);
29ad6f68
DL
288
289 gwrap->graph = graph;
290 gwrap->definition = strdup(def);
291 gwrap->nodewrappers = calloc(vector_active(graph->nodes),
d62a17ae 292 sizeof(gwrap->nodewrappers[0]));
29ad6f68
DL
293 return (PyObject *)gwrap;
294}
295
296static PyMethodDef clippy_methods[] = {
297 {"parse", clippy_parse, METH_VARARGS, "Parse a C file"},
d62a17ae 298 {NULL, NULL, 0, NULL}};
29ad6f68
DL
299
300#if PY_MAJOR_VERSION >= 3
301static struct PyModuleDef pymoddef_clippy = {
302 PyModuleDef_HEAD_INIT,
303 "_clippy",
304 NULL, /* docstring */
305 -1,
306 clippy_methods,
307};
308#define modcreate() PyModule_Create(&pymoddef_clippy)
309#define initret(val) return val;
310#else
311#define modcreate() Py_InitModule("_clippy", clippy_methods)
d62a17ae 312#define initret(val) \
313 do { \
314 if (!val) \
315 Py_FatalError("initialization failure"); \
316 return; \
317 } while (0)
29ad6f68
DL
318#endif
319
320PyMODINIT_FUNC command_py_init(void)
321{
d62a17ae 322 PyObject *pymod;
29ad6f68
DL
323
324 if (PyType_Ready(&typeobj_graph_node) < 0)
325 initret(NULL);
326 if (PyType_Ready(&typeobj_graph) < 0)
327 initret(NULL);
328
329 pymod = modcreate();
330 if (!pymod)
331 initret(NULL);
332
333 Py_INCREF(&typeobj_graph_node);
334 PyModule_AddObject(pymod, "GraphNode", (PyObject *)&typeobj_graph_node);
335 Py_INCREF(&typeobj_graph);
336 PyModule_AddObject(pymod, "Graph", (PyObject *)&typeobj_graph);
337 initret(pymod);
338}