/* Copyright (c) 1996 by Lele Gaifax.  All Rights Reserved
 *
 * This software may be used and distributed freely for any purpose
 * provided that this notice is included unchanged on any and all
 * copies. The author does not warrant or guarantee this software in
 * any way.
 *
 * This file is part of the PyObjC package.
 *
 * $RCSfile: ObjCStreams.m,v $
 * $Revision: 1.1.1.3 $
 * $Date: 1996/11/14 01:50:25 $
 *
 * Created Mon Oct  7 12:55:20 1996.
 * 
 */

#include "ObjC.h"
#include "OC_Stream.h"

#ifndef WITH_FOUNDATION

/*********************************/
/*** ObjCStream implementation ***/
/*********************************/

static const char *
stream_decode_error (NXHandler *handler)
{
  char *errorString = NULL;
    
  switch (handler->code)
    {
    case NX_illegalWrite:
      errorString = "Illegal Write on Stream.";
      break;

    case NX_illegalRead:
      errorString = "Illegal Read on Stream.";
      break;

    case NX_illegalSeek:
      errorString = "Illegal Seek on Stream.";
      break;

    case NX_illegalStream:
      errorString = "Illegal Stream.";
      break;
      
    case NX_streamVMError:
      errorString = "Stream VM Error.";
      break;

    case OC_closedStreamError:
      errorString = "Inappropriate operation on closed stream.";
      break;
      
    case OC_nonSeekableStreamError:
      errorString = "Stream's position cannot change.";
      break;

    case OC_nonWriteableStreamError:
      errorString = "Non writable stream.";
      break;

    case OC_nonReadableStreamError:
      errorString = "Non readable stream.";
      break;
      
    default:
      {
#define ERRMSG "Unknown error code: %d"
	static char errmsg[sizeof ERRMSG + 6];
	
	sprintf(errmsg, ERRMSG, handler->code);
	errorString = errmsg;
#undef ERRMSG
	break;
      }
    }

  return errorString;
}

#define STREAM_DURING NX_DURING

#define STREAM_ENDDURING						      \
	NX_HANDLER							      \
          const char *stream_err_msg = stream_decode_error (&NXLocalHandler); \
									      \
          PyErr_SetString (ObjCStreams_Error, (char *) stream_err_msg);	      \
          Py_XDECREF ((PyObject *) retvalue);				      \
          return NULL;							      \
        NX_ENDHANDLER

#define STREAM_ENDDURING_BLOCK_THREADS					      \
	NX_HANDLER							      \
	  const char *stream_err_msg = stream_decode_error (&NXLocalHandler); \
									      \
          Py_BLOCK_THREADS						      \
 	  PyErr_SetString (ObjCStreams_Error, (char *) stream_err_msg);       \
          Py_XDECREF ((PyObject *) retvalue);				      \
          return NULL;							      \
        NX_ENDHANDLER

#else /* WITH_FOUNDATION */

#include <Foundation/NSException.h>
	  
#define STREAM_DURING NS_DURING

#define STREAM_ENDDURING						  \
	NS_HANDLER							  \
          const char *stream_err_msg = [[localException reason] cString]; \
									  \
          PyErr_SetString (ObjCStreams_Error, (char *) stream_err_msg);	  \
          Py_XDECREF ((PyObject *) retvalue);				  \
          return NULL;							  \
        NS_ENDHANDLER

#define STREAM_ENDDURING_BLOCK_THREADS					  \
	NS_HANDLER							  \
	  const char *stream_err_msg = [[localException reason] cString]; \
									  \
          Py_BLOCK_THREADS						  \
 	  PyErr_SetString (ObjCStreams_Error, (char *) stream_err_msg);	  \
          Py_XDECREF ((PyObject *) retvalue);				  \
          return NULL;							  \
        NS_ENDHANDLER
	  
#endif /* ! WITH_FOUNDATION */

static void
ObjCStream_dealloc (ObjCStream *self)
{
  [self->stream close];
#ifndef WITH_FOUNDATION
  [(id) self->stream free];
#else
  [self->stream release];
#endif

  Py_XDECREF (self->from);
  
  PyMem_DEL (self);
}

static PyObject *
ObjCStream_close (ObjCStream *self, PyObject *args)
{
  if (PyArg_ParseTuple (args, ""))
    {
      [self->stream close];
      Py_INCREF (Py_None);
      return Py_None;
    }

  return NULL;
}

