Skip to content

Instantly share code, notes, and snippets.

@toddsundsted
Created August 5, 2011 02:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save toddsundsted/1126787 to your computer and use it in GitHub Desktop.
Save toddsundsted/1126787 to your computer and use it in GitHub Desktop.
FileIO v1.5p3 Patchfile
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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment