diff -urN parrot-0.0.3/MANIFEST parrot-0.0.3-iopatch/MANIFEST
--- parrot-0.0.3/MANIFEST	Sat Dec  8 16:34:59 2001
+++ parrot-0.0.3-iopatch/MANIFEST	Mon Dec 10 15:25:37 2001
@@ -33,6 +33,7 @@
 examples/assembly/bsr.pasm
 examples/assembly/call.pasm
 examples/assembly/euclid.pasm
+examples/assembly/file.pasm
 examples/assembly/jump.pasm
 examples/assembly/life.pasm
 examples/assembly/local_label.pasm
@@ -67,6 +68,7 @@
 include/parrot/trace.h
 include/parrot/unicode.h
 interpreter.c
+io.c
 languages/jako/bench.jako
 languages/jako/euclid.jako
 languages/jako/fact.jako
diff -urN parrot-0.0.3/Makefile.in parrot-0.0.3-iopatch/Makefile.in
--- parrot-0.0.3/Makefile.in	Sat Dec  8 18:03:44 2001
+++ parrot-0.0.3-iopatch/Makefile.in	Mon Dec 10 15:26:00 2001
@@ -13,7 +13,7 @@
 CLASS_O_FILES = classes/default$(O) classes/perlint$(O) classes/perlstring$(O) \
 classes/perlnum$(O) classes/perlundef$(O)
 
-O_FILES = global_setup$(O) interpreter$(O) parrot$(O) register$(O) \
+O_FILES = global_setup$(O) interpreter$(O) io$(O) parrot$(O) register$(O) \
 core_ops$(O) memory$(O) packfile$(O) stacks$(O) string$(O) encoding$(O) \
 chartype$(O) runops_cores$(O) trace$(O) pmc$(O) \
 encodings/singlebyte$(O) encodings/utf8$(O) encodings/utf16$(O) \
diff -urN parrot-0.0.3/core.ops parrot-0.0.3-iopatch/core.ops
--- parrot-0.0.3/core.ops	Fri Dec  7 15:27:27 2001
+++ parrot-0.0.3-iopatch/core.ops	Mon Dec 10 15:26:14 2001
@@ -70,12 +70,22 @@
 
 ########################################
 
+=item close(p)
+
+Close the file IO stream PMC $1.
+
 =item close(i|ic)
 
 Close file opened on file descriptor $1.
 
 =cut
 
+AUTO_OP close(p) {
+  if( $1 && $1->data ) {
+    ParrotIO_close(interpreter, (ParrotIO *)($1->data));
+  }
+}
+
 AUTO_OP close(i|ic) {
   close($1);
 }
@@ -106,6 +116,11 @@
 
 ########################################
 
+=item B<open>(p, s|sc, s|sc)
+
+Open file named $2 with Perl style mode string specified in $3, and 
+create the IO stream PMC into $1.
+
 =item B<open>(i, s|sc)
 
 Open file named $2 with flags for writing and mode 0644 (rw-r--r--), and save
@@ -123,6 +138,14 @@
 
 =cut
 
