2 * clippy (CLI preparator in python) wrapper for FRR command_graph
3 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
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)
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
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
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.
26 #include "structmember.h"
30 #include "command_graph.h"
34 static PyObject
*graph_to_pyobj(struct wrap_graph
*graph
, struct graph_node
*gn
);
37 * nodes are wrapped as follows:
38 * - instances can only be acquired from a graph
39 * - the same node will return the same wrapper object (they're buffered
41 * - a reference is held onto the graph
42 * - fields are copied for easy access with PyMemberDef
44 struct wrap_graph_node
{
57 struct graph_node
*node
;
58 struct wrap_graph
*wgraph
;
63 * graphs are wrapped as follows:
64 * - they can only be created by parsing a definition string
65 * - there's a table here for the wrapped nodes (nodewrappers), indexed
66 * by "idx" (corresponds to node's position in graph's table of nodes)
67 * - graphs do NOT hold references to nodes (would be circular)
74 struct wrap_graph_node
**nodewrappers
;
77 static PyObject
*refuse_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
79 PyErr_SetString(PyExc_ValueError
, "cannot create instances of this type");
83 #define member(name, type) {(char *)#name, type, offsetof(struct wrap_graph_node, name), READONLY, \
84 (char *)#name " (" #type ")"}
85 static PyMemberDef members_graph_node
[] = {
86 member(allowrepeat
, T_BOOL
),
87 member(type
, T_STRING
),
88 member(deprecated
, T_BOOL
),
89 member(hidden
, T_BOOL
),
90 member(text
, T_STRING
),
91 member(desc
, T_STRING
),
92 member(min
, T_LONGLONG
),
93 member(max
, T_LONGLONG
),
94 member(varname
, T_STRING
),
100 * node.next() -- returns list of all "next" nodes.
101 * this will include circles if the graph has them.
103 static PyObject
*graph_node_next(PyObject
*self
, PyObject
*args
)
105 struct wrap_graph_node
*wrap
= (struct wrap_graph_node
*)self
;
109 && ((struct cmd_token
*)wrap
->node
->data
)->type
== END_TKN
)
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
));
120 * node.join() -- return FORK's JOIN node or None
122 static PyObject
*graph_node_join(PyObject
*self
, PyObject
*args
)
124 struct wrap_graph_node
*wrap
= (struct wrap_graph_node
*)self
;
126 if (!wrap
->node
->data
127 || ((struct cmd_token
*)wrap
->node
->data
)->type
== END_TKN
)
130 struct cmd_token
*tok
= wrap
->node
->data
;
131 if (tok
->type
!= FORK_TKN
)
134 return graph_to_pyobj(wrap
->wgraph
, tok
->forkjoin
);
137 static 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"},
143 static void graph_node_wrap_free(void *arg
)
145 struct wrap_graph_node
*wrap
= arg
;
146 wrap
->wgraph
->nodewrappers
[wrap
->idx
] = NULL
;
147 Py_DECREF(wrap
->wgraph
);
150 static PyTypeObject typeobj_graph_node
= {
151 PyVarObject_HEAD_INIT(NULL
, 0)
152 .tp_name
= "_clippy.GraphNode",
153 .tp_basicsize
= sizeof(struct wrap_graph_node
),
154 .tp_flags
= Py_TPFLAGS_DEFAULT
,
155 .tp_doc
= "struct graph_node *",
156 .tp_new
= refuse_new
,
157 .tp_free
= graph_node_wrap_free
,
158 .tp_members
= members_graph_node
,
159 .tp_methods
= methods_graph_node
,
162 static PyObject
*graph_to_pyobj(struct wrap_graph
*wgraph
, struct graph_node
*gn
)
164 struct wrap_graph_node
*wrap
;
167 for (i
= 0; i
< vector_active(wgraph
->graph
->nodes
); i
++)
168 if (vector_slot(wgraph
->graph
->nodes
, i
) == gn
)
170 if (i
== vector_active(wgraph
->graph
->nodes
)) {
171 PyErr_SetString(PyExc_ValueError
, "cannot find node in graph");
174 if (wgraph
->nodewrappers
[i
]) {
175 PyObject
*obj
= (PyObject
*)wgraph
->nodewrappers
[i
];
180 wrap
= (struct wrap_graph_node
*)typeobj_graph_node
.tp_alloc(&typeobj_graph_node
, 0);
183 wgraph
->nodewrappers
[i
] = wrap
;
187 wrap
->wgraph
= wgraph
;
190 wrap
->allowrepeat
= false;
192 struct cmd_token
*tok
= gn
->data
;
194 #define item(x) case x: wrap->type = #x; break;
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
212 wrap
->deprecated
= (tok
->attr
== CMD_ATTR_DEPRECATED
);
213 wrap
->hidden
= (tok
->attr
== CMD_ATTR_HIDDEN
);
214 wrap
->text
= tok
->text
;
215 wrap
->desc
= tok
->desc
;
216 wrap
->varname
= tok
->varname
;
217 wrap
->min
= tok
->min
;
218 wrap
->max
= tok
->max
;
219 wrap
->allowrepeat
= tok
->allowrepeat
;
222 return (PyObject
*)wrap
;
225 #define member(name, type) {(char *)#name, type, offsetof(struct wrap_graph, name), READONLY, \
226 (char *)#name " (" #type ")"}
227 static PyMemberDef members_graph
[] = {
228 member(definition
, T_STRING
),
233 /* graph.first() - root node */
234 static PyObject
*graph_first(PyObject
*self
, PyObject
*args
)
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
);
241 static PyMethodDef methods_graph
[] = {
242 {"first", graph_first
, METH_NOARGS
, "first graph node"},
246 static PyObject
*graph_parse(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
);
248 static void graph_wrap_free(void *arg
)
250 struct wrap_graph
*wgraph
= arg
;
251 free(wgraph
->nodewrappers
);
252 free(wgraph
->definition
);
255 static PyTypeObject typeobj_graph
= {
256 PyVarObject_HEAD_INIT(NULL
, 0)
257 .tp_name
= "_clippy.Graph",
258 .tp_basicsize
= sizeof(struct wrap_graph
),
259 .tp_flags
= Py_TPFLAGS_DEFAULT
,
260 .tp_doc
= "struct graph *",
261 .tp_new
= graph_parse
,
262 .tp_free
= graph_wrap_free
,
263 .tp_members
= members_graph
,
264 .tp_methods
= methods_graph
,
267 /* top call / entrypoint for python code */
268 static PyObject
*graph_parse(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
270 const char *def
, *doc
= NULL
;
271 struct wrap_graph
*gwrap
;
272 static const char *kwnames
[] = { "cmddef", "doc", NULL
};
274 gwrap
= (struct wrap_graph
*)typeobj_graph
.tp_alloc(&typeobj_graph
, 0);
278 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|s", (char **)kwnames
, &def
, &doc
))
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
);
285 struct cmd_element cmd
= { .string
= def
, .doc
= doc
};
286 cmd_graph_parse (graph
, &cmd
);
287 cmd_graph_names (graph
);
289 gwrap
->graph
= graph
;
290 gwrap
->definition
= strdup(def
);
291 gwrap
->nodewrappers
= calloc(vector_active(graph
->nodes
),
292 sizeof (gwrap
->nodewrappers
[0]));
293 return (PyObject
*)gwrap
;
296 static PyMethodDef clippy_methods
[] = {
297 {"parse", clippy_parse
, METH_VARARGS
, "Parse a C file"},
298 {NULL
, NULL
, 0, NULL
}
301 #if PY_MAJOR_VERSION >= 3
302 static struct PyModuleDef pymoddef_clippy
= {
303 PyModuleDef_HEAD_INIT
,
305 NULL
, /* docstring */
309 #define modcreate() PyModule_Create(&pymoddef_clippy)
310 #define initret(val) return val;
312 #define modcreate() Py_InitModule("_clippy", clippy_methods)
313 #define initret(val) do { \
314 if (!val) Py_FatalError("initialization failure"); \
318 PyMODINIT_FUNC
command_py_init(void)
322 if (PyType_Ready(&typeobj_graph_node
) < 0)
324 if (PyType_Ready(&typeobj_graph
) < 0)
331 Py_INCREF(&typeobj_graph_node
);
332 PyModule_AddObject(pymod
, "GraphNode", (PyObject
*)&typeobj_graph_node
);
333 Py_INCREF(&typeobj_graph
);
334 PyModule_AddObject(pymod
, "Graph", (PyObject
*)&typeobj_graph
);