chiark / gitweb /
@@@ remove debugging print
[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 typedef struct {
93   PyObject_HEAD
94   PyObject *attrs;
95 } optstate_pyobj;
96 static PyTypeObject *optstate_pytype;
97
98 static 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
106 fail:
107   if (me) {
108     Py_XDECREF(me->attrs);
109     Py_DECREF(me);
110   }
111   return (0);
112 }
113
114 static 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);
122 end:
123   return (me);
124 }
125
126 static void optstate_pydealloc(PyObject *me)
127 {
128   optstate_pyobj *st = (optstate_pyobj *)me;
129   Py_DECREF(st->attrs);
130   FREEOBJ(me);
131 }
132
133 static 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
183 struct optextra {
184   PyObject *tag;
185   PyObject *attr;
186 };
187
188 typedef struct {
189   PyObject_HEAD
190   char *stringdata;
191   struct option *longopt;
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;
199 } mdwopt_pyobj;
200 static 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)
204 #define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->stringdata)
205 #define MDWOPT_LONG(o) stativ(((mdwopt_pyobj *)(o))->longopt)
206
207 #define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 2) | 0x200)
208 #define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x3ff) >> 2))
209
210 DA_DECL(obj_v, PyObject *);
211 DA_DECL(opt_v, struct option);
212 DA_DECL(extra_v, struct optextra);
213 DA_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
222 struct 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 };
230 #define OPTBUILD_INIT { DSTR_INIT, 0, DA_INIT, DA_INIT, DA_INIT }
231
232 static PyObject *mdwopt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
233 {
234   PyObject *argvobj = 0, *longoptobj = 0;
235   PyObject *it = 0, *t = 0, *u = 0;
236   const char *p; size_t sz;
237   size_t i;
238   Py_ssize_t n;
239   mdwopt_pyobj *me = 0;
240   const char *shortopt = "";
241   unsigned flags = 0;
242   struct optbuild build = OPTBUILD_INIT;
243   struct option *opt;
244   struct optextra *extra;
245   static const char *const kwlist[] =
246     { "argv", "shortopt", "longopt", "flags", 0 };
247
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. */
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
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. */
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");
285     DA_PUSH(&build.off, build.strbuf.len);
286     TEXT_PTRLEN(t, p, sz); DPUTM(&build.strbuf, p, sz + 1);
287     Py_DECREF(t); t = 0;
288   }
289   if (PyErr_Occurred()) goto end;
290   build.narg = DA_LEN(&build.off);
291   Py_DECREF(it); it = 0;
292
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. */
312       u = PySequence_GetItem(t, 0); if (!u) goto end;
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);
316       Py_DECREF(u); u = 0;
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 {
327         u = PySequence_GetItem(t, 2); if (!u) goto end;
328         if (!convint(u, &opt->has_arg)) goto end;
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;
342     }
343     if (PyErr_Occurred()) goto end;
344     Py_DECREF(it); it = 0;
345   }
346
347   /* Allocate the state value. */
348   t = optstate_pywrap(optstate_pytype); if (!t) goto end;
349
350   /* Allocate our return value. */
351   me = (mdwopt_pyobj *)ty->tp_alloc(ty, 0);
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. */
366   me->argv = xmalloc((build.narg + 1)*sizeof(*me->argv));
367   for (i = 0; i < build.narg; i++)
368     me->argv[i] = me->stringdata + DA(&build.off)[i];
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 =
374       me->stringdata + DA(&build.off)[i + build.narg];
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;
386
387   /* And other random things. */
388   me->prog = 0;
389
390 end:
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
405 }
406
407 static void mdwopt_pydealloc(PyObject *me)
408 {
409   mdwopt_pyobj *m = (mdwopt_pyobj *)me;
410   size_t i;
411
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);
415   FREEOBJ(me);
416 }
417
418 static 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
426 again:
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); }
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;
467       }
468       Py_DECREF(arg); arg = 0;
469       goto again;
470     }
471   }
472
473   rc = Py_BuildValue("(OOi)", val, arg, f);
474 end:
475   Py_XDECREF(val); Py_XDECREF(arg);
476   Py_XDECREF(t); Py_XDECREF(u); Py_XDECREF(v);
477   return (rc);
478 }
479
480 static 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
493 fail:
494   Py_XDECREF(t);
495   Py_XDECREF(rc);
496   return (0);
497 }
498
499 static PyObject *moget_err(PyObject *me, void *hunoz)
500   { mdwopt_pyobj *m = (mdwopt_pyobj *)me; return (getbool(m->opt.err)); }
501 static 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;
509 end:
510   return (rc);
511 }
512
513 static 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 }
522 static int moset_prog(PyObject *me, PyObject *v, void *hunoz)
523 {
524   mdwopt_pyobj *m = (mdwopt_pyobj *)me;
525   const char *p;
526   int rc = -1;
527
528   if (!v) NIERR("__del__");
529   p = TEXT_STR(v); if (!p) goto end;
530   m->opt.prog = (/*unconst*/ char *)p;
531   Py_XDECREF(m->prog); m->prog = v; Py_INCREF(v);
532   rc = 0;
533 end:
534   return (rc);
535 }
536
537 static const PyMemberDef mdwopt_pymembers[] = {
538 #define MEMBERSTRUCT mdwopt_pyobj
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")
544 #undef MEMBERSTRUCT
545   { 0 }
546 };
547
548 static const PyGetSetDef mdwopt_pygetset[] = {
549 #define GETSETNAME(op, name) mo##op##_##name
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")
553 #undef GETSETNAME
554   { 0 }
555 };
556
557 static const PyMethodDef mdwopt_pymethods[] = {
558 #define METHNAME(name) mometh_##name
559 #undef METHNAME
560   { 0 }
561 };
562
563 static 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@ */
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.",
643
644   0,                                    /* @tp_traverse@ */
645   0,                                    /* @tp_clear@ */
646   0,                                    /* @tp_richcompare@ */
647   0,                                    /* @tp_weaklistoffset@ */
648   PyObject_SelfIter,                    /* @tp_iter@ */
649   mdwopt_pynext,                        /* @tp_iternext@ */
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
665 /*----- Main code ---------------------------------------------------------*/
666
667 static 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
676 static 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
685 void ui_pyinit(void)
686 {
687   INITTYPE(optstate, root);
688   INITTYPE(mdwopt, root);
689   addmethods(methods);
690 }
691
692 void ui_pyinsert(PyObject *mod)
693 {
694   INSERT("OptState", optstate_pytype);
695   INSERT("MdwOpt", mdwopt_pytype);
696   setconstants(mod, consts);
697 }
698
699 int ui_pyready(void) { return (set_program_name()); }
700
701 /*----- That's all, folks -------------------------------------------------*/