view Python/codecs.c @ 11999:c94247b5c355 legacy-trunk

Christian Tismer's "trashcan" patch: Added wrapping macros to dictobject.c, listobject.c, tupleobject.c, frameobject.c, traceback.c that safely prevends core dumps on stack overflow. Macros and functions in object.c, object.h. The method is an "elevator destructor" that turns cascading deletes into tail recursive behavior when some limit is hit.
author Guido van Rossum <guido@python.org>
date Mon, 13 Mar 2000 16:01:29 +0000
parents d0e06efb3165
children 7994d9db7f4d
line wrap: on
line source

/* ------------------------------------------------------------------------

   Python Codec Registry and support functions

Written by Marc-Andre Lemburg (mal@lemburg.com).

(c) Copyright CNRI, All Rights Reserved. NO WARRANTY.

   ------------------------------------------------------------------------ */

#include "Python.h"
#include <ctype.h>

/* --- Globals ------------------------------------------------------------ */

static PyObject *_PyCodec_SearchPath;
static PyObject *_PyCodec_SearchCache;

/* Flag used for lazy import of the standard encodings package */
static int import_encodings_called = 0;

/* --- Codec Registry ----------------------------------------------------- */

/* Import the standard encodings package which will register the first
   codec search function. 

   This is done in a lazy way so that the Unicode implementation does
   not downgrade startup time of scripts not needing it.

   Errors are silently ignored by this function. Only one try is made.

*/

static
void import_encodings() 
{
    PyObject *mod;
    
    import_encodings_called = 1;
    mod = PyImport_ImportModule("encodings");
    if (mod == NULL) {
	PyErr_Clear();
	return;
    }
    Py_DECREF(mod);
}

/* Register a new codec search function.

   The search_function's refcount is incremented by this function. */

int PyCodec_Register(PyObject *search_function)
{
    if (!import_encodings_called)
	import_encodings();
    if (search_function == NULL) {
	PyErr_BadArgument();
	return -1;
    }
    if (!PyCallable_Check(search_function)) {
	PyErr_SetString(PyExc_TypeError,
			"argument must be callable");
	return -1;
    }
    return PyList_Append(_PyCodec_SearchPath, search_function);
}

static
PyObject *lowercasestring(const char *string)
{
    register int i;
    int len = strlen(string);
    char *p;
    PyObject *v;
    
    v = PyString_FromStringAndSize(NULL, len);
    if (v == NULL)
	return NULL;
    p = PyString_AS_STRING(v);
    for (i = 0; i < len; i++)
	p[i] = tolower(string[i]);
    return v;
}

/* Lookup the given encoding and return a tuple providing the codec
   facilities.

   The encoding string is looked up converted to all lower-case
   characters. This makes encodings looked up through this mechanism
   effectively case-insensitive.

   If no codec is found, a KeyError is set and NULL returned.  */

PyObject *_PyCodec_Lookup(const char *encoding)
{
    PyObject *result, *args = NULL, *v;
    int i, len;

    if (!import_encodings_called)
	import_encodings();

    /* Convert the encoding to a lower-cased Python string */
    v = lowercasestring(encoding);
    if (v == NULL)
	goto onError;
    PyString_InternInPlace(&v);

    /* First, try to lookup the name in the registry dictionary */
    result = PyDict_GetItem(_PyCodec_SearchCache, v);
    if (result != NULL) {
	Py_INCREF(result);
	return result;
    }
    
    /* Next, scan the search functions in order of registration */
    len = PyList_Size(_PyCodec_SearchPath);
    if (len < 0)
	goto onError;

    args = PyTuple_New(1);
    if (args == NULL)
	goto onError;
    PyTuple_SET_ITEM(args,0,v);

    for (i = 0; i < len; i++) {
	PyObject *func;
	
	func = PyList_GetItem(_PyCodec_SearchPath, i);
	if (func == NULL)
	    goto onError;
	result = PyEval_CallObject(func,args);
	if (result == NULL)
	    goto onError;
	if (result == Py_None) {
	    Py_DECREF(result);
	    continue;
	}
	if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 4) {
	    PyErr_SetString(PyExc_TypeError,
			    "codec search functions must return 4-tuples");
	    Py_DECREF(result);
	    goto onError;
	}
	break;
    }
    if (i == len) {
	/* XXX Perhaps we should cache misses too ? */
	PyErr_SetString(PyExc_LookupError,
			"unkown encoding");
	goto onError;
    }

    /* Cache and return the result */
    PyDict_SetItem(_PyCodec_SearchCache, v, result);
    Py_DECREF(args);
    return result;

 onError:
    Py_XDECREF(args);
    return NULL;
}

static
PyObject *args_tuple(PyObject *object,
		     const char *errors)
{
    PyObject *args;
    
    args = PyTuple_New(1 + (errors != NULL));
    if (args == NULL)
	return NULL;
    Py_INCREF(object);
    PyTuple_SET_ITEM(args,0,object);
    if (errors) {
	PyObject *v;
	
	v = PyString_FromString(errors);
	if (v == NULL) {
	    Py_DECREF(args);
	    return NULL;
	}
	PyTuple_SET_ITEM(args, 1, v);
    }
    return args;
}

