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