@@@ mdwopt test and fix
[mLib-python] / ui.c
CommitLineData
81f68b64
MW
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
34static 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;
42end:
43 Py_XDECREF(p);
44 return (rc);
45}
46
47static 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;
56end:
57 return (0);
58}
59
60/*----- Error reporting ---------------------------------------------------*/
61
62static 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;
69end:
70 return (0);
71}
72
73static 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);
85end:
86 Py_XDECREF(rcobj);
87 return (0);
88}
89
f18b900c
MW
90/*----- Option parser -----------------------------------------------------*/
91
d33275b6
MW
92typedef struct {
93 PyObject_HEAD
94 PyObject *attrs;
95} optstate_pyobj;
96static PyTypeObject *optstate_pytype;
97
98static PyObject *optstate_pywrap(PyTypeObject *ty)
99{
100 optstate_pyobj *me = 0;
101
102 me = (optstate_pyobj *)ty->tp_alloc(ty, 0); if (!me) goto fail;
103 me->attrs = PyDict_New(); if (!me->attrs) goto fail;
104 return ((PyObject *)me);
105
106fail:
107 if (me) {
108 Py_XDECREF(me->attrs);
109 Py_DECREF(me);
110 }
111 return (0);
112}
113
114static PyObject *optstate_pynew(PyTypeObject *ty,
115 PyObject *arg, PyObject *kw)
116{
117 static const char *const kwlist[] = { 0 };
118 PyObject *me = 0;
119
120 if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end;
121 me = optstate_pywrap(ty);
122end:
123 return (me);
124}
125
126static void optstate_pydealloc(PyObject *me)
127{
128 optstate_pyobj *st = (optstate_pyobj *)me;
129 Py_DECREF(st->attrs);
130 FREEOBJ(me);
131}
132
133static const PyTypeObject optstate_pytype_skel = {
134 PyVarObject_HEAD_INIT(0, 0) /* Header */
135 "OptState", /* @tp_name@ */
136 sizeof(optstate_pyobj), /* @tp_basicsize@ */
137 0, /* @tp_itemsize@ */
138
139 optstate_pydealloc, /* @tp_dealloc@ */
140 0, /* @tp_print@ */
141 0, /* @tp_getattr@ */
142 0, /* @tp_setattr@ */
143 0, /* @tp_compare@ */
144 0, /* @tp_repr@ */
145 0, /* @tp_as_number@ */
146 0, /* @tp_as_sequence@ */
147 0, /* @tp_as_mapping@ */
148 0, /* @tp_hash@ */
149 0, /* @tp_call@ */
150 0, /* @tp_str@ */
151 0, /* @tp_getattro@ */
152 0, /* @tp_setattro@ */
153 0, /* @tp_as_buffer@ */
154 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
155 Py_TPFLAGS_BASETYPE,
156
157 /* @tp_doc@ */
158 "OptState()\n"
159 "\n"
160 "A passive and generic container for arbitrary object attributes.",
161
162 0, /* @tp_traverse@ */
163 0, /* @tp_clear@ */
164 0, /* @tp_richcompare@ */
165 0, /* @tp_weaklistoffset@ */
166 0, /* @tp_iter@ */
167 0, /* @tp_iternext@ */
168 0, /* @tp_methods@ */
169 0, /* @tp_members@ */
170 0, /* @tp_getset@ */
171 0, /* @tp_base@ */
172 0, /* @tp_dict@ */
173 0, /* @tp_descr_get@ */
174 0, /* @tp_descr_set@ */
175 offsetof(optstate_pyobj, attrs), /* @tp_dictoffset@ */
176 0, /* @tp_init@ */
177 PyType_GenericAlloc, /* @tp_alloc@ */
178 optstate_pynew, /* @tp_new@ */
179 0, /* @tp_free@ */
180 0 /* @tp_is_gc@ */
181};
182
f18b900c
MW
183struct optextra {
184 PyObject *tag;
185 PyObject *attr;
186};
187
188typedef struct {
189 PyObject_HEAD
fcc8f4ca 190 char *stringdata;
f18b900c 191 struct option *longopt;
fcc8f4ca
MW
192 struct optextra *extra;
193 char **argv;
194 size_t nlong, narg;
195 int flags;
196 mdwopt_data opt;
197 PyObject *prog;
198 PyObject *state;
f18b900c
MW
199} mdwopt_pyobj;
200static PyTypeObject *mdwopt_pytype;
201#define MDWOPT_PYCHECK(o) PyObject_TypeCheck((o), mdwopt_pytype)
202#define MDWOPT_OPT(o) (&((mdwopt_pyobj *)(o))->opt)
203#define MDWOPT_ARGV(o) (((mdwopt_pyobj *)(o))->argv)
fcc8f4ca 204#define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->stringdata)
d33275b6 205#define MDWOPT_LONG(o) stativ(((mdwopt_pyobj *)(o))->longopt)
f18b900c 206
fcc8f4ca
MW
207#define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 2) | 0x200)
208#define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x3ff) >> 2))
209
210DA_DECL(obj_v, PyObject *);
211DA_DECL(opt_v, struct option);
212DA_DECL(extra_v, struct optextra);
213DA_DECL(size_v, size_t);
214
215/* Ordering of strings within `stringdata'.
216 *
217 * * `shortopt' (at the start so we don't need an extra pointer)
218 * * `argv' (individual strings addressed by `argv')
219 * * `longopt' names (in order, addressed by `longopt[i].name')
220 */
221
222struct optbuild {
223 dstr strbuf; /* string buffer */
224 size_t narg; /* number of arguments */
225 size_v off; /* offsets of string starts;
226 * doesn't include `shortopt' */
227 opt_v opt; /* options */
228 extra_v extra; /* option extra data */
229};
d33275b6 230#define OPTBUILD_INIT { DSTR_INIT, 0, DA_INIT, DA_INIT, DA_INIT }
f18b900c 231
d33275b6 232static PyObject *mdwopt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
f18b900c 233{
f18b900c
MW
234 PyObject *argvobj = 0, *longoptobj = 0;
235 PyObject *it = 0, *t = 0, *u = 0;
50845e59 236 const char *p; size_t sz;
fcc8f4ca 237 size_t i;
f18b900c
MW
238 Py_ssize_t n;
239 mdwopt_pyobj *me = 0;
50845e59 240 const char *shortopt = "";
f18b900c 241 unsigned flags = 0;
fcc8f4ca
MW
242 struct optbuild build = OPTBUILD_INIT;
243 struct option *opt;
244 struct optextra *extra;
f18b900c
MW
245 static const char *const kwlist[] =
246 { "argv", "shortopt", "longopt", "flags", 0 };
247
fcc8f4ca
MW
248#define EXTEND(var, vec) do { \
249 DA_ENSURE(&build.vec, 1); \
250 var = &DA(&build.vec)[DA_LEN(&build.vec)]; \
251 DA_EXTEND(&build.vec, 1); \
252} while (0)
253
254#define COPYTAB(slot, base, len) do { \
255 me->slot = xmalloc((len)*sizeof(*me->slot)); \
256 memcpy(me->slot, base, (len)*sizeof(*me->slot)); \
257} while (0)
258
259 /* Collect the arguments. */
f18b900c
MW
260 if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OsOO&:new", KWLIST,
261 &argvobj,
262 &shortopt,
263 &longoptobj,
264 convuint, &flags))
265 goto end;
266 if (!argvobj) {
267 argvobj = PySys_GetObject("argv");
268 if (!argvobj) SYSERR("sys.argv missing");
269 }
270
fcc8f4ca
MW
271 /* Commit the short-options string to the buffer.
272 *
273 * Putting this first means that we don't need a separate pointer. All of
274 * the other things are arrays, so avoiding maintaining a separate index or
275 * pointer for the first element will just make things unnecessarily
276 * complicated.
277 */
278 DPUTM(&build.strbuf, shortopt, strlen(shortopt) + 1);
279
280 /* Collect the arguments to be parsed. */
f18b900c
MW
281 it = PyObject_GetIter(argvobj); if (!it) goto end;
282 for (;;) {
283 t = PyIter_Next(it); if (!t) break;
284 if (!TEXT_CHECK(t)) TYERR("argv should be a sequence of strings");
fcc8f4ca
MW
285 DA_PUSH(&build.off, build.strbuf.len);
286 TEXT_PTRLEN(t, p, sz); DPUTM(&build.strbuf, p, sz + 1);
f18b900c
MW
287 Py_DECREF(t); t = 0;
288 }
289 if (PyErr_Occurred()) goto end;
fcc8f4ca 290 build.narg = DA_LEN(&build.off);
f18b900c
MW
291 Py_DECREF(it); it = 0;
292
fcc8f4ca
MW
293 /* Collect the long-option specifications. */
294 if (longoptobj) {
295 it = PyObject_GetIter(longoptobj); if (!it) goto end;
296 for (;;) {
297
298 /* Get the next item and check that it's basically sensible. */
299 t = PyIter_Next(it); if (!t) break;
300 n = PySequence_Size(t); if (n < 0) goto end;
301 if (n < 2 || n > 4)
302 VALERR("long-options entry should be "
303 "(NAME, VAL, [FLAG = 0, [ATTR = None]])");
304
305 /* Allocate new entries in the options and extra-data tables. */
306 EXTEND(opt, opt);
307 EXTEND(extra, extra);
308 opt->flag = 0;
309 extra->tag = 0; extra->attr = 0;
310
311 /* Get the option name and contribute it to the string buffer. */
f18b900c 312 u = PySequence_GetItem(t, 0); if (!u) goto end;
fcc8f4ca
MW
313 if (!TEXT_CHECK(u)) TYERR("option name should be a string");
314 DA_PUSH(&build.off, build.strbuf.len);
315 TEXT_PTRLEN(u, p, sz); DPUTM(&build.strbuf, p, sz + 1);
f18b900c 316 Py_DECREF(u); u = 0;
fcc8f4ca
MW
317
318 /* Get the option tag and store it in the extra data.
319 * `PySequence_GetItem' bumps the refcount for us.
320 */
321 extra->tag = PySequence_GetItem(t, 1); if (!extra->tag) goto end;
322
323 /* Get the flags for this option. */
324 if (n < 3)
325 opt->has_arg = 0;
326 else {
d33275b6
MW
327 u = PySequence_GetItem(t, 2); if (!u) goto end;
328 if (!convint(u, &opt->has_arg)) goto end;
fcc8f4ca
MW
329 Py_DECREF(u); u = 0;
330 }
331
332 /* Finally, get the attribute name. */
333 if (n < 4)
334 { extra->attr = Py_None; Py_INCREF(Py_None); }
335 else {
336 extra->attr = PySequence_GetItem(t, 3);
337 if (!extra->attr) goto end;
338 }
339
340 /* Done. Let's go round again. */
341 Py_DECREF(t); t = 0;
f18b900c 342 }
fcc8f4ca
MW
343 if (PyErr_Occurred()) goto end;
344 Py_DECREF(it); it = 0;
345 }
346
347 /* Allocate the state value. */
d33275b6 348 t = optstate_pywrap(optstate_pytype); if (!t) goto end;
fcc8f4ca
MW
349
350 /* Allocate our return value. */
d33275b6 351 me = (mdwopt_pyobj *)ty->tp_alloc(ty, 0);
fcc8f4ca
MW
352 me->state = t; t = 0;
353 me->flags = flags;
354 me->narg = build.narg;
355 me->nlong = DA_LEN(&build.opt);
356
357 /* Add a final terminating entry to the long-options table. */
358 EXTEND(opt, opt); opt->name = 0;
359
360 /* Copy the main tables. */
361 COPYTAB(stringdata, build.strbuf.buf, build.strbuf.len);
362 COPYTAB(longopt, DA(&build.opt), DA_LEN(&build.opt));
363 COPYTAB(extra, DA(&build.extra), DA_LEN(&build.extra));
364
365 /* Fill in the `argv' vector. */
d33275b6 366 me->argv = xmalloc((build.narg + 1)*sizeof(*me->argv));
fcc8f4ca 367 for (i = 0; i < build.narg; i++)
d33275b6 368 me->argv[i] = me->stringdata + DA(&build.off)[i];
fcc8f4ca
MW
369 me->argv[build.narg] = 0;
370
371 /* Fix up the string pointers and values in the long-options table. */
372 for (i = 0; i < me->nlong; i++) {
373 me->longopt[i].name =
d33275b6 374 me->stringdata + DA(&build.off)[i + build.narg];
fcc8f4ca
MW
375 me->longopt[i].val = IXTAG(i);
376 }
377
378 /* Initialize the parser state. Set up everything because Python might
379 * ask awkward questions before we're ready.
380 */
381 me->opt.arg = 0;
382 me->opt.opt = -1;
383 me->opt.ind = 0;
384 me->opt.err = 1;
385 me->opt.prog = 0;
f18b900c 386
fcc8f4ca
MW
387 /* And other random things. */
388 me->prog = 0;
f18b900c
MW
389
390end:
fcc8f4ca
MW
391 /* Clean up and go home. */
392 Py_XDECREF(it); Py_XDECREF(t); Py_XDECREF(u);
393 DDESTROY(&build.strbuf);
394 if (!me) for (i = 0; i < DA_LEN(&build.extra); i++) {
395 extra = &DA(&build.extra)[i];
396 Py_XDECREF(extra->tag); Py_XDECREF(extra->attr);
397 }
398 DA_DESTROY(&build.off);
399 DA_DESTROY(&build.opt);
400 DA_DESTROY(&build.extra);
401 return ((PyObject *)me);
402
403#undef EXTEND
404#undef COPYTAB
f18b900c
MW
405}
406
407static void mdwopt_pydealloc(PyObject *me)
408{
409 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
fcc8f4ca 410 size_t i;
f18b900c 411
fcc8f4ca
MW
412 for (i = 0; i < m->nlong; i++)
413 { Py_DECREF(m->extra[i].tag); Py_DECREF(m->extra[i].attr); }
414 xfree(m->stringdata); xfree(m->longopt); xfree(m->extra);
f18b900c
MW
415 FREEOBJ(me);
416}
417
fcc8f4ca
MW
418static PyObject *mdwopt_pynext(PyObject *me)
419{
420 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
421 PyObject *val = 0, *arg = 0, *t = 0, *u = 0, *v = 0;
422 int f, ix, i;
423 unsigned char ch;
424 PyObject *rc = 0;
425
426again:
427 i = mdwopt(m->narg, m->argv, m->stringdata, m->longopt, &ix,
428 &m->opt, m->flags);
429
430 if (i == -1) goto end;
431
432 f = i&OPTF_NEGATED;
433
434 if (m->opt.arg) arg = TEXT_FROMSTR(m->opt.arg);
435 else { arg = Py_None; Py_INCREF(Py_None); }
436
437 if (ix < 0) {
438 ch = i&0xff;
439 val = TEXT_FROMSTRLEN((char *)&ch, 1); if (!val) goto end;
440 } else {
441 if (m->extra[ix].attr == Py_None)
442 { val = m->extra[ix].tag; Py_INCREF(val); }
d33275b6
MW
443 else {
444 if (m->longopt[ix].has_arg&OPTF_SWITCH) {
445 t = PyObject_GetAttr(m->state, m->extra[ix].attr);
446 if (!t && PyErr_ExceptionMatches(PyExc_AttributeError)) {
447 PyErr_Clear();
448 t = PyInt_FromLong(0); if (!t) goto end;
449 }
450 if (!f)
451 { v = PyNumber_Or(t, m->extra[ix].tag); if (!v) goto end; }
452 else {
453 u = PyNumber_Invert(m->extra[ix].tag); if (!u) goto end;
454 v = PyNumber_And(t, u); if (!v) goto end;
455 }
456 if (PyObject_SetAttr(m->state, m->extra[ix].attr, v)) goto end;
457 Py_DECREF(t); t = 0;
458 Py_XDECREF(u); u = 0;
459 Py_DECREF(v); v = 0;
460 } else {
461 if (PyObject_SetAttr(m->state, m->extra[ix].attr,
462 f ?
463 Py_None :
464 m->longopt[ix].has_arg&OPTF_ARG ?
465 arg : m->extra[ix].tag))
466 goto end;
fcc8f4ca 467 }
d33275b6
MW
468 Py_DECREF(arg); arg = 0;
469 goto again;
fcc8f4ca 470 }
fcc8f4ca
MW
471 }
472
473 rc = Py_BuildValue("(OOi)", val, arg, f);
474end:
475 Py_XDECREF(val); Py_XDECREF(arg);
476 Py_XDECREF(t); Py_XDECREF(u); Py_XDECREF(v);
477 return (rc);
478}
479
480static PyObject *moget_argv(PyObject *me, void *hunoz)
481{
482 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
483 PyObject *rc = 0, *t = 0;
484 size_t i = 0;
485
486 rc = PyList_New(m->narg); if (!rc) goto fail;
487 for (i = 0; i < m->narg; i++) {
488 t = TEXT_FROMSTR(m->argv[i]); if (!t) goto fail;
489 PyList_SET_ITEM(rc, i, t); t = 0;
490 }
491 return (rc);
492
493fail:
494 Py_XDECREF(t);
495 Py_XDECREF(rc);
496 return (0);
497}
498
499static PyObject *moget_err(PyObject *me, void *hunoz)
500 { mdwopt_pyobj *m = (mdwopt_pyobj *)me; return (getbool(m->opt.err)); }
501static int moset_err(PyObject *me, PyObject *v, void *hunoz)
502{
503 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
504 int rc = -1;
505
506 if (!v) NIERR("__del__");
507 if (convbool(v, &m->opt.err)) goto end;
508 rc = 0;
509end:
510 return (rc);
511}
512
513static PyObject *moget_prog(PyObject *me, void *hunoz)
514{
515 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
516
517 if (!m->opt.prog) RETURN_NONE;
518 if (!m->prog)
519 { m->prog = TEXT_FROMSTR(m->opt.prog); if (!m->prog) return (0); }
520 RETURN_OBJ(m->prog);
521}
522static int moset_prog(PyObject *me, PyObject *v, void *hunoz)
523{
524 mdwopt_pyobj *m = (mdwopt_pyobj *)me;
50845e59 525 const char *p;
fcc8f4ca
MW
526 int rc = -1;
527
528 if (!v) NIERR("__del__");
529 p = TEXT_STR(v); if (!p) goto end;
50845e59 530 m->opt.prog = (/*unconst*/ char *)p;
fcc8f4ca
MW
531 Py_XDECREF(m->prog); m->prog = v; Py_INCREF(v);
532 rc = 0;
533end:
534 return (rc);
535}
536
f18b900c
MW
537static const PyMemberDef mdwopt_pymembers[] = {
538#define MEMBERSTRUCT mdwopt_pyobj
fcc8f4ca
MW
539 MEMBER(state, T_OBJECT_EX, 0, "M.state = object on which flags are set")
540 MEMRNM(arg, T_STRING, opt.arg, READONLY,
541 "M.arg -> argument of most recent option")
542 MEMRNM(ind, T_INT, opt.ind, READONLY,
543 "M.ind -> index of first non-option argument")
f18b900c
MW
544#undef MEMBERSTRUCT
545 { 0 }
546};
547
548static const PyGetSetDef mdwopt_pygetset[] = {
549#define GETSETNAME(op, name) mo##op##_##name
fcc8f4ca
MW
550 GET (argv, "M.argv -> vector of arguments (permuted)")
551 GETSET(err, "M.err = report errors to `stderr'?")
552 GETSET(prog, "M.prog = program name (to report in errors")
f18b900c
MW
553#undef GETSETNAME
554 { 0 }
555};
556
557static const PyMethodDef mdwopt_pymethods[] = {
558#define METHNAME(name) mometh_##name
559#undef METHNAME
560 { 0 }
561};
562
563static const PyTypeObject mdwopt_pytype_skel = {
564 PyVarObject_HEAD_INIT(0, 0) /* Header */
565 "MdwOpt", /* @tp_name@ */
566 sizeof(mdwopt_pyobj), /* @tp_basicsize@ */
567 0, /* @tp_itemsize@ */
568
569 mdwopt_pydealloc, /* @tp_dealloc@ */
570 0, /* @tp_print@ */
571 0, /* @tp_getattr@ */
572 0, /* @tp_setattr@ */
573 0, /* @tp_compare@ */
574 0, /* @tp_repr@ */
575 0, /* @tp_as_number@ */
576 0, /* @tp_as_sequence@ */
577 0, /* @tp_as_mapping@ */
578 0, /* @tp_hash@ */
579 0, /* @tp_call@ */
580 0, /* @tp_str@ */
581 0, /* @tp_getattro@ */
582 0, /* @tp_setattro@ */
583 0, /* @tp_as_buffer@ */
584 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
585 Py_TPFLAGS_BASETYPE,
586
587 /* @tp_doc@ */
fcc8f4ca
MW
588 "MdwOpt([argv = SEQ], [shortopt = STR], [longopt = SEQ], [flags = 0])\n"
589 "\n"
590 "ARGV is the sequence of arguments to be parsed. If omitted, it\n"
591 "defaults to `sys.argv'.\n"
592 "\n"
593 "SHORTOPT has the form `[+|-|!][:]OPT...', where OPT is `CHAR[+][:[:]]'.\n"
594 "The CHAR names the option character; a `+' indicates that the option\n"
595 "may be negated; a `:' indicates that the option takes an argument, and\n"
596 " `::' means the argument is optional. Before the OPTs, the following\n"
597 "may appear:\n"
598 "\n"
599 " * `+' -- force POSIX option order: end iteration at first non-option;\n"
600 " * `-' -- treat non-options as arguments to option `None';\n"
601 " * `!' -- force default reordering behaviour: extract all options;\n"
602 " * `:' -- return `:' rather than `?' for missing argument.\n"
603 "\n"
604 "LONGOPT is a sequence of tuples (NAME, VAL, [FLAG = 0, [ATTR = None]]):\n"
605 "the NAME is the long-option string; FLAG is a mask of flags listed\n"
606 "below; ATTR is `None' or an attribute name; VAL is the value to return\n"
607 "or, if ATTR is not `None', to store in `state.ATTR'. Flags are:\n"
608 "\n"
609 " * `OPTF_ARGREQ' -- argument is mandatory (like `:');\n"
610 " * `OPTF_ARGOPT' -- argument is optional (like `::');\n"
611 " * `OPTF_SWITCH' -- set or clear VAL bits in ATTR;\n"
612 " * `OPTF_NEGATE' -- option may be negated\n"
613 "\n"
614 "Flags to the function are:\n"
615 " * `OPTF_NOLONGS' -- don't accept long options at all;\n"
616 " * `OPTF_NOSHORTS' -- accept long options with single `-';\n"
617 " * `OPTF_NUMBERS' -- accept numeric options (value `#');\n"
618 " * `OPTF_NEGATION' -- allow options to be negated;\n"
619 " * `OPTF_ENVVAR' -- read options from environment variable;\n"
620 " * `OPTF_NOPROGNAME' -- don't assume program name is in `ARGV[0]'\n."
621 "\n"
622 "The object is iterable, and yields triples of the form (VAL, ARG,\n"
623 "FLAGS): VAL is the option letter (for short options) or VAL slot (for a\n"
624 "long option); ARG is the argument, or `None'; and FLAG is a mask of the\n"
625 "following flags:\n"
626 "\n"
627 " * `OPTF_NEGATED' -- set if the option was negated.\n"
628 "\n"
629 "Special values of VAL are:\n"
630 "\n"
631 " * '?' -- an error was encountered;\n"
632 " * ':' -- a required argument was omitted (if `:' is in SHORTOPT);\n"
633 " * '#' -- a numeric option was found (if `OPTF_NUMBERS' is in FLAGS).\n"
634 "\n"
635 "Useful attributes:\n"
636 "\n"
637 " * `arg' (read-only) -- argument to most recent option, or `None';\n"
638 " * `argv' (read-only) -- vector of arguments to parse (permuted);\n"
639 " * `ind' (read-only) -- index of first non-option argument;\n"
640 " * `err' (read-write) -- boolean: report errors to `stderr'?;\n"
641 " * `prog' (read-write) -- program name (to report in errors);\n"
642 " * `state' (read-write) -- object to accumulate attribute settings\n.",
f18b900c
MW
643
644 0, /* @tp_traverse@ */
645 0, /* @tp_clear@ */
646 0, /* @tp_richcompare@ */
647 0, /* @tp_weaklistoffset@ */
fcc8f4ca
MW
648 PyObject_SelfIter, /* @tp_iter@ */
649 mdwopt_pynext, /* @tp_iternext@ */
f18b900c
MW
650 PYMETHODS(mdwopt), /* @tp_methods@ */
651 PYMEMBERS(mdwopt), /* @tp_members@ */
652 PYGETSET(mdwopt), /* @tp_getset@ */
653 0, /* @tp_base@ */
654 0, /* @tp_dict@ */
655 0, /* @tp_descr_get@ */
656 0, /* @tp_descr_set@ */
657 0, /* @tp_dictoffset@ */
658 0, /* @tp_init@ */
659 PyType_GenericAlloc, /* @tp_alloc@ */
660 mdwopt_pynew, /* @tp_new@ */
661 0, /* @tp_free@ */
662 0 /* @tp_is_gc@ */
663};
664
81f68b64
MW
665/*----- Main code ---------------------------------------------------------*/
666
d33275b6
MW
667static const struct nameval consts[] = {
668 CONST(OPTF_NOARG), CONST(OPTF_ARGREQ), CONST(OPTF_ARGOPT), CONST(OPTF_ARG),
669 CONST(OPTF_SWITCH), CONST(OPTF_NEGATE),
670 CONST(OPTF_NOLONGS), CONST(OPTF_NOSHORTS), CONST(OPTF_NUMBERS),
671 CONST(OPTF_NEGATION), CONST(OPTF_ENVVAR), CONST(OPTF_NOPROGNAME),
672 CONST(OPTF_NEGNUMBER),
673 CONST(OPTF_NEGATED)
674};
675
81f68b64
MW
676static const PyMethodDef methods[] = {
677#define METHNAME(name) meth_##name
678 METH (ego, "ego(PROG): set program name")
679 METH (moan, "moan(MSG): report a warning")
680 KWMETH(die, "die(MSG, [rc = 126]): report a fatal error and exit")
681#undef METHNAME
682 { 0 }
683};
684
685void ui_pyinit(void)
686{
d33275b6 687 INITTYPE(optstate, root);
f18b900c 688 INITTYPE(mdwopt, root);
81f68b64
MW
689 addmethods(methods);
690}
691
692void ui_pyinsert(PyObject *mod)
693{
d33275b6 694 INSERT("OptState", optstate_pytype);
f18b900c 695 INSERT("MdwOpt", mdwopt_pytype);
d33275b6 696 setconstants(mod, consts);
81f68b64
MW
697}
698
699int ui_pyready(void) { return (set_program_name()); }
700
701/*----- That's all, folks -------------------------------------------------*/