/* Build a codec by calling factory(stream[,errors]) or just
   factory(errors) depending on whether the given parameters are
   non-NULL. */

static
PyObject *build_stream_codec(PyObject *factory,
			     PyObject *stream,
			     const char *errors)
{
    PyObject *args, *codec;

    args = args_tuple(stream, errors);
    if (args == NULL)
	return NULL;
    
    codec = PyEval_CallObject(factory, args);
    Py_DECREF(args);
    return codec;
}

/* Convenience APIs to query the Codec registry. 
   
   All APIs return a codec object with incremented refcount.
   
 */

PyObject *PyCodec_Encoder(const char *encoding)
{
    PyObject *codecs;
    PyObject *v;

    codecs = _PyCodec_Lookup(encoding);
    if (codecs == NULL)
	goto onError;
    v = PyTuple_GET_ITEM(codecs,0);
    Py_INCREF(v);
    return v;

 onError:
    return NULL;
}

PyObject *PyCodec_Decoder(const char *encoding)
{
    PyObject *codecs;
    PyObject *v;

    codecs = _PyCodec_Lookup(encoding);
    if (codecs == NULL)
	goto onError;
    v = PyTuple_GET_ITEM(codecs,1);
    Py_INCREF(v);
    return v;

 onError:
    return NULL;
}

PyObject *PyCodec_StreamReader(const char *encoding,
			       PyObject *stream,
			       const char *errors)
{
    PyObject *codecs;

    codecs = _PyCodec_Lookup(encoding);
    if (codecs == NULL)
	goto onError;
    return build_stream_codec(PyTuple_GET_ITEM(codecs,2),stream,errors);

 onError:
    return NULL;
}

PyObject *PyCodec_StreamWriter(const char *encoding,
			       PyObject *stream,
			       const char *errors)
{
    PyObject *codecs;

    codecs = _PyCodec_Lookup(encoding);
    if (codecs == NULL)
	goto onError;
    return build_stream_codec(PyTuple_GET_ITEM(codecs,3),stream,errors);

 onError:
    return NULL;
}

/* Encode an object (e.g. an Unicode object) using the given encoding
   and return the resulting encoded object (usually a Python string).

   errors is passed to the encoder factory as argument if non-NULL. */

PyObject *PyCodec_Encode(PyObject *object,
			 const char *encoding,
			 const char *errors)
{
    PyObject *encoder = NULL;
    PyObject *args = NULL, *result;
    PyObject *v;

    encoder = PyCodec_Encoder(encoding);
    if (encoder == NULL)
	goto onError;

    args = args_tuple(object, errors);
    if (args == NULL)
	goto onError;
    
    result = PyEval_CallObject(encoder,args);
    if (result == NULL)
	goto onError;

    if (!PyTuple_Check(result) || 
	PyTuple_GET_SIZE(result) != 2) {
	PyErr_SetString(PyExc_TypeError,
			"encoder must return a tuple (object,integer)");
	goto onError;
    }
    v = PyTuple_GET_ITEM(result,0);
    Py_INCREF(v);
    /* We don't check or use the second (integer) entry. */

    Py_DECREF(args);
    Py_DECREF(encoder);
    Py_DECREF(result);
    return v;
	
 onError:
    Py_XDECREF(args);
    Py_XDECREF(encoder);
    return NULL;
}

/* Decode an object (usually a Python string) using the given encoding
   and return an equivalent object (e.g. an Unicode object).

   errors is passed to the decoder factory as argument if non-NULL. */

PyObject *PyCodec_Decode(PyObject *object,
			 const char *encoding,
			 const char *errors)
{
    PyObject *decoder = NULL;
    PyObject *args = NULL, *result = NULL;
    PyObject *v;

    decoder = PyCodec_Decoder(encoding);
    if (decoder == NULL)
	goto onError;

    args = args_tuple(object, errors);
    if (args == NULL)
	goto onError;
    
    result = PyEval_CallObject(decoder,args);
    if (result == NULL)
	goto onError;
    if (!PyTuple_Check(result) || 
	PyTuple_GET_SIZE(result) != 2) {
	PyErr_SetString(PyExc_TypeError,
			"decoder must return a tuple (object,integer)");
	goto onError;
    }
    v = PyTuple_GET_ITEM(result,0);
    Py_INCREF(v);
    /* We don't check or use the second (integer) entry. */

    Py_DECREF(args);
    Py_DECREF(decoder);
    Py_DECREF(result);
    return v;
	
 onError:
    Py_XDECREF(args);
    Py_XDECREF(decoder);
    Py_XDECREF(result);
    return NULL;
}

void _PyCodecRegistry_Init()
{
    if (_PyCodec_SearchPath == NULL)
	_PyCodec_SearchPath = PyList_New(0);
    if (_PyCodec_SearchCache == NULL)
	_PyCodec_SearchCache = PyDict_New();
    if (_PyCodec_SearchPath == NULL || 
	_PyCodec_SearchCache == NULL)
	Py_FatalError("can't intialize codec registry");
}

void _PyCodecRegistry_Fini()
{
    Py_XDECREF(_PyCodec_SearchPath);
    Py_XDECREF(_PyCodec_SearchCache);
}