_actions.c revision 2233
591N/A/*
591N/A * CDDL HEADER START
591N/A *
591N/A * The contents of this file are subject to the terms of the
591N/A * Common Development and Distribution License (the "License").
591N/A * You may not use this file except in compliance with the License.
591N/A *
591N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
591N/A * or http://www.opensolaris.org/os/licensing.
591N/A * See the License for the specific language governing permissions
591N/A * and limitations under the License.
591N/A *
591N/A * When distributing Covered Code, include this CDDL HEADER in each
591N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
591N/A * If applicable, add the following below this CDDL HEADER, with the
591N/A * fields enclosed by brackets "[]" replaced with your own identifying
591N/A * information: Portions Copyright [yyyy] [name of copyright owner]
591N/A *
591N/A * CDDL HEADER END
591N/A */
591N/A
591N/A/*
2233N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
591N/A */
591N/A
591N/A#include <Python.h>
591N/A
591N/A#include <string.h>
591N/A
591N/Astatic PyObject *MalformedActionError;
1659N/Astatic PyObject *InvalidActionError;
591N/A
2457N/Astatic int
2457N/Aadd_to_attrs(PyObject *attrs, PyObject *key, PyObject *attr)
591N/A{
591N/A int contains, ret;
591N/A
1846N/A contains = PyDict_Contains(attrs, key);
591N/A if (contains == 0) {
591N/A return (PyDict_SetItem(attrs, key, attr));
629N/A } else if (contains == 1) {
1846N/A PyObject *av = PyDict_GetItem(attrs, key);
629N/A Py_INCREF(av);
591N/A if (PyList_Check(av)) {
629N/A ret = PyList_Append(av, attr);
591N/A Py_DECREF(av);
629N/A return (ret);
629N/A } else {
629N/A PyObject *list;
591N/A if ((list = PyList_New(2)) == NULL)
591N/A return (-1);
591N/A PyList_SET_ITEM(list, 0, av);
591N/A Py_INCREF(attr);
591N/A PyList_SET_ITEM(list, 1, attr);
1846N/A ret = PyDict_SetItem(attrs, key, list);
591N/A Py_DECREF(list);
629N/A return (ret);
629N/A }
629N/A } else if (contains == -1)
591N/A return (-1);
591N/A
591N/A /* Shouldn't ever get here */
591N/A return (0);
591N/A}
591N/A
591N/Astatic void
591N/Aset_malformederr(const char *str, int pos, const char *msg)
1973N/A{
591N/A PyObject *val;
591N/A
591N/A if ((val = Py_BuildValue("sis", str, pos, msg)) != NULL) {
591N/A PyErr_SetObject(MalformedActionError, val);
629N/A Py_DECREF(val);
591N/A }
629N/A}
629N/A
591N/Astatic void
591N/Aset_invaliderr(const char *str, const char *msg)
1973N/A{
1659N/A PyObject *val;
1659N/A
1659N/A if ((val = Py_BuildValue("ss", str, msg)) != NULL) {
1659N/A PyErr_SetObject(InvalidActionError, val);
1659N/A Py_DECREF(val);
1659N/A }
1659N/A}
1659N/A
1659N/A/*ARGSUSED*/
1659N/Astatic PyObject *
591N/A_fromstr(PyObject *self, PyObject *args)
591N/A{
591N/A char *s = NULL;
591N/A char *str = NULL;
1890N/A char *keystr = NULL;
1890N/A int *slashmap = NULL;
2457N/A int strl;
1890N/A int i, ks, vs, keysize;
2233N/A int smlen, smpos;
591N/A char quote;
1659N/A PyObject *type = NULL;
2233N/A PyObject *hash = NULL;
591N/A PyObject *attrs = NULL;
591N/A PyObject *ret = NULL;
591N/A PyObject *key = NULL;
591N/A PyObject *attr = NULL;
591N/A enum {
591N/A KEY, /* key */
591N/A UQVAL, /* unquoted value */
591N/A QVAL, /* quoted value */
591N/A WS /* whitespace */
591N/A } state;
591N/A
591N/A /*
591N/A * If malformed() or invalid() are used, CLEANUP_REFS can only be used
591N/A * after. Likewise, PyMem_Free(str) should not be called before using
1973N/A * malformed() or invalid(). Failure to order this properly will cause
1973N/A * corruption of the exception messages.
1973N/A */
1973N/A#define malformed(msg) set_malformederr(str, i, (msg))
1973N/A#define invalid(msg) set_invaliderr(str, (msg))
1973N/A#define CLEANUP_REFS \
591N/A PyMem_Free(str);\
1659N/A Py_XDECREF(key);\
1846N/A Py_XDECREF(type);\
1890N/A Py_XDECREF(attr);\
1846N/A Py_XDECREF(attrs);\
1846N/A Py_XDECREF(hash);
1846N/A
1846N/A /*
2457N/A * The action string is currently assumed to be a stream of bytes that
2457N/A * are valid UTF-8. This method works regardless of whether the string
591N/A * object provided is a Unicode object, string object, or a character
1890N/A * buffer.
1890N/A */
1890N/A if (PyArg_ParseTuple(args, "et#", "utf-8", &str, &strl) == 0) {
1890N/A PyErr_SetString(PyExc_ValueError, "could not parse argument");
1890N/A return (NULL);
1890N/A }
1890N/A
591N/A s = strpbrk(str, " \t");
591N/A
591N/A i = strl;
591N/A if (s == NULL) {
591N/A malformed("no attributes");
591N/A PyMem_Free(str);
591N/A return (NULL);
1890N/A }
1973N/A
1890N/A if ((type = PyString_FromStringAndSize(str, s - str)) == NULL) {
1973N/A PyMem_Free(str);
1890N/A return (NULL);
591N/A }
1890N/A
1890N/A PyString_InternInPlace(&type);
591N/A
1890N/A ks = vs = s - str;
591N/A state = WS;
2205N/A if ((attrs = PyDict_New()) == NULL) {
2205N/A PyMem_Free(str);
591N/A Py_DECREF(type);
591N/A return (NULL);
629N/A }
1890N/A for (i = s - str; str[i]; i++) {
629N/A if (state == KEY) {
591N/A keysize = i - ks;
629N/A keystr = &str[ks];
591N/A
591N/A if (str[i] == ' ' || str[i] == '\t') {
1659N/A if (PyDict_Size(attrs) > 0 || hash != NULL) {
1659N/A malformed("whitespace in key");
1659N/A CLEANUP_REFS;
591N/A return (NULL);
629N/A }
1973N/A else {
1846N/A if ((hash = PyString_FromStringAndSize(
1973N/A keystr, keysize)) == NULL) {
629N/A CLEANUP_REFS;
591N/A return (NULL);
591N/A }
1846N/A state = WS;
1846N/A }
591N/A } else if (str[i] == '=') {
1846N/A if ((key = PyString_FromStringAndSize(
2457N/A keystr, keysize)) == NULL) {
591N/A CLEANUP_REFS;
591N/A return (NULL);
591N/A }
591N/A
1846N/A if (keysize == 4 && strncmp(keystr, "data",
1846N/A keysize) == 0) {
591N/A invalid("invalid key: 'data'");
1846N/A CLEANUP_REFS;
1659N/A return (NULL);
1973N/A }
1973N/A
1973N/A /*
1846N/A * Pool attribute key to reduce memory usage and
1973N/A * potentially improve lookup performance.
1659N/A */
1659N/A PyString_InternInPlace(&key);
1846N/A
1846N/A if (i == ks) {
1846N/A malformed("impossible: missing key");
1846N/A CLEANUP_REFS;
1846N/A return (NULL);
1846N/A }
629N/A else if (++i == strl) {
1973N/A malformed("missing value");
1846N/A CLEANUP_REFS;
1973N/A return (NULL);
629N/A }
629N/A if (str[i] == '\'' || str[i] == '\"') {
1973N/A state = QVAL;
1846N/A quote = str[i];
1973N/A vs = i + 1;
629N/A } else if (str[i] == ' ' || str[i] == '\t') {
591N/A malformed("missing value");
591N/A CLEANUP_REFS;
591N/A return (NULL);
591N/A }
629N/A else {
1973N/A state = UQVAL;
1846N/A vs = i;
1973N/A }
629N/A } else if (str[i] == '\'' || str[i] == '\"') {
591N/A malformed("quote in key");
591N/A CLEANUP_REFS;
591N/A return (NULL);
591N/A }
629N/A } else if (state == QVAL) {
1973N/A if (str[i] == '\\') {
1846N/A if (i == strl - 1)
1973N/A break;
629N/A /*
591N/A * "slashmap" is a list of the positions of the
591N/A * backslashes that need to be removed from the
591N/A * final attribute string.
591N/A */
591N/A if (slashmap == NULL) {
2233N/A smlen = 16;
2233N/A slashmap = calloc(smlen, sizeof(int));
2233N/A if (slashmap == NULL) {
591N/A PyMem_Free(str);
591N/A return (PyErr_NoMemory());
2233N/A }
2233N/A smpos = 0;
2233N/A /*
2233N/A * Terminate slashmap with an invalid
2233N/A * value so we don't think there's a
2233N/A * slash right at the beginning.
2233N/A */
2233N/A slashmap[smpos] = -1;
2233N/A } else if (smpos == smlen - 1) {
2233N/A smlen *= 2;
2233N/A slashmap = realloc(slashmap,
2233N/A smlen * sizeof(int));
2233N/A if (slashmap == NULL) {
2233N/A PyMem_Free(str);
2233N/A return (PyErr_NoMemory());
2233N/A }
2233N/A }
1890N/A i++;
1890N/A if (str[i] == '\\' || str[i] == quote) {
591N/A slashmap[smpos++] = i - 1 - vs;
1890N/A /*
591N/A * Keep slashmap properly terminated so
591N/A * that a realloc()ed array doesn't give
591N/A * us random slash positions.
2233N/A */
2233N/A slashmap[smpos] = -1;
2233N/A }
2233N/A } else if (str[i] == quote) {
2233N/A state = WS;
2233N/A if (slashmap != NULL) {
2233N/A char *sattr;
591N/A int j, o, attrlen;
591N/A
591N/A attrlen = i - vs;
591N/A sattr = calloc(1, attrlen + 1);
591N/A if (sattr == NULL) {
591N/A PyMem_Free(str);
591N/A free(slashmap);
591N/A return (PyErr_NoMemory());
591N/A }
591N/A /*
1890N/A * Copy the attribute from str into
591N/A * sattr, removing backslashes as
591N/A * slashmap indicates we should.
591N/A */
591N/A for (j = 0, o = 0; j < attrlen; j++) {
591N/A if (slashmap[o] == j) {
591N/A o++;
591N/A continue;
591N/A }
591N/A sattr[j - o] = str[vs + j];
2233N/A }
591N/A
591N/A free(slashmap);
591N/A slashmap = NULL;
591N/A
591N/A if ((attr = PyString_FromStringAndSize(
591N/A sattr, attrlen - o)) == NULL) {
591N/A free(sattr);
591N/A CLEANUP_REFS;
591N/A return (NULL);
591N/A }
591N/A free(sattr);
591N/A } else {
1846N/A Py_XDECREF(attr);
591N/A if ((attr = PyString_FromStringAndSize(
591N/A &str[vs], i - vs)) == NULL) {
591N/A CLEANUP_REFS;
1846N/A return (NULL);
1846N/A }
1846N/A }
1846N/A
1846N/A PyString_InternInPlace(&attr);
1846N/A if (add_to_attrs(attrs, key, attr) == -1) {
1846N/A CLEANUP_REFS;
1846N/A return (NULL);
1846N/A }
2457N/A }
2457N/A } else if (state == UQVAL) {
2457N/A if (str[i] == ' ' || str[i] == '\t') {
2457N/A state = WS;
2457N/A Py_XDECREF(attr);
2457N/A attr = PyString_FromStringAndSize(&str[vs], i - vs);
2457N/A PyString_InternInPlace(&attr);
2457N/A if (add_to_attrs(attrs, key, attr) == -1) {
2457N/A CLEANUP_REFS;
2457N/A return (NULL);
2457N/A }
2457N/A }
2457N/A } else if (state == WS) {
2457N/A if (str[i] != ' ' && str[i] != '\t') {
2457N/A state = KEY;
1846N/A ks = i;
591N/A if (str[i] == '=') {
591N/A malformed("missing key");
591N/A CLEANUP_REFS;
591N/A return (NULL);
1846N/A }
591N/A }
2457N/A }
2457N/A }
2457N/A
2457N/A if (state == QVAL) {
2457N/A if (slashmap != NULL)
2457N/A free(slashmap);
2457N/A
2457N/A malformed("unfinished quoted value");
2457N/A CLEANUP_REFS;
2457N/A return (NULL);
2457N/A }
2457N/A if (state == KEY) {
2457N/A malformed("missing value");
2457N/A CLEANUP_REFS;
2457N/A return (NULL);
1846N/A }
591N/A
591N/A if (state == UQVAL) {
591N/A Py_XDECREF(attr);
591N/A attr = PyString_FromStringAndSize(&str[vs], i - vs);
591N/A PyString_InternInPlace(&attr);
629N/A if (add_to_attrs(attrs, key, attr) == -1) {
1973N/A CLEANUP_REFS;
1846N/A return (NULL);
1973N/A }
629N/A }
591N/A
591N/A PyMem_Free(str);
591N/A if (hash == NULL)
591N/A hash = Py_None;
591N/A
591N/A ret = Py_BuildValue("OOO", type, hash, attrs);
591N/A Py_XDECREF(key);
591N/A Py_XDECREF(attr);
1973N/A Py_DECREF(type);
1846N/A Py_DECREF(attrs);
1973N/A if (hash != Py_None)
591N/A Py_DECREF(hash);
629N/A return (ret);
1973N/A}
1846N/A
1973N/Astatic PyMethodDef methods[] = {
629N/A { "_fromstr", _fromstr, METH_VARARGS },
591N/A { NULL, NULL }
591N/A};
1846N/A
591N/APyMODINIT_FUNC
2457N/Ainit_actions(void)
2457N/A{
2457N/A PyObject *sys, *pkg_actions;
2457N/A PyObject *sys_modules;
2457N/A
2457N/A if (Py_InitModule("_actions", methods) == NULL)
2457N/A return;
2457N/A
2457N/A /*
2457N/A * We need to retrieve the MalformedActionError object from pkg.actions.
2457N/A * We can't import pkg.actions directly, because that would result in a
2457N/A * circular dependency. But the "sys" module has a dict called
2457N/A * "modules" which maps loaded module names to the corresponding module
2457N/A * objects. We can then grab the exception from those objects.
2457N/A */
1846N/A
591N/A if ((sys = PyImport_ImportModule("sys")) == NULL)
591N/A return;
1890N/A
591N/A if ((sys_modules = PyObject_GetAttrString(sys, "modules")) == NULL)
591N/A return;
591N/A
591N/A if ((pkg_actions = PyDict_GetItemString(sys_modules, "pkg.actions"))
1846N/A == NULL) {
1846N/A /* No exception is set */
629N/A PyErr_SetString(PyExc_KeyError, "pkg.actions");
629N/A return;
629N/A }
629N/A
591N/A MalformedActionError = \
591N/A PyObject_GetAttrString(pkg_actions, "MalformedActionError");
591N/A InvalidActionError = \
591N/A PyObject_GetAttrString(pkg_actions, "InvalidActionError");
591N/A}
591N/A