static PyObject *
ObjCStream_flush (ObjCStream *self, PyObject *args)
{
  if (PyArg_ParseTuple (args, ""))
    {
      PyObject *retvalue = NULL;
      
      STREAM_DURING
	[self->stream flushStream];
      STREAM_ENDDURING
	
      Py_INCREF (Py_None);
      return Py_None;
    }

  return NULL;
}

static PyObject *
ObjCStream_read (ObjCStream *self, PyObject *args)
{
  int toread, rtotal, bufsize;
  PyObject *retvalue;
	
  if ([self->stream isClosed])
    {
      PyErr_SetString (ObjCStreams_Error, "Inappropriate operation on closed stream.");
      return NULL;
    }

  toread = -1;
  if (PyArg_ParseTuple(args, "|i;optional number of bytes ", &toread) == 0)
    return NULL;

  if (toread == 0)
    return PyString_FromString ("");
  
  bufsize = toread > 0 ? toread : BUFSIZ;
  retvalue = PyString_FromStringAndSize ((char *)NULL, bufsize);
  if (retvalue == NULL)
    return NULL;

  rtotal = 0;
  Py_BEGIN_ALLOW_THREADS
    for (;;)
      {
	int rcount;
	char *buf = PyString_AS_STRING ((PyStringObject *) retvalue);
	
	STREAM_DURING
	  rcount = [self->stream readBytes:buf+rtotal length:bufsize-rtotal];
	STREAM_ENDDURING_BLOCK_THREADS
	  
	if (rcount == 0)
	  break;

	rtotal += rcount;
	if (rtotal == toread)
	  break;
	if (toread < 0)
	  {
	    bufsize *= 2;

	    if (_PyString_Resize (&retvalue, bufsize) < 0)
	      {
		Py_BLOCK_THREADS
		return NULL;
	      }
	  }
      }
  Py_END_ALLOW_THREADS

  if (rtotal != bufsize)
    _PyString_Resize (&retvalue, rtotal);
    
  return retvalue;
}

/* XXX A better solution would be implementing/using -readLine: and
   -writeLine: on Streams. Since this approach would use optimized
   access to the stream structure (for example, memorystreams already
   cache the data in memory, so there is no need to reread the
   content...). NB: GNU Stream's -writeLine: does append a newline to
   its output. */
   
/* Internal routine to get a line.
   Size argument interpretation:
   > 0: max length;
   = 0: read arbitrary line;
   < 0: strip trailing '\n', raise EOFError if EOF reached immediately. */

static PyObject *
_ObjCStream_readline (ObjCStream *self, int toread)
{
  int rtotal, bufsize;
  char *buf, *bufend;
  char *p;
  PyObject *retvalue;
	
  if ([self->stream isClosed])
    {
      PyErr_SetString (ObjCStreams_Error, "Inappropriate operation on closed stream.");
      return NULL;
    }

  bufsize = toread > 0 ? toread : 100;
  retvalue = PyString_FromStringAndSize ((char *)NULL, bufsize);
  if (retvalue == NULL)
    return NULL;

  p = buf = PyString_AS_STRING ((PyStringObject *) retvalue);
  bufend = buf + bufsize;
  
  rtotal = 0;
  Py_BEGIN_ALLOW_THREADS
    for (;;)
      {
	int status;
	
	STREAM_DURING
	  status = [self->stream readByte:p];
	STREAM_ENDDURING_BLOCK_THREADS

	if (status == EOF)
	  {
	    if (toread < 0 && p == buf)
	      {
		Py_BLOCK_THREADS

		Py_DECREF (retvalue);
		PyErr_SetString (PyExc_EOFError, "EOF when reading a line");
		return NULL;
	      }
	    break;
	  }

	if (*p++ == '\n')
	  {
	    if (toread < 0)	// skip trailing newline
	      p--;
	    break;
	  }

	if (p == bufend)
	  {
	    if (toread > 0)
	      break;

	    rtotal = bufsize;
	    bufsize *= 2;

	    if (_PyString_Resize (&retvalue, bufsize) < 0)
	      {
		Py_BLOCK_THREADS
		return NULL;
	      }

	    buf = PyString_AS_STRING ((PyStringObject *) retvalue);
	    p = buf + rtotal;
	    bufend = buf + bufsize;
	  }
      }
  Py_END_ALLOW_THREADS

  rtotal = p - buf;
  if (rtotal != bufsize)
    _PyString_Resize (&retvalue, rtotal);
    
  return retvalue;
}

