diff --git a/ChangeLog-fileio.txt b/ChangeLog-fileio.txt
new file mode 100644
index 0000000..9596740
--- /dev/null
+++ b/ChangeLog-fileio.txt
@@ -0,0 +1,29 @@
+Version 1.5p3 - Jul 11
+----------------------
+ - Added return value check on calls to binary_to_raw_bytes (via out_filter)
+ - Replaced use of fgets to better handle files with null bytes
+ - No longer accept the tab character as valid clean input data
+
+Version 1.5p2 - Jun 11
+----------------------
+ - Fixed mismatch mymalloc/free
+ - Removed restriction that the final line in text mode must be terminated by a newline
+ - Reset errno before each call
+ - Removed vestigial file_send
+ - Replaced "E_FILE" error with true E_FILE
+
+Version 1.5p1 - Dec 97
+----------------------
+ - Fixed bug where tabs were not included in the input stream
+ - Added CHANGELOG to the distribution
+ - Added README
+
+Version 1.5
+-----------
+ - First version maintained by Andy Bakun.
+ - Fixed bugs where file_eof and file_tell didn't return meaningful results
+   didn't raise errors on invalid file descriptors.
+
+Versions < 1.5
+--------------
+Maintained by Ken Fox.  Really, the initial public version.
diff --git a/FileioDocs.txt b/FileioDocs.txt
new file mode 100644
index 0000000..4f02a24
--- /dev/null
+++ b/FileioDocs.txt
@@ -0,0 +1,432 @@
+  File I/O
+  Ken Fox, Andy Bakun, Todd Sundsted
+
+  This is the documentation for the File I/O (FIO) patch for the Lamb-
+  daMOO server.  FIO adds several administrator-only builtins for manip-
+  ulating files from inside the MOO.  Security is enforced by making
+  these builtins executable with wizard permissions only as well as only
+  allowing access to a directory under the current directory (the one
+  the server is running in).
+
+  1.  Introduction
+
+  1.1.  Purpose
+
+  This patch to the LambdaMOO server adds several new builtins that
+  allow the manipulation of files from MOO code.   The new builtins are
+  structured similarly to the stdio library for C.  This allows MOO-code
+  to perform stream-oriented I/O to files.
+
+  Granting MOO code direct access to files opens a hole in the otherwise
+  fairly good wall that the LambdaMOO server puts up between the OS and
+  the database.  The patch contains the risk as much as possible by
+  restricting where files can be opened and allowing the new builtins to
+  be called by wizard permissions only.  It is still possible execute
+  various forms denial of service attacks, but the MOO server allows
+  this form of attack as well.
+
+  There is a related package available that contains a db front end for
+  this package as well as a help database with help for all the builtin
+  functions and for the front end.  It is not recommended that you use
+  these functions directly.
+
+  1.2.  Copyright, License, and Disclaimer
+
+  Copyright 1996, 1997 by Ken Fox.  Copyright 1997 by Andy Bakun.
+  Copyright 2011 by Todd Sundsted.
+
+  All Rights Reserved
+
+  Permission to use, copy, modify, and distribute this software and its
+  documentation for any purpose and without fee is hereby granted,
+  provided that the above copyright notice appear in all copies and that
+  both that copyright notice and this permission notice appear in
+  supporting documentation.
+
+  KEN FOX AND ANDY BAKUN AND TODD SUNDSTED DISCLAIM ALL WARRANTIES WITH
+  REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL KEN FOX OR ANDY BAKUN
+  OR TODD SUNDSTED BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+  CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+  OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+  NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+  WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+  2.  Installation
+
+  Most of the new code for this patch are in the files extension-
+  fileio.c and extension-fileio.h.  These files contain the header and
+  implementation for all of the new builtin functions.  One line has to
+  be added to bf_register.h and a related line must be added
+  functions.c.
+
+  The distribution archive should contain the text version of this
+  documentation (fileio-README), extension-fileio.c, extension-fileio.h,
+  and fileio.patch.
+
+  If you are going to use patch to install it, just copy fileio.patch
+  into the server directory and apply the patch:
+
+         patch < fileio.patch
+
+  It is recommended that you use patch to install it, but you can also
+  manually make the changes to the server source.  First, copy the
+  extension-fileio.c and .h files into a directory with the freshly
+  untarred server source.  Add extension-fileio.c to the CSRCS line of
+  Makefile.in, add
+
+          extern void register_fileio(void);
+
+  to bf_register.h and add
+
+         register_fileio
+
+  to the list of functions in functions.c.
+
+  Finally, make the files directory in the directory the MOO server is
+  run in.  Only files in this directory will be accessible using this
+  patch.
+
+         mkdir files
+
+  Pathnames passed to these functions are restricted to prevent access
+  outside this directory.
+
+  3.  Functions
+
+  The functions in this patch are grouped into a few categories.  There
+  is a function to open a file and a function for closing a file, a set
+  of functions for doing stream-oriented I/O from files, and a set of
+  housekeeping functions.
+
+  Function documentation includes a prototype, information about the
+  function, and a list of exceptions the function might raise (in
+  addition to the ones outlined in "Error handling".
+
+  WARNING: All of the actual I/O functions in this package are
+  implemented using the stdio portion of libc.  Your system's
+  documentation may have applicable warnings for these functions.  When
+  appropriate, the function documentation will say which libc function
+  is used.
+
+  3.1.  Error handling
+
+  Errors are always handled by raising some kind of exception.  The
+  following exceptions are defined:
+
+     E_FILE
+        This is raised when a stdio call returned an error value.
+        CODE is set to E_FILE, MSG is set to the return of strerror()
+        (which may vary from system to system), and VALUE depends on
+        which function raised the error.  When a function fails
+        because the stdio function returned EOF, VALUE is set to
+        "EOF".
+
+     E_INVARG
+        This is raised for a number of reasons.  The common reasons are
+        an invalid FHANDLE being passed to a function and an invalid
+        pathname specification.  In each of these cases MSG will be set
+        to the cause and VALUE will be the offending value.
+
+     E_PERM
+        This is raised when any of these functions are called with non-
+        wizardly permissions.
+
+  3.2.  Version
+
+  Function: STR file_version()
+
+  Returns the package shortname/version number of this package e.g.
+
+         ;file_version()
+         => "FIO/1.5p3"
+
+  3.3.  Opening and closing of files and related functions
+
+  File streams are associated with FHANDLES.  FHANDLES are similar to
+  the FILE* using stdio.  You get an FHANDLE from file_open.  You should
+  not depend on the actual type of FHANDLEs (currently TYPE_INT).
+  FHANDLEs are not persistent across server restarts.  That is, files
+  open when the server is shut down are closed when it comes back up and
+  no information about open files is saved in the DB.
+
+  3.3.1.  file_open
+
+  Function: FHANDLE file_open(STR pathname, STR mode)
+
+  Raises: E_INVARG if mode is not a valid mode, E_QUOTA if too many
+  files open
+  This opens a file specified by pathname and returns an FHANDLE for it.
+  It ensures pathname is legal.  mode is a string of characters
+  indicating what mode the file is opened in.  The mode string is four
+  characters.
+
+  The first character must be (r)ead, (w)rite, or (a)ppend.  The second
+  must be '+' or '-'.  This modifies the previous argument.
+
+  o  r- opens the file for reading and fails if the file does not exist.
+
+  o  r+ opens the file for reading and writing and fails if the file
+     does not exist.
+
+  o  w- opens the file for writing, truncating if it exists and creating
+     if not.
+
+  o  w+ opens the file for reading and writing, truncating if it exists
+     and creating if not.
+
+  o  a- opens a file for writing, creates it if it does not exist and
+     positions the stream at the end of the file.
+
+  o  a+ opens the file for reading and writing, creates it if does not
+     exist and positions the stream at the end of the file.
+
+     The third character is either (t)ext or (b)inary.  In text mode,
+     data is written as-is from the MOO and data read in by the MOO is
+     stripped of unprintable characters.  In binary mode, data is
+     written filtered through the binary-string->raw-bytes conversion
+     and data is read filtered through the raw-bytes->binary-string
+     conversion.  For example, in text mode writing " 1B" means three
+     bytes are written: ' ' Similarly, in text mode reading " 1B" means
+     the characters ' ' '1' 'B' were present in the file.  In binary
+     mode reading " 1B" means an ASCII ESC was in the file.  In text
+     mode, reading an ESC from a file results in the ESC getting
+     stripped.
+
+  It is not recommended that files containing unprintable ASCII  data be
+  read in text mode, for obvious reasons.
+
+  The final character is either 'n' or 'f'.  If this character is 'f',
+  whenever data is written to the file, the MOO will force it to finish
+  writing to the physical disk before returning.  If it is 'n' then
+  this won't happen.
+
+  This is implemented using fopen().
+
+  3.3.2.  file_close
+
+  Function: void file_close(FHANDLE fh)
+
+  Closes the file associated with fh.
+
+  This is implemented using fclose().
+
+  3.3.3.  file_name
+
+  Function: STR file_name(FHANDLE fh)
+
+  Returns the pathname originally associated with fh by file_open().
+  This is not necessarily the file's current name if it was renamed or
+  unlinked after the fh was opened.
+
+  3.3.4.  file_openmode
+
+  Function: STR file_openmode(FHANDLE fh)
+
+  Returns the mode the file associated with fh was opened in.
+
+  3.4.  Input and Ouput operations
+
+  3.4.1.  file_readline
+
+  Function: STR file_readline(FHANDLE fh)
+
+  Reads the next line in the file and returns it (without the newline).
+
+  Not recommended for use on files in binary mode.
+
+  This is implemented using fgetc().
+
+  3.4.2.  file_readlines
+
+  Function: LIST file_readlines(FHANDLE fh, INT start, INT end)
+
+  Rewinds the file and then reads the specified lines from the file,
+  returning them as a list of strings.  After this operation, the stream
+  is positioned right after the last line read.
+
+  Not recommended for use on files in binary mode.
+
+  This is implemented using fgetc().
+
+  3.4.3.  file_writeline
+
+  Function: void file_writeline(FHANDLE fh, STR line)
+
+  Writes the specified line to the file (adding a newline).
+
+  Not recommended for use on files in binary mode.
+
+  This is implemented using fputs().
+
+  3.4.4.  file_read
+
+  Function: STR file_read(FHANDLE fh, INT bytes)
+
+  Reads up to the specified number of bytes from the file and returns
+  them.
+
+  Not recommended for use on files in text mode.
+
+  This is implemented using fread().
+
+  3.4.5.  file_write
+
+  Function: INT file_write(FHANDLE fh, STR data)
+
+  Writes the specified data to the file.  Returns number of bytes
+  written.
+
+  Not recommended for use on files in text mode.
+
+  This is implemented using fwrite().
+
+  3.4.6.  Getting and setting stream position
+
+  3.4.7.  file_tell
+
+  Function: INT file_tell(FHANDLE fh)
+
+  Returns position in file.
+
+  This is implemented using ftell().
+
+  3.4.8.  file_seek
+
+  Function: void file_seek(FHANDLE fh, INT loc, STR whence)
+
+  Seeks to a particular location in a file.  whence is one of the
+  strings:
+
+  o  "SEEK_SET" - seek to location relative to beginning
+
+  o  "SEEK_CUR" - seek to location relative to current
+
+  o  "SEEK_END" - seek to location relative to end
+
+  This is implemented using fseek().
+
+  3.4.9.  file_eof
+
+  Function: INT file_eof(FHANDLE fh)
+
+  Returns true if and only if fh's stream is positioned at EOF.
+
+  This is implemented using feof().
+
+  3.5.  Housekeeping operations
+
+  3.5.1.  file_size, file_mode, file_last_access, file_last_modify,
+  file_last_change
+
+  Function: INT file_size(STR pathname)
+  Function: STR file_mode(STR pathname)
+  Function: INT file_last_access(STR pathname)
+  Function: INT file_last_modify(STR pathname)
+  Function: INT file_last_change(STR pathname)
+  Function: INT file_size(FHANDLE fh)
+  Function: STR file_mode(FHANDLE fh)
+  Function: INT file_last_access(FHANDLE fh)
+  Function: INT file_last_modify(FHANDLE fh)
+  Function: INT file_last_change(FHANDLE fh)
+
+  Returns the size, mode, last access time, last modify time, or last
+  change time of the specified file.   All of these functions also take
+  FHANDLE arguments and then operate on the open file.
+
+  These are all implemented using fstat() (for open FHANDLEs) or stat()
+  (for pathnames).
+
+  3.5.2.  file_stat
+
+  Function: void file_stat(STR pathname)
+  Function: void file_stat(FHANDLE fh)
+
+  Returns the result of stat() (or fstat()) on the given file.
+  Specifically a list as follows:
+
+  {file size in bytes, file type, file access mode, owner, group,
+       last access, last modify, and last change}
+
+  owner and group are always the empty string.
+
+  It is recommended that the specific information functions file_size,
+  file_type, file_mode, file_last_access, file_last_modify, and
+  file_last_change be used instead.  In most cases only one of these
+  elements is desired and in those cases there's no reason to make and
+  free a list.
+
+  3.5.3.  file_rename
+
+  Function: void file_rename(STR oldpath, STR newpath)
+
+  Attempts to rename the oldpath to newpath.
+
+  This is implemented using rename().
+
+  3.5.4.  file_remove
+
+  Function: void file_remove(STR pathname)
+
+  Attempts to remove the given file.
+
+  This is implemented using remove().
+
+  3.5.5.  file_mkdir
+
+  Function: void file_mkdir(STR pathname)
+
+  Attempts to create the given directory.
+
+  This is implemented using mkdir().
+
+  3.5.6.  file_rmdir
+
+  Function: void file_rmdir(STR pathname)
+
+  Attempts to remove the given directory.
+
+  This is implemented using rmdir().
+
+  3.5.7.  file_list
+
+  Function: LIST file_list(STR pathname, [ANY detailed])
+
+  Attempts to list the contents of the given directory.  Returns a list
+  of files in the directory.  If the detailed argument is provided and
+  true, then the list contains detailed entries, otherwise it contains a
+  simple list of names.
+
+  detailed entry:
+    {STR filename, STR file type, STR file mode, INT file size}
+  normal entry:
+    STR filename
+
+  This is implemented using scandir().
+
+  3.5.8.  file_type
+
+  Function: STR file_type(STR pathname)
+
+  Returns the type of the given pathname, one of "reg", "dir", "dev",
+  "fifo", or "socket".
+
+  This is implemented using stat().
+
+  3.5.9.  file_mode
+
+  Function: STR file_mode(STR filename)
+
+  Returns octal mode for a file (e.g. "644").
+
+  This is implemented using stat().
+
+  3.5.10.  file_chmod
+
+  Function: void file_chmod(STR filename, STR mode)
+
+  Attempts to set mode of a file using mode as an octal string of
+  exactly three characters.
+
+  This is implemented using chmod().
+
diff --git a/Makefile.in b/Makefile.in
index 5e0e9de..f069839 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -35,7 +35,8 @@ CSRCS = ast.c code_gen.c db_file.c db_io.c db_objects.c db_properties.c \
 	log.c malloc.c match.c md5.c name_lookup.c network.c net_mplex.c \
 	net_proto.c numbers.c objects.c parse_cmd.c pattern.c program.c \
 	property.c quota.c ref_count.c regexpr.c server.c storage.c streams.c str_intern.c \
-	sym_table.c tasks.c timers.c unparse.c utils.c verbs.c version.c
+	sym_table.c tasks.c timers.c unparse.c utils.c verbs.c version.c \
+	extension-fileio.c
 
 OPT_NET_SRCS = net_single.c net_multi.c \
 	net_mp_selct.c net_mp_poll.c net_mp_fake.c \
@@ -54,6 +55,7 @@ HDRS =  ast.h bf_register.h code_gen.h db.h db_io.h db_private.h decompile.h \
 	options.h parse_cmd.h parser.h pattern.h program.h quota.h random.h \
 	ref_count.h regexpr.h server.h storage.h streams.h structures.h  str_intern.h \
 	sym_table.h tasks.h timers.h tokens.h unparse.h utils.h verbs.h \
+	extension-fileio.h \
 	version.h
 
 SYSHDRS = my-ctype.h my-fcntl.h my-in.h my-inet.h my-ioctl.h my-math.h \
@@ -290,7 +292,7 @@ depend: ${ALL_CSRCS}
 # Revision 1.3  1992/07/27  18:30:21  pjames
 # Update what vector.o and vector.po depend on.
 ###############################################################################
-	
+
 # Have to do this one manually, since 'make depend' can't hack yacc files.
 parser.o:	my-ctype.h my-math.h my-stdlib.h my-string.h \
 		ast.h code_gen.h config.h functions.h \
diff --git a/README.fileio b/README.fileio
new file mode 100644
index 0000000..a694905
--- /dev/null
+++ b/README.fileio
@@ -0,0 +1,18 @@
+
+Welcome to FileIO.
+
+FileIO is a patch to the LambdaMOO server to allow stdio-style access to
+files.  Please see the file fileio-docs.txt for complete information.
+
+The current version of FileIO, along with documentation in HTML format,
+is on-line at 
+
+  http://www.scinc.com/~abakun/fileio/
+
+The maintainer of FileIO, Andy Bakun, can be reached via e-mail at
+
+  abakun@scinc.com
+
+Bug reports and fixes, patches, extensions, what you've done with
+FileIO, and general complaints are welcome.
+
diff --git a/bf_register.h b/bf_register.h
index 1adb10b..9ca4e9b 100644
--- a/bf_register.h
+++ b/bf_register.h
@@ -27,6 +27,7 @@ extern void register_property(void);
 extern void register_server(void);
 extern void register_tasks(void);
 extern void register_verbs(void);
+extern void register_fileio(void);
 
 /* 
  * $Log: bf_register.h,v $
diff --git a/extension-fileio.c b/extension-fileio.c
new file mode 100644
index 0000000..59c2656
--- /dev/null
+++ b/extension-fileio.c
@@ -0,0 +1,1557 @@
+/*
+ * file i/o server modification
+ */
+
+#define FILE_IO 1
+
+#include <stdio.h>
+
+#include "my-stat.h"
+
+#include <dirent.h>
+
+/* some things are not defined in stdio on all systems -- AAB 06/03/97 */
+#include <sys/types.h>
+#include <errno.h>
+
+#include "my-unistd.h"
+
+#include "my-ctype.h"
+#include "my-string.h"
+#include "structures.h"
+#include "exceptions.h"
+#include "bf_register.h"
+#include "functions.h"
+#include "list.h"
+#include "storage.h"
+#include "utils.h"
+#include "streams.h"
+#include "server.h"
+#include "network.h"
+
+
+#include "tasks.h"
+#include "log.h"
+
+#include "extension-fileio.h"
+
+/* apparently, not defined on some SysVish systems -- AAB 06/03/97 */
+typedef unsigned short umode_t;
+/* your system may define o_mode_t instead -- AAB 06/03/97 */
+/* typedef o_mode_t umode_t; */
+
+/*****************************************************
+ * Utility functions
+ *****************************************************/
+
+const char *raw_bytes_to_clean(const char *buffer, int buflen) {
+  static Stream *s = 0;
+  int i;
+
+  if(!s) 
+	 s = new_stream(100);
+
+  for (i = 0; i < buflen; i++) {
+	 unsigned char c = buffer[i];
+	 
+	 if (isgraph(c) || c == ' ')
+		stream_add_char(s, c);
+	 /* else drop it on the floor */
+  }
+  
+  return reset_stream(s);
+}	 
+
+const char *clean_to_raw_bytes(const char *buffer, int *buflen) {
+  *buflen = strlen(buffer);
+  return buffer;
+}	 
+
+
+/******************************************************
+ * Module-internal data structures
+ *****************************************************/
+
+/*
+ *  File types are either TEXT or BINARY
+ */
+
+typedef struct file_type *file_type;
+
+struct file_type { 
+    
+  const char* (*in_filter)(const char *data, int buflen);
+
+  const char* (*out_filter)(const char *data, int *buflen);
+
+};
+
+file_type file_type_binary = NULL;
+file_type file_type_text = NULL;
+
+
+
+#define FILE_O_READ       1
+#define FILE_O_WRITE      2
+#define FILE_O_FLUSH      4
+
+typedef unsigned char file_mode;
+
+typedef struct file_handle file_handle;
+
+struct file_handle {
+  char  valid;               /* Is this a valid entry?   */
+  char *name;                /* pathname of the file     */
+  file_type type;            /* text or binary, sir?     */
+  file_mode mode;            /* readin', writin' or both */
+ 
+  FILE  *file;               /* the actual file handle   */  
+};
+
+typedef struct line_buffer line_buffer;
+
+struct line_buffer {
+  char *line;
+  struct line_buffer *next;
+};
+
+/***************************************************************
+ * Version and package informaion
+ ***************************************************************/
+
+char file_package_name[]    = "FIO";
+char file_package_version[] = "1.5p3";
+
+
+/***************************************************************
+ * File <-> FHANDLE descriptor table interface
+ ***************************************************************/
+
+
+file_handle file_table[FILE_IO_MAX_FILES];
+
+char file_handle_valid(Var fhandle) {
+  int32 i = fhandle.v.num;
+  if(fhandle.type != TYPE_INT)
+	 return 0;
+  if((i < 0) || (i >= FILE_IO_MAX_FILES))
+	 return 0;
+  return file_table[i].valid;
+}
+
+
+FILE *file_handle_file(Var fhandle) {
+  int32 i = fhandle.v.num;
+  return file_table[i].file;
+}
+
+const char *file_handle_name(Var fhandle) {
+  int32 i = fhandle.v.num;
+  return file_table[i].name;
+}
+
+file_type file_handle_type(Var fhandle) {
+  int32 i = fhandle.v.num;
+  return file_table[i].type;
+}  
+
+file_mode file_handle_mode(Var fhandle) {
+  int32 i = fhandle.v.num;
+  return file_table[i].mode;
+}  
+
+
+void file_handle_destroy(Var fhandle) {
+  int32 i = fhandle.v.num;
+  file_table[i].file = NULL;
+  file_table[i].valid = 0;
+  free_str(file_table[i].name);
+}
+
+
+int32 file_allocate_next_handle(void) {
+  static int32 current_handle = 0;
+  int32 wrapped = current_handle;
+
+  if(current_handle > FILE_IO_MAX_FILES)
+	 wrapped = current_handle = 0;
+
+  while(current_handle < FILE_IO_MAX_FILES) {
+	 if(!file_table[current_handle].valid)
+		break;
+
+	 current_handle++;
+	 if(current_handle > FILE_IO_MAX_FILES)
+		current_handle = 0;
+	 if(current_handle == wrapped)
+		current_handle = FILE_IO_MAX_FILES;
+  }
+  if(current_handle == FILE_IO_MAX_FILES) {
+	 current_handle = 0;
+	 return -1;
+  }
+  return current_handle;
+}
+
+
+Var file_handle_new(const char *name, file_type type, file_mode mode) {
+  Var r;
+  int32 handle = file_allocate_next_handle();
+  
+  r.type = TYPE_INT;
+  r.v.num = handle;
+
+  if(handle >= 0) {
+	 file_table[handle].valid = 1;
+	 file_table[handle].name = str_dup(name);
+	 file_table[handle].type = type;
+	 file_table[handle].mode = mode;
+  }
+
+  return r;
+}
+
+void file_handle_set_file(Var fhandle, FILE *f) {
+  int32 i = fhandle.v.num;
+  file_table[i].file = f;
+}
+
+/***************************************************************
+ * Interface for modestrings
+ ***************************************************************/
+
+/*
+ *  Convert modestring to settings for type and mode.  
+ *  Returns pointer to stdio modestring if successfull and
+ *  NULL if not.
+ */ 
+
+const char *file_modestr_to_mode(const char *s, file_type *type, file_mode *mode) {
+  static char buffer[4] = {0, 0, 0, 0};
+  int p = 0;
+  file_type t;
+  file_mode m = 0;
+
+  if(!file_type_binary) {
+	 file_type_binary = mymalloc(sizeof(struct file_type), M_STRING);
+	 file_type_text = mymalloc(sizeof(struct file_type), M_STRING);
+	 file_type_binary->in_filter = raw_bytes_to_binary;
+	 file_type_binary->out_filter = binary_to_raw_bytes;
+	 file_type_text->in_filter = raw_bytes_to_clean;
+	 file_type_text->out_filter = clean_to_raw_bytes;
+  }
+
+  
+  if(strlen(s) != 4)
+	 return 0;
+  
+  if(s[0] == 'r')           m |= FILE_O_READ;
+  else if(s[0] == 'w')      m |= FILE_O_WRITE;
+  else if(s[0] == 'a')      m |= FILE_O_WRITE;
+  else 
+	 return NULL;
+  
+  
+  buffer[p++] = s[0];
+  
+  if(s[1] == '+') {
+	 m |= (s[0] == 'r') ? FILE_O_WRITE : FILE_O_READ;
+	 buffer[p++] = '+';
+  } else if (s[1] != '-') {
+	 return NULL;
+  }
+
+  if(s[2] == 't')            t = file_type_text;
+  else if (s[2] == 'b') {
+	 t = file_type_binary;
+	 buffer[p++] = 'b';
+  } else
+	 return NULL;
+
+  if(s[3] == 'f')            m |= FILE_O_FLUSH;
+  else if (s[3] != 'n')
+	 return NULL;
+
+  *type = t;    *mode = m;
+  buffer[p] = 0;
+  return buffer;
+}
+  
+
+/***************************************************************
+ * Various error handlers
+ ***************************************************************/
+
+package 
+file_make_error(const char *errtype, const char *msg) {
+  package p;
+  Var value;
+
+  value.type = TYPE_STR;
+  value.v.str = str_dup(errtype);
+
+  p.kind = BI_RAISE;
+  p.u.raise.code.type = TYPE_ERR;
+  p.u.raise.code.v.err = E_FILE;
+  p.u.raise.msg = str_dup(msg);
+  p.u.raise.value = value;
+
+  return p;
+}
+
+package file_raise_errno(const char *value_str) {
+  char *strerr;
+  
+  if(errno) {
+	 strerr = strerror(errno);
+	 return file_make_error(value_str, strerr);
+  }  else {
+	 return file_make_error("End of file", "End of file");
+  }
+
+}
+
+package file_raise_notokcall(const char *funcid, Objid progr) {
+  return make_error_pack(E_PERM);
+}
+
+package file_raise_notokfilename(const char *funcid, const char *pathname) {
+  Var p;   
+
+  p.type = TYPE_STR;
+  p.v.str = str_dup(pathname);
+  return make_raise_pack(E_INVARG, "Invalid pathname", p);
+}
+
+/***************************************************************
+ * Security verification
+ ***************************************************************/
+
+int file_verify_caller(Objid progr) {
+  return is_wizard(progr);
+}
+
+int file_verify_path(const char *pathname) {
+  /*
+	*  A pathname is OK does not contain a
+   *  any of instances the substring "/."
+   */
+
+  if(pathname[0] == '\0')
+	 return 1;
+
+  if((strlen(pathname) > 1) && (pathname[0] == '.') && (pathname[1] == '.'))
+		return 0;
+
+  if(strindex(pathname, "/.", 0))
+		return 0;
+  
+  return 1;
+}
+
+/***************************************************************
+ * Common code for FHANDLE-using functions
+ **************************************************************/
+
+FILE *file_handle_file_safe(Var handle) {
+  if(!file_handle_valid(handle))
+	 return NULL;
+  else
+	 return file_handle_file(handle);
+}
+
+const char *file_handle_name_safe(Var handle) {
+  if(!file_handle_valid(handle))
+	 return NULL;
+  else
+	 return file_handle_name(handle);
+}
+
+/***************************************************************
+ * Common code for file opening functions
+ ***************************************************************/
+
+const char *file_resolve_path(const char *pathname) {
+  static Stream *s = 0;
+  
+  if(!s)
+	 s = new_stream(strlen(pathname) + strlen(FILE_SUBDIR) + 1);
+  
+  if(!file_verify_path(pathname))
+	 return NULL;
+  
+  stream_add_string(s, FILE_SUBDIR);
+  if(pathname[0] == '/')
+	 stream_add_string(s, pathname + 1);
+  else
+	 stream_add_string(s, pathname);
+  
+  return reset_stream(s);
+  
+}
+
+/***************************************************************
+ * Built in functions
+ * file_version
+ ***************************************************************/
+
+static package
+bf_file_version(Var arglist, Byte next, void *vdata, Objid progr)
+{ 
+  char tmpbuffer[50];
+  Var rv;
+  
+  sprintf(tmpbuffer, "%s/%s", file_package_name, file_package_version);
+  
+  rv.type = TYPE_STR;
+  rv.v.str = str_dup(tmpbuffer);
+
+  return make_var_pack(rv);
+
+}
+  
+
+/***************************************************************
+ * File open and close.
+ ***************************************************************/
+
+
+/*
+ * FHANDLE file_open(STR name, STR mode)
+ */
+
+static package
+bf_file_open(Var arglist, Byte next, void *vdata, Objid progr)
+{ 
+  package r;
+  Var fhandle;
+  const char *real_filename;
+  const char *filename = arglist.v.list[1].v.str;
+  const char *mode = arglist.v.list[2].v.str;
+  const char *fmode;
+  file_mode rmode;
+  file_type type;
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr))
+	 r = file_raise_notokcall("file_open", progr);
+  else if ((real_filename = file_resolve_path(filename)) == NULL)
+	 r = file_raise_notokfilename("file_open", filename);
+  else if ((fmode = file_modestr_to_mode(mode, &type, &rmode)) == NULL)
+	 r = make_raise_pack(E_INVARG, "Invalid mode string", var_ref(arglist.v.list[2]));
+  else if ((fhandle = file_handle_new(filename, type, rmode)).v.num < 0)
+	 r = make_raise_pack(E_QUOTA, "Too many files open", zero);
+  else if ((f = fopen(real_filename, fmode)) == NULL) {
+	 file_handle_destroy(fhandle);
+	 r = file_raise_errno("file_open");
+  } else {
+	 /* phew, we actually got a successfull open */
+	 file_handle_set_file(fhandle, f);		  
+	 r = make_var_pack(fhandle);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * void file_close(FHANDLE handle);
+ */
+
+static package
+bf_file_close(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr))
+	 r = file_raise_notokcall("file_close", progr);
+  else if ((f = file_handle_file_safe(fhandle)) == NULL)
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  else {
+	 fclose(f);
+	 file_handle_destroy(fhandle);
+	 r = no_var_pack();
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * STR file_name(FHANDLE handle)
+ */
+
+static package
+bf_file_name(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  const char *name;
+  Var rv;   
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_name", progr);
+  } else if ((name = file_handle_name_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else {
+	 rv.type = TYPE_STR;
+	 rv.v.str = str_dup(name);
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+static package
+bf_file_openmode(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  char buffer[5] = {0, 0, 0, 0, 0};
+  file_mode mode;
+  file_type type;
+  Var rv;   
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_name", progr);
+  } else if (!file_handle_valid(fhandle)) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else {
+	 type = file_handle_type(fhandle);
+	 mode = file_handle_mode(fhandle);
+	 if(mode & FILE_O_READ) {
+		buffer[0] = 'r';
+	 } else if(mode & FILE_O_WRITE) {
+		buffer[0] = 'w';
+	 }
+	 if(mode & (FILE_O_READ | FILE_O_WRITE))
+		buffer[1] = '+';
+	 else
+		buffer[1] = '-';
+
+	 if(type == file_type_binary)
+		buffer[2] = 'b';
+	 else
+		buffer[2] = 't';
+	 
+	 if(mode & FILE_O_FLUSH)
+		buffer[3] = 'f';
+	 else
+		buffer[3] = 'n';
+	 
+
+	 rv.type = TYPE_STR;
+	 rv.v.str = str_dup(buffer);
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+
+
+/**********************************************************
+ * string (line-based) i/o                                
+ **********************************************************/
+
+/* 
+ * common functionality of file_readline and file_readlines 
+ */
+
+static const char *file_read_line(Var fhandle, int *count) {
+  static Stream *str = 0;
+  FILE *f;
+  int c;
+
+  f = file_handle_file(fhandle);
+  
+  if(str == 0)
+	 str = new_stream(FILE_IO_BUFFER_LENGTH);
+  
+  while((c = fgetc(f)) != EOF && c != '\n')
+	 stream_add_char(str, c);
+
+  if(c == EOF && stream_length(str) == 0) {
+	 reset_stream(str);
+	 *count = 0;
+	 return NULL;
+  }
+
+  *count = stream_length(str);
+  return reset_stream(str);
+}
+  
+
+/*
+ * STR file_readline(FHANDLE handle)
+ */
+
+static package
+bf_file_readline(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  Var rv;
+  int len;
+  file_mode mode;
+  file_type type;
+  const char *line;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_readline", progr);
+  } else if (!file_handle_valid(fhandle)) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else if (!(mode = file_handle_mode(fhandle)) & FILE_O_READ)
+	 r = make_raise_pack(E_INVARG, "File is open write-only", fhandle);
+  else {
+	 type = file_handle_type(fhandle);
+	 if((line = file_read_line(fhandle, &len)) == NULL)
+		r = file_raise_errno("readline");
+	 else {		
+		rv.type = TYPE_STR;
+		rv.v.str = str_dup((type->in_filter)(line, len));
+		r = make_var_pack(rv);
+	 }
+  }
+  free_var(arglist);
+  return r;			 
+}
+
+/*
+ * STR file_readlines(FHANDLE handle, INT start, INT end)
+ */
+
+void free_line_buffer(line_buffer *head, int strings_too) {
+  line_buffer *next;
+  if(head) {
+	 next = head->next;
+	 myfree(head, M_STRUCT);
+	 head = next;
+	 while(head != NULL) {
+		next = head->next;
+		if(strings_too)
+		  free_str(head->line);
+		myfree(head, M_STRUCT);
+		head = next;
+	 }   
+  }
+}
+    
+line_buffer *new_line_buffer(char *line) {
+  line_buffer *p = mymalloc(sizeof(line_buffer), M_STRUCT);
+  p->line = line;
+  p->next = NULL;
+  return p;
+}
+
+static package
+bf_file_readlines(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  int32 begin = arglist.v.list[2].v.num;
+  int32 end   = arglist.v.list[3].v.num;
+  int32 begin_loc = 0, linecount = 0;
+  file_type type;
+  file_mode mode;
+  Var rv;
+  int current_line = 0, len = 0, i = 0;
+  const char *line = NULL;
+  FILE *f;
+  line_buffer *linebuf_head = NULL, *linebuf_cur = NULL;
+
+  errno = 0;
+
+  if((begin < 1) || (begin > end))
+	 return make_error_pack(E_INVARG);
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_readlines", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else if (!(mode = file_handle_mode(fhandle)) & FILE_O_READ)
+	 r = make_raise_pack(E_INVARG, "File is open write-only", fhandle);
+  else {
+
+	 /* Back to the beginning ... */
+	 rewind(f);
+
+	 /* "seek" to that line */
+	 begin--;
+	 while((current_line != begin) 
+			 && ((line = file_read_line(fhandle, &len)) != NULL)) 
+		current_line++;	
+	 
+	 if(((begin != 0) && (line == NULL)) || ((begin_loc = ftell(f)) == -1))
+		r = file_raise_errno("read_line");
+	 else {
+		type = file_handle_type(fhandle);
+
+		/* 
+		 * now that we have where to begin, it's time to slurp lines 
+		 * and seek to EOF or to the end_line, whichever comes first
+		 */
+		
+		linebuf_head = linebuf_cur = new_line_buffer(NULL);
+		
+		while((current_line != end) 
+				&& ((line = file_read_line(fhandle, &len)) != NULL)) {
+		  linebuf_cur->next = new_line_buffer(str_dup((type->in_filter)(line, len)));
+		  linebuf_cur = linebuf_cur->next;
+		  
+		  current_line++;
+		  }	
+		linecount =  current_line - begin;
+
+		linebuf_cur = linebuf_head->next;
+
+		if(fseek(f, begin_loc, SEEK_SET) == -1) {
+		  free_line_buffer(linebuf_head, 1);
+		  r = file_raise_errno("seeking");
+		} else {
+		  rv = new_list(linecount);		  
+		  i = 1;
+		  while(linebuf_cur != NULL) {
+			 rv.v.list[i].type = TYPE_STR;
+			 rv.v.list[i].v.str = linebuf_cur->line;
+			 linebuf_cur = linebuf_cur->next;
+			 i++;
+		  }
+		  free_line_buffer(linebuf_head, 0);
+		  r = make_var_pack(rv);
+		}
+	 }
+  }
+
+  free_var(arglist);
+  return r;			 
+}
+
+/*
+ * void file_writeline(FHANDLE handle, STR line)
+ */
+
+static package
+bf_file_writeline(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];  
+  const char *buffer = arglist.v.list[2].v.str;
+  const char *rawbuffer;
+  file_mode mode;
+  file_type type;
+  int len;
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_writeline", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else if (!(mode = file_handle_mode(fhandle)) & FILE_O_WRITE)
+	 r = make_raise_pack(E_INVARG, "File is open read-only", fhandle);
+  else {
+	 type = file_handle_type(fhandle);
+	 if((rawbuffer = (type->out_filter)(buffer, &len)) == NULL)
+		r = make_raise_pack(E_INVARG, "Invalid binary string", fhandle);
+	 else if((fputs(rawbuffer, f) == EOF) || (fputc('\n', f) != '\n'))
+		r = file_raise_errno(file_handle_name(fhandle));
+	 else {
+		  if(mode & FILE_O_FLUSH) {
+			 fflush(f);
+		  }
+		r = no_var_pack();
+	 }
+  }
+  free_var(arglist);
+  return r;			 
+}
+
+/********************************************************
+ * binary i/o
+ ********************************************************/
+
+/*
+ * STR file_read(FHANDLE handle, INT record_length)
+ */
+
+static package
+bf_file_read(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+
+  Var fhandle = arglist.v.list[1];
+  file_mode mode;
+  file_type type;
+  int32 record_length = arglist.v.list[2].v.num;
+  int32 read_length;
+
+  char buffer[FILE_IO_BUFFER_LENGTH];
+
+  Var rv;
+
+  static Stream *str = 0;
+  int len = 0, read = 0;
+
+  FILE *f;
+
+  errno = 0;
+
+  read_length = (record_length > sizeof(buffer)) ? sizeof(buffer) : record_length;
+
+  if(str == 0)
+	 str = new_stream(FILE_IO_BUFFER_LENGTH);
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_read", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else if (!(mode = file_handle_mode(fhandle)) & FILE_O_READ)
+	 r = make_raise_pack(E_INVARG, "File is open write-only", fhandle);
+  else {
+	 type = file_handle_type(fhandle);
+
+  try_again:
+	 read = fread(buffer, sizeof(char), read_length, f);
+	 if(!read && !len) {
+		/* 
+		 * No more to read.  This is only an error if nothing
+		 * has been read so far.
+		 *
+		 */
+		r = file_raise_errno(file_handle_name(fhandle));		
+	 } else if (read && ((len += read) < record_length)){
+		/*
+		 * We got something this time, but it isn't enough.
+		 */
+		stream_add_string(str, (type->in_filter)(buffer, read));
+		read = 0;
+		goto try_again;
+	 } else {
+		/*
+		 * We didn't get anything last time, but we have something already
+		 * OR
+		 * We got everything we need.
+		 */
+		
+		stream_add_string(str, (type->in_filter)(buffer, read));
+
+		rv.type = TYPE_STR;
+		rv.v.str = str_dup(reset_stream(str));					 
+
+		r = make_var_pack(rv);
+	 }
+  }
+  free_var(arglist);
+  return r;			 
+}
+
+/*
+ * void file_flush(FHANDLE handle)
+ */
+
+static package
+bf_file_flush(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_flush", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else {
+	 if(fflush(f))
+		r = file_raise_errno("flushing");
+	 else
+		r = no_var_pack();
+  }
+  free_var(arglist);
+  return r;
+}
+
+
+/*
+ * INT file_write(FHANDLE fh, STR data)
+ */
+
+static package
+bf_file_write(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1], rv;  
+  const char *buffer = arglist.v.list[2].v.str;
+  const char *rawbuffer;
+  file_mode mode;
+  file_type type;
+  int len;
+  int written;
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_write", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", fhandle);
+  } else if (!(mode = file_handle_mode(fhandle)) & FILE_O_WRITE)
+	 r = make_raise_pack(E_INVARG, "File is open read-only", fhandle);
+  else {
+	 type = file_handle_type(fhandle);
+	 if((rawbuffer = (type->out_filter)(buffer, &len)) == NULL)
+		r = make_raise_pack(E_INVARG, "Invalid binary string", fhandle);
+	 else if(!(written = fwrite(rawbuffer, sizeof(char), len, f)))
+		r = file_raise_errno(file_handle_name(fhandle));
+	 else {
+		if(mode & FILE_O_FLUSH)
+		  fflush(f);
+		rv.type = TYPE_INT;
+		rv.v.num = written;
+		r = make_var_pack(rv);
+	 }
+  }
+  free_var(arglist);
+  return r;			 
+}
+
+
+/************************************************
+ * navigating the file
+ ************************************************/
+
+/*
+ * void file_seek(FHANDLE handle, FLOC location, STR whence)
+ * whence in {"SEEK_SET", "SEEK_CUR", "SEEK_END"}
+ */
+
+static package
+bf_file_seek(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  int32 seek_to = arglist.v.list[2].v.num;
+  const char *whence = arglist.v.list[3].v.str;
+  int whnce = 0, whence_ok = 1;
+  FILE *f;
+
+  errno = 0;
+
+  if(!mystrcasecmp(whence, "SEEK_SET"))
+	 whnce = SEEK_SET;
+  else if (!mystrcasecmp(whence, "SEEK_CUR"))
+	 whnce = SEEK_CUR;
+  else if (!mystrcasecmp(whence, "SEEK_END"))
+	 whnce = SEEK_END;
+  else 
+	 whence_ok = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_seek", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", var_ref(fhandle));
+  } else if (!whence_ok) {
+	 r = make_raise_pack(E_INVARG, "Invalid whence", zero);
+  } else {
+	 if(fseek(f, seek_to, whnce))
+		r = file_raise_errno(file_handle_name(fhandle));		
+	 else
+		r = no_var_pack();
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * FLOC file_tell(FHANDLE handle)
+ */
+
+static package
+bf_file_tell(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  Var rv;
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_tell", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", var_ref(fhandle));
+  } else {
+	 rv.type = TYPE_INT;
+	 if((rv.v.num = ftell(f)) < 0)
+		r = file_raise_errno(file_handle_name(fhandle));		
+	 else
+		r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * INT file_eof(FHANDLE handle)
+ */
+
+static package
+bf_file_eof(Var arglist, Byte next, void *vdata, Objid progr)
+{
+  package r;
+  Var fhandle = arglist.v.list[1];
+  Var rv;
+  FILE *f;
+
+  errno = 0;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_eof", progr);
+  } else if ((f = file_handle_file_safe(fhandle)) == NULL) {
+	 r = make_raise_pack(E_INVARG, "Invalid FHANDLE", var_ref(fhandle));
+  } else {
+	 rv.type = TYPE_INT;
+	 rv.v.num = feof(f);	 
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*****************************************************************
+ * Functions that stat()
+ *****************************************************************/
+
+/*
+ * (internal) int(statok) file_stat(Var filespec, package *r, struct stat *buf)
+ */
+
+int file_stat(Objid progr, Var filespec, package *r, struct stat *buf) {
+  int statok = 0;
+  
+  if(!file_verify_caller(progr)) {
+	 *r = file_raise_notokcall("file_stat", progr);
+  } else if (filespec.type == TYPE_STR) {
+	 const char *filename = filespec.v.str;
+	 const char *real_filename;
+
+	 if((real_filename = file_resolve_path(filename)) == NULL) {
+		*r =  file_raise_notokfilename("file_stat", filename);
+	 } else {
+		if(stat(real_filename, buf) != 0)		  
+		  *r = file_raise_errno(filename);
+		else {
+		  statok = 1;
+		}
+	 }
+  } else {
+	 FILE *f;
+	 if((f = file_handle_file_safe(filespec)) == NULL)
+		*r = make_raise_pack(E_INVARG, "Invalid FHANDLE", filespec);
+	 else {
+		if(fstat(fileno(f), buf) != 0)
+		  *r = file_raise_errno(file_handle_name(filespec));
+		else {
+		  statok = 1;
+		}
+	 }
+  }
+  return statok;
+}
+
+const char *file_type_string(umode_t st_mode) {
+  if(S_ISREG(st_mode))
+	 return "reg";
+  else if (S_ISDIR(st_mode))
+	 return "dir";
+  else if (S_ISFIFO(st_mode))
+	 return "fifo";
+  else if (S_ISBLK(st_mode))
+	 return "block";
+  else if (S_ISSOCK(st_mode))
+	 return "socket";
+  else
+	 return "unknown";
+}
+
+const char *file_mode_string(umode_t st_mode) {
+  static Stream *s = 0;
+  if(!s)
+	 s = new_stream(4);
+  stream_printf(s, "%03o", st_mode & 0777);
+  return reset_stream(s);
+}
+ 
+/*
+ * INT file_size(STR filename)
+ * INT file_size(FHANDLE fh)
+ */
+
+static package
+bf_file_size(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_INT;
+	 rv.v.num = buf.st_size;
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * STR file_mode(STR filename)
+ * STR file_mode(FHANDLE fh)
+ */
+
+static package
+bf_file_mode(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_STR;
+	 rv.v.str = str_dup(file_mode_string(buf.st_mode));
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * STR file_type(STR filename)
+ * STR file_type(FHANDLE fh)
+ */
+
+static package
+bf_file_type(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_STR;
+	 rv.v.str = str_dup(file_type_string(buf.st_mode));
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * INT file_last_access(STR filename)
+ * INT file_last_access(FHANDLE fh)
+ */
+
+static package
+bf_file_last_access(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_INT;
+	 rv.v.num = buf.st_atime;
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * INT file_last_modify(STR filename)
+ * INT file_last_modify(FHANDLE fh)
+ */
+
+static package
+bf_file_last_modify(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_INT;
+	 rv.v.num = buf.st_mtime;
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * INT file_last_change(STR filename)
+ * INT file_last_change(FHANDLE fh)
+ */
+
+static package
+bf_file_last_change(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv.type = TYPE_INT;
+	 rv.v.num = buf.st_ctime;
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*
+ * INT file_stat(STR filename)
+ * INT file_stat(FHANDLE fh)
+ */
+
+static package
+bf_file_stat(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  Var     rv;
+  Var filespec = arglist.v.list[1];
+  struct stat buf;
+
+  if (file_stat(progr, filespec, &r, &buf)) {
+	 rv = new_list(8);
+	 rv.v.list[1].type = TYPE_INT;
+	 rv.v.list[1].v.num = buf.st_size;
+	 rv.v.list[2].type = TYPE_STR;
+	 rv.v.list[2].v.str = str_dup(file_type_string(buf.st_mode));
+	 rv.v.list[3].type = TYPE_STR;
+	 rv.v.list[3].v.str = str_dup(file_mode_string(buf.st_mode));
+	 rv.v.list[4].type = TYPE_STR;
+	 rv.v.list[4].v.str = str_dup("");
+	 rv.v.list[5].type = TYPE_STR;
+	 rv.v.list[5].v.str = str_dup("");
+	 rv.v.list[6].type = TYPE_INT;
+	 rv.v.list[6].v.num = buf.st_atime;
+	 rv.v.list[7].type = TYPE_INT;
+	 rv.v.list[7].v.num = buf.st_mtime;
+	 rv.v.list[8].type = TYPE_INT;
+	 rv.v.list[8].v.num = buf.st_ctime;
+	 r = make_var_pack(rv);
+  }
+  free_var(arglist);
+  return r;
+}
+
+/*****************************************************************
+ * Housekeeping functions
+ *****************************************************************/
+
+/*
+ * LIST file_list(STR pathname, [ANY detailed])
+ */
+
+int file_list_select(const struct dirent *d) {
+  const char *name = d->d_name;
+  int l = strlen(name);
+  if((l == 1) && (name[0] == '.'))
+	 return 0;
+  else if ((l == 2) && (name[0] == '.') && (name[1] == '.'))
+	 return 0;
+  else
+	 return 1;
+}
+
+static package
+bf_file_list(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  /* modified to use opendir/readdir which is slightly more "standard"
+     than the original scandir method.   -- AAB 06/03/97
+   */
+  package r;
+  const char *pathspec = arglist.v.list[1].v.str;
+  const char *real_pathname;
+  int	detailed = (arglist.v.list[0].v.num > 1
+						? is_true(arglist.v.list[2])
+						: 0);
+  
+    if(!file_verify_caller(progr)) {
+        r = file_raise_notokcall("file_list", progr);
+    } else if((real_pathname = file_resolve_path(pathspec)) == NULL) {
+        r =  file_raise_notokfilename("file_list", pathspec);
+    } else {
+        DIR *curdir;
+        Stream *s = new_stream(64);
+        int failed = 0;
+        struct stat buf;
+        Var     rv, detail;
+	struct dirent *curfile;
+
+	if (!(curdir = opendir (real_pathname)))
+		r = file_raise_errno(pathspec);
+	else {
+		rv = new_list(0);
+		while ( (curfile = readdir(curdir)) != 0 ) {
+		    if (strncmp(curfile->d_name, ".", 2) != 0 && strncmp(curfile->d_name, "..", 3) != 0) {
+			if (detailed) {
+				stream_add_string(s, real_pathname);
+				stream_add_char(s, '/');
+				stream_add_string(s, curfile->d_name);
+				if (stat(reset_stream(s), &buf) != 0) {
+					failed = 1;
+					break;
+				} else {
+					detail = new_list(4);
+					detail.v.list[1].type = TYPE_STR;
+					detail.v.list[1].v.str = str_dup(curfile->d_name);
+					detail.v.list[2].type = TYPE_STR;
+					detail.v.list[2].v.str = str_dup(file_type_string(buf.st_mode));
+					detail.v.list[3].type = TYPE_STR;
+					detail.v.list[3].v.str = str_dup(file_mode_string(buf.st_mode));
+					detail.v.list[4].type = TYPE_INT;
+					detail.v.list[4].v.num = buf.st_size;
+				}
+			} else {
+				detail.type = TYPE_STR;
+				detail.v.str = str_dup(curfile->d_name);
+			}
+			rv = listappend(rv, detail);
+		    }
+		}
+		if(failed) {
+			free_var(rv);
+			r = file_raise_errno(pathspec);
+		} else
+			r = make_var_pack(rv);
+		closedir(curdir);
+	}
+  	free_stream(s);
+    }
+    free_var(arglist);
+    return r;
+}
+
+
+/*
+ * void file_mkdir(STR pathname)
+ */
+
+static package
+bf_file_mkdir(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  const char *pathspec = arglist.v.list[1].v.str;
+  const char *real_pathname;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_mkdir", progr);
+  } else if((real_pathname = file_resolve_path(pathspec)) == NULL) {
+	 r =  file_raise_notokfilename("file_mkdir", pathspec);
+  } else {
+	 if(mkdir(real_pathname, 0777) != 0) 
+		r = file_raise_errno(pathspec);
+	 else 
+		r = no_var_pack();
+	 
+  }
+  free_var(arglist);
+  return r;
+}			 
+
+/*
+ * void file_rmdir(STR pathname)
+ */
+
+static package
+bf_file_rmdir(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  const char *pathspec = arglist.v.list[1].v.str;
+  const char *real_pathname;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_rmdir", progr);
+  } else if((real_pathname = file_resolve_path(pathspec)) == NULL) {
+	 r =  file_raise_notokfilename("file_rmdir", pathspec);
+  } else {
+	 if(rmdir(real_pathname) != 0)
+		r = file_raise_errno(pathspec);
+	 else 
+		r = no_var_pack();
+	 
+  }
+  free_var(arglist);
+  return r;
+}			 
+
+/*
+ * void file_remove(STR pathname)
+ */
+
+static package
+bf_file_remove(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  const char *pathspec = arglist.v.list[1].v.str;
+  const char *real_pathname;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_remove", progr);
+  } else if((real_pathname = file_resolve_path(pathspec)) == NULL) {
+	 r =  file_raise_notokfilename("file_remove", pathspec);
+  } else {
+	 if(remove(real_pathname) != 0)
+		r = file_raise_errno(pathspec);
+	 else 
+		r = no_var_pack();	 
+  }
+  free_var(arglist);
+  return r;
+}			 
+
+/*
+ * void file_rename(STR pathname)
+ */
+
+static package
+bf_file_rename(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  const char *fromspec = arglist.v.list[1].v.str;
+  const char *tospec = arglist.v.list[2].v.str;
+  char *real_fromspec = NULL;
+  const char *real_tospec;
+  
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_rename", progr);
+  } else if((real_fromspec = str_dup(file_resolve_path(fromspec))) == NULL) {
+	 r =  file_raise_notokfilename("file_rename", fromspec);
+  } else if((real_tospec = file_resolve_path(tospec)) == NULL) {
+	 r =  file_raise_notokfilename("file_rename", tospec);
+  } else {
+	 if(rename(real_fromspec, real_tospec) != 0)
+		r = file_raise_errno("rename");
+	 else 
+		r = no_var_pack();	 
+  }
+  if(real_fromspec)
+	 free_str(real_fromspec);
+  free_var(arglist);
+  return r;
+}			 
+
+
+
+/*
+ * void file_chmod(STR pathname, STR mode)
+ */
+
+
+int file_chmodstr_to_mode(const char *modespec, mode_t *newmode) {
+  mode_t m = 0;
+  int i = 0, fct = 1;
+
+  if(strlen(modespec) != 3)
+	 return 0;
+  else {
+	 for(i = 2; i >= 0; i--) {
+		char c = modespec[i];
+		if(!((c  >= '0') && (c <= '7')))
+		  return 0;
+		else {
+		  m += fct * (c - '0');
+		}
+		fct *= 8;
+	 }
+  }
+  *newmode = m;
+  return 1;
+}
+
+static package
+bf_file_chmod(Var arglist, Byte next, void *vdata, Objid progr)
+{  
+  package r;
+  const char *pathspec = arglist.v.list[1].v.str;
+  const char *modespec = arglist.v.list[2].v.str;
+  mode_t newmode;
+  const char *real_filename;
+
+  if(!file_verify_caller(progr)) {
+	 r = file_raise_notokcall("file_chmod", progr);
+  } else if(!file_chmodstr_to_mode(modespec, &newmode)) {
+	 r = make_raise_pack(E_INVARG, "Invalid mode string", zero);	 
+  } else if((real_filename = file_resolve_path(pathspec)) == NULL) {
+	 r =  file_raise_notokfilename("file_chmod", pathspec);
+  } else {
+	 if(chmod(real_filename, newmode) != 0)
+		r = file_raise_errno("chmod");
+	 else 
+		r = no_var_pack();	 
+  }
+  free_var(arglist);
+  return r;
+}
+
+/************************************************************************/
+
+void
+register_fileio(void)
+{
+#if FILE_IO
+
+  register_function("file_version", 0, 0, bf_file_version);
+
+  register_function("file_open", 2, 2, bf_file_open, TYPE_STR, TYPE_STR);
+  register_function("file_close", 1, 1, bf_file_close, TYPE_INT);
+  register_function("file_name", 1, 1, bf_file_name, TYPE_INT);
+  register_function("file_openmode", 1, 1, bf_file_openmode, TYPE_INT);
+
+
+  register_function("file_readline", 1, 1, bf_file_readline, TYPE_INT);
+  register_function("file_readlines", 3, 3, bf_file_readlines, TYPE_INT, TYPE_INT, TYPE_INT);
+  register_function("file_writeline", 2, 2, bf_file_writeline, TYPE_INT, TYPE_STR);
+
+  register_function("file_read", 2, 2, bf_file_read, TYPE_INT, TYPE_INT);
+  register_function("file_write", 2, 2, bf_file_write, TYPE_INT, TYPE_STR);
+  register_function("file_flush", 1, 1, bf_file_flush, TYPE_INT);
+
+
+  register_function("file_seek", 3, 3, bf_file_seek, TYPE_INT, TYPE_INT, TYPE_STR);
+  register_function("file_tell", 1, 1, bf_file_tell, TYPE_INT);
+
+  register_function("file_eof", 1, 1, bf_file_eof, TYPE_INT);
+
+  register_function("file_list", 1, 2, bf_file_list, TYPE_STR, TYPE_ANY);
+  register_function("file_mkdir", 1, 1, bf_file_mkdir, TYPE_STR);
+  register_function("file_rmdir", 1, 1, bf_file_rmdir, TYPE_STR);
+  register_function("file_remove", 1, 1, bf_file_remove, TYPE_STR);
+  register_function("file_rename", 2, 2, bf_file_rename, TYPE_STR, TYPE_STR);
+  register_function("file_chmod", 2, 2, bf_file_chmod, TYPE_STR, TYPE_STR);
+
+  register_function("file_size", 1, 1, bf_file_size, TYPE_ANY);
+  register_function("file_mode", 1, 1, bf_file_mode, TYPE_ANY);
+  register_function("file_type", 1, 1, bf_file_type, TYPE_ANY);
+  register_function("file_last_access", 1, 1, bf_file_last_access, TYPE_ANY);
+  register_function("file_last_modify", 1, 1, bf_file_last_modify, TYPE_ANY);
+  register_function("file_last_change", 1, 1, bf_file_last_change, TYPE_ANY);
+  register_function("file_stat", 1, 1, bf_file_stat, TYPE_ANY);
+
+#endif
+}
diff --git a/extension-fileio.h b/extension-fileio.h
new file mode 100644
index 0000000..466c698
--- /dev/null
+++ b/extension-fileio.h
@@ -0,0 +1,15 @@
+/*
+ * extension-fileio.h 
+ *
+ */
+
+#ifndef EXT_FILE_IO_H
+
+#define EXT_FILE_IO_H 1
+
+#define FILE_IO_MAX_FILES 256
+#define FILE_SUBDIR "files/"
+
+#define FILE_IO_BUFFER_LENGTH 4096
+
+#endif
diff --git a/functions.c b/functions.c
index 5c786f9..d8176e1 100644
--- a/functions.c
+++ b/functions.c
@@ -54,7 +54,8 @@ static registry bi_function_registries[] =
     register_property,
     register_server,
     register_tasks,
-    register_verbs
+    register_verbs,
+    register_fileio
 };
 
 void
diff --git a/keywords.c b/keywords.c
index 0a64bd5..e98a5a1 100644
--- a/keywords.c
+++ b/keywords.c
@@ -1,5 +1,5 @@
 /* C code produced by gperf version 2.1p1 (K&R C version, modified by Pavel) */
-/* Command-line: pgperf -aCIptT -k1,3,$ keywords.gperf  */
+/* Command-line: pgperf/pgperf -aCIptT -k1,3,$ keywords.gperf  */
 
 #include "my-ctype.h"
 
@@ -35,179 +35,133 @@
 #define MIN_HASH_VALUE 3
 #define MAX_HASH_VALUE 106
 /*
-   35 keywords
-   104 is the maximum key range
- */
+   36 keywords
+  104 is the maximum key range
+*/
 
 static int
-hash(register const char *str, register int len)
+hash (register const char *str, register int len)
 {
-    static const unsigned char hash_table[] =
+  static const unsigned char hash_table[] =
     {
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
-	106, 106, 106, 106, 106, 106, 106, 10, 0, 45,
-	0, 0, 0, 10, 106, 45, 106, 10, 106, 35,
-	5, 106, 5, 10, 0, 25, 55, 106, 35, 5,
-	106, 10, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+     106, 106, 106, 106, 106, 106, 106,  10,   0,  45,
+      20,   0,   0,  10, 106,  45, 106,  10, 106,  35,
+       5, 106,   5,  10,   0,  25,  55, 106,  35,   5,
+     106,  10, 106, 106, 106, 106, 106, 106,
     };
-    register int hval = len;
-
-    switch (hval) {
-    default:
-    case 3:
-	hval += hash_table[tolower((unsigned char) str[2])];
-    case 2:
-    case 1:
-	hval += hash_table[tolower((unsigned char) str[0])];
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+      case 3:
+        hval += hash_table[tolower((unsigned char) str[2])];
+      case 2:
+      case 1:
+        hval += hash_table[tolower((unsigned char) str[0])];
     }
-    return hval + hash_table[tolower((unsigned char) str[len - 1])];
+  return hval + hash_table[tolower((unsigned char) str[len - 1])];
 }
 
 static int
-case_strcmp(register const char *str, register const char *key)
+case_strcmp (register const char *str, register const char *key)
 {
-    int ans = 0;
+  int ans = 0;
 
-    while (!(ans = tolower(*str) - (int) *key) && *str)
-	str++, key++;
+  while (!(ans = tolower(*str) - (int) *key) && *str)
+    str++, key++;
 
-    return ans;
+  return ans;
 }
 
 const struct keyword *
-in_word_set(register const char *str, register int len)
+in_word_set (register const char *str, register int len)
 {
 
-    static const struct keyword wordlist[] =
+  static const struct keyword  wordlist[] =
     {
-	{"",},
-	{"",},
-	{"",},
-	{"for", DBV_Prehistory, tFOR},
-	{"",},
-	{"endif", DBV_Prehistory, tENDIF},
-	{"endfor", DBV_Prehistory, tENDFOR},
-	{"e_range", DBV_Prehistory, tERROR, E_RANGE},
-	{"endwhile", DBV_Prehistory, tENDWHILE},
-	{"e_recmove", DBV_Prehistory, tERROR, E_RECMOVE},
-	{"",},
-	{"e_none", DBV_Prehistory, tERROR, E_NONE},
-	{"",},
-	{"e_propnf", DBV_Prehistory, tERROR, E_PROPNF},
-	{"fork", DBV_Prehistory, tFORK},
-	{"break", DBV_BreakCont, tBREAK},
-	{"endtry", DBV_Exceptions, tENDTRY},
-	{"endfork", DBV_Prehistory, tENDFORK},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"finally", DBV_Exceptions, tFINALLY},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"e_quota", DBV_Prehistory, tERROR, E_QUOTA},
-	{"",},
-	{"else", DBV_Prehistory, tELSE},
-	{"",},
-	{"elseif", DBV_Prehistory, tELSEIF},
-	{"",},
-	{"any", DBV_Exceptions, tANY},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"e_div", DBV_Prehistory, tERROR, E_DIV},
-	{"e_args", DBV_Prehistory, tERROR, E_ARGS},
-	{"e_varnf", DBV_Prehistory, tERROR, E_VARNF},
-	{"e_verbnf", DBV_Prehistory, tERROR, E_VERBNF},
-	{"",},
-	{"",},
-	{"e_perm", DBV_Prehistory, tERROR, E_PERM},
-	{"if", DBV_Prehistory, tIF},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"in", DBV_Prehistory, tIN},
-	{"e_invind", DBV_Prehistory, tERROR, E_INVIND},
-	{"",},
-	{"while", DBV_Prehistory, tWHILE},
-	{"e_nacc", DBV_Prehistory, tERROR, E_NACC},
-	{"",},
-	{"continue", DBV_BreakCont, tCONTINUE},
-	{"",},
-	{"",},
-	{"e_type", DBV_Prehistory, tERROR, E_TYPE},
-	{"e_float", DBV_Float, tERROR, E_FLOAT},
-	{"e_invarg", DBV_Prehistory, tERROR, E_INVARG},
-	{"",},
-	{"",},
-	{"return", DBV_Prehistory, tRETURN},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"try", DBV_Exceptions, tTRY},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"e_maxrec", DBV_Prehistory, tERROR, E_MAXREC},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"",},
-	{"except", DBV_Exceptions, tEXCEPT},
+      {"",}, {"",}, {"",}, 
+      {"for", 		DBV_Prehistory, tFOR},
+      {"",}, {"",}, 
+      {"e_file", 		DBV_FileIO,	tERROR,	E_FILE},
+      {"e_range", 	DBV_Prehistory, tERROR,	E_RANGE},
+      {"",}, 
+      {"e_recmove", 	DBV_Prehistory, tERROR,	E_RECMOVE},
+      {"",}, 
+      {"e_none", 		DBV_Prehistory, tERROR,	E_NONE},
+      {"",}, 
+      {"e_propnf", 	DBV_Prehistory, tERROR,	E_PROPNF},
+      {"fork", 		DBV_Prehistory, tFORK},
+      {"break", 		DBV_BreakCont, tBREAK},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"finally", 	DBV_Exceptions, tFINALLY},
+      {"",}, {"",}, 
+      {"endif", 		DBV_Prehistory, tENDIF},
+      {"endfor", 		DBV_Prehistory, tENDFOR},
+      {"e_quota", 	DBV_Prehistory, tERROR,	E_QUOTA},
+      {"endwhile", 	DBV_Prehistory, tENDWHILE},
+      {"else", 		DBV_Prehistory, tELSE},
+      {"",}, 
+      {"elseif", 		DBV_Prehistory, tELSEIF},
+      {"",}, 
+      {"any", 		DBV_Exceptions, tANY},
+      {"",}, {"",}, 
+      {"endtry", 		DBV_Exceptions, tENDTRY},
+      {"endfork", 	DBV_Prehistory, tENDFORK},
+      {"",}, {"",}, {"",}, 
+      {"e_args", 		DBV_Prehistory, tERROR,	E_ARGS},
+      {"e_varnf", 	DBV_Prehistory, tERROR,	E_VARNF},
+      {"e_verbnf", 	DBV_Prehistory, tERROR,	E_VERBNF},
+      {"",}, {"",}, 
+      {"e_perm", 		DBV_Prehistory, tERROR,	E_PERM},
+      {"if", 		DBV_Prehistory, tIF},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"in", 		DBV_Prehistory, tIN},
+      {"",}, {"",}, 
+      {"while", 		DBV_Prehistory, tWHILE},
+      {"e_nacc", 		DBV_Prehistory, tERROR,	E_NACC},
+      {"",}, 
+      {"continue", 	DBV_BreakCont, tCONTINUE},
+      {"",}, 
+      {"e_div", 		DBV_Prehistory, tERROR,	E_DIV},
+      {"e_type", 		DBV_Prehistory, tERROR,	E_TYPE},
+      {"e_float", 	DBV_Float,	tERROR,	E_FLOAT},
+      {"e_invarg", 	DBV_Prehistory, tERROR,	E_INVARG},
+      {"",}, {"",}, 
+      {"return", 		DBV_Prehistory, tRETURN},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"e_invind", 	DBV_Prehistory, tERROR,	E_INVIND},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"try", 		DBV_Exceptions, tTRY},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"e_maxrec", 	DBV_Prehistory, tERROR,	E_MAXREC},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"except", 		DBV_Exceptions, tEXCEPT},
     };
 
