/* $Id: NSLintermodule.c,v 1.56 2004/07/01 11:02:58 ht Exp $*/
#include <stdarg.h>
#include "Python.h"
#include <assert.h>
/* need this for internals of NSL_Doctype_I */
#include "sys/nsllib.h"
#include "nsl.h"
#include "lt-safe.h"
/* need this for internals of RXP DTD */
#include "sys/dtd.h"
#include "sys/input.h"
#define HashTable HashTableOther
#include "sys/xmlparser.h"
#undef HashTable
#include "string16.h"
#include "./hash.h"

#ifdef WIN32
#define PYXML_API __declspec(dllexport)
#else
#define PYXML_API
#endif

#if CHAR_SIZE == 8
#define PyCharStar_FromCharStar PyString_FromStringAndSize
#define IsCharStar(obj) PyString_Check(obj)
#define CheckCharStar(obj,aname,fname)  if (!PyString_Check(obj)) { \
  return error(aname "argument to" fname "not a string"); \
}
#define ExtractCharStar PyString_AsString
#define PyCharStar_FromLatin1 PyString_FromString
#define Free(obj)
#else
#define PyCharStar_FromCharStar(str, len) PyUnicode_DecodeUTF16((char*)(str), 2*(len), NULL, NULL)
#define IsCharStar(obj) (PyString_Check(obj) || PyUnicode_Check(obj))
#define CheckCharStar(obj,aname,fname)  if (!PyUnicode_Check(obj) && !PyString_Check(obj)) { \
  return error(aname "argument to" fname " not a string (8- or 16-bit)"); \
}
#define ExtractCharStar PyUnicodeOrString_AsZTUnicode
#define PyCharStar_FromLatin1(str) PyUnicode_DecodeLatin1(str,strlen(str),"strict")
#define Free(obj) free(obj)
#endif

static PyObject *xXMLError;	/* old name XMLError conflicts! */

static void Object_Forget(void *obj);
static void Object_Remember(void *obj, PyObject *py_obj);
static PyObject *Object_Find(void *obj);

static void initDoctypeNames(void);

static PyObject *File_Encapsulate(NSL_File file,Char *source);
static PyObject *Item_Encapsulate(NSL_Item *item, NSL_Doctype dt, PyObject *owner);
static PyObject *Bit_Encapsulate(NSL_Bit *bit, NSL_Doctype dt);
static PyObject *Data_Encapsulate(NSL_Data *data, NSL_Doctype dt, PyObject *owner);
static PyObject *Doctype_Encapsulate(NSL_Doctype data);
static PyObject *Query_Encapsulate(NSL_Query query);
static PyObject *BuildAttributes(ElementDefinition ed);
static PyObject *BuildNsdict(NSL_Item *item);
static char16 *PyUnicodeOrString_AsZTUnicode(PyObject *strOrUni);

static void File_Dealloc(PyObject *self);
static void Item_Dealloc(PyObject *self);
static void Bit_Dealloc(PyObject *self);
static void Doctype_Dealloc(PyObject *self);
static void ContentParticle_Dealloc(PyObject *self);
static void ElementType_Dealloc(PyObject *self);
static void AttrDefn_Dealloc(PyObject *self);
static void Query_Dealloc(PyObject *self);
static void OOB_Dealloc(PyObject *self);
static void ERef_Dealloc(PyObject *self);

static int NSL_Setattr(PyObject *self, char *name, PyObject *value);
static int Item_Setattr(PyObject *self, char *name, PyObject *value);

static PyObject *Bit_Getattr(PyObject *self, char *name);
static PyObject *Item_Getattr(PyObject *self, char *name);
static PyObject *OOB_Getattr(PyObject *self, char *name);
static PyObject *ERef_Getattr(PyObject *self, char *name);
static PyObject *File_Getattr(PyObject *self, char *name);
static PyObject *Doctype_Getattr(PyObject *self, char *name);
static PyObject *ContentParticle_Getattr(PyObject *self, char *name);
static PyObject *ElementType_Getattr(PyObject *self, char *name);
static PyObject *AttrDefn_Getattr(PyObject *self, char *name);
static PyObject *Query_Getattr(PyObject *self, char *name);

static int gc_enable = 1;	/* controls automatic freeing of NSL data */

#ifndef TRACE
#define trace(x)
#else
#define trace(x) xprintf x
#endif

#ifndef ALLOC_DEBUG
#define alloc_debug(x)
#else
#define alloc_debug(x) xprintf x
#endif

static void xprintf(char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    vfprintf(stderr, format, ap);
    fflush(stderr);
}

static void *error(char *format, ...)
{
    va_list ap;
    char buf[200];		/* XXX */

    va_start(ap, format);
    vsprintf(buf, format, ap);
    PyErr_SetString(xXMLError, buf);

    return NULL;
}

/* 
 * We keep track of the Python object encapsulating an NSL object
 * so that we never have two encapsulations of the same object.
 * Currently we only do this for items, since there's no way to get
 * anything else twice (?).
 */

static HashTable objects;	/* maps NSL objects to Python objects */

static void Object_Remember(void *obj, PyObject *py_obj)
{
#if 1
    int found;
    HashEntry e;

    alloc_debug(("Remembering object at %p\n", obj));
    e = hash_find_or_add(objects, (void *)&obj, &found);
alloc_debug(("found = %d\n", found));
    assert(found == 0);
    e->value = py_obj;

#endif
}

static void Object_Forget(void *obj)
{
#if 1
    int found;
    HashEntry e;

    alloc_debug(("Forgetting object at %p\n", obj));
    e = hash_find_or_add(objects, (void *)&obj, &found);
    assert(found == 1);
    hash_remove(objects, e);

#endif
}

static PyObject *Object_Find(void *obj)
{
#if 1
    HashEntry e;

    alloc_debug(("Looking for object at %p\n", obj));
    e = hash_find(objects, (void *)&obj);
    alloc_debug(("(%sfound)\n", e ? "" : "not "));
    return e ? (PyObject *)e->value : 0;
#else
    return 0;
#endif
}

typedef struct AllocList {
    void *data;
    struct AllocList *next;
} *AllocList;

Char *AllocList_strdup(Char *string, AllocList *listp)
{
    AllocList a;

    a = malloc(sizeof(struct AllocList));
    if(!a)
	return 0;

    a->data = Strdup(string);
    if(!a->data)
	return 0;

    a->next = *listp;
    *listp = a;

    return a->data;
}

AllocList AllocList_nconc(AllocList list1, AllocList list2)
{
    AllocList a;

    if(!list1)
	return list2;

    for(a=list1; a->next; a=a->next)
	;

    a->next = list2;

    return list1;
}

void AllocList_free(AllocList list)
{
    AllocList next;

    while(list)
    {
	free(list->data);
	next = list->next;
	free(list);
	list = next;
    }
}

/* 
 * Types corresponding to NSL structures.
 */

/*
 * To simplify the code, all the types have the userdata field immediately
 * after the PyObject_Head so that we don't need separate access functions
 * for each type.
 *
 * Similarly, BitObject and ItemObject have the owner and
 * alloclist fields in the same place in both structures.
 *
 * Yes, this is legal Ansi C!
 */

/* Generic type just for accessing userdata. */

typedef struct {
    PyObject_HEAD
    PyObject *userdata;
    NSL_File file;
} NSLObject;

/* File type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  NSL_File file;
  PyObject *doctype;		/* DoctypeObject, cached */
  Char *source;
} FileObject;

static PyTypeObject FileType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_File",			/*tp_name */
  sizeof(FileObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  File_Dealloc,			/*tp_dealloc */
  0,			/*tp_print */
  File_Getattr,			/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Doctype type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  NSL_Doctype doctype;
  PyObject *elementTypes;	/* PyDict, cached */
  PyObject *generalEntities;	/* PyDict, cached */
  PyObject *parameterEntities;	/* PyDict, cached */
  PyObject *name;		/* PyString */
} DoctypeObject;

static PyTypeObject DoctypeType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_Doctype",		/*tp_name */
  sizeof(DoctypeObject),	/*tp_basicsize */
  0,				/*tp_itemsize */
  Doctype_Dealloc,		/*tp_dealloc */
  0,				/*tp_print */
  Doctype_Getattr,		/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* ContentParticle type */

typedef struct {
    PyObject_HEAD
    PyObject *userdata;
    CPType type;
    int repetition;
    PyObject *name;		/* This is a PyString */
    PyObject *children; /* This is a PyTuple */
} ContentParticleObject;

static PyTypeObject ContentParticleType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_ContentParticle",		/*tp_name */
  sizeof(ContentParticleObject),	/*tp_basicsize */
  0,				/*tp_itemsize */
  ContentParticle_Dealloc,		/*tp_dealloc */
  0,				/*tp_print */
  ContentParticle_Getattr,		/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Element Type type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  PyObject *name; /* PyString */
  ContentType type;
  PyObject *particle;		/* ContentParticleObject */
  PyObject *attrDefns;		/* PyDict */
} ElementTypeObject;

static PyTypeObject ElementTypeType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_ElementType",		/*tp_name */
  sizeof(ElementTypeObject),	/*tp_basicsize */
  0,				/*tp_itemsize */
  ElementType_Dealloc,		/*tp_dealloc */
  0,				/*tp_print */
  ElementType_Getattr,		/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Attribute Definition type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  PyObject *name; /* PyString */
  AttributeType type;
  PyObject *allowedValues;		/* PyTuple */
  DefaultType defType;
  PyObject *defValue;		/* PyString */
} AttrDefnObject;

static PyTypeObject AttrDefnType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_AttrDefn",		/*tp_name */
  sizeof(AttrDefnObject),	/*tp_basicsize */
  0,				/*tp_itemsize */
  AttrDefn_Dealloc,		/*tp_dealloc */
  0,				/*tp_print */
  AttrDefn_Getattr,		/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Generic type for accessing Bit and Item alloclist and owner */

typedef struct {
    PyObject_HEAD
    PyObject *userdata;
    AllocList alloclist;	/* Strings we must free */
    PyObject *owner;
} BitOrItemObject;

/* Bit type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  AllocList alloclist;	/* Strings we must free */
  PyObject *owner;		/* Always null in a Bit */
  /* next 4 copied from NSL_bit */
  int flags;
  NSL_BI_Type type;
  union {
    NSL_Item *item; /* type NSL_start_bit */
    Char *body; /* text, pi */
    void * data; /* NSL_internal_bit */
  } value;
  const Char *label;
  const Char *llabel;
  const Char *nsuri;
  const Char *prefix;
  PyObject *body;		/* PyString, cache */
  PyObject *pLabel;		/* PyString, cache */
  PyObject *pLlabel;		/* PyString, cache */
  PyObject *pNsuri;		/* PyString, cache */
  PyObject *pPrefix;		/* PyString, cache */
  int count;
  NSL_Doctype doctype;	/* Doctype it belongs to */
} BitObject;

static PyTypeObject BitType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_Bit",			/*tp_name */
  sizeof(BitObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  Bit_Dealloc,			/*tp_dealloc */
  0,				/*tp_print */
  Bit_Getattr,			/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Item type */

typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  AllocList alloclist;	/* Strings we must free */
  PyObject *owner;		/* Bit or Item whose ref count we increment */
  NSL_Item *item;
  PyObject *label;		/* PyString, cache */
  PyObject *llabel;		/* PyString, cache */
  PyObject *nsuri;		/* PyString, cache */
  PyObject *prefix;		/* PyString, cache */
  PyObject *nsdict;		/* PyDict, cache */
  PyObject *data;		/* DataObject, cache */
  int count;
  NSL_Doctype doctype;	/* Doctype it belongs to */
} ItemObject;

