typedef struct {
PyObject_HEAD
- mdwopt_data opt;
- char **argv, *stringdata;
- char *shortopt;
+ char *stringdata;
struct option *longopt;
+ struct optextra *extra;
+ char **argv;
+ size_t nlong, narg;
+ int flags;
+ mdwopt_data opt;
+ PyObject *prog;
+ PyObject *state;
} mdwopt_pyobj;
static PyTypeObject *mdwopt_pytype;
#define MDWOPT_PYCHECK(o) PyObject_TypeCheck((o), mdwopt_pytype)
#define MDWOPT_OPT(o) (&((mdwopt_pyobj *)(o))->opt)
#define MDWOPT_ARGV(o) (((mdwopt_pyobj *)(o))->argv)
-#define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->shortopt)
+#define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->stringdata)
#define MDWOPT_LONG(o) (((mdwopt_pyobj *)(o))->longopt)
-#define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 1))
-#define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x1ff) >> 1))
+#define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 2) | 0x200)
+#define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x3ff) >> 2))
+
+DA_DECL(obj_v, PyObject *);
+DA_DECL(opt_v, struct option);
+DA_DECL(extra_v, struct optextra);
+DA_DECL(size_v, size_t);
+
+/* Ordering of strings within `stringdata'.
+ *
+ * * `shortopt' (at the start so we don't need an extra pointer)
+ * * `argv' (individual strings addressed by `argv')
+ * * `longopt' names (in order, addressed by `longopt[i].name')
+ */
+
+struct optbuild {
+ dstr strbuf; /* string buffer */
+ size_t narg; /* number of arguments */
+ size_v off; /* offsets of string starts;
+ * doesn't include `shortopt' */
+ opt_v opt; /* options */
+ extra_v extra; /* option extra data */
+};
+#define OPTBUILD_INIT { DSTR_INIT, 0, DA_INIT, DA_INIT }
static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw)
{
- DA_DECL(obj_v, PyObject *);
- DA_DECL(opt_v, struct option);
- DA_DECL(size_v, size_t);
-
PyObject *argvobj = 0, *longoptobj = 0;
PyObject *it = 0, *t = 0, *u = 0;
char *p; size_t sz;
+ size_t i;
Py_ssize_t n;
mdwopt_pyobj *me = 0;
- char *shortopt;
+ char *shortopt = "";
unsigned flags = 0;
- int f;
- opt_v opts = DA_INIT;
- obj_v tags = DA_INIT;
- size_v off = DA_INIT;
- dstr strbuf = DSTR_INIT;
- size_t narg;
+ struct optbuild build = OPTBUILD_INIT;
+ struct option *opt;
+ struct optextra *extra;
static const char *const kwlist[] =
{ "argv", "shortopt", "longopt", "flags", 0 };
+#define EXTEND(var, vec) do { \
+ DA_ENSURE(&build.vec, 1); \
+ var = &DA(&build.vec)[DA_LEN(&build.vec)]; \
+ DA_EXTEND(&build.vec, 1); \
+} while (0)
+
+#define COPYTAB(slot, base, len) do { \
+ me->slot = xmalloc((len)*sizeof(*me->slot)); \
+ memcpy(me->slot, base, (len)*sizeof(*me->slot)); \
+} while (0)
+
+ /* Collect the arguments. */
if (!PyArg_ParseTupleAndKeywords(arg, kw, "|OsOO&:new", KWLIST,
&argvobj,
&shortopt,
if (!argvobj) SYSERR("sys.argv missing");
}
+ /* Commit the short-options string to the buffer.
+ *
+ * Putting this first means that we don't need a separate pointer. All of
+ * the other things are arrays, so avoiding maintaining a separate index or
+ * pointer for the first element will just make things unnecessarily
+ * complicated.
+ */
+ DPUTM(&build.strbuf, shortopt, strlen(shortopt) + 1);
+
+ /* Collect the arguments to be parsed. */
it = PyObject_GetIter(argvobj); if (!it) goto end;
for (;;) {
t = PyIter_Next(it); if (!t) break;
if (!TEXT_CHECK(t)) TYERR("argv should be a sequence of strings");
- TEXT_PTRLEN(t, p, sz);
- DA_PUSH(&off, strbuf.len);
- DPUTM(&strbuf, p, sz + 1);
+ DA_PUSH(&build.off, build.strbuf.len);
+ TEXT_PTRLEN(t, p, sz); DPUTM(&build.strbuf, p, sz + 1);
Py_DECREF(t); t = 0;
}
if (PyErr_Occurred()) goto end;
- narg = DA_LEN(&off);
+ build.narg = DA_LEN(&build.off);
Py_DECREF(it); it = 0;
- it = PyObject_GetIter(longoptobj); if (!it) goto end;
- for (;;) {
- t = PyIter_Next(it); if (!t) break;
- n = PySequence_Size(t); if (n < 0) goto end;
- if (n < 2 || n > 4)
- VALERR("long-options entry should be "
- "(OPT, TAG, [FLAGS = 0, [ATTR = None]])");
-
- u = PySequence_GetItem(t, 0); if (!u) goto end;
- if (!TEXT_CHECK(u)) TYERR("option name should be a string");
- TEXT_PTRLEN(u, p, sz);
- DA_PUSH(&off, strbuf.len);
- DPUTM(&strbuf, p, sz + 1);
- Py_DECREF(u); u = 0;
-
- u = PySequence_GetItem(t, 1); if (!u) goto end;
- DA_PUSH(&tags, u); u = 0;
-
- if (n < 3)
- f = 0;
- else {
+ /* Collect the long-option specifications. */
+ if (longoptobj) {
+ it = PyObject_GetIter(longoptobj); if (!it) goto end;
+ for (;;) {
+
+ /* Get the next item and check that it's basically sensible. */
+ t = PyIter_Next(it); if (!t) break;
+ n = PySequence_Size(t); if (n < 0) goto end;
+ if (n < 2 || n > 4)
+ VALERR("long-options entry should be "
+ "(NAME, VAL, [FLAG = 0, [ATTR = None]])");
+
+ /* Allocate new entries in the options and extra-data tables. */
+ EXTEND(opt, opt);
+ EXTEND(extra, extra);
+ opt->flag = 0;
+ extra->tag = 0; extra->attr = 0;
+
+ /* Get the option name and contribute it to the string buffer. */
u = PySequence_GetItem(t, 0); if (!u) goto end;
- if (getint(u, &f)) goto end;
+ if (!TEXT_CHECK(u)) TYERR("option name should be a string");
+ DA_PUSH(&build.off, build.strbuf.len);
+ TEXT_PTRLEN(u, p, sz); DPUTM(&build.strbuf, p, sz + 1);
Py_DECREF(u); u = 0;
+
+ /* Get the option tag and store it in the extra data.
+ * `PySequence_GetItem' bumps the refcount for us.
+ */
+ extra->tag = PySequence_GetItem(t, 1); if (!extra->tag) goto end;
+
+ /* Get the flags for this option. */
+ if (n < 3)
+ opt->has_arg = 0;
+ else {
+ u = PySequence_GetItem(t, 0); if (!u) goto end;
+ if (convint(u, &opt->has_arg)) goto end;
+ Py_DECREF(u); u = 0;
+ }
+
+ /* Finally, get the attribute name. */
+ if (n < 4)
+ { extra->attr = Py_None; Py_INCREF(Py_None); }
+ else {
+ extra->attr = PySequence_GetItem(t, 3);
+ if (!extra->attr) goto end;
+ }
+
+ /* Done. Let's go round again. */
+ Py_DECREF(t); t = 0;
}
+ if (PyErr_Occurred()) goto end;
+ Py_DECREF(it); it = 0;
+ }
+
+ /* Allocate the state value. */
+ t = PyBaseObject_Type.tp_alloc(&PyBaseObject_Type, 0);
+ if (!t) goto end;
+
+ /* Allocate our return value. */
+ me = (mdwopt_pyobj *)cls->tp_alloc(cls, 0);
+ me->state = t; t = 0;
+ me->flags = flags;
+ me->narg = build.narg;
+ me->nlong = DA_LEN(&build.opt);
+
+ /* Add a final terminating entry to the long-options table. */
+ EXTEND(opt, opt); opt->name = 0;
+
+ /* Copy the main tables. */
+ COPYTAB(stringdata, build.strbuf.buf, build.strbuf.len);
+ COPYTAB(longopt, DA(&build.opt), DA_LEN(&build.opt));
+ COPYTAB(extra, DA(&build.extra), DA_LEN(&build.extra));
+
+ /* Fill in the `argv' vector. */
+ me->argv = xmalloc(build.narg*sizeof(*me->argv));
+ for (i = 0; i < build.narg; i++)
+ me->argv[i] = me->stringdata + DA(&build.off)[i + 1];
+ me->argv[build.narg] = 0;
+
+ /* Fix up the string pointers and values in the long-options table. */
+ for (i = 0; i < me->nlong; i++) {
+ me->longopt[i].name =
+ me->stringdata + DA(&build.off)[i + build.narg + 1];
+ me->longopt[i].val = IXTAG(i);
+ }
+
+ /* Initialize the parser state. Set up everything because Python might
+ * ask awkward questions before we're ready.
+ */
+ me->opt.arg = 0;
+ me->opt.opt = -1;
+ me->opt.ind = 0;
+ me->opt.err = 1;
+ me->opt.prog = 0;
-
+ /* And other random things. */
+ me->prog = 0;
end:
- return (PyObject *)me;
+ /* Clean up and go home. */
+ Py_XDECREF(it); Py_XDECREF(t); Py_XDECREF(u);
+ DDESTROY(&build.strbuf);
+ if (!me) for (i = 0; i < DA_LEN(&build.extra); i++) {
+ extra = &DA(&build.extra)[i];
+ Py_XDECREF(extra->tag); Py_XDECREF(extra->attr);
+ }
+ DA_DESTROY(&build.off);
+ DA_DESTROY(&build.opt);
+ DA_DESTROY(&build.extra);
+ return ((PyObject *)me);
+
+#undef EXTEND
+#undef COPYTAB
}
static void mdwopt_pydealloc(PyObject *me)
{
mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+ size_t i;
- xfree(m->stringdata);
- xfree(m->longopt);
+ for (i = 0; i < m->nlong; i++)
+ { Py_DECREF(m->extra[i].tag); Py_DECREF(m->extra[i].attr); }
+ xfree(m->stringdata); xfree(m->longopt); xfree(m->extra);
FREEOBJ(me);
}
+static PyObject *mdwopt_pynext(PyObject *me)
+{
+ mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+ PyObject *val = 0, *arg = 0, *t = 0, *u = 0, *v = 0;
+ int f, ix, i;
+ unsigned char ch;
+ PyObject *rc = 0;
+
+again:
+ i = mdwopt(m->narg, m->argv, m->stringdata, m->longopt, &ix,
+ &m->opt, m->flags);
+
+ if (i == -1) goto end;
+
+ f = i&OPTF_NEGATED;
+
+ if (m->opt.arg) arg = TEXT_FROMSTR(m->opt.arg);
+ else { arg = Py_None; Py_INCREF(Py_None); }
+
+ if (ix < 0) {
+ ch = i&0xff;
+ val = TEXT_FROMSTRLEN((char *)&ch, 1); if (!val) goto end;
+ } else {
+ if (m->extra[ix].attr == Py_None)
+ { val = m->extra[ix].tag; Py_INCREF(val); }
+ else if (m->longopt[ix].has_arg&OPTF_SWITCH) {
+ t = PyObject_GetAttr(m->state, m->extra[ix].attr);
+ if (!t && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ t = PyInt_FromLong(0); if (!t) goto end;
+ }
+ if (!f)
+ { v = PyNumber_Or(t, m->extra[ix].tag); if (!v) goto end; }
+ else {
+ u = PyNumber_Invert(m->extra[ix].tag); if (!u) goto end;
+ v = PyNumber_And(t, u); if (!v) goto end;
+ }
+ if (PyObject_SetAttr(m->state, m->extra[ix].attr, v)) goto end;
+ Py_DECREF(t); Py_XDECREF(u); Py_DECREF(v);
+ } else {
+ if (PyObject_SetAttr(m->state, m->extra[ix].attr,
+ f ?
+ Py_None :
+ m->longopt[ix].has_arg&OPTF_ARG ?
+ arg : m->extra[ix].tag))
+ goto end;
+ }
+ Py_DECREF(arg);
+ goto again;
+ }
+
+ rc = Py_BuildValue("(OOi)", val, arg, f);
+end:
+ Py_XDECREF(val); Py_XDECREF(arg);
+ Py_XDECREF(t); Py_XDECREF(u); Py_XDECREF(v);
+ return (rc);
+}
+
+static PyObject *moget_argv(PyObject *me, void *hunoz)
+{
+ mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+ PyObject *rc = 0, *t = 0;
+ size_t i = 0;
+
+ rc = PyList_New(m->narg); if (!rc) goto fail;
+ for (i = 0; i < m->narg; i++) {
+ t = TEXT_FROMSTR(m->argv[i]); if (!t) goto fail;
+ PyList_SET_ITEM(rc, i, t); t = 0;
+ }
+ return (rc);
+
+fail:
+ Py_XDECREF(t);
+ Py_XDECREF(rc);
+ return (0);
+}
+
+static PyObject *moget_err(PyObject *me, void *hunoz)
+ { mdwopt_pyobj *m = (mdwopt_pyobj *)me; return (getbool(m->opt.err)); }
+static int moset_err(PyObject *me, PyObject *v, void *hunoz)
+{
+ mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+ int rc = -1;
+
+ if (!v) NIERR("__del__");
+ if (convbool(v, &m->opt.err)) goto end;
+ rc = 0;
+end:
+ return (rc);
+}
+
+static PyObject *moget_prog(PyObject *me, void *hunoz)
+{
+ mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+
+ if (!m->opt.prog) RETURN_NONE;
+ if (!m->prog)
+ { m->prog = TEXT_FROMSTR(m->opt.prog); if (!m->prog) return (0); }
+ RETURN_OBJ(m->prog);
+}
+static int moset_prog(PyObject *me, PyObject *v, void *hunoz)
+{
+ mdwopt_pyobj *m = (mdwopt_pyobj *)me;
+ char *p;
+ int rc = -1;
+
+ if (!v) NIERR("__del__");
+ p = TEXT_STR(v); if (!p) goto end;
+ m->opt.prog = p;
+ Py_XDECREF(m->prog); m->prog = v; Py_INCREF(v);
+ rc = 0;
+end:
+ return (rc);
+}
+
static const PyMemberDef mdwopt_pymembers[] = {
#define MEMBERSTRUCT mdwopt_pyobj
+ MEMBER(state, T_OBJECT_EX, 0, "M.state = object on which flags are set")
+ MEMRNM(arg, T_STRING, opt.arg, READONLY,
+ "M.arg -> argument of most recent option")
+ MEMRNM(ind, T_INT, opt.ind, READONLY,
+ "M.ind -> index of first non-option argument")
#undef MEMBERSTRUCT
{ 0 }
};
static const PyGetSetDef mdwopt_pygetset[] = {
#define GETSETNAME(op, name) mo##op##_##name
+ GET (argv, "M.argv -> vector of arguments (permuted)")
+ GETSET(err, "M.err = report errors to `stderr'?")
+ GETSET(prog, "M.prog = program name (to report in errors")
#undef GETSETNAME
{ 0 }
};
Py_TPFLAGS_BASETYPE,
/* @tp_doc@ */
- "MdwOpt([argv = SEQ], [shortopt = STR], [longopt = SEQ], [flags = 0])",
+ "MdwOpt([argv = SEQ], [shortopt = STR], [longopt = SEQ], [flags = 0])\n"
+ "\n"
+ "ARGV is the sequence of arguments to be parsed. If omitted, it\n"
+ "defaults to `sys.argv'.\n"
+ "\n"
+ "SHORTOPT has the form `[+|-|!][:]OPT...', where OPT is `CHAR[+][:[:]]'.\n"
+ "The CHAR names the option character; a `+' indicates that the option\n"
+ "may be negated; a `:' indicates that the option takes an argument, and\n"
+ " `::' means the argument is optional. Before the OPTs, the following\n"
+ "may appear:\n"
+ "\n"
+ " * `+' -- force POSIX option order: end iteration at first non-option;\n"
+ " * `-' -- treat non-options as arguments to option `None';\n"
+ " * `!' -- force default reordering behaviour: extract all options;\n"
+ " * `:' -- return `:' rather than `?' for missing argument.\n"
+ "\n"
+ "LONGOPT is a sequence of tuples (NAME, VAL, [FLAG = 0, [ATTR = None]]):\n"
+ "the NAME is the long-option string; FLAG is a mask of flags listed\n"
+ "below; ATTR is `None' or an attribute name; VAL is the value to return\n"
+ "or, if ATTR is not `None', to store in `state.ATTR'. Flags are:\n"
+ "\n"
+ " * `OPTF_ARGREQ' -- argument is mandatory (like `:');\n"
+ " * `OPTF_ARGOPT' -- argument is optional (like `::');\n"
+ " * `OPTF_SWITCH' -- set or clear VAL bits in ATTR;\n"
+ " * `OPTF_NEGATE' -- option may be negated\n"
+ "\n"
+ "Flags to the function are:\n"
+ " * `OPTF_NOLONGS' -- don't accept long options at all;\n"
+ " * `OPTF_NOSHORTS' -- accept long options with single `-';\n"
+ " * `OPTF_NUMBERS' -- accept numeric options (value `#');\n"
+ " * `OPTF_NEGATION' -- allow options to be negated;\n"
+ " * `OPTF_ENVVAR' -- read options from environment variable;\n"
+ " * `OPTF_NOPROGNAME' -- don't assume program name is in `ARGV[0]'\n."
+ "\n"
+ "The object is iterable, and yields triples of the form (VAL, ARG,\n"
+ "FLAGS): VAL is the option letter (for short options) or VAL slot (for a\n"
+ "long option); ARG is the argument, or `None'; and FLAG is a mask of the\n"
+ "following flags:\n"
+ "\n"
+ " * `OPTF_NEGATED' -- set if the option was negated.\n"
+ "\n"
+ "Special values of VAL are:\n"
+ "\n"
+ " * '?' -- an error was encountered;\n"
+ " * ':' -- a required argument was omitted (if `:' is in SHORTOPT);\n"
+ " * '#' -- a numeric option was found (if `OPTF_NUMBERS' is in FLAGS).\n"
+ "\n"
+ "Useful attributes:\n"
+ "\n"
+ " * `arg' (read-only) -- argument to most recent option, or `None';\n"
+ " * `argv' (read-only) -- vector of arguments to parse (permuted);\n"
+ " * `ind' (read-only) -- index of first non-option argument;\n"
+ " * `err' (read-write) -- boolean: report errors to `stderr'?;\n"
+ " * `prog' (read-write) -- program name (to report in errors);\n"
+ " * `state' (read-write) -- object to accumulate attribute settings\n.",
0, /* @tp_traverse@ */
0, /* @tp_clear@ */
0, /* @tp_richcompare@ */
0, /* @tp_weaklistoffset@ */
- 0, /* @tp_iter@ */
- 0, /* @tp_iternext@ */
+ PyObject_SelfIter, /* @tp_iter@ */
+ mdwopt_pynext, /* @tp_iternext@ */
PYMETHODS(mdwopt), /* @tp_methods@ */
PYMEMBERS(mdwopt), /* @tp_members@ */
PYGETSET(mdwopt), /* @tp_getset@ */