_actions.c revision 1659
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/*
1659N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
591N/A * Use is subject to license terms.
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
591N/Astatic int
591N/Aadd_to_attrs(PyObject *attrs, PyObject *key, PyObject *attr)
591N/A{
591N/A int contains;
629N/A int ret;
591N/A
591N/A contains = PyDict_Contains(attrs, key);
629N/A if (contains == 0) {
629N/A ret = PyDict_SetItem(attrs, key, attr);
629N/A Py_DECREF(key);
629N/A Py_DECREF(attr);
629N/A return (ret);
629N/A } else if (contains == 1) {
591N/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);
629N/A Py_DECREF(av);
629N/A Py_DECREF(key);
629N/A Py_DECREF(attr);
629N/A return (ret);
591N/A } else {
591N/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 PyList_SET_ITEM(list, 1, attr);
629N/A ret = PyDict_SetItem(attrs, key, list);
629N/A Py_DECREF(list);
629N/A Py_DECREF(key);
629N/A return (ret);
591N/A }
591N/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)
591N/A{
591N/A PyObject *val;
591N/A
629N/A if ((val = Py_BuildValue("sis", str, pos, msg)) != NULL) {
591N/A PyErr_SetObject(MalformedActionError, val);
629N/A Py_DECREF(val);
629N/A }
591N/A return (NULL);
591N/A}
591N/A
1659N/Astatic void *
1659N/Aset_invaliderr(const char *str, const char *msg)
1659N/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 return (NULL);
1659N/A}
1659N/A
591N/A/*ARGSUSED*/
591N/Astatic PyObject *
591N/A_fromstr(PyObject *self, PyObject *args)
591N/A{
1659N/A char *s, *str, *keystr, *slashmap = NULL;
591N/A int strl;
1659N/A int i, ks, vs, keysize;
591N/A char quote;
591N/A PyObject *type = NULL;
591N/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#define malformed(msg) set_malformederr(str, i, (msg))
1659N/A#define invalid(msg) set_invaliderr(str, (msg))
591N/A
591N/A if (PyArg_ParseTuple(args, "s#", &str, &strl) == 0) {
591N/A PyErr_SetString(PyExc_ValueError, "could not parse argument");
591N/A return (NULL);
591N/A }
591N/A
591N/A s = strpbrk(str, " \t");
591N/A
591N/A i = strl;
591N/A if (s == NULL)
591N/A return (malformed("no attributes"));
591N/A
591N/A if ((type = PyString_FromStringAndSize(str, s - str)) == NULL)
591N/A return (NULL);
591N/A
591N/A ks = vs = s - str;
591N/A state = WS;
629N/A if ((attrs = PyDict_New()) == NULL) {
629N/A Py_DECREF(type);
591N/A return (NULL);
629N/A }
591N/A for (i = s - str; str[i]; i++) {
591N/A if (state == KEY) {
1659N/A keysize = i - ks;
1659N/A keystr = &str[ks];
1659N/A
591N/A if (str[i] == ' ' || str[i] == '\t') {
629N/A if (PyDict_Size(attrs) > 0 || hash != NULL) {
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("whitespace in key"));
629N/A }
591N/A else {
591N/A if ((hash = PyString_FromStringAndSize(
1659N/A keystr, keysize)) == NULL)
591N/A return (NULL);
591N/A state = WS;
591N/A }
591N/A } else if (str[i] == '=') {
591N/A if ((key = PyString_FromStringAndSize(
1659N/A keystr, keysize)) == NULL)
591N/A return (NULL);
1659N/A
1659N/A if (keysize == 4 && strncmp(keystr, "data", keysize) == 0) {
1659N/A Py_DECREF(key);
1659N/A Py_DECREF(type);
1659N/A Py_DECREF(attrs);
1659N/A Py_XDECREF(hash);
1659N/A return (invalid("invalid key: 'data'"));
1659N/A }
1659N/A
629N/A if (i == ks) {
629N/A Py_DECREF(key);
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
629N/A return (malformed("impossible: missing key"));
629N/A }
629N/A else if (++i == strl) {
629N/A Py_DECREF(key);
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("missing value"));
629N/A }
591N/A if (str[i] == '\'' || str[i] == '\"') {
591N/A state = QVAL;
591N/A quote = str[i];
591N/A vs = i + 1;
629N/A } else if (str[i] == ' ' || str[i] == '\t') {
629N/A Py_DECREF(key);
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("missing value"));
629N/A }
591N/A else {
591N/A state = UQVAL;
591N/A vs = i;
591N/A }
629N/A } else if (str[i] == '\'' || str[i] == '\"') {
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("quote in key"));
629N/A }
591N/A } else if (state == QVAL) {
591N/A if (str[i] == '\\') {
591N/A if (i == strl - 1)
591N/A break;
591N/A /*
591N/A * "slashmap" is a simple bitmap (bytemap?)
591N/A * keeping track of what characters are
591N/A * backslashes that need to be removed
591N/A * from the final attribute string.
591N/A * All other bytes are NUL bytes.
591N/A */
591N/A if (slashmap == NULL) {
591N/A int smlen = strl - (i - vs);
591N/A slashmap = calloc(1, smlen + 1);
591N/A if (slashmap == NULL)
591N/A return (PyErr_NoMemory());
591N/A }
591N/A i++;
591N/A if (str[i] == '\\' || str[i] == quote) {
591N/A slashmap[i - 1 - vs] = '\\';
591N/A }
591N/A } else if (str[i] == quote) {
591N/A state = WS;
591N/A if (slashmap != NULL) {
591N/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 free(slashmap);
591N/A return (PyErr_NoMemory());
591N/A }
591N/A /*
591N/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[j] == '\\') {
591N/A o++;
591N/A continue;
591N/A }
591N/A sattr[j - o] = str[vs + j];
591N/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 return (NULL);
591N/A }
591N/A free(sattr);
591N/A } else if ((attr = PyString_FromStringAndSize(
591N/A &str[vs], i - vs)) == NULL)
591N/A return (NULL);
591N/A if (add_to_attrs(attrs, key, attr) == -1)
591N/A return (NULL);
591N/A }
591N/A } else if (state == UQVAL) {
591N/A if (str[i] == ' ' || str[i] == '\t') {
591N/A state = WS;
591N/A attr = PyString_FromStringAndSize(&str[vs], i - vs);
591N/A if (add_to_attrs(attrs, key, attr) == -1)
591N/A return (NULL);
591N/A }
591N/A } else if (state == WS) {
591N/A if (str[i] != ' ' && str[i] != '\t') {
591N/A state = KEY;
591N/A ks = i;
629N/A if (str[i] == '=') {
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("missing key"));
629N/A }
591N/A }
591N/A }
591N/A }
591N/A
591N/A if (state == QVAL) {
591N/A if (slashmap != NULL)
591N/A free(slashmap);
591N/A
629N/A Py_DECREF(key);
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("unfinished quoted value"));
591N/A }
629N/A if (state == KEY) {
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A Py_XDECREF(hash);
591N/A return (malformed("missing value"));
629N/A }
591N/A
591N/A if (state == UQVAL) {
591N/A attr = PyString_FromStringAndSize(&str[vs], i - vs);
591N/A if (add_to_attrs(attrs, key, attr) == -1)
591N/A return (NULL);
591N/A }
591N/A
591N/A if (hash == NULL)
591N/A hash = Py_None;
591N/A
591N/A ret = Py_BuildValue("OOO", type, hash, attrs);
629N/A Py_DECREF(type);
629N/A Py_DECREF(attrs);
629N/A if (hash != Py_None)
629N/A Py_DECREF(hash);
591N/A return (ret);
591N/A}
591N/A
591N/Astatic PyMethodDef methods[] = {
591N/A { "_fromstr", _fromstr, METH_VARARGS },
591N/A { NULL, NULL }
591N/A};
591N/A
591N/APyMODINIT_FUNC
591N/Ainit_actions(void)
591N/A{
591N/A PyObject *sys, *pkg_actions;
591N/A PyObject *sys_modules;
591N/A
591N/A if (Py_InitModule("_actions", methods) == NULL)
591N/A return;
591N/A
591N/A /*
591N/A * We need to retrieve the MalformedActionError object from pkg.actions.
591N/A * We can't import pkg.actions directly, because that would result in a
591N/A * circular dependency. But the "sys" module has a dict called
591N/A * "modules" which maps loaded module names to the corresponding module
591N/A * objects. We can then grab the exception from those objects.
591N/A */
591N/A
591N/A if ((sys = PyImport_ImportModule("sys")) == NULL)
591N/A return;
591N/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"))
591N/A == NULL) {
591N/A /* No exception is set */
591N/A PyErr_SetString(PyExc_KeyError, "pkg.actions");
591N/A return;
591N/A }
591N/A
591N/A MalformedActionError = \
591N/A PyObject_GetAttrString(pkg_actions, "MalformedActionError");
1659N/A InvalidActionError = \
1659N/A PyObject_GetAttrString(pkg_actions, "InvalidActionError");
591N/A}