-    if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) {
-	register int key = hash(str, len);
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = hash (str, len);
 
-	if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) {
-	    register const char *s = wordlist[key].name;
+      if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)
+        {
+          register const char *s = wordlist[key].name;
 
-	    if (*s == tolower(*str) && !case_strcmp(str + 1, s + 1))
-		return &wordlist[key];
-	}
+          if (*s == tolower(*str) && !case_strcmp (str + 1, s + 1))
+            return &wordlist[key];
+        }
     }
-    return 0;
+  return 0;
 }
 
 const struct keyword *
@@ -216,19 +170,12 @@ find_keyword(const char *word)
     return in_word_set(word, strlen(word));
 }
 
-char rcsid_keywords[] = "$Id: keywords.c,v 1.3 1998/12/14 13:17:55 nop Exp $";
+char rcsid_keywords[] = "$Id: keywords.gperf,v 1.1 1997/03/03 03:45:02 nop Exp $";
 
-/* 
- * $Log: keywords.c,v $
- * Revision 1.3  1998/12/14 13:17:55  nop
- * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
- *
- * Revision 1.2  1997/03/03 04:18:45  nop
- * GNU Indent normalization
- *
- * Revision 1.1.1.1  1997/03/03 03:45:00  nop
- * LambdaMOO 1.8.0p5
- *
+/* $Log: keywords.gperf,v $
+/* Revision 1.1  1997/03/03 03:45:02  nop
+/* Initial revision
+/*
  * Revision 2.2  1996/02/08  06:33:21  pavel
  * Added `break', `continue', and E_FLOAT.  Updated copyright notice for 1996.
  * Release 1.8.0beta1.
diff --git a/keywords.gperf b/keywords.gperf
index 6bf53ab..60e2d21 100644
--- a/keywords.gperf
+++ b/keywords.gperf
@@ -62,6 +62,7 @@ E_NACC,		DBV_Prehistory, tERROR,	E_NACC
 E_INVARG,	DBV_Prehistory, tERROR,	E_INVARG
 E_QUOTA,	DBV_Prehistory, tERROR,	E_QUOTA
 E_FLOAT,	DBV_Float,	tERROR,	E_FLOAT
+E_FILE,		DBV_FileIO,	tERROR,	E_FILE
 %%
 
 const struct keyword *
diff --git a/storage.h b/storage.h
index c071823..6e34c3c 100644
--- a/storage.h
+++ b/storage.h
@@ -35,6 +35,9 @@ typedef enum Memory_Type {
     M_REF_ENTRY, M_REF_TABLE, M_VC_ENTRY, M_VC_TABLE, M_STRING_PTRS,
     M_INTERN_POINTER, M_INTERN_ENTRY, M_INTERN_HUNK,
 
+    /* where no more specific type applies */
+    M_STRUCT,
+
     Sizeof_Memory_Type
 
 } Memory_Type;
diff --git a/structures.h b/structures.h
index f0cff94..66015d4 100644
--- a/structures.h
+++ b/structures.h
@@ -39,7 +39,8 @@ typedef int32 Objid;
  */
 enum error {
     E_NONE, E_TYPE, E_DIV, E_PERM, E_PROPNF, E_VERBNF, E_VARNF, E_INVIND,
-    E_RECMOVE, E_MAXREC, E_RANGE, E_ARGS, E_NACC, E_INVARG, E_QUOTA, E_FLOAT
+    E_RECMOVE, E_MAXREC, E_RANGE, E_ARGS, E_NACC, E_INVARG, E_QUOTA, E_FLOAT,
+    E_FILE
 };
 
 /* Do not reorder or otherwise modify this list, except to add new elements at
diff --git a/unparse.c b/unparse.c
index a58156a..ba3d4eb 100644
--- a/unparse.c
+++ b/unparse.c
@@ -71,6 +71,8 @@ unparse_error(enum error e)
 	return "Resource limit exceeded";
     case E_FLOAT:
 	return "Floating-point arithmetic error";
+    case E_FILE:
+	return "File error";
     }
 
     return "Unknown Error";
@@ -112,6 +114,8 @@ error_name(enum error e)
 	return "E_QUOTA";
     case E_FLOAT:
 	return "E_FLOAT";
+    case E_FILE:
+	return "E_FILE";
     }
 
     return "E_?";
diff --git a/version.h b/version.h
index 3dde0c8..d3da6be 100644
--- a/version.h
+++ b/version.h
@@ -45,6 +45,8 @@ typedef enum {
 				 * change exists solely to turn off special
 				 * bug handling in read_bi_func_data().
 				 */
+    DBV_FileIO,			/* Includes addition of the 'E_FILE' keyword.
+				 */
     Num_DB_Versions		/* Special: the current version is this - 1. */
 } DB_Version;
 
