chiark / gitweb /
4316d93a926b806bd02e8e6557d8f7d2d1345140
[mLib-python] / ui.c
1 /* -*-c-*-
2  *
3  * mLib user interface
4  *
5  * (c) 2019 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the Python interface to mLib.
11  *
12  * mLib/Python is free software: you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation; either version 2 of the License, or (at your
15  * option) any later version.
16  *
17  * mLib/Python is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with mLib/Python.  If not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25  * USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "mLib-python.h"
31
32 /*----- Program name ------------------------------------------------------*/
33
34 static int set_program_name(void)
35 {
36   PyObject *p = TEXT_FROMSTR(pn__name);
37   int rc = -1;
38
39   if (!home_module) SYSERR("home module not set");
40   if (PyObject_SetAttrString(home_module, "quis", p)) goto end;
41   pn__name = TEXT_PTR(p); p = 0; rc = 0;
42 end:
43   Py_XDECREF(p);
44   return (rc);
45 }
46
47 static PyObject *meth_ego(PyObject *me, PyObject *arg)
48 {
49   char *p;
50   const char *old;
51
52   if (!PyArg_ParseTuple(arg, "s:ego", &p)) goto end;
53   old = pn__name; ego(p);
54   if (set_program_name()) { pn__name = old; goto end; }
55   RETURN_NONE;
56 end:
57   return (0);
58 }
59
60 /*----- Error reporting ---------------------------------------------------*/
61
62 static PyObject *meth_moan(PyObject *me, PyObject *arg)
63 {
64   char *p;
65
66   if (!PyArg_ParseTuple(arg, "s:moan", &p)) goto end;
67   moan("%s", p);
68   RETURN_NONE;
69 end:
70   return (0);
71 }
72
73 static PyObject *meth_die(PyObject *me, PyObject *arg, PyObject *kw)
74 {
75   const char *const kwlist[] = { "msg", "rc", 0 };
76   char *p;
77   int rc = 126;
78   PyObject *rcobj = 0;
79
80   if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|i:moan", KWLIST, &p, &rc))
81     goto end;
82   rcobj = PyInt_FromLong(rc); if (!rcobj) goto end;
83   moan("%s", p);
84   PyErr_SetObject(PyExc_SystemExit, rcobj);
85 end:
86   Py_XDECREF(rcobj);
87   return (0);
88 }
89
90 /*----- Option parser -----------------------------------------------------*/
91
92 struct optextra {
93   PyObject *tag;
94   PyObject *attr;
95 };
96
97 typedef struct {
98   PyObject_HEAD
99   mdwopt_data opt;
100   char **argv, *stringdata;
101   char *shortopt;
102   struct option *longopt;
103 } mdwopt_pyobj;
104 static PyTypeObject *mdwopt_pytype;
105 #define MDWOPT_PYCHECK(o) PyObject_TypeCheck((o), mdwopt_pytype)
106 #define MDWOPT_OPT(o) (&((mdwopt_pyobj *)(o))->opt)
107 #define MDWOPT_ARGV(o) (((mdwopt_pyobj *)(o))->argv)
108 #define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->shortopt)
109 #define MDWOPT_LONG(o) (((mdwopt_pyobj *)(o))->longopt)
110
111 #define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 1))
112 #define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x1ff) >> 1))
113
114 static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
115 {
116   DA_DECL(obj_v, PyObject *);
117   DA_DECL(opt_v, struct option);
118   DA_DECL(size_v, size_t);
119
120   PyObject *argvobj = 0, *longoptobj = 0;
121   PyObject *it = 0, *t = 0, *u = 0;
122   char *p; size_t sz;
123   Py_ssize_t n;
124   mdwopt_pyobj *me = 0;
125   char *shortopt;
126   unsigned flags = 0;
127   int f;
128   opt_v opts = DA_INIT;
129   obj_v tags = DA_INIT;
130   size_v off = DA_INIT;
131   dstr strbuf = DSTR_INIT;
132   size_t narg;
133   static const char *const kwlist[] =
134     { "argv", "shortopt", "longopt", "flags", 0 };
135
136   if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OsOO&:new", KWLIST,
137                                    &argvobj,
138                                    &shortopt,
139                                    &longoptobj,
140                                    convuint, &flags))
141     goto end;
142   if (!argvobj) {
143     argvobj = PySys_GetObject("argv");
144     if (!argvobj) SYSERR("sys.argv missing");
145   }
146
147   it = PyObject_GetIter(argvobj); if (!it) goto end;
148   for (;;) {
149     t = PyIter_Next(it); if (!t) break;
150     if (!TEXT_CHECK(t)) TYERR("argv should be a sequence of strings");
151     TEXT_PTRLEN(t, p, sz);
152     DA_PUSH(&off, strbuf.len);
153     DPUTM(&strbuf, p, sz + 1);
154     Py_DECREF(t); t = 0;
155   }
156   if (PyErr_Occurred()) goto end;
157   narg = DA_LEN(&off);
158   Py_DECREF(it); it = 0;
159
160   it = PyObject_GetIter(longoptobj); if (!it) goto end;
161   for (;;) {
162     t = PyIter_Next(it); if (!t) break;
163     n = PySequence_Size(t); if (n < 0) goto end;
164     if (n < 2 || n > 4)
165       VALERR("long-options entry should be "
166              "(OPT, TAG, [FLAGS = 0, [ATTR = None]])");
167
168     u = PySequence_GetItem(t, 0); if (!u) goto end;
169     if (!TEXT_CHECK(u)) TYERR("option name should be a string");
170     TEXT_PTRLEN(u, p, sz);
171     DA_PUSH(&off, strbuf.len);
172     DPUTM(&strbuf, p, sz + 1);
173     Py_DECREF(u); u = 0;
174
175     u = PySequence_GetItem(t, 1); if (!u) goto end;
176     DA_PUSH(&tags, u); u = 0;
177
178     if (n < 3)
179       f = 0;
180     else {
181       u = PySequence_GetItem(t, 0); if (!u) goto end;
182       if (getint(u, &f)) goto end;
183       Py_DECREF(u); u = 0;
184     }
185
186     
187
188 end:
189   return (PyObject *)me;
190 }
191
192 static void mdwopt_pydealloc(PyObject *me)
193 {
194   mdwopt_pyobj *m = (mdwopt_pyobj *)me;
195
196   xfree(m->stringdata);
197   xfree(m->longopt);
198   FREEOBJ(me);
199 }
200
201 static const PyMemberDef mdwopt_pymembers[] = {
202 #define MEMBERSTRUCT mdwopt_pyobj
203 #undef MEMBERSTRUCT
204   { 0 }
205 };
206
207 static const PyGetSetDef mdwopt_pygetset[] = {
208 #define GETSETNAME(op, name) mo##op##_##name
209 #undef GETSETNAME
210   { 0 }
211 };
212
213 static const PyMethodDef mdwopt_pymethods[] = {
214 #define METHNAME(name) mometh_##name
215 #undef METHNAME
216   { 0 }
217 };
218
219 static const PyTypeObject mdwopt_pytype_skel = {
220   PyVarObject_HEAD_INIT(0, 0)           /* Header */
221   "MdwOpt",                             /* @tp_name@ */
222   sizeof(mdwopt_pyobj),                 /* @tp_basicsize@ */
223   0,                                    /* @tp_itemsize@ */
224
225   mdwopt_pydealloc,                     /* @tp_dealloc@ */
226   0,                                    /* @tp_print@ */
227   0,                                    /* @tp_getattr@ */
228   0,                                    /* @tp_setattr@ */
229   0,                                    /* @tp_compare@ */
230   0,                                    /* @tp_repr@ */
231   0,                                    /* @tp_as_number@ */
232   0,                                    /* @tp_as_sequence@ */
233   0,                                    /* @tp_as_mapping@ */
234   0,                                    /* @tp_hash@ */
235   0,                                    /* @tp_call@ */
236   0,                                    /* @tp_str@ */
237   0,                                    /* @tp_getattro@ */
238   0,                                    /* @tp_setattro@ */
239   0,                                    /* @tp_as_buffer@ */
240   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
241     Py_TPFLAGS_BASETYPE,
242
243   /* @tp_doc@ */
244   "MdwOpt([argv = SEQ], [shortopt = STR], [longopt = SEQ], [flags = 0])",
245
246   0,                                    /* @tp_traverse@ */
247   0,                                    /* @tp_clear@ */
248   0,                                    /* @tp_richcompare@ */
249   0,                                    /* @tp_weaklistoffset@ */
250   0,                                    /* @tp_iter@ */
251   0,                                    /* @tp_iternext@ */
252   PYMETHODS(mdwopt),                    /* @tp_methods@ */
253   PYMEMBERS(mdwopt),                    /* @tp_members@ */
254   PYGETSET(mdwopt),                     /* @tp_getset@ */
255   0,                                    /* @tp_base@ */
256   0,                                    /* @tp_dict@ */
257   0,                                    /* @tp_descr_get@ */
258   0,                                    /* @tp_descr_set@ */
259   0,                                    /* @tp_dictoffset@ */
260   0,                                    /* @tp_init@ */
261   PyType_GenericAlloc,                  /* @tp_alloc@ */
262   mdwopt_pynew,                         /* @tp_new@ */
263   0,                                    /* @tp_free@ */
264   0                                     /* @tp_is_gc@ */
265 };
266
267 /*----- Main code ---------------------------------------------------------*/
268
269 static const PyMethodDef methods[] = {
270 #define METHNAME(name) meth_##name
271   METH  (ego,           "ego(PROG): set program name")
272   METH  (moan,          "moan(MSG): report a warning")
273   KWMETH(die,          "die(MSG, [rc = 126]): report a fatal error and exit")
274 #undef METHNAME
275   { 0 }
276 };
277
278 void ui_pyinit(void)
279 {
280   INITTYPE(mdwopt, root);
281   addmethods(methods);
282 }
283
284 void ui_pyinsert(PyObject *mod)
285 {
286   INSERT("MdwOpt", mdwopt_pytype);
287 }
288
289 int ui_pyready(void) { return (set_program_name()); }
290
291 /*----- That's all, folks -------------------------------------------------*/