+AUTO_OP open(p, s|sc, s|sc) {
+  ParrotIO * io;
+  STRING *path = $2;
+  STRING *mode = $3;
+  io = ParrotIO_open(interpreter, path, mode, 0);
+  $1 = new_io_pmc(interpreter, io);
+}
+
 AUTO_OP open(i, s|sc) {
   STRING *s = $2;
   $1 = open(s->bufstart, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_OPEN_MODE);
@@ -207,6 +230,10 @@
 
 ########################################
 
+=item read(p, s, i|ic)
+
+Read $3 bytes from IO stream $1 into string $2.
+
 =item read(i, i|ic)
 
 Read an INTVAL from file descriptor $2 into $1.
@@ -221,6 +248,29 @@
 
 =cut
 
+AUTO_OP read(p, s, i|ic) {
+  char *tmp;
+  ParrotIO * io;
+  STRING *s;
+  INTVAL len = $3;
+
+  /* BUG in freeing memory allocated by mem_allocate_aligned at the
+	moment...
+  string_destroy($2);
+  */
+  tmp = malloc(len + 1);
+  io = (ParrotIO *)$1->data;
+  if( io ) {
+    ParrotIO_read(interpreter, io, tmp, len, 0, 0);
+  } else {
+    len = 1;
+    tmp[0] = 0;
+  }
+  s = string_make(interpreter, tmp, len, 0, 0, 0);
+  $2 = s;
+  free(tmp);
+}
+
 AUTO_OP read(i, i|ic) {
   read($2, &($1), sizeof(INTVAL));
 }
@@ -273,6 +323,10 @@
 
 ########################################
 
+=item write(p, s|sc)
+
+Write $2 to IO stream $1.
+
 =item write(i|ic, i|ic)
 
 =item write(i|ic, n|nc)
@@ -283,6 +337,14 @@
 
 =cut
 
+AUTO_OP write(p, s|sc) {
+  STRING * s = $2;
+  ParrotIO * io = (ParrotIO*)$1->data;
+  INTVAL count = string_length(s);
+  if( io )
+    ParrotIO_write(interpreter, io, s->bufstart, count, 0, 0);
+}
+
 AUTO_OP write(i|ic, i|ic) {
   INTVAL * i = &($2);
   write($1, i, sizeof(INTVAL));
diff -urN parrot-0.0.3/examples/assembly/file.pasm parrot-0.0.3-iopatch/examples/assembly/file.pasm
--- parrot-0.0.3/examples/assembly/file.pasm	Wed Dec 31 19:00:00 1969
+++ parrot-0.0.3-iopatch/examples/assembly/file.pasm	Wed Dec 12 01:19:33 2001
@@ -0,0 +1,31 @@
+# Open an IO stream and do 100 line writes to test buffering
+# Rotate text in line each write
+	set S1, "/tmp/test.txt"
+	set S2, ">>"
+	open P1, S1, S2
+	set S3, "Computers will never be self-aware, much less understand women."
+	set I0, 0
+loop:	ge I0, 100, getout
+	write P1, S3
+	write P1, "\n"
+	inc I0
+	substr S4, S3, 62, 1
+	chopn S3, 1
+	concat S4, S3
+	set S3, S4
+	branch loop
+
+getout: close P1
+	set S2, "<"
+	open P1, S1, S2
+
+	set I0, 0
+loop2:	ge I0, 100, getout2
+	read P1, S1, 64 
+	print S1
+	inc I0
+	branch loop2
+
+getout2:
+	close P1
+	end
diff -urN parrot-0.0.3/include/parrot/io.h parrot-0.0.3-iopatch/include/parrot/io.h
--- parrot-0.0.3/include/parrot/io.h	Mon Sep 17 21:16:59 2001
+++ parrot-0.0.3-iopatch/include/parrot/io.h	Wed Dec 12 00:30:46 2001
@@ -13,7 +13,73 @@
 #if !defined(PARROT_IO_H_GUARD)
 #define PARROT_IO_H_GUARD
 
-#define Init_IO(x)
+#include "parrot/parrot.h"
+
+#ifndef SSIZE_MAX
+#define SSIZE_MAX 8192          /* EEK! */
+#endif
+
+/* Average block size of most systems (usually varies from 2k-8k),
+ * later we can add some config to query it from the system at
+ * build time (struct stat.st_blksize maybe).
+ */
+#define PIO_BLKSIZE 4096 
+
+#define PIO_LINEBUFSIZE 256 
+
+
+enum {
+        PIO_TYPE_FILE,
+        PIO_TYPE_PIPE,
+        PIO_TYPE_SOCKET,
+        PIO_TYPE_MAX
+};
+
+enum {
+        PIO_BUFTYPE_NONE = 0,
+        PIO_BUFTYPE_FULL,
+        PIO_BUFTYPE_LINE,
+        PIO_BUFTYPE_MAX
+};
+
+/* Future stuff ... */
+/*
+typedef struct { } ParrotIOFilter;
+*/
+
+typedef struct {
+        INTVAL  bufsize;
+        unsigned char * buf;
+        unsigned char * head;
+} ParrotIOBuf;
+
+typedef struct {
+        INTVAL  fd;             /* Low level OS descriptor */
+        INTVAL  mode;           /* Read/Write/etc. */
+        INTVAL  flags;          /* Buffering types, mmapped, etc. */
+        INTVAL  iotype;         /* What type of stream is this */
+        off_t   filepos;        /* The current real file pointer */
+        INTVAL  buftype;
+        ParrotIOBuf in;
+        ParrotIOBuf out;
+        /* ParrotIOFilter * filter[2]; */
+} ParrotIO;
+
+
+
+extern ParrotIO *      new_io_header(struct Parrot_Interp *, INTVAL, INTVAL, INTVAL);
+extern struct PMC *    new_io_pmc(struct Parrot_Interp *, ParrotIO *);
+
+extern void            ParrotIO_init(struct Parrot_Interp *);
+
+extern ParrotIO *      ParrotIO_open(struct Parrot_Interp *, STRING*, STRING*, void *);
+extern INTVAL          ParrotIO_close(struct Parrot_Interp *, ParrotIO *);
+extern void            ParrotIO_flush(struct Parrot_Interp *, ParrotIO *);
+extern INTVAL          ParrotIO_read(struct Parrot_Interp *, ParrotIO *, void *, size_t, INTVAL, void *);
+extern INTVAL          ParrotIO_write(struct Parrot_Interp *, ParrotIO *, void *, size_t, INTVAL, void *);
+extern INTVAL          ParrotIO_setbuf(struct Parrot_Interp *, ParrotIO *, INTVAL, INTVAL);
+
+#define Init_IO(x)      ParrotIO_init(x)
 
 #endif
 
diff -urN parrot-0.0.3/include/parrot/pmc.h parrot-0.0.3-iopatch/include/parrot/pmc.h
--- parrot-0.0.3/include/parrot/pmc.h	Wed Dec  5 11:58:30 2001
+++ parrot-0.0.3-iopatch/include/parrot/pmc.h	Mon Dec 10 15:26:52 2001
@@ -18,6 +18,7 @@
     enum_class_PerlInt,
     enum_class_PerlNum,
     enum_class_PerlString,
+    enum_class_ParrotIO,
     enum_class_max
 };
 VAR_SCOPE VTABLE Parrot_base_vtables[enum_class_max];
diff -urN parrot-0.0.3/io.c parrot-0.0.3-iopatch/io.c
--- parrot-0.0.3/io.c	Wed Dec 31 19:00:00 1969
+++ parrot-0.0.3-iopatch/io.c	Wed Dec 12 13:23:35 2001
@@ -0,0 +1,388 @@
+/* io.c
+ *  Copyright: (When this is determined...it will go here)
+ *  CVS Info
+ *  Overview:
+ *      This is Parrot IO subsystem API. For now we implement
+ *      simple buffering. See TODO in notes.
+ *      This is quick and dirty to get the IO system off the ground;
+ *      consider it a straw man to be beaten, burnt and rebuilt.
+ *              -Melvin
+ *  Data Structure and Algorithms:
+ *      Uses the IO PMC defined in io.h
+ *  History:
+ *  Notes:     
+ *      TODO
+ *      Seeking needs to be implemented.
+ *      Finish buffering.
+ *      Portably implement STDIN, STDOUT, STDERR and
+ *              line buffering so no more stdio.
+ *      Pipes (process opens)
+ *      Filters?
+ *      Callbacks?
+ *  References:
+ */
+
+#include "parrot/parrot.h"
+
+ParrotIO * pio_stdin;
+ParrotIO * pio_stdout;
+ParrotIO * pio_stderr;
+
+/* Don't allocate buffers until an IO is done in given direction */
+ParrotIO * new_io_header(struct Parrot_Interp *interpreter, INTVAL iotype,
+                        INTVAL flags, INTVAL mode) {
+        ParrotIO * new_io;
+        new_io = (ParrotIO *)malloc(sizeof(ParrotIO));
+        new_io->flags = flags;
+        new_io->mode = mode;
+        new_io->iotype = iotype;
+        new_io->buftype = PIO_BUFTYPE_FULL;
+        new_io->filepos = -1;
+        new_io->in.buf = NULL;
+        new_io->in.head = NULL;
+        new_io->out.buf = NULL;
+        new_io->out.head = NULL;
+        return new_io;
+}
+
+void free_io_header(ParrotIO *io) {
+        if( io->in.buf )
+                free(io->in.buf);
+        if( io->out.buf )
+                free(io->out.buf);
+        free(io);
+}
+
+
+/*
+ * Initialize some stuff or just print really cool message.
+ */
+void ParrotIO_init(struct Parrot_Interp * interpreter) {
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "Parrot IO: Initialized.\n");
+        }
+
+/* Standard input and output will be line-buffered, error will
+ * be unbuffered. Line buffering not done yet.
+ */
+/*
+        pio_stdin = new_io_header(interpreter, PIO_TYPE_FILE, 0, O_RDONLY);
+        pio_stdout = new_io_header(interpreter, PIO_TYPE_FILE, 0, O_WRONLY);
+        pio_stderr = new_io_header(interpreter, PIO_TYPE_FILE, 0, O_WRONLY);
+        ParrotIO_setbuf(interpreter, pio_stdin, PIO_LINEBUFSIZE,
+                                                        PIO_BUFTYPE_LINE);
+        ParrotIO_setbuf(interpreter, pio_stdout, PIO_LINEBUFSIZE,
+                                                        PIO_BUFTYPE_LINE);
+        ParrotIO_setbuf(interpreter, pio_stderr, 0, PIO_BUFTYPE_NONE);
+*/
+}
+
+ParrotIO * ParrotIO_STDIN() {
+        return pio_stdin;
+}
+
+ParrotIO * ParrotIO_STDOUT() {
+        return pio_stdout;
+}
+
+ParrotIO * ParrotIO_STDERR() {
+        return pio_stderr;
+}
+
+
+
+/*
+ * API for controlling buffering specifics on an IO stream
+ * Should we allow passing in of the buffer itself? Sometimes
+ * this is useful if you pass a local stack buffer and close the
+ * stream before you return.
+ */
+INTVAL ParrotIO_setbuf(struct Parrot_Interp * interpreter, ParrotIO * io,
+                                INTVAL bufsize, INTVAL buftype) {
+        if( io->out.buf && io->out.buf != io->out.head )
+                ParrotIO_flush(interpreter, io);
+        /* If caller is turning off buffering just set
+         * flags and return and leave buffer allocated.
+         */
+        if( buftype == PIO_BUFTYPE_NONE ) {
+                io->buftype = PIO_BUFTYPE_NONE;
+                return 1;
+        } else if( buftype == PIO_BUFTYPE_FULL || buftype == PIO_BUFTYPE_LINE ) {
+                if( bufsize <= 0 ) {
+                        io->in.bufsize = PIO_BLKSIZE;
+                        io->out.bufsize = PIO_BLKSIZE;
+                }
+                else {
+                        io->in.bufsize = bufsize;
+                        io->out.bufsize = bufsize;
+                }
+
+                if( io->in.buf )
+                        free(io->in.buf);
+                if( io->out.buf )
+                        free(io->out.buf);
+
+                io->in.buf = io->in.head = malloc(bufsize);
+                io->out.buf = io->out.head = malloc(bufsize);
+                io->buftype = buftype;
+                if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                        fprintf(stderr, "ParrotIO_setbuf: Allocated %d byte buffers for stream (descriptor %d)\n", bufsize, io->fd );
+                }
+                return 1;
+        } else {
+                if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                        fprintf(stderr, "ParrotIO_setbuf: Invalid buffering type %d\n",
+                                buftype );
+                }
+        }
+        return 0;
+}
+
+
+ParrotIO * ParrotIO_open(struct Parrot_Interp * interpreter,
+			STRING * spath, STRING * smode, void * callback) {
+        ParrotIO * io;
+        int flags, type, mode, fd;
+        unsigned char * modeptr;
+        type = PIO_TYPE_FILE;
+        flags = 0;
+        mode = DEFAULT_OPEN_MODE;
+        modeptr = (unsigned char *)smode->bufstart;
+
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "ParrotIO_open: %s, %s\n",
+                        (const char *)spath->bufstart, modeptr );
+        }
+
+        /* Set mode flags - <, >, >>, +<, +>, |, |>, |< */
+        switch(*modeptr) {
+                case '+':
+                        flags = O_RDWR;
+                        break;
+                case '<':
+                        flags = O_RDONLY;
+                        break;
+                case '>':       
+                        flags = O_WRONLY | O_CREAT;
+                        if( *(++modeptr) == '>')
+                                flags |= O_APPEND;
+                        else if(*modeptr != 0)
+                                return 0;
+                        else flags |= O_TRUNC;
+                        break;
+                case '|':
+                        type = PIO_TYPE_PIPE;
+                        break;
+                default:
+                        return 0;
+        }
+
+        if(type == PIO_TYPE_FILE) {
+                if((fd = open( (const char *)spath->bufstart, flags, mode )) != -1 ) {
+                        io = new_io_header(interpreter, type, flags, mode);
+                        io->fd = fd;
+                        ParrotIO_setbuf(interpreter, io, PIO_BLKSIZE, PIO_BUFTYPE_FULL);
+                        return io;
+                } else {
+                        /* Error.. */
+                        /* Do we have some sort of Parrot errno mechanism to use? */
+                        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                                perror( "ParrotIO_open: " ); 
+                        }
+                        return 0;
+                }
+        } else if(type == PIO_TYPE_PIPE) {
+                /* Can't use popen here, evil stdio! :)
+                 * So pipe, fork, exec.
+                 * Implement this later, we have to add
+                 * the child handling code as well.
+                 */
+        }
+
+        return 0;
+}
+
+
+INTVAL ParrotIO_close(struct Parrot_Interp * interpreter, ParrotIO * io) {
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "ParrotIO_close: %d\n", io->fd );
+        }
+
+        ParrotIO_flush(interpreter, io);
+        close( io->fd );
+        io->fd = -1;
+	return 0;
+}
+
+
+/* For now we assume that an IO buf will be no larger than
+ * PIO_BLKSIZE.
+ */
+void ParrotIO_flush(struct Parrot_Interp * interpreter, ParrotIO * io) {
+        int err;
+        int to_write;
+        if( !io->out.buf || io->out.buf == io->out.head )
+                return;
+        if( io->out.head )
+                to_write = io->out.head - io->out.buf;
+        else to_write = io->out.bufsize;
+
+        write_buffer:
+        err = write(io->fd, io->out.buf, to_write);
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "ParrotIO_flush: Flushing %d bytes\n", to_write );
+        }
+        if( err >= 0 ) {
+                io->out.head = io->out.buf;
+                return;
+        }
+        else {
+                switch(errno) {
+                        case EAGAIN:    break;
+#ifdef EINTR
+                        case EINTR:     goto write_buffer;
+#endif
+                }
+        }
+}
+
+
+/* Logic:
+        If bytes requested are < buffer size and bytes available 
+        are > bytes requested
+                1) Calculate buffer size increase if needed
+                2) Read MIN( buffer size, file bytes left ) into
+                        io->in.buf
+        This logic will only apply to regular files. Sockets and
+                pipes will have modified buffering.
+*/
+INTVAL ParrotIO_read(struct Parrot_Interp * interpreter, ParrotIO * io,
+			void * buffer, size_t len, INTVAL flags, void * callback) {
+        int bytes;
+        int trylen;
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "ParrotIO_read: %d bytes\n", len );
+        }
+
+        /* If this is first read we have to allocate the buffer
+         * that was sized in open()
+         */
+        if( !io->in.buf ) {
+                io->in.buf = io->in.head = malloc(io->in.bufsize);
+        }
+
+        /* On a regular file we can safely attempt to read more
+         * bytes than are available.
+         */
+        bytes = read(io->fd, buffer, len);
+	return bytes;
+}
+
+
+/*
+ * Write copies to IO buffer when possible, else flushes a full block
+ * and then does full block writes from user buffer and leaves
+ * any block fragment in buffer for next flush.
+ * If stream is non-buffered all writes skip the buffer.
+ * TODO:
+ *      Line buffering.
+ */
+INTVAL ParrotIO_write(struct Parrot_Interp * interpreter, ParrotIO * io,
+			void * buffer, size_t len, INTVAL flags, void * callback) {
+        int err;
+        int bytes;
+        int to_write;
+        char * ptr;
+        int space_left;
+
+        if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                fprintf(stderr, "ParrotIO_write: %d bytes\n", len );
+        }
+
+        if( io->buftype == PIO_BUFTYPE_NONE )
+                goto write_through;
+
+        space_left = io->out.bufsize - (io->out.head - io->out.buf);
+
+        /* Enough space in buffer, copy it in.. */
+        if( space_left >= len ) {
+                if((interpreter->flags & PARROT_DEBUG_FLAG) != 0) {
+                        fprintf(stderr, "ParrotIO_write: Write to buffer %d bytes\n", len );
+                }
+                memcpy( io->out.head, buffer, len );
+                io->out.head += len;
+                space_left -= len; 
+                bytes = len;
+                len = 0;
+        } else {
+                /* Else fill buffer, flush, and write rest */
+                memcpy( io->out.head, buffer, space_left );
+                /* Head null means buffer is full, this should only be
+                 * set right before we call flush. No other routines
+                 * check for nulls.
+                 */
+                io->out.head = NULL;
+                len -= space_left;
+                bytes = space_left;
+                /* Save pointer to remainder of user passed buffer */
+                ptr = buffer + space_left;
+                space_left = 0;
+        }
+
+        /* Flush a full buffer */
+        if( space_left == 0 ) {
+                ParrotIO_flush( interpreter, io );
+        }
+        if( len == 0 ) 
+                goto done;
+
+        /* Flushed a buffer, now see if rest will fit */
+        if( len < io->out.bufsize ) {
+                memcpy( io->out.head, ptr, len );
+                io->out.head += len;
+                goto done;
+        }
+
+        /* We have at least a full block to write so....
+         * Now skip memcpy() and write directly from the user
+         * buffer while there are full blocks to write and copy
+         * remainder to the IO stream block buffer. If stream is
+         * non-buffered write it all.
+         */ 
+        write_through:
+        while( len > io->out.bufsize ||
+                        (len > 0 && io->buftype == PIO_BUFTYPE_NONE)) {
+                if( len > io->out.bufsize )
+                        to_write = io->out.bufsize;
+                else to_write = len;
+                if((err = write(io->fd, ptr, to_write)) >= 0 ) {
+                        ptr += err;
+                        len -= err; 
+                        bytes += err;
+                } else {
+                        switch(errno) {
+                                case EAGAIN:    return bytes;
+#ifdef EAGAIN
+                                case EINTR:     goto write_through;
+#endif
+                        }
+                }
+        }
+
+        done:
+	return bytes;
+}
+
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+*/
+
diff -urN parrot-0.0.3/resources.c parrot-0.0.3-iopatch/resources.c
--- parrot-0.0.3/resources.c	Mon Oct 22 15:27:05 2001
+++ parrot-0.0.3-iopatch/resources.c	Mon Dec 10 15:26:57 2001
@@ -29,3 +29,11 @@
 void free_string(STRING *string) {
   free(string);
 }
+
+PMC *new_io_pmc(struct Parrot_Interp *interpreter, ParrotIO * io) {
+  PMC * new_pmc;
+  new_pmc = new_pmc_header(interpreter);
+  new_pmc->data = io;
+  new_pmc->vtable = YOU_LOSE_VTABLE;
+  return new_pmc;
+}
