Created
August 5, 2011 02:08
-
-
Save toddsundsted/1126787 to your computer and use it in GitHub Desktop.
FileIO v1.5p3 Patchfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
+ | |
+ [email protected] | |
+ | |
+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