static PyTypeObject ItemType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_Item",			/*tp_name */
  sizeof(ItemObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  Item_Dealloc,			/*tp_dealloc */
  0,				/*tp_print */
  Item_Getattr,			/*tp_getattr */
  Item_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* OOB type for PIs and comments in document instance */
typedef struct {
  PyObject_HEAD
  PyObject *userdata;
  PyObject *owner;		/* Bit or Item whose ref count we increment */
  const Char *data;
  PyObject *sData;		/* PyString, cache */
  PyObject *type;
  int count;
} OOBObject;

static PyTypeObject OOBType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_OOB",			/*tp_name */
  sizeof(OOBObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  OOB_Dealloc,			/*tp_dealloc */
  0,				/*tp_print */
  OOB_Getattr,			/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* ERef type for entity references in document instance */
typedef struct {
    PyObject_HEAD
    PyObject *userdata;
    PyObject *name;
} ERefObject;

static PyTypeObject ERefType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_ERef",			/*tp_name */
  sizeof(ERefObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  ERef_Dealloc,			/*tp_dealloc */
  0,				/*tp_print */
  ERef_Getattr,			/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Query type */

typedef struct {
    PyObject_HEAD
    PyObject *userdata;
    NSL_Query query;
    int count;
} QueryObject;

static PyTypeObject QueryType =
{
  PyObject_HEAD_INIT (NULL)
  0,				/*ob_size */
  "NSL_Query",			/*tp_name */
  sizeof(QueryObject),		/*tp_basicsize */
  0,				/*tp_itemsize */
  Query_Dealloc,		/*tp_dealloc */
  0,				/*tp_print */
  Query_Getattr,		/*tp_getattr */
  NSL_Setattr,			/*tp_setattr */
  0,				/*tp_compare */
  0,				/*tp_repr */
  0,				/*tp_as_number */
  0,				/*tp_as_sequence */
  0,				/*tp_as_mapping */
  0,				/*tp_hash */
};

/* Methods */

static int NSL_Setattr(PyObject *self, char *name, PyObject *value)
{
    NSLObject *obj = (NSLObject *)self;

    if(strcmp(name, "userdata") != 0)
    {
	error("%s is not a settable attribute", name);
	return -1;
    }

    Py_DECREF(obj->userdata);
    Py_INCREF(value);
    obj->userdata = value;

    return 0;
}

static PyObject *BuildParticle(struct content_particle *cp) {
  ContentParticleObject *cpo=PyObject_NEW(ContentParticleObject,
					  &ContentParticleType);
  if (!cpo) {
    return NULL;
  };
  Py_INCREF(Py_None);
  cpo->userdata = Py_None;
  cpo->type=cp->type;
  if (cp->repetition) {
    /* embarassing hack */
    int i=cp->repetition-'*';
    if (i>1) {
      cpo->repetition=3;	/* ? */
    }
    else {
      cpo->repetition=i+1;	/* *: 1 +: 2 */
    };
  }
  else {
    cpo->repetition=0;
  };
  if (cp->type==CP_name) {
    cpo->name=PyCharStar_FromCharStar(cp->name,Strlen(cp->name));
  }
  else {
    Py_INCREF(Py_None);
    cpo->name=Py_None;
  };
  if (cp->type==CP_seq || cp->type==CP_choice) {
    int i;
    PyObject *list=PyTuple_New(cp->nchildren);
    if (!list) {
      return NULL;
    };
    for (i=0;i<cp->nchildren;i++) {
      PyTuple_SET_ITEM(list,i,BuildParticle(cp->children[i]));
    };
    cpo->children=list;
  }
  else {
    Py_INCREF(Py_None);
    cpo->children=Py_None;
  };
  return (PyObject *)cpo;
}

static PyObject *CPRepNames[3];
static PyObject *CPTypeNames[4];

static PyObject *ContentParticle_Getattr(PyObject *self, char *name) {
  ContentParticleObject *this=(ContentParticleObject *)self;
  if(strcmp(name, "userdata") == 0)
    {
      Py_INCREF(((NSLObject *)self)->userdata);
      return ((NSLObject *)self)->userdata;
    }
  else if (strcmp(name,"name") == 0) {
    Py_INCREF(this->name);
    return this->name;
  }
  else if (strcmp(name,"repetition") == 0) {
    if (this->repetition) {
      Py_INCREF(CPRepNames[this->repetition-1]);
      return CPRepNames[this->repetition-1];
    }
    else {
      Py_INCREF(Py_None);
      return Py_None;
    }
  }
  else if (strcmp(name,"type") == 0) {
    Py_INCREF(CPTypeNames[this->type]);
    return CPTypeNames[this->type];
  }
  else if (strcmp(name,"children") == 0) {
    Py_INCREF(this->children);
    return this->children;
  };

  return error("Unknown ContentParticle attribute %s", name);
}

static PyObject *BuildElementTypes(Dtd rxp_dtd) {
  /* Should detect effectively empty dtd and return empty dictionary */
  ElementDefinition ed;
  ElementTypeObject *eto;
  PyObject *dict=PyDict_New();
  if (!dict) {
    return NULL;
  };
  for (ed=NextElementDefinition(rxp_dtd,NULL);
       ed;
       ed=NextElementDefinition(rxp_dtd,ed)) {
    eto=PyObject_NEW(ElementTypeObject, &ElementTypeType);
    if (!eto) {
      return NULL;
    };
    Py_INCREF(Py_None);
    eto->userdata = Py_None;
    eto->name=PyCharStar_FromCharStar(ed->name,Strlen(ed->name));
    eto->type=ed->type;
    if (ed->particle) {
      eto->particle=BuildParticle(ed->particle);
    }
    else {
      Py_INCREF(Py_None);
      eto->particle = Py_None;
    }      
    eto->attrDefns=BuildAttributes(ed);
    PyDict_SetItem(dict,eto->name,(PyObject *)eto);
    Py_DECREF(eto);		/* SetItem does INCREFs on both args */
  }
  return dict;
}

static PyObject *BuildEntities(Dtd rxp_dtd, int pe) {
  /* Should detect effectively empty dtd and return empty dictionary */
  /* cheap preliminary version, doesn't detect/provide URLs from external
     entities */
  struct entity *ed;
  Entity (*entmapper)(Dtd, Entity) = pe ? NextParameterEntity : NextEntity;
  PyObject *dict=PyDict_New();
  if (!dict) {
    return NULL;
  };
  for (ed=entmapper(rxp_dtd,NULL);
       ed;
       ed=entmapper(rxp_dtd,ed)) {
    PyObject *val;
    PyObject *name=PyCharStar_FromCharStar(ed->name,Strlen(ed->name));
    if (ed->type==ET_internal) {
      val=PyCharStar_FromCharStar(ed->text,Strlen(ed->text));
    }
    else {
      val=PyString_FromString((char *)ed->systemid);
    }
    PyDict_SetItem(dict,name,val);
    Py_DECREF(name);
    Py_DECREF(val);
  }
  return dict;
}

static PyObject *CTypeNames[6];

static PyObject *ElementType_Getattr(PyObject *self, char *name) {
  ElementTypeObject *this=(ElementTypeObject *)self;
  if(strcmp(name, "userdata") == 0) {
      Py_INCREF(this->userdata);
      return this->userdata;
  }
  else if (strcmp(name, "name") == 0) {
    Py_INCREF(this->name);
    return this->name;
  }
  else if (strcmp(name,"type") == 0) {
      Py_INCREF(CTypeNames[this->type]);
      return CTypeNames[this->type];
  }
  else if (strcmp(name,"particle") == 0) {
    Py_INCREF(this->particle);
    return this->particle;
  }
  else if (strcmp(name,"attrDefns") == 0) {
    Py_INCREF(this->attrDefns);
    return this->attrDefns;
  };

  return error("Unknown ElementType attribute %s", name);
}


static PyObject *sddNames[3];
static PyObject *CEncNames[CE_enum_count];
static PyObject *CEncDict;
static PyObject *XMLVersions[3];

static PyObject *Doctype_Getattr(PyObject *self, char *name) {
  DoctypeObject *this=((DoctypeObject *)self);
    if(strcmp(name, "userdata") == 0)
    {
	Py_INCREF(((NSLObject *)self)->userdata);
	return ((NSLObject *)self)->userdata;
    };
    /* is this stale? Causes crash! 
    if(strcmp(name, "ddb") == 0)
    return PyString_FromString(((NSL_Doctype)this->doctype)->ddbfile); */

    if(strcmp(name, "doctypeStatement") == 0) {
      const Char *dts=((NSL_Doctype)this->doctype)->doctypeStatement;
      if (dts) {
	return PyCharStar_FromCharStar(dts,Strlen(dts));
      }
      else {
	Py_INCREF(Py_None);
	return Py_None;
      }
    }; 

    if (strcmp(name, "encoding") == 0) {
      PyObject *cen=CEncNames[this->doctype->fallbackEncodingDeclaration];
      Py_INCREF(cen);
      return cen;
    };

    if (strcmp(name, "xencoding") == 0) {
      PyObject *cen=CEncNames[this->doctype->defaultOutputEncoding];
      Py_INCREF(cen);
      return cen;
    };

    if (strcmp(name,"sdd") == 0) {
      PyObject *sdd=sddNames[this->doctype->sdd];
      Py_INCREF(sdd);
      return sdd;			   
    };

    if (strcmp(name,"elementTypes")==0) {
      if (this->elementTypes==Py_None) {
	Py_DECREF(this->elementTypes);
	this->elementTypes=BuildElementTypes(this->doctype->rxp_dtd);
      };
      Py_INCREF(this->elementTypes);
      return this->elementTypes;
    };

    if (strcmp(name,"entities")==0) {
      if (this->generalEntities==Py_None) {
	Py_DECREF(this->generalEntities);
	this->generalEntities=BuildEntities(this->doctype->rxp_dtd, 0);
      };
      Py_INCREF(this->generalEntities);
      return this->generalEntities;
    };

    if (strcmp(name,"parameterEntities")==0) {
      if (this->parameterEntities==Py_None) {
	Py_DECREF(this->parameterEntities);
	this->parameterEntities=BuildEntities(this->doctype->rxp_dtd, 1);
      };
      Py_INCREF(this->parameterEntities);
      return this->parameterEntities;
    };

    if(strcmp(name, "name") == 0) {
	Py_INCREF(this->name);
	return this->name;
    }

    return error("Unknown Doctype attribute %s", name);
}

static PyObject *pDoctypeFromDdb(PyObject *self, PyObject *args)
{
    char *filename;
    NSL_Doctype dt;

    if(!PyArg_ParseTuple(args, "s", &filename))
	return NULL;
    
    dt = DoctypeFromDdb(filename);
    if(!dt)
	return error("Can't determine doctype");

    return Doctype_Encapsulate(dt);
}

static PyObject *BuildAttributes(ElementDefinition ed) {
  PyObject *dict;
  AttributeDefinition ad;
  AttrDefnObject *ao;
  dict=PyDict_New();
  if (!dict) {
    return NULL;
  };
  for (ad=NextAttributeDefinition(ed,NULL);
       ad;
       ad=NextAttributeDefinition(ed,ad)) {
    ao=PyObject_NEW(AttrDefnObject, &AttrDefnType);
    if (!ao) {
      return NULL;
    };
    Py_INCREF(Py_None);
    ao->userdata = Py_None;
    ao->name=PyCharStar_FromCharStar(ad->name,Strlen(ad->name));
    ao->type=ad->type;
    ao->defType=ad->default_type;
    if (ad->default_value) {
      ao->defValue=PyCharStar_FromCharStar(ad->default_value,Strlen(ad->default_value));
    }
    else {
      Py_INCREF(Py_None);
      ao->defValue=Py_None;
    };
    if (ad->allowed_values) {
      int i;
      Char **avp;
      PyObject *list;
      for (i=0,avp=ad->allowed_values;*avp;avp++,i++);
      list=PyTuple_New(i);
      if (!list) {
	return NULL;
      };
      for (i=0,avp=ad->allowed_values;*avp;avp++,i++) {
	PyTuple_SET_ITEM(list,i,PyCharStar_FromCharStar(*avp,Strlen(*avp)));
      };
      ao->allowedValues=list;
    }
    else {
      Py_INCREF(Py_None);
      ao->allowedValues=Py_None;
    };
    PyDict_SetItem(dict,ao->name,(PyObject *)ao);
    Py_DECREF(ao->name);
  }
  return dict;
}

static PyObject *ATypeNames[16];
static PyObject *ADTypeNames[6];

static PyObject *AttrDefn_Getattr(PyObject *self, char *name) {
  AttrDefnObject *this=(AttrDefnObject *)self;
  if(strcmp(name, "userdata") == 0) {
      Py_INCREF(this->userdata);
      return this->userdata;
  }
  else if (strcmp(name, "name") == 0) {
    Py_INCREF(this->name);
    return this->name;
  }
  else if (strcmp(name,"type") == 0) {
      Py_INCREF(ATypeNames[this->type]);
      return ATypeNames[this->type];
  }
  else if (strcmp(name,"defType") == 0) {
      Py_INCREF(ADTypeNames[this->defType]);
      return ADTypeNames[this->defType];
  }
  else if (strcmp(name,"defValue") == 0) {
    Py_INCREF(this->defValue);
    return this->defValue;
  }
  else if (strcmp(name,"allowedValues") == 0) {
    Py_INCREF(this->allowedValues);
    return this->allowedValues;
  };

  return error("Unknown AttrDefn attribute %s", name);
}


static PyObject *pOpen(PyObject *self, PyObject *args)
{
    char *filename;
    int type;
    PyObject *dtype;
    NSL_File file;

    if(PyArg_ParseTuple(args, "sOi", &filename, &dtype, &type))
    {
	if(dtype == Py_None)
	    dtype = NULL;
	else if(dtype->ob_type != &DoctypeType)
	    return error("Second arg to Open is not a Doctype");
    }
    else
    {
      PyErr_Clear(); /* because PyArg_ParseTuple raised an error */
	if(PyArg_ParseTuple(args, "si", &filename, &type))
	    dtype = NULL;
	else
	    return NULL;
    }

    if(type & ~(NSL_write_flags|NSL_read_flags))
	return error("Bad NSL file type 0x%x", type);

    file = SFopen(filename, 
		  dtype ? ((DoctypeObject *)dtype)->doctype : NULL,
		  type);
    if(!file)
	return error("Can't open file");

    return File_Encapsulate(file,(Char*)NULL);
}

static PyObject *pFOpen(PyObject *self, PyObject *args)
{
    int type;
    PyObject *pfile, *dtype;
    NSL_File file;

    if(PyArg_ParseTuple(args, "OOi", &pfile, &dtype, &type))
    {
	if(dtype == Py_None)
	    dtype = NULL;
	else if(dtype->ob_type != &DoctypeType)
	    return error("Second arg to FOpen is not a Doctype");
    }
    else
    {
        PyErr_Clear();
	if(PyArg_ParseTuple(args, "Oi", &pfile, &type))
	    dtype = NULL;
	else
	    return NULL;
    }

    if(!PyFile_Check(pfile))
	return error("First arg to FOpen is not a file");

    if(type & ~(NSL_write_flags|NSL_read_flags))
	return error("Bad NSL file type 0x%x", type);

    file = SFFopen(PyFile_AsFile(pfile), 
		   dtype ? ((DoctypeObject *)dtype)->doctype : NULL,
		   type,
		   PyString_AsString(PyFile_Name(pfile)));
    if(!file)
	return error("Can't open file");

    return File_Encapsulate(file,(Char*)NULL);
}

static PyObject *pOpenURL(PyObject *self, PyObject *args) {
    int type, encoding;
    PyObject *dtype;
    char *url;
    NSL_File file;

    if(PyArg_ParseTuple(args, "sOii", &url, &dtype, &encoding, &type))
    {
	if(dtype == Py_None)
	    dtype = NULL;
	else if(dtype->ob_type != &DoctypeType)
	    return error("Second arg to OpenURL is not a Doctype");
    }
    else
    {
        PyErr_Clear();
	if(PyArg_ParseTuple(args, "sii", &url, &encoding, &type))
	    dtype = NULL;
	else
	    return NULL;
    }

    if(type & ~(NSL_write_flags|NSL_read_flags))
	return error("Bad NSL file type 0x%x", type);

    if ((encoding < 0) || (encoding >= CE_enum_count)) {
      return error("Bad encoding %d",encoding);
    }

    file = OpenURL(url,
		   dtype ? ((DoctypeObject *)dtype)->doctype : NULL,
		   type,
		   encoding,
		   0);
    if(!file)
	return error("Can't open file");

    return File_Encapsulate(file,(Char*)NULL);
}

static PyObject *pOpenStream(PyObject *self, PyObject *args) {
    int type, encoding;
    PyObject *pfile, *dtype, *baseURI;
    NSL_File file;

    if(PyArg_ParseTuple(args, "OOOii", &pfile, &dtype, &baseURI,
			&encoding, &type)) {
      if(baseURI == Py_None) {
	baseURI = NULL;
      }
      else {
	CheckCharStar(baseURI,"third","OpenStream");
      }
    }
    else {
      PyErr_Clear();
      baseURI = NULL;
      if (! PyArg_ParseTuple(args, "OOii", &pfile, &dtype, &encoding, &type)) {
        PyErr_Clear();
	if(PyArg_ParseTuple(args, "Oii", &pfile, &encoding, &type))
	  dtype = NULL;
	else
	  return NULL;
      }
    }

    if(!PyFile_Check(pfile))
	return error("First arg to FOpen is not a file");
    if(dtype == Py_None)
      dtype = NULL;
    else if(dtype->ob_type != &DoctypeType)
      return error("Second arg to OpenStream is not a Doctype");

    if(type & ~(NSL_write_flags|NSL_read_flags))
	return error("Bad NSL file type 0x%x", type);

    if ((encoding < 0) || (encoding >= CE_enum_count)) {
      return error("Bad encoding %d",encoding);
    }

    file = OpenStream(PyFile_AsFile(pfile),
		      dtype ? ((DoctypeObject *)dtype)->doctype : NULL,
		      type,
		      encoding,
		      PyString_AsString(baseURI ? baseURI :
					PyFile_Name(pfile)));
    if(!file)
	return error("Can't open file");

    return File_Encapsulate(file,(Char*)NULL);
}

static PyObject *pOpenString(PyObject *self, PyObject *args) {
    int type;
    PyObject *dtype,*sourceObj;
    Char *source;
    NSL_File file;

    if(PyArg_ParseTuple(args, "OOi", &sourceObj, &dtype, &type))
    {
	if(dtype == Py_None)
	    dtype = NULL;
	else if(dtype->ob_type != &DoctypeType)
	    return error("Second arg to OpenString is not a Doctype");
    }
    else
    {
        PyErr_Clear();
	if(PyArg_ParseTuple(args, "Oi", &sourceObj, &type))
	    dtype = NULL;
	else
	    return NULL;
    }

    CheckCharStar(sourceObj,"first","OpenString");
    source=ExtractCharStar(sourceObj);

    if(type > NSL_read_flags)
	return error("Bad NSL file type %d", type);

    file = OpenString(source,
		      dtype ? ((DoctypeObject *)dtype)->doctype : NULL,
		      type);
    if(!file)
	return error("Can't open file");

    return File_Encapsulate(file,source);
}

static PyObject *pClose(PyObject *self, PyObject *args)
{
    PyObject *f;

    if(!PyArg_ParseTuple(args, "O", &f))
	return NULL;

    SFclose(((FileObject *)f)->file);

    Py_INCREF(Py_None);
    return Py_None;
}

static NSL_Bit FakeBit;		/* for filling in and passing to PrintBit */

static PyObject *pPrint(PyObject *self, PyObject *args)
{
    PyObject *obj, *file;

    trace(("Print... "));

    if(PyArg_ParseTuple(args, "OO", &file, &obj)) {
      if(file->ob_type != &FileType)	
	return error("First arg to Print is not a File");
      if(obj->ob_type == &BitType) {
	FakeBit.type=((BitObject *)obj)->type;
	FakeBit.value.body=((BitObject *)obj)->value.body;
	FakeBit.flags=((BitObject *)obj)->flags;
	FakeBit.label=(Char*)(((BitObject *)obj)->label);
	PrintBit(((FileObject *)file)->file, &FakeBit);
      }
      else if(obj->ob_type == &ItemType) {
	PrintItem(((FileObject *)file)->file, ((ItemObject *)obj)->item);
      }
      else if (IsCharStar(obj)) {
	Char *ps;
	PrintText(((FileObject *)file)->file, ps=ExtractCharStar(obj));
	Free(ps);
      }
      else {
	return error("Second arg to Print is not Text, Bit or Item");
      }
    }
    else {
      return NULL;
    }
    trace(("done\n"));

    Py_INCREF(Py_None);
    return Py_None;
}


static PyObject *pPrintTextLiteral(PyObject *self, PyObject *args)
{
    PyObject *file,*textobj;
    Char *ps;

    if(!PyArg_ParseTuple(args, "OO", &file, &textobj))
	return NULL;
    if(file->ob_type != &FileType)	
	return error("First arg to PrintTextLiteral is not a File");
    CheckCharStar(textobj,"second","PrintTextLiteral");
    

    PrintTextLiteral(((FileObject *)file)->file, ps=ExtractCharStar(textobj));
    Free(ps);

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *pPrintEndTag(PyObject *self, PyObject *args)
{
    PyObject *file,*label;
    Char *ps;

    if(!PyArg_ParseTuple(args, "OO", &file, &label))
	return NULL;

    if(file->ob_type != &FileType)	
	return error("First arg to PrintEndTag is not a File");
    CheckCharStar(label,"second","PrintEndTag");
    
    PrintEndTag(((FileObject *)file)->file, ps=ExtractCharStar(label));
    Free(ps);

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *pPrintStartTag(PyObject *self, PyObject *args)
{
    PyObject *file;
    PyObject *obj;
    Char *ps;

    if (!PyArg_ParseTuple(args, "OO", &file, &obj)) {
      return NULL;
    }
    if (file->ob_type != &FileType) {
      return error("First arg to PrintStartTag is not a File");
    }
    if (obj->ob_type == &ItemType) {
      PrintItemStartTag(((FileObject *)file)->file,
			((ItemObject *)obj)->item);
    }
    else {
      CheckCharStar(obj,"second","PrintStartTag");
    
      PrintStartTag(((FileObject *)file)->file, ps=ExtractCharStar(obj));
      Free(ps);
    }
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *pForceNewline(PyObject *self, PyObject *args)
{
    PyObject *file;

    if(!PyArg_ParseTuple(args, "O", &file))
	return NULL;

    if(file->ob_type != &FileType)	
	return error("Arg to ForceNewline is not a File");
    
    ForceNewline(((FileObject *)file)->file);

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *File_Getattr(PyObject *self, char *name) {
  FileObject *this=((FileObject *)self);
    NSL_File file = this->file;

    if(strcmp(name, "userdata") == 0)
    {
	Py_INCREF(this->userdata);
	return this->userdata;
    }
    if(strcmp(name, "doctype") == 0) {
      if (this->doctype==Py_None) {
	NSL_Doctype dt = DoctypeFromFile(file);
	if (dt) {
	  Py_DECREF(this->doctype);
	  this->doctype=Doctype_Encapsulate(dt);
	};
	Py_INCREF(this->doctype);
	return this->doctype;
      }
      else {
	Py_INCREF(this->doctype);
	return this->doctype;
      };
    };

    if (strcmp(name, "XMLVersion") == 0) {
      XMLVersion vv = file->pstate->xml_version;
      PyObject *xv=XMLVersions[vv==XV_1_0?1:(vv==XV_1_1?2:0)];
      Py_INCREF(xv);
      return xv;
    };

    if(strcmp(name, "where") == 0)
    {
	/* Should not copy strings every time! XXX */
	/* Maybe we should pop_while_at_eoe here? */
	int linenum = -1, charnum = -1;
	PyObject *url, *entname, *result;
	InputSource s = file->pstate->source;
	
	if(s->entity->name)
	    entname = PyCharStar_FromCharStar(s->entity->name,Strlen(s->entity->name));
	else
	    entname = PyString_FromString("unnamed entity");
	SourceLineAndChar(s, &linenum, &charnum);
	url = PyString_FromString(EntityDescription(s->entity));

	result = PyTuple_New(4);
	PyTuple_SET_ITEM(result, 0, entname);
	PyTuple_SET_ITEM(result, 1, PyInt_FromLong(linenum+1));
	PyTuple_SET_ITEM(result, 2, PyInt_FromLong(charnum+1));
	PyTuple_SET_ITEM(result, 3, url);

	return result;
    }
    if(strcmp(name, "seenValidityError") == 0)
    {
	return PyInt_FromLong(file->pstate->seen_validity_error);
    }

    return error("Unknown File attribute %s", name);
}

char *describe_bit(NSL_Bit *bit)
{
    static char buf[100];
    char* xb=0;

    switch(bit->type)
    {
      case NSL_start_bit:
	sprintf(buf, "<%.80s>", Char_to_char8(bit->label,xb));
	if (xb) {
	  free(xb);
	}
	break;
      case NSL_end_bit:
      sprintf(buf, "</%.80s>", Char_to_char8(bit->label,xb));
	if (xb) {
	  free(xb);
	}
	break;
      case NSL_text_bit:
	sprintf(buf, "\"%.80s\"", Char_to_char8(bit->value.body,xb));
	if (xb) {
	  free(xb);
	}
	break;
      default:
	sprintf(buf, "[bit type %d]", bit->type);
	break;
    }

    return buf;
}

char *describe_item(NSL_Item *item)
{
    static char buf[100];
    char *xb=0;

    switch(item->type)
    {
      case NSL_non_empty:
      case NSL_empty:
	sprintf(buf, "<%.80s>", Char_to_char8(item->label,xb));
	if (xb) {
	  free(xb);
	}
	break; 
      case NSL_inchoate:
	sprintf(buf, "<inchoate %.80s>", Char_to_char8(item->label,xb));
	if (xb) {
	  free(xb);
	}
	break; 
      default:
	sprintf(buf, "[item type %d]", item->type);
	break;
    }

    return buf;
}

static PyObject *bit_or_item_typename[13];

static PyObject *Bit_Encapsulate(NSL_Bit *bit, NSL_Doctype dt)
{
    static int count = 0;
    BitObject *b = (BitObject *)PyObject_NEW(BitObject, &BitType);

    if(!b)
	return NULL;

    Py_INCREF(Py_None);
    b->userdata = Py_None;
    Py_INCREF(Py_None);
    b->body = Py_None;
    Py_INCREF(Py_None);
    b->pLabel = Py_None;
    b->pLlabel = 0;
    b->pNsuri = 0;
    b->pPrefix = 0;
    b->owner=NULL;
    b->type = bit->type;
    b->value.body = bit->value.body;
    b->flags = bit->flags;
    b->label = bit->label;
    b->llabel = bit->llabel;
    b->nsuri = bit->nsuri;
    b->prefix = bit->prefix;
    b->alloclist = 0;
    b->count = count++;
    b->doctype = dt;

    alloc_debug(("Bit %d at %p (%s) encapsulated\n",
	    b->count, bit, describe_bit(bit)));

    return (PyObject *)b;
}

static PyObject *Item_Encapsulate(NSL_Item *item, NSL_Doctype dt, PyObject *owner)
{
    static int count = 0;
    ItemObject *i;
    PyObject *pi;

    if((pi = Object_Find((void *)item)))
    {
	i = (ItemObject *)pi;
	alloc_debug(("Returning existing item %d at %p (%s)\n",
		    i->count, item, describe_item(i->item)));
	Py_INCREF(pi);
	return pi;
    }

    i = PyObject_NEW(ItemObject, &ItemType);
    if(!i)
	return NULL;
    pi = (PyObject *)i;

    Py_INCREF(Py_None);
    i->userdata = Py_None;
    Py_INCREF(Py_None);
    i->label = Py_None;
    i->llabel = 0;
    i->nsuri = 0;
    i->prefix = 0;
    i->nsdict = 0;
    Py_INCREF(Py_None);
    i->data = Py_None;
    i->item = item;
    i->alloclist = 0;
    i->count = count++;
    i->doctype = dt;

    if(owner)
	Py_INCREF(owner);
    i->owner = owner;

    Object_Remember(item, pi);

    alloc_debug(("Item %d at %p (%s) encapsulated\n",
	    i->count, item, describe_item(item)));

    return pi;
}

static PyObject *OOB_Encapsulate(const Char *data, PyObject *owner,
				 const char *type)
{
    static int count = 0;
    OOBObject *i;
    PyObject *pi;

    i = PyObject_NEW(OOBObject, &OOBType);
    if(!i)
	return NULL;
    pi = (PyObject *)i;

    Py_INCREF(Py_None);
    i->userdata = Py_None;
    i->data = data;
    Py_INCREF(Py_None);
    i->sData=Py_None;
    i->type = PyString_FromString(type);
    i->count = count++;

    if(owner)
	Py_INCREF(owner);
    i->owner = owner;

    alloc_debug(("OOB %d: %s encapsulated\n",
	    i->count, data));

    return pi;
}

static PyObject *ERef_Encapsulate(const Char *name)
{
    ERefObject *e = (ERefObject *)PyObject_NEW(ERefObject, &ERefType);
    if (!e) {
      return NULL;
    };

    Py_INCREF(Py_None);
    e->userdata = Py_None;
    e->name=PyCharStar_FromCharStar(name,Strlen(name));

    alloc_debug(("ERef: %s ref\n", name));

    return (PyObject *)e;
}

static PyObject *File_Encapsulate(NSL_File file,Char *source)
{
    FileObject *f = (FileObject *)PyObject_NEW(FileObject, &FileType);
    if(!f)
	return NULL;

    Py_INCREF(Py_None);
    f->userdata = Py_None;
    f->file = file;
    Py_INCREF(Py_None);
    f->doctype = Py_None;
    f->source = source;
    return (PyObject *)f;
}

static void initDoctypeNames() {
  int i;
  CPRepNames[0]=PyString_FromString("*");
  CPRepNames[1]=PyString_FromString("+");
  CPRepNames[2]=PyString_FromString("?");
  CPTypeNames[0]=PyString_FromString("#PCDATA");
  CPTypeNames[1]=PyString_FromString("NAME");
  CPTypeNames[2]=PyString_FromString("SEQUENCE");
  CPTypeNames[3]=PyString_FromString("CHOICE");
  CTypeNames[0]=PyString_FromString("MIXED");
  CTypeNames[1]=PyString_FromString("ANY");
  CTypeNames[2]=PyString_FromString("BOGUS1");
  CTypeNames[3]=PyString_FromString("BOGUS2");
  CTypeNames[4]=PyString_FromString("EMPTY");
  CTypeNames[5]=PyString_FromString("ELEMENT");
  ATypeNames[0]=PyString_FromString("CDATA");
  ATypeNames[1]=PyString_FromString("BOGUS1");
  ATypeNames[2]=PyString_FromString("BOGUS2");
  ATypeNames[3]=PyString_FromString("NMTOKEN");
  ATypeNames[4]=PyString_FromString("BOGUS3");
  ATypeNames[5]=PyString_FromString("ENTITY");
  ATypeNames[6]=PyString_FromString("IDREF");
  ATypeNames[7]=PyString_FromString("BOGUS4");
  ATypeNames[8]=PyString_FromString("BOGUS5");
  ATypeNames[9]=PyString_FromString("NMTOKENS");
  ATypeNames[10]=PyString_FromString("BOGUS6");
  ATypeNames[11]=PyString_FromString("ENTITIES");
  ATypeNames[12]=PyString_FromString("IDREFS");
  ATypeNames[13]=PyString_FromString("ID");
  ATypeNames[14]=PyString_FromString("NOTATION");
  ATypeNames[15]=PyString_FromString("ENUMERATION");
  ADTypeNames[0]=PyString_FromString("#REQUIRED");
  ADTypeNames[1]=PyString_FromString("BOGUS1");
  ADTypeNames[2]=PyString_FromString("#IMPLIED");
  ADTypeNames[3]=PyString_FromString("BOGUS2");
  ADTypeNames[4]=PyString_FromString("NONE");
  ADTypeNames[5]=PyString_FromString("#FIXED");
  sddNames[0]=PyString_FromString("unspecified");
  sddNames[1]=PyString_FromString("no");
  sddNames[2]=PyString_FromString("yes");
  XMLVersions[0]=Py_None;
  XMLVersions[1]=PyString_FromString("1.0");
  XMLVersions[2]=PyString_FromString("1.1");
  CEncDict=PyDict_New();
  for (i=0;i<CE_enum_count;i++) {
    CEncNames[i]=PyString_FromString(CharacterEncodingName[i]);
    PyDict_SetItem(CEncDict,CEncNames[i],PyInt_FromLong(i));
  }
}

static PyObject *Doctype_Encapsulate(NSL_Doctype doctype)
{
    DoctypeObject *d = 
	(DoctypeObject *)PyObject_NEW(DoctypeObject, &DoctypeType);

    if(!d)
	return NULL;
    Py_INCREF(Py_None);
    d->userdata = Py_None;
    Py_INCREF(Py_None);
    d->elementTypes = Py_None;
    Py_INCREF(Py_None);
    d->generalEntities = Py_None;
    Py_INCREF(Py_None);
    d->parameterEntities = Py_None;
    d->doctype = doctype;
    if (doctype->XMLMode) {
      if (doctype->rxp_dtd->name) {
	d->name = PyCharStar_FromCharStar(doctype->rxp_dtd->name,
					  Strlen(doctype->rxp_dtd->name));
      }
      else {
	d->name = PyString_FromString("none");
      }
    }
    else {
      /* XXX It's in the DDB somewhere */
      d->name = PyString_FromString("unknown");
    }

    return (PyObject *)d;
}

static PyObject *Query_Encapsulate(NSL_Query query)
{
    QueryObject *q = 
	(QueryObject *)PyObject_NEW(QueryObject, &QueryType);

    if(!q)
	return NULL;

    Py_INCREF(Py_None);
    q->userdata = Py_None;
    q->query = query;
    return (PyObject *)q;
}

static PyObject *Data_Encapsulate(NSL_Data *data, NSL_Doctype dt, PyObject *owner)
{
    int len, i;
    NSL_Data *d;
    PyObject *list;

    for(len=0, d=data; d; len++, d=d->next)
	;

    list = PyTuple_New(len);
    if (!list) {
      return NULL;
    };

    for(i=0, d=data; i<len; i++, d=d->next)
    {
      switch (d->type) {
      case NSL_text_data:
	    PyTuple_SET_ITEM(list, i, PyCharStar_FromCharStar(d->first,Strlen(d->first)));
	    break;
      case NSL_pi_data:
      case NSL_comment_data:
      case NSL_cdata_data:
	PyTuple_SET_ITEM(list, i,
		       OOB_Encapsulate(d->first, owner,
				       d->type==NSL_pi_data?"pi":
				       (d->type==NSL_cdata_data?"cdata":"comment")));
	break;
      case NSL_item_data:
	    PyTuple_SET_ITEM(list, i, 
			    Item_Encapsulate((NSL_Item *)d->first, dt, owner));
	    break;
      case NSL_eref_data:
	PyTuple_SET_ITEM(list, i,
		       ERef_Encapsulate(d->first));
	break;
      default:
	assert(0);
      }
    }

    return list;
}


static PyObject *OOB_Getattr(PyObject *self, char *name) {
  OOBObject *this=((OOBObject *)self);
  if(strcmp(name, "type") == 0) {
    Py_INCREF(this->type);
    return this->type;
  };
  if(strcmp(name, "data") == 0) {
    if (this->sData==Py_None) {
      Py_DECREF(this->sData);
      this->sData=PyCharStar_FromCharStar(this->data,Strlen(this->data));
    };
    Py_INCREF(this->sData);
    return this->sData;
  };

  if(strcmp(name, "userdata") == 0)
    {
      Py_INCREF(this->userdata);
      return this->userdata;
    };

  return error("Unknown OOB attribute %s", name);
}

static PyObject *ERef_Getattr(PyObject *self, char *name) {
  ERefObject *this=((ERefObject *)self);
  if(strcmp(name, "name") == 0) {
    Py_INCREF(this->name);
    return this->name;
  };

  if(strcmp(name, "userdata") == 0)
    {
      Py_INCREF(this->userdata);
      return this->userdata;
    };

  return error("Unknown ERef attribute %s", name);
}

static PyObject *pGetNextBit(PyObject *self, PyObject *args)
{
    PyObject *file;
    NSL_Bit *bit;

    if(!PyArg_ParseTuple(args, "O", &file))
	return NULL;

    if(file->ob_type != &FileType)	
	return error("Arg to GetNextBit is not a File");

    bit = GetNextBit(((FileObject *)file)->file);
    if(!bit)
    {
	Py_INCREF(Py_None);
	return Py_None;
    }

    return Bit_Encapsulate(bit, DoctypeFromFile(((FileObject *)file)->file));
}

static PyObject *Bit_Getattr(PyObject *self, char *name) {
  BitObject *this=((BitObject *)self);

  if(strcmp(name, "userdata") == 0)
    {
      Py_INCREF(this->userdata);
      return this->userdata;
    }

  if (strcmp(name, "type") == 0) {
    Py_INCREF(bit_or_item_typename[this->type]);
    return bit_or_item_typename[this->type];
  }

  if(strcmp(name, "item") == 0) {
    /* no need for cache here, as Item_Encapsulate will check hash table */
    if(this->type == NSL_start_bit || this->type == NSL_empty_bit)
      return Item_Encapsulate(this->value.item, this->doctype, self);
    else
      return error("Bit is not of type start");
  }

  if(strcmp(name, "body") == 0) {
    if (this->body==Py_None) {
      if(this->type == NSL_text_bit || this->type == NSL_pi_bit ||
	 this->type == NSL_doctype_bit || this->type == NSL_comment_bit) {
	Py_DECREF(this->body);
	this->body=PyCharStar_FromCharStar(this->value.body,Strlen(this->value.body));
      }
      else {
	return error("Bit is not of type text, doctype, comment or pi");
      };
    };
    Py_INCREF(this->body);
    return this->body;
  };

  if(strcmp(name, "isCData") == 0)
    {
      if(this->type == NSL_text_bit)
	return PyInt_FromLong(this->flags & NSL_text_isCData);
      else
	return error("Bit is not of type text, doctype, comment or pi");
    }

  if(strcmp(name, "isERef") == 0)
    {
      if(this->type == NSL_text_bit)
	return PyInt_FromLong(this->flags & NSL_text_isERef);
      else
	return error("Bit is not of type text, doctype, comment or pi");
    }

  if(strcmp(name, "label") == 0) {
    if (this->pLabel==Py_None) {
      if (this->type == NSL_start_bit || this->type == NSL_end_bit || 
	  this->type == NSL_empty_bit) {
	Py_DECREF(this->pLabel);
	this->pLabel=PyCharStar_FromCharStar(this->label,Strlen(this->label));
      }
      else {
	return error("Bit is not of type start, empty or end");
      };
    };
    Py_INCREF(this->pLabel);
    return this->pLabel;
  };

  if(strcmp(name, "llabel") == 0) {
    if (!this->pLlabel) {
      if (this->type == NSL_start_bit || this->type == NSL_end_bit || 
	  this->type == NSL_empty_bit) {
	if (this->llabel) {
	  this->pLlabel = PyCharStar_FromCharStar(this->llabel,
						  Strlen(this->llabel));
	  }
	else {
	  Py_INCREF(Py_None);
	  this->pLlabel = Py_None;
	}
      }
      else {
	return error("Bit is not of type start, empty or end");
      };
    };
    Py_INCREF(this->pLlabel);
    return this->pLlabel;
  };

  if(strcmp(name, "nsuri") == 0) {
    if (!this->pNsuri) {
      if (this->type == NSL_start_bit || this->type == NSL_end_bit || 
	  this->type == NSL_empty_bit) {
	if (this->nsuri) {
	  this->pNsuri = PyCharStar_FromCharStar(this->nsuri,
						 Strlen(this->nsuri));
	  if (!this->pNsuri) {
	    Py_INCREF(Py_None);
	    this->pNsuri = Py_None;
	  }
	}
	else {
	  Py_INCREF(Py_None);
	  this->pNsuri = Py_None;
	}
      }
      else {
	return error("Bit is not of type start, empty or end");
      };
    };
    Py_INCREF(this->pNsuri);
    return this->pNsuri;
  };

  if(strcmp(name, "prefix") == 0) {
    if (!this->pPrefix) {
      if (this->type == NSL_start_bit || this->type == NSL_end_bit || 
	  this->type == NSL_empty_bit) {
	if (this->prefix) {
	  this->pPrefix = PyCharStar_FromCharStar(this->prefix,
						  Strlen(this->prefix));
	}
	else {
	  Py_INCREF(Py_None);
	  this->pPrefix = Py_None;
	}
      }
      else {
	return error("Bit is not of type start, empty or end");
      };
    };
    Py_INCREF(this->pPrefix);
    return this->pPrefix;
  };

  return error("Unknown Bit attribute %s", name);
}

/* Builds a Data from a Python list, to be attached to parent.  Checks
   that the items in the Data are not in another Data (though they can
   be in parent's Data, since that will be going away).
   NB Must not be called with empty list since returns null for error. XXX */

static NSL_Data *Data_Build(PyObject *pdata, PyObject *pparent) {
  PyObject *pdatum;
  int i, ndata;
  NSL_Data *data;
  NSL_Data *datum, **datump;
  NSL_Item *parent = ((ItemObject *)pparent)->item;
  int isTuple=PyTuple_Check(pdata);

  /* Check that the list is good */

  ndata = isTuple?PyTuple_GET_SIZE(pdata):PyList_GET_SIZE(pdata);
  for (i=0; i<ndata; i++) {
    pdatum = isTuple?PyTuple_GET_ITEM(pdata, i):PyList_GET_ITEM(pdata, i);
    if(pdatum->ob_type == &ItemType)
      {
	if(((ItemObject *)pdatum)->item->in &&
	   ((ItemObject *)pdatum)->item->in->in != parent)
	  return error("Items in new Data must not be part "
		       "of an another Item's Data");
      }
    else if (!IsCharStar(pdatum)) {
      return error("Data is neither a string (8- or 16-bit) nor an Item");
    }
  }
  /* printf("list ok\n"); */
  datump = &data;
  for(i=0; i<ndata; i++)
    {
      pdatum = isTuple?PyTuple_GET_ITEM(pdata, i):PyList_GET_ITEM(pdata, i);
      *datump = datum = NewNullNSLData(parent->doctype);
      datum->in = parent;
      if(pdatum->ob_type == &ItemType)
	{
	  ItemObject *child_pitem = (ItemObject *)pdatum;
	  NSL_Item *child_item = child_pitem->item;

	  datum->type = NSL_item_data;
	  datum->first = child_item;
#if 0
	  why was this here?
	    Py_INCREF(child_pitem);
#else
	  Py_INCREF(pparent);
#endif
	  child_pitem->owner = pparent;
	  child_item->in = datum;
	  /* Give owner responsibility for freeing any strings */
	  /* XXX shouldn't we follow up the parent chain here? */
	  ((ItemObject *)pparent)->alloclist = 
	    AllocList_nconc(((ItemObject *)pparent)->alloclist,
			    child_pitem->alloclist);
	  child_pitem->alloclist = 0;
	}
      else
	{
	  Char *ds;
	  datum->type = NSL_text_data;
	  /* XXX shouldn't we follow up the parent chain here? */
	  datum->first = AllocList_strdup(ds=ExtractCharStar(pdatum),
					  &((ItemObject *)pparent)->alloclist);
	  Free(ds);
	  if(!datum->first)
	    return (void *)PyErr_NoMemory(); /* XXX should undo things */
	}
      datump = &datum->next;
    }
  *datump = 0;

  return data;
}

static PyObject *pItem(PyObject *self, PyObject *args)
{
    PyObject *pdata, *pitem, *pdt, *labelObj;
    NSL_Item *item;
    NSL_Doctype dt;
    Char *label;

    if(!PyArg_ParseTuple(args, "OOO", &pdt, &labelObj, &pdata))
	return NULL;

    if(pdt->ob_type != &DoctypeType)
	return error("First arg to Item is not a Doctype");
    dt = ((DoctypeObject *)pdt)->doctype;

    CheckCharStar(labelObj,"second","Item");
    label=ExtractCharStar(labelObj);

    if(pdata != Py_None && !PyList_Check(pdata) && !PyTuple_Check(pdata))
	return error("Third arg to Item is not a list, tuple or None");
    
    item = NewNullNSLItem(dt, label, Strlen(label));
    pitem = Item_Encapsulate(item, dt, 0);

    if (pdata == Py_None) {
      item->data = 0;
    }
    else {
      item->data = Data_Build(pdata, pitem);
      if (!item->data) {
	/* sigh */
	Item_Dealloc(pitem);
	return NULL;
      };
    }

    item->type = (item->data ? NSL_non_empty : NSL_empty);

    return pitem;
}

static PyObject *Item_Getattr(PyObject *self, char *name) {
  ItemObject *this=((ItemObject *)self);
  NSL_Item *item = this->item;

  assert(item->type != NSL_free);

  if(strcmp(name, "type") == 0) {
    Py_INCREF(bit_or_item_typename[item->type]);
    return bit_or_item_typename[item->type];
  };

  if(strcmp(name, "label") == 0) {
    if (this->label==Py_None) {
      Py_DECREF(this->label);
      this->label=PyCharStar_FromCharStar(item->label,Strlen(item->label));
    };
    Py_INCREF(this->label);
    return this->label;
  }

  if(strcmp(name, "llabel") == 0) {
    if (!this->llabel) {
      if (item->llabel) {
	this->llabel = PyCharStar_FromCharStar(item->llabel,
					       Strlen(item->llabel));
      }
      else {
	Py_INCREF(Py_None);
	this->llabel=Py_None;
      }
    };
    Py_INCREF(this->llabel);
    return this->llabel;
  }

  if(strcmp(name, "nsuri") == 0) {
    if (!this->nsuri) {
      if (item->nsuri) {
	this->nsuri = PyCharStar_FromCharStar(item->nsuri,
					    Strlen(item->nsuri));
	}
      else {
	Py_INCREF(Py_None);
	this->nsuri = Py_None;
      }
    };
    Py_INCREF(this->nsuri);
    return this->nsuri;
  }

  if(strcmp(name, "prefix") == 0) {
    if (!this->prefix) {
      if (item->prefix) {
	this->prefix = PyCharStar_FromCharStar(item->prefix,
						  Strlen(item->prefix));
	}
      else {
	Py_INCREF(Py_None);
	this->prefix = Py_None;
      }
    };
    Py_INCREF(this->prefix);
    return this->prefix;
  }

  if(strcmp(name, "nsdict") == 0) {
    if (!this->nsdict) {
	this->nsdict = BuildNsdict(item);
    };
    Py_INCREF(this->nsdict);
    return this->nsdict;
  }

  if(strcmp(name, "data") == 0) {
    if(item->type != NSL_inchoate) {
      if (this->data==Py_None) {
	Py_DECREF(this->data);
	this->data=Data_Encapsulate(item->data, this->doctype, self);
      };
      Py_INCREF(this->data);
      return this->data;
    }
    else
      return error("Can't extract data from inchoate item; call ItemParse first");
  }
    
  if(strcmp(name, "parent") == 0)
    {
      PyObject *parent;
	
      if(!item->in)
	{
	  Py_INCREF(Py_None);
	  return Py_None;
	}
      parent = Item_Encapsulate(item->in->in, this->doctype, this->owner);
      if(!this->owner)
	{
	  this->owner = parent;
	  Py_INCREF(parent);
	}

      return parent;
    }

  if(strcmp(name, "userdata") == 0)
    {
      Py_INCREF(this->userdata);
      return this->userdata;
    }

  return error("Unknown Item attribute %s", name);
}
    
static PyObject *BuildNsdict(NSL_Item *item)
{
    PyObject *dict, *uri, *prefix;
    NSL_Item *ancestor;
    ItemObject *pancestor = 0;
    NamespaceBinding n, m;

    if(!item->ns_dict)
    {
	/* Not doing namespaces */
	Py_INCREF(Py_None);
	return Py_None;
    }

    /* See if we can re-use an ancestor's dictionary.  We find most
       ancient ancestor with the same dictionary, and store the
       python dictionary on that ancestors python encapsulation, so
       that accessing children's dictionaries before parents' won't
       result in multiple copies. */

    ancestor = item;
    while(ancestor->in && ancestor->in->in->ns_dict == item->ns_dict)
	ancestor = ancestor->in->in;

    if(ancestor != item)
    {
	pancestor = (ItemObject *)Item_Encapsulate(ancestor, (NSL_Doctype)item->doctype, 0);
	if(pancestor->nsdict)
	{
	    /* We found a dictionary we can reuse */
	    Py_INCREF(pancestor->nsdict);
	    return pancestor->nsdict;
	}
    }

    /* We need to build the dictionary */

    dict = PyDict_New();

    for(n=item->ns_dict; n; n=n->parent)
    {
	/* Ignore it if we've already seen it */
	for(m=item->ns_dict; m!=n; m=m->parent)
	{
	    if(m->prefix == n->prefix)
		goto done;
	    if(m->prefix && n->prefix && Strcmp(m->prefix, n->prefix) == 0)
		goto done;
	}

	if(!n->namespace)
	    /* undefining default namespace */
	    goto done;
	
	uri = PyCharStar_FromCharStar(n->namespace->nsname,
				      Strlen(n->namespace->nsname));
	if (!uri) {
	  uri=Py_None;
	  Py_INCREF(Py_None);
	}

	if (n->prefix) {
	  prefix=PyCharStar_FromCharStar(n->prefix,Strlen(n->prefix));
	  PyDict_SetItem(dict, prefix, uri);
	  Py_DECREF(prefix);    /* incremented by SetItem */
	}
	else {
	    PyDict_SetItem(dict, Py_None, uri);
	}
	Py_DECREF(uri);		/* incremented by SetItem */

    done:
	;
    }

    /* If we found an ancestor needing the same dictionary, let it have it */

    if(pancestor)
    {
	Py_INCREF(dict);
	pancestor->nsdict = dict;
    }

    return dict;
}

static int Item_Setattr(PyObject *self, char *name, PyObject *value) {
  ItemObject *this=((ItemObject *)self);
  NSL_Item *item = this->item;
  NSL_Data *data, *datum;

  if (strcmp(name, "data") == 0) {
    /* Check we got a good list */

    if ((!PyList_Check(value)) && (!PyTuple_Check(value))) {
      error("Attempt to set Item Data to a non-list");
      return -1;
    }
    if(item->type != NSL_non_empty) {
      error("Can only set Data for non-empty Items");
      return -1;
    }

    /* Build the new Data */
    /* printf("building new data\n"); */
    if ((PyTuple_Check(value)?
	 PyTuple_GET_SIZE(value):PyList_GET_SIZE(value)) != 0) {
      data = Data_Build(value, self);
      if(!data)
	return -1;
    }
    else
      data = 0;

    /* Fix up the old Data */
    /* printf("munging old data\n"); */

    for(datum = item->data; datum; datum = datum->next)
      {
	if(datum->type == NSL_text_data)
	  /* should we free it? XXX */
	  /* probably not, if we allocated it it will be freed later
	     because it's on the item's alloclist. */
	  ;
	else if(datum->type == NSL_item_data)
	  {
	    NSL_Item *subitem = datum->first;
	    /* If it's in the new list, leave it alone.  We can tell
	       because it's "in" will no longer point to this Data. */
	    if(subitem->in != datum)
	      ;
	    else
	      {
		/* It's no longer in the list, so clear its "in". */
		subitem->in = 0;
		/* We ought to free it if it and its children are not
		   referenced from Python, and should fix up their
		   owners if they are. XXX */
	      }
	  }
      }
    /* printf("ok\n"); */
    /* Install the new data */

    item->data = data;
    Py_DECREF(this->data);
    Py_INCREF(Py_None);
    this->data=Py_None;

    return 0;
  }

  return NSL_Setattr(self, name, value);
}

static PyObject *pItemParse(PyObject *self, PyObject *args)
{
    PyObject *file, *item;

    if(!PyArg_ParseTuple(args, "OO", &file, &item))
	return NULL;

    if(file->ob_type != &FileType)	
	return error("First arg to ItemParse is not a File");
    if(item->ob_type != &ItemType)	
	return error("Second arg to ItemParse is not an Item");

    ItemParse(((FileObject *)file)->file, ((ItemObject *)item)->item);

    Py_INCREF(item);
    
    return item;
}

static PyObject *pLookupPrefix(PyObject *self, PyObject *args)
{
    PyObject *pitem, *pprefix;
    const Char *uri;
    Char *prefix;

    if(!PyArg_ParseTuple(args, "OO", &pitem, &pprefix))
	return NULL;

    if(pitem->ob_type != &ItemType)	
	return error("First arg to LookupPrefix is not an Item");

    if(pprefix == Py_None)
	prefix = 0;
    CheckCharStar(pprefix,"second","LookupPrefix");
    prefix = ExtractCharStar(pprefix);

    uri = LookupPrefix(((ItemObject *)pitem)->item, prefix);
    Free(prefix);

    if(uri)
	return PyCharStar_FromCharStar(uri,Strlen(uri));
    else
    {
	Py_INCREF(Py_None);
	return Py_None;
    }
}

static PyObject *pGetAttrStringVal(PyObject *self, PyObject *args)
{
    PyObject *item,*pname;
    Char *name;
    const Char *value,*uname;

    if(!PyArg_ParseTuple(args, "OO", &item, &pname))
	return NULL;

    if(((ItemObject *)item)->ob_type != &ItemType)	
	return error("First arg to GetAttr[String]Value is not an Item");

    CheckCharStar(pname,"second","GetAttrStringVal");
    name=ExtractCharStar(pname);

    uname = AttrUniqueName(((ItemObject *)item)->doctype, name, Strlen(name));
    Free(name);
    if(!uname)
    {
	Py_INCREF(Py_None);
	return Py_None;
    }

    value = GetAttrStringVal(((ItemObject *)item)->item, uname);
    if(!value || value==NSL_Implied_Attribute_Value)
    {
	Py_INCREF(Py_None);
	return Py_None;
    }
	
    return PyCharStar_FromCharStar(value,Strlen(value));
}

static PyObject *pPutAttrVal(PyObject *self, PyObject *args)
{
    PyObject *it,*pname,*pvalue;
    ItemObject *item;
    BitOrItemObject *top;
    const Char *uname,*svalue;
    Char *value,*name;

    if(!PyArg_ParseTuple(args, "OOO", &it, &pname, &pvalue))
	return NULL;

    if(it->ob_type != &ItemType)	
	return error("First arg to PutAttrValue is not an Item");
    item = (ItemObject *)it;

    CheckCharStar(pvalue,"third","PutAttrVal");
    value=ExtractCharStar(pvalue);
    CheckCharStar(pname,"second","PutAttrVal");
    name=ExtractCharStar(pname);

    uname = AttrUniqueName(((ItemObject *)item)->doctype, name, Strlen(name));
    Free(name);

    for(top=(BitOrItemObject *)item; top->owner; top=(BitOrItemObject *)top->owner)
	;
    svalue = AllocList_strdup(value, &top->alloclist);
    Free(value);
    return PyInt_FromLong(PutAttrVal(item->item, uname, svalue));
}

static PyObject *pNewAttrVal(PyObject *self, PyObject *args)
{
    PyObject *it,*pname,*pvalue;
    ItemObject *item;
    BitOrItemObject *top;
    const Char *uname,*svalue;
    Char *name,*value;

    if(!PyArg_ParseTuple(args, "OOO", &it, &pname, &pvalue))
	return NULL;

    if(it->ob_type != &ItemType)	
	return error("First arg to NewAttrValue is not an Item");
    item = (ItemObject *)it;

    CheckCharStar(pvalue,"third","NewAttrVal");
    value=ExtractCharStar(pvalue);
    CheckCharStar(pname,"second","NewAttrVal");
    name=ExtractCharStar(pname);

    uname = AttrUniqueName(((ItemObject *)item)->doctype, name, Strlen(name));
    Free(name);

    for(top=(BitOrItemObject *)item; top->owner; top=(BitOrItemObject *)top->owner)
	;
    svalue = AllocList_strdup(value, &top->alloclist);
    Free(value);

    NewAttrVal(item->item, uname, svalue);

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *Query_Getattr(PyObject *self, char *name)
{
    if(strcmp(name, "userdata") == 0)
    {
	Py_INCREF(((NSLObject *)self)->userdata);
	return ((NSLObject *)self)->userdata;
    }

    return error("Unknown Query attribute %s", name);
}

static PyObject *pParseQuery2(PyObject *self, PyObject *args, int regexp)
{
    PyObject *pdt,*qstrobj;
    Char *qstr,*dq;
    NSL_Query query;
    NSL_Doctype dt;

    if(!PyArg_ParseTuple(args, "OO", &pdt, &qstrobj))
	return NULL;

    if(pdt->ob_type != &DoctypeType)
	return error("First arg to ParseQuery/ParseQueryR is not a Doctype");
    dt = ((DoctypeObject *)pdt)->doctype;

    CheckCharStar(qstrobj,"second","ParseQuery");

    /* ParseQuery modifies its argument and keeps pointers into it, sigh */
    qstr = Strdup(dq=ExtractCharStar(qstrobj));
    Free(dq);

    query = (regexp ? ParseQueryR : ParseQuery)(dt, qstr);
    if(!query)			/* should probably signal error XXX */
    {
	Py_INCREF(Py_None);
	return Py_None;
    }
	
    return Query_Encapsulate(query);
}

static PyObject *pParseQuery(PyObject *self, PyObject *args)
{
    return pParseQuery2(self, args, 0);
}

static PyObject *pParseQueryR(PyObject *self, PyObject *args)
{
    return pParseQuery2(self, args, 1);
}

static PyObject *pGetNextQueryItem(PyObject *self, PyObject *args)
{
    PyObject *query, *infile, *outfile;
    NSL_Item *item;
    NSL_File coutfile;

    if(PyArg_ParseTuple(args, "OOO", &infile, &query, &outfile))
    {
	if(outfile->ob_type == &FileType)
	    coutfile = ((FileObject *)outfile)->file;
	else if(outfile == Py_None)
	    coutfile = 0;
	else
	   return error("Third arg to GetNextQueryItem is not a File or None");
    } else {
      PyErr_Clear();
      if(PyArg_ParseTuple(args, "OO", &infile, &query))
	coutfile = 0;
    else
	return NULL;
    }
    if(infile->ob_type != &FileType)	
	return error("First arg to GetNextQueryItem is not a File");
    if(query->ob_type != &QueryType)	
	return error("Second arg to GetNextQueryItem is not a Query");

    trace(("GetNextQueryItem... "));

    item = GetNextQueryItem(((FileObject *)infile)->file,
			    ((QueryObject *)query)->query,
			    coutfile);

    trace(("done\n"));

    if(!item)
    {
	Py_INCREF(Py_None);
	return Py_None;
    }
	
    return Item_Encapsulate(item, DoctypeFromFile(((FileObject *)infile)->file), 0);
}

static PyObject *pRetrieveQueryItem(PyObject *self, PyObject *args) {
  PyObject *query, *uitem, *from;
  NSL_Item *item;


  if(PyArg_ParseTuple(args, "OOO", &uitem, &query, &from))
    {
      if(from == Py_None)
	from = NULL;
      else if(from->ob_type != &ItemType)
	return error("Third arg to RetrieveQueryItem is not an Item");
    }
  else { PyErr_Clear();
  if(PyArg_ParseTuple(args, "OO", &uitem, &query))
    from = NULL;
  else
    return NULL;
  }
  if(uitem->ob_type != &ItemType)	
    return error("First arg to RetrieveQueryItem is not an Item");
  if(query->ob_type != &QueryType)	
    return error("Second arg to RetrieveQueryItem is not a Query");

  item = RetrieveQueryItem(((ItemObject *)uitem)->item,
			   ((QueryObject *)query)->query,
			   from ? ((ItemObject *)from)->item : 0);
  if(!item)
    {
      Py_INCREF(Py_None);
      return Py_None;
    }
	
  return Item_Encapsulate(item, ((ItemObject *)uitem)->doctype, (PyObject *)uitem);
}
/* pRetrieveQueryData -- should be as similar as possible
 * to pRetrieveQueryItem, but using RetrieveQueryData,
 * hence able to get at PCDATA as well as items. 
 *
 * In C RetrieveQueryData returns a boolean if it succeeds, and
 * places a pointer to a suitable NSL_Data in a location 
 * provided by the caller. But PyXML does not expose the NSL_Data
 * type, providing only strings and items. 
 *
 * One idea is to return a tuple containing the enclosing item
 * and a sublist of that items item.data such that the head of 
 * the list is the true return value. This tuple is sufficient
 * to allow resumption of the traversal at the right point, and
 * it doesn't require new Python types. 
 *
 * Henry also points out that 99.99% of the time we will want
 * to call RetrieveQueryData with exactly the data that was
 * returned from the last call to RetrieveQueryData, so it is
 * probably worth caching that.
 */

static PyObject * pRetrieveQueryData(PyObject *self, PyObject *args) {

  PyObject * from,*uitem,*query;

   if(PyArg_ParseTuple(args, "OO", &uitem, &query))
    from = NULL;
   /* XXX - not implemented yet */
  Py_INCREF(Py_None);
  return Py_None;
}

static void File_Dealloc(PyObject *self) {
  FileObject *this=((FileObject*)self);
  if(!gc_enable)
    return;

  alloc_debug(("File deallocated\n"));
    
  Py_DECREF(this->userdata);
  Py_DECREF(this->doctype);
  if (this->source) {
    Free(this->source);
  };

  PyObject_FREE(self);

}

static void Bit_Dealloc(PyObject *self) {
  BitObject *bit = (BitObject *)self;

  if(!gc_enable)
    return;

  alloc_debug(("Bit %d deallocated\n", bit->count));

  switch (bit->type) {
  case NSL_start_bit:
  case NSL_empty_bit:
    FreeItem(bit->value.item);
    break;
  case NSL_text_bit:
  case NSL_comment_bit:
  case NSL_pi_bit:
  case NSL_doctype_bit:
    sfree((char*)bit->value.body);
    break;
  default:
    break;
  };

  AllocList_free(bit->alloclist);

  Py_DECREF(bit->userdata);
  Py_DECREF(bit->body);
  Py_DECREF(bit->pLabel);
  Py_XDECREF(bit->pLlabel);
  Py_XDECREF(bit->pNsuri);

  PyObject_FREE(self);

}

static void Item_Dealloc(PyObject *self) {
  ItemObject *item = (ItemObject *)self;

  if(!gc_enable)
    return;

  if(item->owner)
    {
      alloc_debug(("Item %d deallocated (not really)\n", item->count));
      Object_Forget(item->item);
    }
  else
    {
      alloc_debug(("Item %d deallocated\n", item->count));
      if(item->item->type == NSL_bad)
	fprintf(stderr, "Warning: trying to free bad item\n");
      else
	{
	  Object_Forget(item->item);
	  FreeItem(item->item);
	}
      AllocList_free(item->alloclist);
    }

  Py_DECREF(item->userdata);
  if (item->owner) {
    Py_DECREF(item->owner);
  };
  Py_DECREF(item->label);
  Py_XDECREF(item->llabel);
  Py_XDECREF(item->nsuri);
  Py_XDECREF(item->nsdict);
  Py_DECREF(item->data);
  PyObject_FREE(self);

}

static void OOB_Dealloc(PyObject *self) {
  OOBObject *oob = (OOBObject *)self;

  if(!gc_enable)
    return;

  if(oob->owner)
    {
      alloc_debug(("oob %d deallocated (not really)\n", oob->count));
      Py_DECREF(oob->owner);
    }
  else
    {
      alloc_debug(("oob %d deallocated\n", oob->count));
      sfree((char*)oob->data);
    }

  Py_DECREF(oob->userdata);
  Py_DECREF(oob->sData);
  Py_DECREF(oob->type);

  PyObject_FREE(self);

}

static void ERef_Dealloc(PyObject *self) {
  ERefObject *er = (ERefObject *)self;

  if(!gc_enable)
    return;

  alloc_debug(("eref deallocated\n"));

  Py_DECREF(er->userdata);
  Py_DECREF(er->name);

  PyObject_FREE(self);

}

static void Doctype_Dealloc(PyObject *self) {
  DoctypeObject *this=((DoctypeObject*)self);
  if(!gc_enable)
    return;

  alloc_debug(("Doctype deallocated\n"));

  Py_DECREF(this->userdata);
  Py_DECREF(this->elementTypes);
  Py_DECREF(this->generalEntities);
  Py_DECREF(this->parameterEntities);
  Py_DECREF(this->name);

  PyObject_FREE(self);

}

static void ContentParticle_Dealloc(PyObject *self) {
  ContentParticleObject *this=((ContentParticleObject*)self);
  if(!gc_enable)
    return;

  alloc_debug(("ContentParticle deallocated\n"));

  Py_DECREF(this->userdata);
  Py_DECREF(this->name);
  Py_DECREF(this->children);

  PyObject_FREE(self);

}

static void ElementType_Dealloc(PyObject *self) {
  ElementTypeObject *this=((ElementTypeObject*)self);
    if(!gc_enable)
	return;

    alloc_debug(("ElementType deallocated\n"));
  Py_DECREF(this->userdata);
  Py_DECREF(this->name);
  Py_DECREF(this->particle);
  Py_DECREF(this->attrDefns);

    PyObject_FREE(self);

}

static void AttrDefn_Dealloc(PyObject *self) {
  AttrDefnObject *this=((AttrDefnObject*)self);
  
    if(!gc_enable)
	return;

    alloc_debug(("Attribute deallocated\n"));

  Py_DECREF(this->userdata);
  Py_DECREF(this->name);
  Py_DECREF(this->allowedValues);
  Py_DECREF(this->defValue);

    PyObject_FREE(self);

}

static void Query_Dealloc(PyObject *self) {
  QueryObject *this=((QueryObject*)self);
  if(!gc_enable)
    return;

  alloc_debug(("Query deallocated\n"));

  FreeQuery(this->query);

  Py_DECREF(this->userdata);

  PyObject_FREE(self);

}

static PyObject *pAutoFreeNSLObjects(PyObject *self, PyObject *args)
{
    PyObject *arg;

    if(!PyArg_ParseTuple(args, "O", &arg))
	return NULL;

    gc_enable = PyObject_IsTrue(arg);

    Py_INCREF(Py_None);
    return Py_None;
}


/* 
 Get a Python object which mirrors the actual attributes on
 an item. 
 Author:Chris Brew
 Date: 7 January 1998
 Rewritten: Henry S. Thompson, 1998.11.11
 */
static PyObject * pItemActualAttributes(PyObject *self, PyObject *args) {
  PyObject *arg,*list,*pair;
  struct NSL_Attr * attrs, *attr;
  int len, i;

  if(!PyArg_ParseTuple(args,"O", &arg))
    return NULL;
  if(arg->ob_type != &ItemType)	
    return error("First arg to ItemActualAttributes is not an Item");
  attrs=((ItemObject*)arg)->item->attr; 

  for(len=0,attr = attrs;
      attr != NULL; 
      len++,attr = attr->next) 
    ;

  list = PyTuple_New(len);
  if (!list) {
    return NULL;
  };
    
  for(i=0,attr = attrs; i < len;  i++,attr = attr->next) {
    pair=PyTuple_New(2);
    if (!pair) {
      return NULL;
    };
    PyTuple_SET_ITEM(pair,0,PyCharStar_FromCharStar(attr->name,Strlen(attr->name)));
    PyTuple_SET_ITEM(pair,1,PyCharStar_FromCharStar(attr->value.string,Strlen(attr->value.string)));
    PyTuple_SET_ITEM(list, i, pair);
  }

  return list;
}

/*
 * Instead of name-value pairs, this returns name-value-uri-localname
 * quadruples.  It should be replaced by something cooler later.
 */

static PyObject * pItemActualAttributesNS(PyObject *self, PyObject *args) {
  PyObject *arg,*list,*pair,*name;
  struct NSL_Attr * attrs, *attr;
  int len, i;

  if(!PyArg_ParseTuple(args,"O", &arg))
    return NULL;
  if(arg->ob_type != &ItemType)	
    return error("First arg to ItemActualAttributesNS is not an Item");
  attrs=((ItemObject*)arg)->item->attr; 

  for(len=0,attr = attrs;
      attr != NULL; 
      len++,attr = attr->next) 
    ;

  list = PyTuple_New(len);
  if (!list) {
    return NULL;
  };
    
  for(i=0,attr = attrs; i < len;  i++,attr = attr->next) {
    pair=PyTuple_New(4);
    if (!pair) {
      return NULL;
    };
    name=PyCharStar_FromCharStar(attr->name,Strlen(attr->name));
    PyTuple_SET_ITEM(pair,0,name);
    PyTuple_SET_ITEM(pair,1,PyCharStar_FromCharStar(attr->value.string,Strlen(attr->value.string)));
    if (attr->nsuri) {
	PyTuple_SET_ITEM(pair,2,PyCharStar_FromCharStar(attr->nsuri,
						      Strlen(attr->nsuri)));
	PyTuple_SET_ITEM(pair,3,PyCharStar_FromCharStar(attr->lname,
							Strlen(attr->lname)));
    }
    else {
      Py_INCREF(PyTuple_SET_ITEM(pair, 2, Py_None));
      if (attr->lname) {
	Py_INCREF(PyTuple_SET_ITEM(pair,3,name)); /* must be the same */
      }
      else {
	Py_INCREF(PyTuple_SET_ITEM(pair, 3, Py_None));
      };
    }
    PyTuple_SET_ITEM(list, i, pair);
  }
  return list;
}

#if CHAR_SIZE != 8

static char16 *PyUnicodeOrString_AsZTUnicode(PyObject *strOrUni) {
  if PyUnicode_Check(strOrUni) {
#if Py_UNICODE_SIZE == 2
    int size=PyUnicode_GET_DATA_SIZE(strOrUni);
    char16 *res=malloc(size+sizeof(char16));
    if (!res) {
      return NULL;
    };
    memcpy(res,PyUnicode_AS_UNICODE(strOrUni),size);
    res[PyUnicode_GET_SIZE(strOrUni)]=(char16)0;
#else
    int size=PyUnicode_GET_SIZE(strOrUni);
    char16 *res=malloc((size+1)*sizeof(char16));
    Py_UNICODE *p=PyUnicode_AS_UNICODE(strOrUni);
    Py_UNICODE c;
    int i;
    for ( i=0; i<size; i++ ) {
      res[i]=(c = p[i]) > 65535?(char16)'?':(char16)c;
    }
    res[size]=(char16)0;
#endif
    return res;			/* !!! Never Freed !!! */
  }
  else {
    /* PyString */
    int size=PyString_GET_SIZE(strOrUni);
    char16 *res=malloc(sizeof(char16)*(size+1));
    if (!res) {
      return NULL;
    };
    translate_latin1_utf16(PyString_AS_STRING(strOrUni),res);
    res[size]=(char16)0;
    return res;			/* !!! Never freed !!! */
  }
}
#endif

static PyMethodDef NSLMethods[] = {
    {"Item",               pItem,              METH_VARARGS, "no comment"},
    {"ItemParse",          pItemParse,         METH_VARARGS, "no comment"},    
    {"LookupPrefix",       pLookupPrefix,      METH_VARARGS, "no comment"},    
    {"GetNextBit",         pGetNextBit,        METH_VARARGS, "no comment"},    
    {"Open",               pOpen,              METH_VARARGS, "no comment"},    
    {"FOpen",              pFOpen,             METH_VARARGS, "no comment"},    
    {"OpenURL",            pOpenURL,           METH_VARARGS, "no comment"},    
    {"OpenStream",         pOpenStream,        METH_VARARGS, "no comment"},    
    {"OpenString",         pOpenString,        METH_VARARGS, "no comment"},    
    {"Close",              pClose,             METH_VARARGS, "no comment"},    
    {"Print",              pPrint,             METH_VARARGS, "no comment"},    
    {"ForceNewline",       pForceNewline,      METH_VARARGS, "no comment"},    
    {"PrintTextLiteral",   pPrintTextLiteral,  METH_VARARGS, "no comment"},
    {"PrintEndTag",        pPrintEndTag,       METH_VARARGS, "no comment"},
    {"PrintStartTag",      pPrintStartTag,     METH_VARARGS, "no comment"},
    {"GetAttrStringVal",   pGetAttrStringVal,  METH_VARARGS, "no comment"},
    {"GetAttrVal",         pGetAttrStringVal,  METH_VARARGS, "no comment"},
    {"PutAttrVal",         pPutAttrVal,        METH_VARARGS, "no comment"},
    {"NewAttrVal",         pNewAttrVal,        METH_VARARGS, "no comment"},
    {"ParseQuery",         pParseQuery,        METH_VARARGS, "no comment"},
    {"ParseQueryR",        pParseQueryR,       METH_VARARGS, "no comment"},
    {"GetNextQueryItem",   pGetNextQueryItem,  METH_VARARGS, "no comment"},
    {"RetrieveQueryItem",  pRetrieveQueryItem, METH_VARARGS, "no comment"},
    {"RetrieveQueryData",  pRetrieveQueryData, METH_VARARGS, "no comment"},
    {"DoctypeFromDdb",     pDoctypeFromDdb,    METH_VARARGS, "no comment"},
    {"AutoFreeNSLObjects", pAutoFreeNSLObjects,
     METH_VARARGS, "no comment"},
    {"ItemActualAttributes",pItemActualAttributes,
     METH_VARARGS, "no comment"},
    {"ItemActualAttributesNS",pItemActualAttributesNS,
     METH_VARARGS, "no comment"},
    {NULL,    NULL, 0, NULL}
};

extern PYXML_API void initLTXMLinter(void)
{
    PyObject *m , *d;

    FileType.ob_type=&PyType_Type;
    DoctypeType.ob_type=&PyType_Type;
    ElementTypeType.ob_type=&PyType_Type;
    AttrDefnType.ob_type=&PyType_Type;
    ContentParticleType.ob_type=&PyType_Type;
    BitType.ob_type=&PyType_Type;
    ItemType.ob_type=&PyType_Type;
    OOBType.ob_type=&PyType_Type;
    ERefType.ob_type=&PyType_Type;
    QueryType.ob_type=&PyType_Type;
    m = Py_InitModule("LTXMLinter", NSLMethods);
    d = PyModule_GetDict(m);
    xXMLError = PyString_FromString("LTXMLinter.error");
    bit_or_item_typename[0]=PyString_FromString("bad"); /* bits */
    bit_or_item_typename[1]=PyString_FromString("start");
    bit_or_item_typename[2]=PyString_FromString("end");
    bit_or_item_typename[3]=PyString_FromString("empty");
    bit_or_item_typename[4]=PyString_FromString("eof");
    bit_or_item_typename[5]=PyString_FromString("text");
    bit_or_item_typename[6]=PyString_FromString("pi");
    bit_or_item_typename[7]=PyString_FromString("doctype");
    bit_or_item_typename[8]=PyString_FromString("comment");
    bit_or_item_typename[9]=PyString_FromString("inchoate"); /* items */
    bit_or_item_typename[10]=PyString_FromString("non_empty");
    bit_or_item_typename[11]=PyString_FromString("empty");
    bit_or_item_typename[12]=PyString_FromString("free");
    PyDict_SetItemString(d, "NSL_read",
			 PyInt_FromLong(NSL_read));
    PyDict_SetItemString(d, "NSL_read_all_bits",
			 PyInt_FromLong(NSL_read_all_bits));
    PyDict_SetItemString(d, "NSL_read_no_consume_prolog",
			 PyInt_FromLong(NSL_read_no_consume_prolog));
    PyDict_SetItemString(d, "NSL_read_no_normalise_attributes",
			 PyInt_FromLong(NSL_read_no_normalise_attributes));
    PyDict_SetItemString(d, "NSL_read_declaration_warnings",
			 PyInt_FromLong(NSL_read_declaration_warnings));
    PyDict_SetItemString(d, "NSL_read_strict",
			 PyInt_FromLong(NSL_read_strict));
    PyDict_SetItemString(d, "NSL_read_no_expand",
			 PyInt_FromLong(NSL_read_no_expand));
    PyDict_SetItemString(d, "NSL_read_validate",
			 PyInt_FromLong(NSL_read_validate));
    PyDict_SetItemString(d, "NSL_read_namespaces",
			 PyInt_FromLong(NSL_read_namespaces));
    PyDict_SetItemString(d, "NSL_read_defaulted_attributes",
			 PyInt_FromLong(NSL_read_defaulted_attributes));
    PyDict_SetItemString(d, "NSL_read_relaxed_any",
			 PyInt_FromLong(NSL_read_relaxed_any));
    PyDict_SetItemString(d, "NSL_read_allow_undeclared_nsattributes",
		 PyInt_FromLong(NSL_read_allow_undeclared_nsattributes));
    PyDict_SetItemString(d, "NSL_read_flags", PyInt_FromLong(NSL_read_flags));
    PyDict_SetItemString(d, "NSL_write", PyInt_FromLong(NSL_write));
    PyDict_SetItemString(d, "NSL_write_no_doctype",
			 PyInt_FromLong(NSL_write_no_doctype));
    PyDict_SetItemString(d, "NSL_write_no_expand",
			 PyInt_FromLong(NSL_write_no_expand));
    PyDict_SetItemString(d, "NSL_write_plain",
			 PyInt_FromLong(NSL_write_plain));
    PyDict_SetItemString(d, "NSL_write_fancy",
			 PyInt_FromLong(NSL_write_fancy));
    PyDict_SetItemString(d, "NSL_write_canonical",
			 PyInt_FromLong(NSL_write_canonical));
    PyDict_SetItemString(d, "NSL_write_default",
			 PyInt_FromLong(NSL_write_default));
    PyDict_SetItemString(d, "NSL_write_style",
			 PyInt_FromLong(NSL_write_style));
    PyDict_SetItemString(d, "NSL_write_flags",
			 PyInt_FromLong(NSL_write_flags));
    PyDict_SetItemString(d, "error", xXMLError);
    initDoctypeNames();
    PyDict_SetItemString(d, "CharacterEncodingNames", CEncDict);
    PyDict_SetItemString(d, "FileType", (PyObject *)&FileType);
    PyDict_SetItemString(d, "DoctypeType", (PyObject *)&DoctypeType);
    PyDict_SetItemString(d, "ElementTypeType", (PyObject *)&ElementTypeType);
    PyDict_SetItemString(d, "AttrDefnType", (PyObject *)&AttrDefnType);
    PyDict_SetItemString(d, "ContentParticleType",
			 (PyObject *)&ContentParticleType);
    PyDict_SetItemString(d, "BitType", (PyObject *)&BitType);
    PyDict_SetItemString(d, "ItemType", (PyObject *)&ItemType);
    PyDict_SetItemString(d, "OOBType", (PyObject *)&OOBType);
    PyDict_SetItemString(d, "ERefType", (PyObject *)&ERefType);
    PyDict_SetItemString(d, "QueryType", (PyObject *)&QueryType);
    NSLInit(1);			/* Hmm, what error level do we want? XXX */

    objects = create_hash_table(100, sizeof(void *));
}
/*
   $Log: NSLintermodule.c,v $
   Revision 1.56  2004/07/01 11:02:58  ht
   added XMLVersion property to Files

   Revision 1.55  2004/01/27 12:32:24  ht
   handle incoming 32-bit-wide PyUnicode

   Revision 1.54  2003/10/10 13:51:32  ht
   use _correct_ python free on objects

   Revision 1.53  2003/10/10 13:24:36  ht
   use python free on objects

   Revision 1.52  2003/09/02 15:46:02  ht
   get arg checking for OpenStream right

   Revision 1.51  2003/09/01 18:15:03  ht
   nsuri is 16 bits now

   Revision 1.50  2003/09/01 16:40:49  ht
   cleanup unused var after error

   Revision 1.49  2003/09/01 14:08:35  ht
   add 5-arg't version of OpenStream, with baseURI arg't

   Revision 1.48  2003/06/03 15:20:17  ht
   work with 16- or 32-bit Python

   Revision 1.47  2002/11/02 19:27:59  ht
   handle latin-1 NS URIs properly

   Revision 1.46  2002/07/03 16:54:40  ht
   approved 2.2 method table

   Revision 1.45  2002/06/17 10:35:50  ht
   missing attr->None

   Revision 1.44  2002/02/02 14:23:04  ht
   make gcc happier in various ways

   Revision 1.43  2001/11/23 17:01:31  ht
   share attribute name and local name if they're the same

   Revision 1.42  2001/11/22 17:14:04  ht
   add prefix to bits and items

   Revision 1.41  2001/06/10 21:21:02  ht
   cant free OpenString source right away

   Revision 1.40  2001/06/09 19:05:49  ht
   fix lurking unicode argument bugs in ...AttrVal functions,
   free result of ExtractCharStar when it is unicode

   Revision 1.39  2000/10/30 15:12:29  ht
   more doctype access,
   more RXP flags

   Revision 1.38  2000/08/27 20:14:22  ht
   merged Char branch in to main

   Revision 1.37.2.12  2000/07/04 15:41:07  aqw
   change name to PyLTXML

   Revision 1.37.2.11  2000/05/31 13:23:32  aqw
   Oops, overdid some of the 16-bit stuff, make sure it still works as
   8-bit

   Revision 1.37.2.10  2000/05/22 15:42:51  aqw
   Add access to encodings:  CharacterEncodingTables (a dict from names to ints);
                             Provide OpenStream and OpenURL

   Revision 1.37.2.9  2000/05/22 12:53:09  aqw
   Finish handling Unicode string arguments
   add xencoding property to doctype
   (move log to end)

   Revision 1.37.2.8  2000/05/14 12:29:44  ht
   Allow PyString or PyUnicode as string arguments in 16-bit mode

   Revision 1.37.2.7  2000/05/08 20:59:17  ht
   support Unicode pattern to ParseQuery

   Revision 1.37.2.6  2000/05/05 14:51:47  ht
   upgrade buildNsDict to handle 16-bit prefixes

   Revision 1.37.2.5  2000/05/04 09:25:44  ht
   8-bit works OK, starting to add explicit Unicode support

   Revision 1.37.2.4  2000/05/03 12:40:03  ht
   add some missing INCREFs of Py_None to fix obscure access violation
   during finalisation of class hierarchy :-(

   Revision 1.37.2.3  2000/05/02 20:26:06  aqw
   compiles OK

   Revision 1.37.2.2  2000/05/02 11:17:51  ht
   starting to try to compile it, change to PyString_FromStringAndSize

   Revision 1.37.2.1  2000/05/01 22:01:02  bu
   start work on 16-bit enabled version

   Revision 1.37  2000/03/10 19:54:44  ht
   remove NextParamENtity stub
   track CEncNames more closely

 * Revision 1.36  2000/01/08  11:22:10  ht
 * add ; to make VC6 happy
 *
 * Revision 1.35  2000/01/07  17:12:31  richard
 * nsdict attribute for items
 *
 * Revision 1.34  2000/01/05  13:44:36  richard
 * NSL_read_defaulted_attributes
 *
 * Revision 1.33  1999/12/02  20:01:48  richard
 * namespace bindings
 *
 * Revision 1.32  1999/11/29  16:31:31  richard
 * Namespace stuff
 *
 * Revision 1.31  1999/11/26  19:02:29  richard
 * Preliminary line number support
 *
 * Revision 1.30  1999/08/06  13:26:37  richard
 * Add parameterEntities attribute to doctype.  (NextParameterEntity
 * should be removed when it is added to RXP.)
 *
 * Revision 1.29  1999/07/12  12:50:44  ht
 * rolled out free change, replaced with sfree which made VC++ debugger
 * happy.
 *
 * Revision 1.28  1999/07/11  18:48:24  ht
 * Added some DECREFs, added doctype to NewNullNSLData, fixed bug in ERef_dealloc
 *
 * Revision 1.27  1999/06/07  12:00:05  ht
 * include internal and external
 *
   Revision 1.26  1999/06/07 11:40:03  ht
   Add preliminary access to general entities

   Revision 1.25  1999/01/15 18:43:11  richard
   Last change caused SEGV for documents with no dtd - fixed.

 * Revision 1.24  1999/01/15  14:30:47  richard
 * Add PrintTextLiteral.
 * Add name attribute to doctype.
 *
 * Revision 1.23  1998/12/14  15:53:10  chrisbr
 * stub for pRetrieveQueryData
 *
 * Revision 1.22  1998/12/09  11:36:09  chrisbr
 * If PrintStartTag has an Item as its second argument, print the
 * start tag of that item, including any attributes specified on that
 * item. If the argument is a string, print a new start tag without
 * attributes.
 *
 * Revision 1.21  1998/12/08  11:32:38  chrisbr
 * Added PrintStartTag to match PrintEndTag
 *
 * Revision 1.20  1998/12/07  15:35:08  ht
 * pass error from Data_Build through Item
 *

   Revision 1.19  1998/12/07 15:18:45  richard
   Return None for non-existent attributes instead of SIGSEGV.
   Remove obsolete GetAttr[String]Value duplication.

 * Revision 1.18  1998/11/20  14:52:07  ht
 * oops, bug fix for bit bodies
 *
   Revision 1.17  1998/11/12 16:24:54  ht
   add OpenString

   Revision 1.16  1998/11/12 11:15:14  ht
   DECREF all embedded PyObjects in all Dealloc methods
   explicit initialisation of 'owner' field

   Revision 1.15  1998/11/12 09:40:58  ht
   all lists -> tuples

   Revision 1.14  1998/11/11 09:11:20  ht
   all Py_BuildValue gone
   rework ItemActualAttributes
   add NSL_read_validate

   Revision 1.13  1998/11/07 17:52:21  ht
   Completed pass adding caching
   Empty dict attrDefns if none
   Need to check PyObject field initialisation, deallocation carefully

   Revision 1.12  1998/11/06 10:56:38  ht
   part way through caching and cleanup pass

 * Revision 1.11  1998/11/05  17:25:32  ht
 * added attribute definitions to DTD
 * began making more use of static PyStrings instead of rebuilding
 *
 * Revision 1.10  1998/11/05  15:37:40  ht
 * basic DTD access working
 *
 * Revision 1.9  1998/11/04  19:27:37  ht
 * from richard and henry's airplane effort to access DTD info
 *
   Revision 1.2  1998/10/04 19:16:23  ht
   add types for element content and content models (not all functions
   yet)
*/