static PyObject *
ObjCStream_readline (ObjCStream *self, PyObject *args)
{
  int toread = -1;
  
  if (PyArg_ParseTuple (args, "|i;optional number of bytes", &toread))
    {
      if (toread == 0)
	return PyString_FromString ("");
      else
	return _ObjCStream_readline (self, (toread < 0 ? 0 : toread));
    }

  return NULL;
}

static PyObject *
ObjCStream_readlines (ObjCStream *self, PyObject *args)
{
  if (PyArg_ParseTuple (args, ""))
    {
      PyObject *list = PyList_New (0);
      int rtotal;
      int bufsize;
      char *buffer, *bufend;
      register char *startl;
      
      if (!list)
	return NULL;

#if 1
      
      buffer = malloc (bufsize = BUFSIZ);
      if (!buffer)
	{
	  PyErr_NoMemory();
	  return NULL;
	}

      rtotal = 0;
      Py_BEGIN_ALLOW_THREADS
	for (;;)
	  {
	    int rcount;
	    PyObject *retvalue = NULL;
	    
	    STREAM_DURING
	      rcount = [self->stream readBytes:buffer+rtotal length:bufsize-rtotal];
	    STREAM_ENDDURING_BLOCK_THREADS
	  
	    if (rcount == 0)
	      break;
	    else if (rcount == bufsize-rtotal)
	      {
		bufsize *= 2;

		buffer = realloc (buffer, bufsize);
		if (! buffer)
		  {
		    Py_BLOCK_THREADS
		    PyErr_NoMemory();
		    return NULL;
		  }
	      }
	    
	    rtotal += rcount;
	  }
      Py_END_ALLOW_THREADS

      startl = buffer;
      bufend = startl+rtotal;
      
      while (startl < bufend)
	{
	  char *endl = memchr (startl, '\n', bufend - startl);
	  int size;
	  PyObject *line;
	  
	  if (endl)
	    size = endl - startl + 1;
	  else
	    size = bufend - startl;
	  
	  line = PyString_FromStringAndSize (startl, size);
	  if (!line || PyList_Append (list, line))
	    {
	      Py_XDECREF (line);
	      Py_DECREF (list);
	      free (buffer);
	      return NULL;
	    }
	  else
	    Py_DECREF (line);

	  startl += size;
	}

      free (buffer);
      
#else  // this is slower

      for (;;)
	{
	  PyObject *line = _ObjCStream_readline (self, 0);

	  if (line && PyString_Size (line) == 0)
	    {
	      Py_DECREF (line);
	      break;
	    }
	  else if (!line || PyList_Append (list, line))
	    {
	      Py_XDECREF (line);
	      Py_DECREF (list);
	      return NULL;
	    }
	  else
	    Py_DECREF (line);
	}
      
#endif
      
      return list;
    }

  return NULL;
}

