-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathfileio-1.5p3.patch
2533 lines (2508 loc) · 67.5 KB
/
fileio-1.5p3.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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
+
+
+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);