static PyObject *
ObjCStream_seek (ObjCStream *self, PyObject *args)
{
  long offset;
  int whence = OCS_FROMSTART;
  PyObject *retvalue = NULL;
  
  if (!PyArg_ParseTuple (args, "l|i;offset and optionally one of FROMSTART (default),FROMEND,FROMCURRENT", &offset, &whence))
    return NULL;
  
  STREAM_DURING
    [self->stream setStreamPosition:offset seekMode:whence];
  STREAM_ENDDURING
    
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
ObjCStream_tell (ObjCStream *self, PyObject *args)
{
  if (PyArg_ParseTuple (args, ""))
    {
      PyObject *retvalue = NULL;
      long offset;

      STREAM_DURING
	offset = [self->stream streamPosition];
      STREAM_ENDDURING

      retvalue = PyInt_FromLong (offset);
      return retvalue;
    }

  return NULL;
}

static PyObject *
ObjCStream_write (ObjCStream *self, PyObject *args)
{
  char *s;
  int l;

  if (PyArg_ParseTuple (args, "s#;string", &s, &l))
    {
      PyObject *retvalue = NULL;

      Py_BEGIN_ALLOW_THREADS
      STREAM_DURING
	errno = 0;
	if ([self->stream writeBytes:s length:l] != l)
	  {
	    Py_BLOCK_THREADS
	    PyErr_SetFromErrno (PyExc_IOError);	// XXX does stream set errno?
	    return NULL;
	  }
      STREAM_ENDDURING_BLOCK_THREADS
      Py_END_ALLOW_THREADS

      Py_INCREF (Py_None);
      return Py_None;
    }

  return NULL;
}

static PyObject *
ObjCStream_writelines (ObjCStream *self, PyObject *args)
{
  PyObject *list;

  if (PyArg_ParseTuple (args, "O!;list of strings", &PyList_Type, &list))
    {
      int nlines = PyList_Size (list);
      int i;
      PyObject *retvalue = NULL;

      Py_BEGIN_ALLOW_THREADS
	errno = 0;
	for (i=0; i<nlines; i++)
	  {
	    PyObject *line = PyList_GET_ITEM ((PyListObject *) list, i);

	    if (!PyString_Check (line))
	      {
		Py_BLOCK_THREADS
		PyErr_SetString (PyExc_TypeError, "writelines() requires list of strings");
		return NULL;
	      }
	    else
	      {
		int len = PyString_Size (line);

		STREAM_DURING
		  if ([self->stream writeBytes:PyString_AS_STRING ((PyStringObject *) line)
			   length:len] != len)
		    {
		      Py_BLOCK_THREADS
		      PyErr_SetFromErrno (PyExc_IOError); // XXX does stream set errno?
		      return NULL;
		    }
		STREAM_ENDDURING_BLOCK_THREADS
	      }
	  }
      Py_END_ALLOW_THREADS

      Py_INCREF (Py_None);
      return Py_None;
    }

  return NULL;
}

static char ObjCStream_savetofile_doc[] =
FUNDOC("Save the content of the stream to a file.",
       ".savetofile (NAME)",
       "NAME\t: name of the file",
       "None");
static PyObject *
ObjCStream_savetofile (ObjCStream *self, PyObject *args)
{
  char *name;

  if (PyArg_ParseTuple (args, "s;filename", &name))
    {
      PyObject *retvalue = NULL;
      
      Py_BEGIN_ALLOW_THREADS
      STREAM_DURING
	errno = 0;
	if (![(id) self->stream saveToFilename:name])
	  {
	    Py_BLOCK_THREADS
	    PyErr_SetFromErrno (PyExc_IOError);	// XXX does stream set errno?
	    return NULL;
	  }
      STREAM_ENDDURING
      Py_END_ALLOW_THREADS

      Py_INCREF (Py_None);
      return Py_None;      
    }

  return NULL;
}

static PyMethodDef ObjCStream_methods[] =
{
  { "close",	(PyCFunction) ObjCStream_close,		METH_VARARGS, NULL },
  { "flush",	(PyCFunction) ObjCStream_flush,		METH_VARARGS, NULL },  
  { "read",	(PyCFunction) ObjCStream_read,		METH_VARARGS, NULL },
  { "readline",	(PyCFunction) ObjCStream_readline,	METH_VARARGS, NULL },
  { "readlines",(PyCFunction) ObjCStream_readlines,	METH_VARARGS, NULL },
  { "seek",	(PyCFunction) ObjCStream_seek,		METH_VARARGS, NULL },
  { "tell",	(PyCFunction) ObjCStream_tell,		METH_VARARGS, NULL },
  { "write",	(PyCFunction) ObjCStream_write,		METH_VARARGS, NULL },
  { "writelines",(PyCFunction) ObjCStream_writelines,	METH_VARARGS, NULL },

  { "savetofile",(PyCFunction) ObjCStream_savetofile,	METH_VARARGS, ObjCStream_savetofile_doc },
  { 0, 0, 0, 0 }
};

static PyObject *
ObjCStream_getattr (ObjCPointer *self, char *name)
{
  PyObject *method;

  method = Py_FindMethod (ObjCStream_methods, (PyObject *) self, name);

  return method;
}

PyTypeObject ObjCStream_Type =
{
  PyObject_HEAD_INIT(&PyType_Type)
  0,					      /*ob_size*/
  "ObjCStream",				      /*tp_name*/
  sizeof (ObjCStream),			      /*tp_basicsize*/
  0,					      /*tp_itemsize*/
  
  /* methods */
  (destructor) ObjCStream_dealloc,	      /*tp_dealloc*/
  (printfunc) 0,			      /*tp_print*/
  (getattrfunc) ObjCStream_getattr,	      /*tp_getattr*/
  (setattrfunc) 0,			      /*tp_setattr*/
  (cmpfunc) 0,				      /*tp_compare*/
  (reprfunc) 0,				      /*tp_repr*/
  0,					      /*tp_as_number*/
  0,					      /*tp_as_sequence*/
  0,					      /*tp_as_mapping*/
  (hashfunc) 0,				      /*tp_hash*/
  (ternaryfunc) 0,			      /*tp_call*/
  (reprfunc) 0,				      /*tp_str*/
  (getattrofunc) 0,	      		      /*tp_getattro*/
  (setattrofunc) 0,			      /*tp_setattro*/

  /* Space for future expansion */
  0L,0L,
  
  "Wrapper around a Objective-C Stream"	      /* Documentation string */
};

ObjCStream *
ObjCStream_new_from_file_and_mode (const char *name, int mode)
{
  ObjCStream *retvalue = PyObject_NEW (ObjCStream, &ObjCStream_Type);
  
  if (!retvalue)
    return NULL;

  retvalue->from = NULL;

  STREAM_DURING
    retvalue->stream = [OC_Stream newFromFilename:name withMode:mode];
  STREAM_ENDDURING

  if (retvalue->stream == nil)
    {
      Py_DECREF (retvalue);
      return NULL;
    }
  else
    {
      retvalue->from = PyString_FromString ((char *) name);
    }
  
#ifdef WITH_FOUNDATION
  [retvalue->stream retain];
#endif
  
  return retvalue;
}

ObjCStream *
ObjCStream_new_from_file (const char *name)
{
  ObjCStream *retvalue = PyObject_NEW (ObjCStream, &ObjCStream_Type);
  
  if (!retvalue)
    return NULL;

  retvalue->from = NULL;

  STREAM_DURING
    retvalue->stream = [OC_Stream newFromFilename:name];
  STREAM_ENDDURING

  if (retvalue->stream == nil)
    {
      Py_DECREF (retvalue);
      return NULL;
    }
  else
    {
      retvalue->from = PyString_FromString ((char *) name);
    }
  
#ifdef WITH_FOUNDATION
  [retvalue->stream retain];
#endif

  
  return retvalue;
}

ObjCStream *
ObjCStream_new (OC_Stream *stream)
{
  ObjCStream *retvalue = PyObject_NEW (ObjCStream, &ObjCStream_Type);
  
  if (!retvalue)
    return NULL;

  retvalue->stream = stream;
#ifdef WITH_FOUNDATION
  [retvalue->stream retain];
#endif
  retvalue->from = NULL;
  
  return retvalue;
}
 
#ifndef WITH_FOUNDATION
NXStream *
ObjCStream_stream (ObjCStream *self)
{
  return self->stream ? [(id) self->stream stream] : NULL;
}

ObjCStream *
ObjCStream_new_from_stream (NXStream *stream)
{
  return ObjCStream_new ([OC_Stream newFromStream:stream]);
}

#endif /* WITH_FOUNDATION */

ObjCStream *
ObjCStream_new_from_memory_with_mode (void *p, int len, int mode)
{
  ObjCStream *retvalue = PyObject_NEW (ObjCStream, &ObjCStream_Type);
  
  if (!retvalue)
    return NULL;

  retvalue->from = NULL;

  STREAM_DURING
    retvalue->stream = [OC_Stream newFromMemory:p length:len withMode:mode];
  STREAM_ENDDURING

  if (retvalue->stream == nil)
    {
      Py_DECREF (retvalue);
      return NULL;
    }

#ifdef WITH_FOUNDATION
  [retvalue->stream retain];
#endif
  
  return retvalue;
}
  
static char ObjCStreams_openfile_doc[] =
FUNDOC("Open a file with the underlying Streams facility",
       ".openfile (NAME, MODE)",
       "NAME\t: the name of the file\n\
\t\tMODE\t: optional mode, defaults to READONLY; may be READONLY, READWRITE, WRITEONLY",
       "An ObjCStream instance");
static PyObject *
ObjCStreams_openfile (PyObject *self, PyObject *args)
{
  char *filename;
  int mode = OCS_READONLY;

  if (PyArg_ParseTuple (args, "s|i;filename and an optional mode from READONLY (default),READWRITE,WRITEONLY", &filename, &mode))
    {
      return (PyObject *) ObjCStream_new_from_file_and_mode (filename, mode);
    }

  return NULL;
}

static char ObjCStreams_openmemory_doc[] =
FUNDOC("Open a chunk of memory with the underlying Streams facility",
       ".openmemory (MEM, MODE)",
       "MEM\t: a string to be read as a stream, or None (not in READONLY mode)\n\
\t\tMODE\t: optional mode, defaults to READONLY; may be READONLY, READWRITE, WRITEONLY",
       "An ObjCStream instance");
static PyObject *
ObjCStreams_openmemory (PyObject *self, PyObject *args)
{
  PyObject *memory;
  int mode = OCS_READONLY;

  if (PyArg_ParseTuple (args, "O|i;a string or None and an optional mode from READONLY (default),READWRITE,WRITEONLY", &memory, &mode))
    {
      char *m;
      int len;
      ObjCStream *retvalue;
      
      if (mode != OCS_READONLY)
	{
	  if (memory != Py_None)
	    {
	      PyErr_SetString (ObjCStreams_Error, "Writeable memory stream requires None as first parameter");
	      return NULL;
	    }

	  m = NULL;
	  len = 0;
	}
      else
	{
	  if (PyString_Check (memory))
	    {
	      m = PyString_AsString (memory);
	      len = PyString_Size (memory);
	    }
	  else
	    {
	      PyErr_SetString (ObjCStreams_Error, "Expected a string object");
	      return NULL;
	    }
	}
      
      retvalue = ObjCStream_new_from_memory_with_mode (m, len, mode);
      if (mode == OCS_READONLY)
	{
	  retvalue->from = memory;
	  Py_INCREF (retvalue->from);
	}

      return (PyObject *) retvalue;
    }

  return NULL;
}

#if defined(NeXT) && !defined(GNU_RUNTIME)
static char ObjCStreams_openport_doc[] =
FUNDOC("Open a Mach port with the underlying Streams facility",
       ".openport (PORT, MODE)",
       "PORT\t: the Mach port number\n\
\t\tMODE\t: optional mode, defaults to READONLY; may be READONLY, READWRITE, WRITEONLY",
       "An ObjCStream instance");
static PyObject *
ObjCStreams_openport (PyObject *self, PyObject *args)
{
  port_t port;
  int mode = OCS_READONLY;

  if (PyArg_ParseTuple (args, "i|i;port number and an optional mode from READONLY (default),READWRITE,WRITEONLY", &port, &mode))
    {
      PyObject *retvalue = NULL;
      NXStream *s;

      STREAM_DURING
	s = NXOpenPort (port, mode);
      STREAM_ENDDURING

      if (! s)
	{
	  PyErr_SetString (ObjCStreams_Error, "Cannot open MACH port.");
	  return NULL;
	}
      
      retvalue = (PyObject *) ObjCStream_new_from_stream (s);
      return retvalue;
    }

  return NULL;
}
#endif

static PyMethodDef ObjCStreams_methods[] =
{
  { "openfile",		(PyCFunction) ObjCStreams_openfile,	METH_VARARGS,	ObjCStreams_openfile_doc },
  { "openmemory",	(PyCFunction) ObjCStreams_openmemory,	METH_VARARGS,	ObjCStreams_openmemory_doc },
#if defined(NeXT) && !defined(GNU_RUNTIME)
  { "openport",		(PyCFunction) ObjCStreams_openport,	METH_VARARGS,	ObjCStreams_openport_doc },
#endif
  { 0, 0, 0, 0 }
};

PyObject *ObjCStreams_Error;

void
initObjCStreams (void)
{
  PyObject *m, *d;

  m = Py_InitModule4 ("ObjCStreams", ObjCStreams_methods, NULL, NULL, PYTHON_API_VERSION);
  d = PyModule_GetDict (m);

  ObjCStreams_Error = PyString_FromString ("ObjCStreams.error");
  PyDict_SetItemString (d, "error", ObjCStreams_Error);

#define DEFCONST(name,val) do {						\
                             PyObject *pval = PyInt_FromLong (val);	\
			     PyDict_SetItemString (d, name, pval);	\
			     Py_DECREF (pval);				\
                           } while (0)

  DEFCONST("READONLY", OCS_READONLY);
  DEFCONST("WRITEONLY", OCS_WRITEONLY);
  DEFCONST("READWRITE", OCS_READWRITE);
  DEFCONST("APPEND", OCS_APPEND);

  DEFCONST("FROMSTART", OCS_FROMSTART);
  DEFCONST("FROMCURRENT", OCS_FROMCURRENT);
  DEFCONST("FROMEND", OCS_FROMEND);

  if (PyErr_Occurred())
    Py_FatalError ("can't initialize module ObjCStreams");
}

/*
** Local Variables:
** change-log-default-name:"../ChangeLog.PyObjC"
** End:
*/
