use swab(), since we do not provide a swab2() implementation.
[AROS-Contrib.git] / regina / files.c
blob48f0c68b11dcd5d1a36f196ba53b28880c038b23
1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * This module is a real pain, since file I/O is one of the features
22 * that varies most between different platforms. And what makes it
23 * even more of a pain, is the fact that it must be coordinated with
24 * the handling of the condition NOTREADY. Anyway, here are the
25 * decisions set up before (well ... during) the implementation that
26 * guide how this this thing is supposed to work.
28 * There are four kind of routines, structured in four levels:
30 * (1)---------+ (2)--------+
31 * | builtin | ----> | general | B C library
32 * | functions | A | routines | ----> Routines
33 * +-----------+ +----------+
34 * | |
35 * | |
36 * | V (3)--------+
37 * +----------------->+---> | Error |
38 * | routines |
39 * +----------+
41 * 1) Builtin functions, these has the "std_" prefix which is standard
42 * for all buildin functions. The task for these functions are to
43 * process parameters, call (2) which specializes on operations (like
44 * read, write, position etc), and return a decent answer back to its
45 * caller. There is one routine in this level for each of the
46 * functions in the library of built-in functions. Most of them are
47 * std_* functions, but there are a few others too.
49 * 2) These are general operations for reading, writing, positioning,
50 * etc. They may call the C library routines directly, or
51 * indirectly, through calls to (3). The interface (A) between (1)
52 * and (2) is based on the local structure fileboxptr and strengs.
53 * There are one function in this level for each of the basic
54 * operations needed to be performed on a file. Opening, closing,
55 * reading a line, writing a line, line positioning, reading chars,
56 * writing chars, positioning chars, counting lines, counting
57 * chars, etc. The interface (B) to the C library routines uses
58 * FILE* and char* for its operations.
60 * 3) General routines to perform 'trivial' tasks. In this level,
61 * things like retriving Rexx's file table entries are implemented,
62 * and all the errorhandling. These are called from both the two
63 * previous levels.
65 * There are three standard files, called "<stdin>", "<stdout>" and
66 * "<stderr>" (note that the "<" and ">" are part of the filename.)
67 * These are handles for the equivalent Unix standard files. This
68 * might cause problems if you actually do want a file calls that, or
69 * if one of these files is closed, and the more information is
70 * written to it (I can easily visulize Users trying to delete such a
71 * file :-)) So the standard files -- having set flag SURVIVOR -- will
72 * never be closed or reopened.
74 * Error_file is called by that routine which actually discovers the
75 * problem. If it is an CALL ON condition, it will set the FLAG_FAKE
76 * flag, which all other routines will check for.
80 * Bug in LCC complier wchar.h that incorrectly says it defines stat struct
81 * but doesn't
83 #if defined(__LCC__)
84 # include <sys/stat.h>
85 #endif
87 #include "rexx.h"
88 #include <errno.h>
89 #include <stdio.h>
90 #include <string.h>
91 #ifdef HAVE_ASSERT_H
92 # include <assert.h>
93 #endif
94 #ifdef HAVE_LIMITS_H
95 # include <limits.h>
96 #endif
97 #include <time.h>
98 #if defined(VMS)
99 # include <stat.h>
100 #elif defined(OS2)
101 # include <sys/stat.h>
102 # ifdef HAVE_UNISTD_H
103 # include <unistd.h>
104 # endif
105 #elif (defined(__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || defined(__LCC__)
106 # include <sys/stat.h> /* MH 10-06-96 */
107 # include <fcntl.h> /* MH 10-06-96 */
108 # ifdef HAVE_UNISTD_H
109 # include <unistd.h> /* MH 10-06-96 */
110 # endif
111 # if defined(_MSC_VER) && !defined(__WINS__)
112 # include <io.h>
113 # endif
114 #elif defined(WIN32) && defined(__IBMC__) /* LM 26-02-99 */
115 # include <io.h>
116 # include <sys/stat.h>
117 # include <fcntl.h>
118 #elif defined(MAC)
119 # include "mac.h"
120 #else
121 # include <sys/stat.h>
122 # ifdef HAVE_PWD_H
123 # include <pwd.h>
124 #endif
125 # ifdef HAVE_GRP_H
126 # include <grp.h>
127 # endif
128 # include <fcntl.h>
129 # ifdef HAVE_UNISTD_H
130 # include <unistd.h>
131 # endif
132 #endif
134 #ifdef HAVE_DIRECT_H
135 # include <direct.h>
136 #endif
138 #ifdef __EMX__
139 # include <io.h>
140 #endif
142 #ifdef WIN32
143 # ifdef _MSC_VER
144 # if _MSC_VER >= 1100
145 /* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
146 # pragma warning(disable: 4115 4201 4214 4514)
147 # endif
148 # endif
149 # include <windows.h>
150 # ifdef _MSC_VER
151 # if _MSC_VER >= 1100
152 # pragma warning(default: 4115 4201 4214)
153 # endif
154 # endif
155 # if defined(__WATCOMC__) || defined(__BORLANDC__)
156 # include <io.h>
157 # endif
158 #endif
160 #if (defined(_AMIGA) || defined(__AROS__)) && defined(ACCESS_READ)
161 # undef ACCESS_READ
162 #endif
163 #if (defined(_AMIGA) || defined(__AROS__)) && defined(ACCESS_WRITE)
164 # undef ACCESS_WRITE
165 #endif
167 #ifndef min
168 # define min(a,b) (((a) < (b)) ? (a) : (b))
169 #endif
172 * Stuff for FSTAT
174 #if defined(S_IFDIR) && !defined(S_ISDIR)
175 # define S_ISDIR(mode) (((mode) & S_IFDIR)==S_IFDIR)
176 #endif
177 #if defined(S_IFREG) && !defined(S_ISREG)
178 # define S_ISREG(mode) (((mode) & S_IFREG)==S_IFREG)
179 #endif
180 #if defined(S_IFCHR) && !defined(S_ISCHR)
181 # define S_ISCHR(mode) (((mode) & S_IFCHR)==S_IFCHR)
182 #endif
183 #if defined(S_IFBLK) && !defined(S_ISBLK)
184 # define S_ISBLK(mode) (((mode) & S_IFBLK)==S_IFBLK)
185 #endif
186 #if defined(S_IFLNK) && !defined(S_ISLNK)
187 # define S_ISLNK(mode) (((mode) & S_IFLNK)==S_IFLNK)
188 #endif
189 #if defined(S_IFFIFO) && !defined(S_ISFIFO)
190 # define S_ISFIFO(mode) (((mode) & S_IFFIFO)==S_IFFIFO)
191 #endif
192 #if defined(S_IFSOCK) && !defined(S_ISSOCK)
193 # define S_ISSOCK(mode) (((mode) & S_IFSOCK)==S_IFSOCK)
194 #endif
195 #if defined(S_IFNAM) && !defined(S_ISNAM)
196 # define S_ISNAM(mode) (((mode) & S_IFNAM)==S_IFNAM)
197 #endif
199 #if !defined(ACCESSPERMS)
200 # if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
201 # define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
202 # else
203 # if defined(S_IREAD) && defined(S_IWRITE) && defined(S_IEXEC)
204 # define ACCESSPERMS (S_IREAD|S_IWRITE|S_IEXEC)
205 # else
206 # define ACCESSPERMS (0xfff)
207 # endif
208 # endif
209 #endif
211 * The macrodefinition below defines the various modes in which a
212 * file may be opened. These modes are:
214 * READ - Open for readonly access. The current read position
215 * is set to the the start of the file. If the file does
216 * not exist, report an error.
218 * WRITE - Open for read and write. The current read position is
219 * set to the start of the file, while the current write
220 * position is set to EOF. If file does not exist, create
221 * it. If file does exist, use existing data.
223 * UPDATE - The combined operation of READ and WRITE, but if file
224 * does not exist, issue an error.
226 * APPEND - Open in APPEND mode, i.e. open for writeonly, position
227 * at the End-Of-File, and (if possible) open in a mode
228 * that disallows positioning. The file will be a transient
229 * file. If the file does not exist, create it.
231 * CREATE - Open for write, but if the file does exist, truncate
232 * it at the beginning after opening it.
234 #define ACCESS_NONE 0
235 #define ACCESS_READ 1
236 #define ACCESS_WRITE 2
237 #define ACCESS_UPDATE 3
238 #define ACCESS_APPEND 4
239 #define ACCESS_CREATE 5
240 #define ACCESS_STREAM_APPEND 6
241 #define ACCESS_STREAM_REPLACE 7
244 * These macros is used to set the value of the 'oper' in the filebox
245 * data structure. If last operation on a file was a read or a write,
246 * set 'oper' to OPER_READ or OPER_WRITE, respectively. If last
247 * operation was repositioning or flushing, use OPER_NONE. See
248 * description of 'oper' field in definition of 'filebox'.
250 #define OPER_NONE 0
251 #define OPER_READ 1
252 #define OPER_WRITE 2
255 * Flags, carrying information about files. The 'flag' field in the
256 * 'filebox' structure is set to values matching these defintions. The
257 * meaning of each of these flags is:
259 * PERSIST - Set if file is persistent, if unset, file is treated
260 * as a transient file.
261 * EOF - Currently not in use
262 * READ - File has been opened for read access.
263 * WRITE - File has been opened for write access.
264 * CREATE - Currently not in use
265 * ERROR - Set if the file is in error state. If operations are
266 * attempted performed on files in state error, the
267 * NOTREADY condition will in general be raised, and the
268 * operation will fail.
269 * SURVIVOR - Set for certain special files; the default streams, which
270 * is not really to be closed or reopened.
271 * FAKE - Meaningful only if ERROR is set. If FAKE is set, and
272 * an operation on the file is attempted, the operation is
273 * 'faked' (NOTREADY is not triggered, and the result returned
274 * for write operations does not report that the output was
275 * not written.
276 * WREOF - Current write position is at EOF. If line output is
277 * performed, there is no need to truncate the file.
278 * RDEOF - Current read position is at EOF. Reading EOF raises the
279 * NOTREADY condition, but does not put the file into error
280 * state.
281 * AFTER_RDEOF - Bit of a hack here. This flag is set after an attempt
282 * (Added by MH) is made to read a stream once the RDEOF flag is set.
283 * The reason for this is that all the "read" stream
284 * functions; LINEIN, LINES, CHARIN, etc set the RDEOF
285 * flag at the point that they determine a RDEOF has
286 * occurred. This is usually at the end of the function.
287 * Therefore a LINEIN that reads EOF sets RDEOF and a
288 * subsequent call to STREAM(stream,'S') will return
289 * NOTREADY. This to me is logical, but the behaviour
290 * of other interpreters is that the first call to
291 * STREAM(stream,'S') after reaching EOF should still return
292 * READY. Only when ANOTHER "read" stream function is
293 * called does STREAM(stream,'S') return NOTREADY.
294 * SWAPPED - This flag is set if the file is currently swapped out, that
295 * is, the file is closed in order to let another file use
296 * the system's file table sloth freed when the file was
297 * temporarily closed.
299 #define FLAG_PERSIST 0x0001
300 #define FLAG_EOF 0x0002
301 #define FLAG_READ 0x0004
302 #define FLAG_WRITE 0x0008
303 #define FLAG_CREATE 0x0010
304 #define FLAG_ERROR 0x0020
305 #define FLAG_SURVIVOR 0x0040
306 #define FLAG_FAKE 0x0080
307 #define FLAG_WREOF 0x0100
308 #define FLAG_RDEOF 0x0200
309 #define FLAG_SWAPPED 0x0400
310 #define FLAG_AFTER_RDEOF 0x0800
313 * So, what is the big difference between FAKE and ERROR. Well, when a
314 * file gets it ERROR flag set, it signalizes that the file is in
315 * error state, and that no fileoperations should be performed on it.
316 * The FAKE flag is only meaningful when the ERROR flag is set. If set
317 * the FAKE flag tells that file operations should be faked in order to
318 * give the user the impression that everything is OK, while if FAKE is
319 * not set, errors are returned.
321 * The clue is that if a statement contains several operations on one
322 * file, and the first operation bombs, CALL ON NOTREADY will not take
323 * effect before the next statement boundary at the same procedural
324 * level So, for the rest of the file operations until that statement
325 * has finished, the FAKE flag is set, and signalizes that OK result
326 * should be returned whenever positioning or write is performed, and
327 * that NOTREADY should not be raised again.
329 * The reason for the RDEOF flag is that reading beyond EOF is not really
330 * a capital crime, and a lot of programmers are likely to do that, and
331 * expect things to be OK after repositioning current read position to
332 * another part of the file. If a file is put into ERROR state, it has
333 * to be explicitly reset in order to do any useful to it afterwards.
334 * Therefore, if EOF is seen on input, RDEOF is set, and NOTREADY is
335 * raised, but the file is not put into ERROR state.
339 * The following macros defines symbolic names to the commands available
340 * in the Rexx built-in function STREAM(). The meaning of each of these
341 * commands are:
343 * READ - Opens the file with the corresponding mode. For a deeper
344 * WRITE description of each of these modes, see the defininition
345 * APPEND of the ACCESS_* macros. STREAM() is used to explicitly
346 * UPDATE open a file, while Rexx is totally happy with the
347 * CREATE traditional implicit opening, i.e. that the file is
348 * opened for the needed access at the time when it is
349 * first used. If the file to be opened is already open,
350 * it will first be closed, and then opened in the
351 * specified mode.
353 * CLOSE - Closes a file, works for any type of access. But if
354 * the file is a default stream, it will not be closed.
355 * Default streams should not be closed.
357 * FLUSH - Performs flushing on the file. Actually, I'm not so
358 * sure whether that is very interesting, since flushing
359 * is always performed after a write, anyway. Though, it
360 * might become an important function if the automatic
361 * flushing after write is removed (e.g. to improve speed).
363 * STATUS - Returns status information assiciated with the file as
364 * a human readable string. The information returned is the
365 * internal information that Rexx stores in the Rexx file
366 * table entry for that file. Use FSTAT to get information
367 * about the file from the operating system. See the
368 * function 'getrexxstatus()' for more information about
369 * the layout of the returned string.
371 * FSTAT - Returns status information associated with the file as
372 * a human readable string. The information returned is the
373 * information normally returned by the stat() system call
374 * under Unix (i.e. size, dates, access modes, etc). Use
375 * STATUS to get Rexx's information about the file. See
376 * the function 'getstatus()' for more information about
377 * the layout of the string returned.
379 * RESET - Resets the file after an error. Of course, this will
380 * only work for files which are 'resettable'. If the error
381 * is too serious, resetting will help little to fix the
382 * problem. E.g. writing beyond end-of-file can easily be
383 * fixed by RESET, trying to use a file which is named
384 * by an invalid syntax can not be correctly reset.
386 * READABLE - Checks that the file in question is available in the
387 * WRITABLE mode given, for the user that is executing the script.
388 * EXECUTABLE I.e. READABLE will return '1' for a file, if the file
389 * is readable for the user, else '0' is returned. Note
390 * that FSTAT returns the information about the accessmodes
391 * for a file, these returns the 'accessmode' which is
392 * relevant for a particular user. Also note that if your
393 * machine are using suid-bit (i.e. Unix), this function
394 * will check for the real uid, not the effective uid.
395 * Consequently, it may not give the wanted result for
396 * suid rexx scripts, see the Unix access() function. (And
397 * anyway, suid scripts are a _very_ bad idea under Unix,
398 * so this is probably not a problem ... :-)
400 #define COMMAND_NONE 0
401 #define COMMAND_READ 1
402 #define COMMAND_WRITE 2
403 #define COMMAND_APPEND 3
404 #define COMMAND_UPDATE 4
405 #define COMMAND_CREATE 5
406 #define COMMAND_CLOSE 6
407 #define COMMAND_FLUSH 7
408 #define COMMAND_STATUS 8
409 #define COMMAND_FSTAT 9
410 #define COMMAND_RESET 10
411 #define COMMAND_READABLE 11
412 #define COMMAND_WRITEABLE 12
413 #define COMMAND_EXECUTABLE 13
414 #define COMMAND_LIST 14
415 #define COMMAND_QUERY_DATETIME 15
416 #define COMMAND_QUERY_EXISTS 16
417 #define COMMAND_QUERY_HANDLE 17
418 #define COMMAND_QUERY_SEEK 18
419 #define COMMAND_QUERY_SIZE 19
420 #define COMMAND_QUERY_STREAMTYPE 20
421 #define COMMAND_QUERY_TIMESTAMP 21
422 #define COMMAND_QUERY_POSITION 23
423 #define COMMAND_QUERY 24
424 #define COMMAND_QUERY_POSITION_READ 25
425 #define COMMAND_QUERY_POSITION_WRITE 26
426 #define COMMAND_QUERY_POSITION_SYS 27
427 #define COMMAND_QUERY_POSITION_READ_CHAR 28
428 #define COMMAND_QUERY_POSITION_READ_LINE 29
429 #define COMMAND_QUERY_POSITION_WRITE_CHAR 30
430 #define COMMAND_QUERY_POSITION_WRITE_LINE 31
431 #define COMMAND_OPEN 32
432 #define COMMAND_OPEN_READ 33
433 #define COMMAND_OPEN_WRITE 34
434 #define COMMAND_OPEN_BOTH 35
435 #define COMMAND_OPEN_BOTH_APPEND 36
436 #define COMMAND_OPEN_BOTH_REPLACE 37
437 #define COMMAND_OPEN_WRITE_APPEND 38
438 #define COMMAND_OPEN_WRITE_REPLACE 39
439 #define COMMAND_SEEK 40
440 #define COMMAND_POSITION 41
442 #define STREAMTYPE_UNKNOWN 0
443 #define STREAMTYPE_PERSISTENT 1
444 #define STREAMTYPE_TRANSIENT 2
446 * Define TRUE_TRL_IO, if you want the I/O system to be even more like
447 * TRL. It will try to mimic the behaviour in TRL exactly. Note that if
448 * you _do_ define this, you might experience a degrade in runtime
449 * performance.
451 #define TRUE_TRL_IO
454 * There are two ways to report an error for file I/O operations. Either
455 * as an "error" or as a "warning". Both will raise the NOTREADY
456 * condition, but only ERROR will actually put the file into ERROR mode.
457 * Warnings are used for e.g. EOF while reading. Both are implemented
458 * by the same routine.
460 #define file_error(a,b,c) handle_file_error(TSD,a,b,c,1)
461 #define file_warning(a,b,c) handle_file_error(TSD,a,b,c,0)
464 * CASE_SENSITIVE_FILENAMES is used to determine if internal file
465 * pointers respect the case of files and treat "ABC" as a different
466 * file to "abc".
468 #ifdef UNIX
469 # define CASE_SENSITIVE_FILENAMES
470 #endif
472 * Regina truncates a file when repositioning by the use of a line
473 * count. That is, if the file has ten lines, and you use the BIF
474 * lineout(file,,4), it will be truncated after the fourth line.
475 * Truncating is not performed for character repositioning.
477 * If you don't want truncating after line repositioning, undefine
478 * the macro HAVE_FTRUNCATE in config.h. Also, if your system doesn't
479 * have ftruncate(), undefine HAVE_FTRUNCATE, and survive without the
480 * truncating.
482 * The function ftruncate() is a BSDism; if you have trouble finding
483 * it, try linking with -lbsd or -lucb or something like that. Since
484 * it is not a standard POSIX feature, some machines may generate
485 * warnings during compilation. Let's help these machines ...
487 #if defined(FIX_PROTOS) && defined(HAVE_FTRUNCATE)
488 # if defined(ultrix)
489 int ftruncate( int fd, int length ) ;
490 # endif
491 #endif
494 * Since development of Ultrix has ceased, and they never managed to
495 * fix a few things, we want to define a few things, just in order
496 * to kill a few warnings ...
498 #if defined(FIX_PROTOS) && defined(FIX_ALL_PROTOS) && defined(ultrix)
499 int fstat( int fd, struct stat *buf ) ;
500 int stat( char *path, struct stat *buf ) ;
501 #endif
505 * Here comes another 'sunshine-story' ... Since SunOS don't have
506 * a decent set of include-files in the standard version of the OS,
507 * their <stdio.h> don't define these macros. Instead, Sun seems to
508 * survive with the old custom of using the numberic values of these
509 * macros directly. If compiled with "SunKlugdes" defined, try to
510 * fix this.
512 * If you are using gcc on a Sun, you may want to run the program
513 * fixincludes that comes with gcc. It will fix this more permanently.
514 * At least one recent version of GCC for VMS doesn't have this
517 #if defined(SunKludges) || (defined(__GNUC__) && defined(VMS))
518 # define SEEK_SET 0
519 # define SEEK_CUR 1
520 # define SEEK_END 2
521 #endif
524 * Some machines don't defined these ... they should!
526 #if defined(VMS) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || (defined(WIN32) && defined(__BORLANDC__)) || defined(__LCC__)
527 # define F_OK 0
528 # define X_OK 1
529 # define W_OK 2
530 # define R_OK 4
531 #endif
534 * Here is the datastructure in which to store the information about
535 * open files. The storage format of the file table is discussed
536 * elsewhere. The fields used to handle storing are 'next' and 'prev'
537 * which is used to implement a double linked list of files having
538 * the same hashfunc; and 'newer' and 'older' which are used to maintain
539 * a large double linked list of all files in order of the most
540 * recently used file.
542 * The other fields are:
544 * fileptr - Pointer to the filehandle use by the system when
545 * accessing the file through the normal I/O calls.
546 * If this pointer is NULL, it means that the file is
547 * not currently open.
548 * oper - Holds the value that tells whether the most recent
549 * operation on this file was a read or a write. This has
550 * importance for flushing, since a read can't imediately
551 * follow a write (or vice versa) without a flush (or
552 * a repositioning) inbetween. Takes the values OPER_READ,
553 * OPER_WRITE or OPER_NONE (signalizes that most recent
554 * operation can be followed by either read or write).
555 * flag - Bitfield that holds information about the file. The
556 * significance of the various fields are described by
557 * the FLAG_* macros.
558 * error - Most recently 'errno' code for this file. It could have
559 * been stored into 'errmsg' instead, but that would require
560 * copying of data which might not be used. If undefined,
561 * it will have the value 0.
562 * readpos - The current read position in the file, as a character
563 * position. Note that this is in 'C-syntax', i.e. the
564 * first character in the file is numbered "0". A value of
565 * -1 means that the value is unknown or undefined.
566 * readline - The line number of the current read position, which must
567 * be positive if define. A value of zero means that the
568 * line number is undefined or unknown. If current read
569 * position is at an EOL, 'readline' refers to the line
570 * preceding the EOL.
571 * writepos - Similar to 'readpos' but for current write position.
572 * writeline - Similar to 'readline' but for current write position.
573 * filename0 - Pointer to string containing the filename assiciated
574 * with this file. This string is garanteed to have an
575 * ASCII NUL following the last character, so it can be
576 * used directly in file operations. This field *must*
577 * be defined.
578 * errmsg - Error message associated with the file. Some errors are
579 * not trapped during call to system routines, and these
580 * does not have an error message defined by the opsys.
581 * E.g. when positioning current read position after EOF.
582 * This field stores errormessages for these situations.
583 * If undefined, it will be a NULL pointer.
585 * Both errmsg and error can not be defined simultaneously.
588 typedef struct fileboxtype *fileboxptr ;
589 typedef const struct fileboxtype *cfileboxptr ;
590 typedef struct fileboxtype {
591 FILE *fileptr ;
592 unsigned char oper ;
593 size_t readpos, writepos, thispos ;
594 int flag, error, readline, writeline, linesleft ;
595 fileboxptr prev, next ; /* within a filehash entry */
596 fileboxptr newer, older ;
597 streng *filename0 ;
598 streng *errmsg ;
599 } filebox ;
601 /* POSIX denies read and write operations on streams without intermediate
602 * fflush, fseek, fsetpos or rewind (list from EMX). We use the following
603 * macros to switch directly before an I/O operation. "Useful" fseeks should
604 * be error checked. This is not necessary here since the following operation
605 * will fault in case of an error.
608 #define SWITCH_OPER_READ(fptr) {if (fptr->oper==OPER_WRITE) \
609 fseek(fptr->fileptr,0l,SEEK_CUR); \
610 fptr->oper=OPER_READ;}
611 #define SWITCH_OPER_WRITE(fptr) {if (fptr->oper==OPER_READ) \
612 fseek(fptr->fileptr,0l,SEEK_CUR); \
613 fptr->oper=OPER_WRITE;}
615 typedef struct
616 { /* fil_tsd: static variables of this module (thread-safe) */
618 * The following two pointers are pointers into the doble linked list
619 * of all files in the file table. They points to the most recently
620 * used file, and the least recently used open file. Note that the latter
621 * of these are _not_ the same as the last file in the list. If the
622 * Rexx' file table contains more files than the system's file table
623 * can contain, 'lrufile' will point to the last open file in the double
624 * linked list. Files further out in the list are 'swapped' out.
626 fileboxptr mrufile;
628 fileboxptr stdio_ptr[6];
629 void * rdarea;
630 fileboxptr filehash[131];
631 int rol_size ; /* readoneline() */
632 char * rol_string ; /* readoneline() */
633 int got_eof ; /* readkbdline() */
634 } fil_tsd_t; /* thread-specific but only needed by this module. see
635 * init_filetable
638 * Structure to define stream types; names and whether persistent,transient or unknown
640 typedef struct
642 int streamtype;
643 char *streamname;
644 } stream_type_t;
646 #define STREAMTYPE_DIRECTORY 1
647 #define STREAMTYPE_CHARACTERSPECIAL 2
648 #define STREAMTYPE_BLOCKSPECIAL 3
649 #define STREAMTYPE_REGULARFILE 4
650 #define STREAMTYPE_FIFO 5
651 #define STREAMTYPE_SYMBOLICLINK 6
652 #define STREAMTYPE_SOCKET 7
653 #define STREAMTYPE_SPECIALNAME 8
655 static const stream_type_t stream_types[] =
657 { STREAMTYPE_UNKNOWN , "" },
658 { STREAMTYPE_UNKNOWN , " Directory" },
659 { STREAMTYPE_PERSISTENT," CharacterSpecial" },
660 { STREAMTYPE_PERSISTENT," BlockSpecial" },
661 { STREAMTYPE_PERSISTENT," RegularFile" },
662 { STREAMTYPE_UNKNOWN , " FIFO" },
663 { STREAMTYPE_UNKNOWN , " SymbolicLink" },
664 { STREAMTYPE_UNKNOWN , " Socket" },
665 { STREAMTYPE_UNKNOWN , " SpecialName" },
668 static int positioncharfile( tsd_t *TSD, const char *bif, int argno, fileboxptr fileptr, int oper, long where, int from );
669 static int positionfile( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, int lineno, int from );
670 static void handle_file_error( tsd_t *TSD, fileboxptr ptr, int rc, const char *errmsg, int level) ;
671 static int flush_output( tsd_t *TSD, fileboxptr ptr );
674 * Based on the st_mode filed returned from stat(), determine the Regina "stream type".
675 * The returned value is an index into stream_types array, which allows the caller to
676 * determine if the stream is persistent, transient or unknown, and also allows the
677 * user to look up the stream type name.
678 * Added to resolve 802114.
680 static int determine_stream_type( int mode )
682 #ifdef S_ISDIR
683 if ( S_ISDIR(mode) )
684 return STREAMTYPE_DIRECTORY;
685 #endif
686 #ifdef S_ISCHR
687 if ( S_ISCHR(mode) )
688 return STREAMTYPE_CHARACTERSPECIAL;
689 #endif
690 #ifdef S_ISBLK
691 if ( S_ISBLK(mode) )
692 return STREAMTYPE_BLOCKSPECIAL;
693 #endif
694 #ifdef S_ISREG
695 if ( S_ISREG(mode) )
696 return STREAMTYPE_REGULARFILE;
697 #endif
698 #ifdef S_ISFIFO
699 if ( S_ISFIFO(mode) )
700 return STREAMTYPE_FIFO;
701 #endif
702 #ifdef S_ISLNK
703 if ( S_ISLNK(mode) )
704 return STREAMTYPE_SYMBOLICLINK;
705 #endif
706 #ifdef S_ISSOCK
707 if ( S_ISSOCK(mode) )
708 return STREAMTYPE_SOCKET;
709 #endif
710 #ifdef S_ISNAM
711 if ( S_ISNAM(mode) )
712 return STREAMTYPE_SPECIALNAME;
713 #endif
714 return STREAMTYPE_UNKNOWN;
718 * Marks all entries in the filetable. Used only by the memory
719 * management. Does not really change anything, so you can in general
720 * forget this one. This routine is called from memory.c in order to
721 * mark all statically defined data in this file.
723 #ifdef TRACEMEM
724 void mark_filetable( const tsd_t *TSD )
726 fileboxptr ptr=NULL ;
727 fil_tsd_t *ft;
729 ft = (fil_tsd_t *)TSD->fil_tsd;
730 for (ptr=ft->mrufile; ptr; ptr=ptr->older)
732 markmemory( ptr, TRC_FILEPTR ) ;
733 markmemory( ptr->filename0, TRC_FILEPTR ) ;
734 if (ptr->errmsg)
735 markmemory( ptr->errmsg, TRC_FILEPTR ) ;
738 if (ft->rdarea)
739 markmemory( ft->rdarea, TRC_FILEPTR ) ;
742 #endif /* TRACEMEM */
744 #if defined(WIN32) && defined(_MSC_VER)
746 * This is a replacement fo the BSD ftruncate() function.
747 * The code in this function was written by Les Moull.
750 int ftruncate( int fd, long pos )
752 HANDLE h = (HANDLE)_get_osfhandle( fd ) ;
754 if (SetFilePointer( h, pos, NULL, FILE_BEGIN) == 0xFFFFFFFF)
755 return -1;
757 if ( !SetEndOfFile( h ) )
758 return -1;
760 return 0;
762 #endif
764 #if defined(__WATCOMC__) && defined(__QNX__)
765 # define ftruncate( fd, pos ) ltrunc( fd, pos, SEEK_SET )
766 #endif
769 * This command maps the string 'cmd' into a number which is to be
770 * interpreted according to the settings of the COMMAND_ macros.
771 * The input strings must be one of the valid command, or else the
772 * COMMAND_NONE value is returned.
774 * Well, this routine should really have been implemented differently,
775 * since sequential searching through a list of strings is not very
776 * efficient. But still, it is not so many entries in the list, and
777 * this function is not going to be called often, so I suppose it
778 * doesn't matter too much. Ideallistic, it should be rewritten to
779 * a binary search.
782 static char get_command( streng *cmd )
784 Str_upper(cmd);
786 if (cmd->len==4 && !memcmp(cmd->value, "READ", 4))
787 return COMMAND_READ ;
788 if (cmd->len==5 && !memcmp(cmd->value, "WRITE", 5))
789 return COMMAND_WRITE ;
790 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
791 return COMMAND_APPEND ;
792 if (cmd->len==6 && !memcmp(cmd->value, "UPDATE", 6))
793 return COMMAND_UPDATE ;
794 if (cmd->len==6 && !memcmp(cmd->value, "CREATE", 6))
795 return COMMAND_CREATE ;
796 if (cmd->len==5 && !memcmp(cmd->value, "CLOSE", 5))
797 return COMMAND_CLOSE ;
798 if (cmd->len==5 && !memcmp(cmd->value, "FLUSH", 5))
799 return COMMAND_FLUSH ;
800 if (cmd->len==6 && !memcmp(cmd->value, "STATUS", 6))
801 return COMMAND_STATUS ;
802 if (cmd->len==5 && !memcmp(cmd->value, "FSTAT", 5))
803 return COMMAND_FSTAT ;
804 if (cmd->len==5 && !memcmp(cmd->value, "RESET", 5))
805 return COMMAND_RESET ;
806 if (cmd->len==8 && !memcmp(cmd->value, "READABLE", 8))
807 return COMMAND_READABLE ;
808 if (cmd->len==8 && !memcmp(cmd->value, "WRITABLE", 8))
809 return COMMAND_WRITEABLE ;
810 if (cmd->len==10 && !memcmp(cmd->value, "EXECUTABLE", 10))
811 return COMMAND_EXECUTABLE ;
812 if (cmd->len==4 && !memcmp(cmd->value, "LIST", 4))
813 return COMMAND_LIST ;
814 if (cmd->len>=4 && !memcmp(cmd->value, "OPEN", 4))
815 return COMMAND_OPEN ;
816 if (cmd->len>=5 && !memcmp(cmd->value, "QUERY", 5))
817 return COMMAND_QUERY ;
818 if (cmd->len>=4 && !memcmp(cmd->value, "SEEK", 4))
819 return COMMAND_SEEK ;
820 if (cmd->len>=8 && !memcmp(cmd->value, "POSITION", 8))
821 return COMMAND_POSITION ;
822 return COMMAND_NONE ;
825 static char get_querycommand( const streng *cmd )
827 if (cmd->len==8 && !memcmp(cmd->value, "DATETIME", 8))
828 return COMMAND_QUERY_DATETIME ;
829 if (cmd->len==6 && !memcmp(cmd->value, "EXISTS", 6))
830 return COMMAND_QUERY_EXISTS ;
831 if (cmd->len==6 && !memcmp(cmd->value, "HANDLE", 6))
832 return COMMAND_QUERY_HANDLE ;
833 if (cmd->len>=4 && !memcmp(cmd->value, "SEEK", 4))
834 return COMMAND_QUERY_SEEK ;
835 if (cmd->len>=8 && !memcmp(cmd->value, "POSITION", 8))
836 return COMMAND_QUERY_POSITION ;
837 if (cmd->len==4 && !memcmp(cmd->value, "SIZE", 4))
838 return COMMAND_QUERY_SIZE ;
839 if (cmd->len==10 && !memcmp(cmd->value, "STREAMTYPE", 10))
840 return COMMAND_QUERY_STREAMTYPE ;
841 if (cmd->len==9 && !memcmp(cmd->value, "TIMESTAMP", 9))
842 return COMMAND_QUERY_TIMESTAMP ;
843 return COMMAND_NONE ;
846 static char get_querypositioncommand( const streng *cmd )
848 if (cmd->len>=4 && !memcmp(cmd->value, "READ", 4))
849 return COMMAND_QUERY_POSITION_READ ;
850 if (cmd->len>=5 && !memcmp(cmd->value, "WRITE", 5))
851 return COMMAND_QUERY_POSITION_WRITE ;
852 if (cmd->len==3 && !memcmp(cmd->value, "SYS", 3))
853 return COMMAND_QUERY_POSITION_SYS ;
854 return COMMAND_NONE ;
857 static char get_querypositionreadcommand( const streng *cmd )
859 if (cmd->len==4 && !memcmp(cmd->value, "CHAR", 4))
860 return COMMAND_QUERY_POSITION_READ_CHAR ;
861 if (cmd->len==4 && !memcmp(cmd->value, "LINE", 4))
862 return COMMAND_QUERY_POSITION_READ_LINE ;
863 if (cmd->len==0)
864 return COMMAND_QUERY_POSITION_READ_CHAR ;
865 return COMMAND_NONE ;
868 static char get_querypositionwritecommand( const streng *cmd )
870 if (cmd->len==4 && !memcmp(cmd->value, "CHAR", 4))
871 return COMMAND_QUERY_POSITION_WRITE_CHAR ;
872 if (cmd->len==4 && !memcmp(cmd->value, "LINE", 4))
873 return COMMAND_QUERY_POSITION_WRITE_LINE ;
874 if (cmd->len==0)
875 return COMMAND_QUERY_POSITION_WRITE_CHAR ;
876 return COMMAND_NONE ;
879 static char get_opencommand( const streng *cmd )
881 if (cmd->len>=4 && !memcmp(cmd->value, "BOTH", 4))
882 return COMMAND_OPEN_BOTH ;
883 if (cmd->len==4 && !memcmp(cmd->value, "READ", 4))
884 return COMMAND_OPEN_READ ;
885 if (cmd->len>=5 && !memcmp(cmd->value, "WRITE", 5))
886 return COMMAND_OPEN_WRITE ;
887 if (cmd->len==0)
888 return COMMAND_OPEN_BOTH ;
889 return COMMAND_NONE ;
892 static char get_opencommandboth( const streng *cmd )
894 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
895 return COMMAND_OPEN_BOTH_APPEND ;
896 if (cmd->len==7 && !memcmp(cmd->value, "REPLACE", 7))
897 return COMMAND_OPEN_BOTH_REPLACE ;
898 if (cmd->len==0)
899 return COMMAND_OPEN_BOTH ;
900 return COMMAND_NONE ;
903 static char get_opencommandwrite( const streng *cmd )
905 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
906 return COMMAND_OPEN_WRITE_APPEND ;
907 if (cmd->len==7 && !memcmp(cmd->value, "REPLACE", 7))
908 return COMMAND_OPEN_WRITE_REPLACE ;
909 if (cmd->len==0)
910 return COMMAND_OPEN_WRITE ;
911 return COMMAND_NONE ;
915 /* ==================================================================== */
916 /* level 3 routines */
919 * Returns the fileboxptr of a file, if is has already been opened.
920 * If it does not exist in Rexx's file table, a NULL pointer is
921 * returned in stead. It is easy to change the datastruction in
922 * which the file table is stored.
924 * If using VMS, or another opsys that has a caseinsensitive file
925 * system, maybe it should disregard the case of the filename. In
926 * general, maybe it should 'normalize' the file name before storing
927 * it in the file table (do we sence an upcoming namei() :-)
930 #define FILEHASH_SIZE (sizeof(((fil_tsd_t*)0)->filehash) / \
931 sizeof(((fil_tsd_t*)0)->filehash[0]))
933 #ifdef CASE_SENSITIVE_FILENAMES
934 #define filehashvalue(strng) (hashvalue(strng->value, strng->len) % FILEHASH_SIZE)
935 #else
936 #define filehashvalue(strng) (hashvalue_ic(strng->value, strng->len) % FILEHASH_SIZE)
937 #endif
939 static void removefileptr( const tsd_t *TSD, cfileboxptr ptr )
941 fil_tsd_t *ft;
943 ft = (fil_tsd_t *)TSD->fil_tsd;
945 if (ft->mrufile==ptr)
946 ft->mrufile = ptr->older ;
948 if (ptr->older)
949 ptr->older->newer = ptr->newer ;
951 if (ptr->newer)
952 ptr->newer->older = ptr->older ;
954 if (ptr->next)
955 ptr->next->prev = ptr->prev ;
957 if (ptr->prev)
958 ptr->prev->next = ptr->next ;
959 else
960 ft->filehash[filehashvalue(ptr->filename0)] = ptr->next ;
963 /* enterfileptr initializes a fileboxptr. It must be allocated and the
964 * following fields must already been set:
965 * errmsg, error, fileptr, flag, filename0
967 static void enterfileptr( const tsd_t *TSD, fileboxptr ptr )
969 int hashval=0 ;
970 fil_tsd_t *ft;
972 ft = (fil_tsd_t *)TSD->fil_tsd;
975 * First, get the magic number for this file. Note that when we're
976 * doing hashing like this, we *may* get trouble on machines that
977 * don't differ between upper and lower case letters in filenames.
979 hashval = filehashvalue(ptr->filename0) ;
981 * Then, link it into the list of values having the same hashvalue
983 ptr->next = ft->filehash[hashval] ;
984 if (ptr->next)
985 ptr->next->prev = ptr ;
986 ft->filehash[hashval] = ptr ;
987 ptr->prev = NULL ;
990 * Then, link it into the 'global' list of files, sorted by how
991 * recently they have been used.
993 ptr->older = ft->mrufile ;
994 if (ptr->older)
995 ptr->older->newer = ptr ;
996 ptr->newer = NULL ;
997 ft->mrufile = ptr ;
999 ptr->readline = 0 ;
1000 ptr->linesleft = 0 ;
1001 ptr->writeline = 0 ;
1002 ptr->thispos = (size_t) EOF ;
1003 ptr->readpos = (size_t) EOF ;
1004 ptr->writepos = (size_t) EOF ;
1005 ptr->oper = OPER_NONE;
1008 /* swapout_file swaps out one closeable file. The state persists. Use
1009 * swapin_file to reuse it.
1010 * The given fileboxptr MUST NOT be swapped out. It indicates a file which
1011 * should be swapped in after this operation. dont_swap may be NULL.
1013 static void swapout_file( tsd_t *TSD, fileboxptr dont_swap )
1015 fil_tsd_t *ft;
1016 fileboxptr start, run, found;
1018 ft = (fil_tsd_t *)TSD->fil_tsd;
1020 * Too many open files simultaneously, we have to close one down
1021 * in order to free one file descriptor, but only if there actually
1022 * are some files that can be closed down.
1024 found = NULL;
1026 if ( ( start = dont_swap ) == NULL ) /* any start point is better than */
1027 start = ft->mrufile ; /* mru head. We need the opposite */
1029 /* first try finding an older file */
1030 for ( run = start ; run ; run = run->older )
1032 if ( ( (run->flag & ( FLAG_SURVIVOR | FLAG_SWAPPED ) ) == 0 ) &&
1033 ( run->fileptr != NULL ) &&
1034 ( run != dont_swap ) )
1035 found = run ; /* continue looking for an older file */
1038 /* if !found, try finding a more recent swapable file */
1039 if ( found == NULL )
1041 for ( run = start ; run ; run = run->newer )
1043 if ( ( (run->flag & ( FLAG_SURVIVOR | FLAG_SWAPPED ) ) == 0 ) &&
1044 ( run->fileptr != NULL ) &&
1045 ( run != dont_swap ) )
1047 found = run ; /* least newer swapable file */
1048 break;
1052 if ( found == NULL )
1053 exiterror( ERR_SYSTEM_FAILURE, 0 ) ;
1055 flush_output( TSD, found );
1059 /* swapout_all swaps out all closeable files. The states persist. Use
1060 * swapin_file to reuse them.
1061 * This function is useful when exiting the external's interface.
1063 static void swapout_all( tsd_t *TSD )
1065 fil_tsd_t *ft;
1066 fileboxptr run;
1068 ft = (fil_tsd_t *)TSD->fil_tsd;
1070 for ( run = ft->mrufile ; run ; run = run->older )
1072 flush_output( TSD, run );
1076 #ifdef VMS
1077 static const char *acc_mode[] = { "r", "r+", "a" } ;
1078 #else
1079 static const char *acc_mode[] = { "rb", "r+b", "ab" } ;
1080 #endif
1082 #define ACCMODE_READ 0
1083 #define ACCMODE_RDWRT 1
1084 #define ACCMODE_WRITE 2
1085 #define ACCMODE_NONE 3
1087 static void swapin_file( tsd_t *TSD, fileboxptr ptr )
1089 int faccess=0, itmp=0 ;
1092 * First, just try to reopen the file, we _might_ have a vacant
1093 * entry in the system file table, so, use that.
1095 itmp = (ptr->flag & (FLAG_READ | FLAG_WRITE)) ;
1096 if (itmp==(FLAG_READ | FLAG_WRITE))
1097 faccess = ACCMODE_RDWRT ;
1098 else if (itmp==(FLAG_READ))
1099 faccess = ACCMODE_READ ;
1100 else if (itmp==(FLAG_WRITE))
1101 faccess = ACCMODE_WRITE ;
1102 else
1103 faccess = ACCMODE_NONE ;
1105 if (faccess == ACCMODE_NONE)
1106 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
1108 tryagain:
1109 #ifdef SGIkludges
1110 errno = EMFILE ;
1111 #else
1112 errno = 0 ;
1113 #endif
1114 ptr->fileptr = fopen( ptr->filename0->value, acc_mode[faccess] ) ;
1115 if ((!ptr->fileptr) && (errno == EMFILE))
1117 swapout_file( TSD, ptr ) ;
1118 goto tryagain ;
1121 ptr->flag &= ~(FLAG_SWAPPED) ;
1122 if (ptr->fileptr==NULL)
1123 file_error( ptr, errno, NULL ) ;
1124 else
1126 if ( ptr->thispos == (size_t) EOF )
1127 fseek( ptr->fileptr, 0, SEEK_SET ) ;
1128 else
1129 fseek( ptr->fileptr, ptr->thispos, SEEK_SET ) ;
1131 * If the swapped-in file was already after EOF; ie
1132 * ptr->flag contained FLAG_RDEOF, then force eof()
1133 * to return true.
1135 if ( ptr->flag & FLAG_RDEOF )
1137 fseek( ptr->fileptr, 0, SEEK_END );
1138 fgetc( ptr->fileptr );
1144 * Compares two filesnames, either with ignoring or respecting the letter case.
1146 int filename_cmp( const streng *name1, const streng *name2 )
1148 #ifdef CASE_SENSITIVE_FILENAMES
1149 return Str_cmp( name1, name2 );
1150 #else
1151 return Str_ccmp( name1, name2 );
1152 #endif
1155 static fileboxptr getfileptr( tsd_t *TSD, const streng *name )
1157 int hashval ;
1158 fileboxptr ptr ;
1159 fil_tsd_t *ft;
1161 ft = (fil_tsd_t *)TSD->fil_tsd;
1163 hashval = filehashvalue( name ) ;
1165 * First, try to find the correct file in this slot of the
1166 * hash table. If one is found, ptr points to it, else, ptr is set
1167 * to NULL
1169 for (ptr=ft->filehash[hashval];ptr;ptr=ptr->next)
1171 if ( !filename_cmp( name, ptr->filename0 ) )
1172 break ;
1175 * In order not to create any problems later, just return with NULL
1176 * (signifying that no file was found) if that is the case. Then we may
1177 * able to assume that ptr _is_ set later.
1179 if (!ptr)
1180 return NULL ;
1183 * Now, put the file in front of the list of files stored by how
1184 * recently they were used. We assume that any access to a file is
1185 * equivalent to the file being used.
1187 if ( ptr != ft->mrufile )
1189 if ( ptr->newer )
1191 ptr->newer->older = ptr->older ;
1193 if ( ptr->older )
1195 ptr->older->newer = ptr->newer ;
1198 ptr->older = ft->mrufile ;
1199 ptr->newer = NULL ;
1200 ft->mrufile->newer = ptr ;
1201 ft->mrufile = ptr ;
1203 if ( ptr != ft->filehash[ hashval ] )
1205 /* Why the hell do we use ft->mrufile? Is it useful for anything?
1206 * The following code speeds up getfileptr much more.
1207 * If the current file pointer (ptr) is not the first file pointer
1208 * in the list for this hash value; reposition it so that it is
1209 * at the front of the list, and move the current file pointer
1210 * that is at the front of the list after ptr.
1211 * Fixes bug 594553
1213 if ( ptr->next )
1214 ptr->next->prev = ptr->prev ;
1215 if ( ptr->prev )
1216 ptr->prev->next = ptr->next ;
1218 ptr->prev = NULL;
1219 ptr->next = ft->filehash[ hashval ];
1220 ft->filehash[ hashval ]->prev = ptr;
1221 ft->filehash[ hashval ] = ptr ;
1225 * If this file has been swapped out, we have to reopen it, so we can
1226 * continue to access it.
1228 if (ptr->flag & FLAG_SWAPPED)
1229 swapin_file( TSD, ptr ) ;
1231 return ptr ;
1235 static void flush_input( cfileboxptr dummy )
1237 dummy = dummy; /* keep compiler happy */
1238 return ;
1243 * flush_output does close the file. The purpose of this function is to free
1244 * up space for opening another file while maintaining all state information about
1245 * this file if/when it is needed again.
1246 * Returns -1 in case of an error, 0 on success.
1248 static int flush_output( tsd_t *TSD, fileboxptr ptr )
1250 int h;
1252 errno = 0;
1254 if ( ptr->fileptr == NULL || ptr->flag & FLAG_SWAPPED )
1255 return 0;
1257 if ( ptr->flag & FLAG_SURVIVOR )
1259 if ( ptr->flag & FLAG_WRITE )
1261 if ( fflush( ptr->fileptr ) != 0 )
1263 file_error( ptr, errno, NULL );
1264 return -1;
1267 return 0;
1270 if ( fflush( ptr->fileptr ) != 0 )
1272 h = errno;
1273 fclose( ptr->fileptr );
1274 ptr->fileptr = NULL;
1275 ptr->flag |= FLAG_SWAPPED;
1276 file_error( ptr, h, NULL );
1277 return -1;
1279 if ( fclose( ptr->fileptr ) == EOF )
1281 h = errno;
1282 ptr->fileptr = NULL;
1283 ptr->flag |= FLAG_SWAPPED;
1284 file_error( ptr, h, NULL );
1285 return -1;
1288 ptr->fileptr = NULL;
1289 ptr->flag |= FLAG_SWAPPED;
1290 return 0;
1294 * Sets up the internal filetable for REXX, and initializes it with
1295 * the three standard files under Unix, stderr, stdout og and stdin.
1296 * Should only be called once, from the main routine. We should also
1297 * add code to register the routine for marking memory from this
1298 * routine.
1300 * As a shortcut to access these three default files, there is a
1301 * variable 'stdio_ptr' which contains pointers to them. This allows
1302 * for quick access to the default streams.
1303 * The function returns 1 on success, 0 if memory is short.
1305 * IMPORTANT!
1306 * The entry for stdin must be the same as the following #define for
1307 * DEFAULT_STDIN_INDEX below. Never changes it.
1308 * This assumption is used in readkbdline().
1310 #define DEFAULT_STDIN_INDEX 0
1311 #define DEFAULT_STDOUT_INDEX 1
1312 #define DEFAULT_STDERR_INDEX 2
1313 int init_filetable( tsd_t *TSD )
1315 int i=0 ;
1316 fil_tsd_t *ft;
1318 if (TSD->fil_tsd != NULL)
1319 return(1);
1321 if ( ( TSD->fil_tsd = MallocTSD( sizeof(fil_tsd_t) ) ) == NULL )
1322 return(0);
1323 ft = (fil_tsd_t *)TSD->fil_tsd;
1324 memset( ft, 0, sizeof(fil_tsd_t) );
1326 for ( i = 0; i < 6; i++ )
1328 ft->stdio_ptr[i] = (fileboxptr)MallocTSD( sizeof( filebox )) ;
1329 ft->stdio_ptr[i]->errmsg = NULL ;
1330 ft->stdio_ptr[i]->error = 0 ;
1333 ft->stdio_ptr[0]->fileptr = ft->stdio_ptr[3]->fileptr = stdin ;
1334 ft->stdio_ptr[1]->fileptr = ft->stdio_ptr[4]->fileptr = stdout ;
1335 ft->stdio_ptr[2]->fileptr = ft->stdio_ptr[5]->fileptr = stderr ;
1337 ft->stdio_ptr[0]->flag = ft->stdio_ptr[3]->flag = ( FLAG_SURVIVOR + FLAG_READ ) ;
1338 ft->stdio_ptr[1]->flag = ft->stdio_ptr[4]->flag = ( FLAG_SURVIVOR + FLAG_WRITE ) ;
1339 ft->stdio_ptr[2]->flag = ft->stdio_ptr[5]->flag = ( FLAG_SURVIVOR + FLAG_WRITE ) ;
1341 ft->stdio_ptr[0]->filename0 = Str_crestrTSD( "<stdin>" ) ;
1342 ft->stdio_ptr[1]->filename0 = Str_crestrTSD( "<stdout>" ) ;
1343 ft->stdio_ptr[2]->filename0 = Str_crestrTSD( "<stderr>" ) ;
1344 ft->stdio_ptr[3]->filename0 = Str_crestrTSD( "stdin" ) ;
1345 ft->stdio_ptr[4]->filename0 = Str_crestrTSD( "stdout" ) ;
1346 ft->stdio_ptr[5]->filename0 = Str_crestrTSD( "stderr" ) ;
1348 for (i=0; i<6; i++)
1349 enterfileptr( TSD, ft->stdio_ptr[i] ) ;
1351 return(1);
1354 void purge_filetable( tsd_t *TSD )
1356 fileboxptr ptr1, ptr2, save_ptr1, save_ptr2 ;
1357 int i;
1358 fil_tsd_t *ft;
1360 ft = (fil_tsd_t *)TSD->fil_tsd;
1361 /* Naming this the "removal loop". */
1362 for ( ptr1=ft->mrufile; ptr1; )
1364 save_ptr1 = ptr1->older ;
1365 for ( ptr2=ptr1; ptr2; )
1367 save_ptr2 = ptr2->next ; /* this was moved from third parm of loop
1368 so that it did not address the free'd
1369 memory. See if statement below. */
1371 * If this is one of the default streams, don't let it be closed.
1372 * These file shall stay open, whatever happens.
1375 * JH 19991105 if was modified to include the next 5 statements. Originally,
1376 * the file was not closed, but all other references to it were deleted. In
1377 * situations where one *.exe invokes Rexx mutiple times, subsequent calls to
1378 * the standard streams caused an error. (getfileptr() failed, the file name
1379 * for stdio_ptr[?] comes up blank.)
1381 if (!(ptr2->flag & FLAG_SURVIVOR)
1382 && ptr2->fileptr)
1384 fclose( ptr2->fileptr ) ;
1386 removefileptr( TSD, ptr2 ) ;
1388 if (ptr2->errmsg)
1389 Free_stringTSD( ptr2->errmsg ) ;
1391 Free_stringTSD( ptr2->filename0 ) ;
1392 FreeTSD( ptr2 ) ;
1394 ptr2 = save_ptr2 ;
1396 ptr1 = save_ptr1 ;
1399 ft->mrufile = NULL;
1402 * Now lets be absolutely paranoid, and remove all entries from the
1403 * filehash table...
1405 memset( ft->filehash, 0, sizeof(ft->filehash) );
1407 * JH 19991105 The following loop was added to re-instate the std streams into the
1408 * hash table. It seems easier to do this then to muck around with reseting the pointers
1409 * as the fileboxptr's are deleted. Cannot modify the loop above to look at filenames
1410 * before removing from filehas table, it might be pointing to a fileboxptr that got removed
1411 * by the "removal loop".
1413 for (i=0; i<6; i++)
1415 enterfileptr( TSD, ft->stdio_ptr[i] ) ;
1420 * checkProperStreamName raises 40.27 if errno describes an error leading
1421 * to the assumption that the filename was malformed according to ANSI 9.2.1.
1423 static void checkProperStreamName( tsd_t *TSD, streng *kill, const char *fn,
1424 int eno )
1426 static const int bad[] = {
1427 #if defined(ENAMETOOLONG)
1428 ENAMETOOLONG,
1429 #endif
1433 int i;
1435 for ( i = 0; bad[i] != 0; i++ )
1437 if ( eno == bad[i] )
1440 * ANSI 9.2.1 wants us to raise 40.27 if stream is malformed.
1441 * Feel free to provide more errno values describing this situation.
1443 if ( kill )
1444 Free_stringTSD( kill ) ;
1445 exiterror( ERR_INCORRECT_CALL, 27, BIFname( TSD ), fn );
1451 * Sets the proper error conditions for the file, including providing a
1452 * a hook into the CALL/SIGNAL ON system. Now, we also like to set some
1453 * other information, like the status of the file (taken from rc).
1455 * First parameter is the file to operate on, the second and third
1456 * parameters are the error message to set (they can't both be defined),
1457 * and the last parameter is the level of 'severity'. If set, the file
1458 * is thrown into error state.
1460 static void handle_file_error( tsd_t *TSD, fileboxptr ptr, int rc, const char *errmsg, int level)
1462 trap *traps=NULL ;
1464 assert( !(rc && errmsg) ) ;
1466 if ((ptr->flag & FLAG_ERROR) && (ptr->flag & FLAG_FAKE))
1469 * If we are faking for this file already, don't bother to do anything
1470 * more. In particular, we do not want to set a new error, since that
1471 * will in general only overwrite the old (and probably more relevant)
1472 * error message. However, faking are _only_ done when NOTREADY is
1473 * being trapped.
1475 return ;
1477 else
1480 * If the file is not already in error, set the ERROR flag, and record
1481 * the error message. Also, clear the FAKE flag. This flag is only
1482 * defined when the ERROR flag is set, and we don't want any old
1483 * values laying around (it will be set later if needed).
1485 if (level)
1487 ptr->flag &= ~FLAG_FAKE ;
1488 ptr->flag |= FLAG_ERROR ;
1490 else if (ptr->flag & FLAG_RDEOF)
1493 * If the file was in RDEOF state; ie EOF was read on the file
1494 * set the AFTER_RDEOF flag to ensure STREAM(stream,'S') works
1495 * like other interpreters.
1497 ptr->flag |= FLAG_AFTER_RDEOF;
1500 checkProperStreamName( TSD, ptr->errmsg, Str_val( ptr->filename0 ), rc );
1503 * Set the error message, but only if one was given. This routine
1504 * can be called _without_ any errormessage, and if so, keep the
1505 * old one (if any)
1507 if (rc || errmsg)
1509 if (ptr->errmsg)
1510 Free_stringTSD( ptr->errmsg ) ;
1512 ptr->error = rc ;
1513 if ( errmsg )
1514 ptr->errmsg = Str_creTSD( errmsg ) ;
1515 else
1516 #ifdef WIN32
1519 * For Win32 always get the last error, and store that;
1520 * it seems to be more meaningful than strerror( errno )
1521 * Address bug ???? FIXME
1523 CHAR LastError[256];
1524 ULONG last_error = GetLastError();
1525 if ( last_error )
1527 FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), LastError, 256, NULL ) ;
1528 ptr->errmsg = Str_creTSD( LastError ) ;
1530 else
1531 ptr->errmsg = NULL;
1533 #else
1534 ptr->errmsg = NULL;
1535 #endif
1539 * OK, the file has been put into ERROR state, now we must check
1540 * to see if we should raise the NOTREADY condition. If NOTREADY
1541 * is not currently enabled, don't bother to try to raise it.
1543 traps = gettraps( TSD, TSD->currlevel ) ;
1544 if (traps[SIGNAL_NOTREADY].on_off)
1547 * The NOTREADY condition is being trapped; set the FAKE flag
1548 * so that we don't create more errors for this file. But _only_
1549 * set the FAKE flag if NOTREADY is trapped by method CALL.
1550 * Then raise the condition ...
1552 if (!traps[SIGNAL_NOTREADY].invoked)
1553 ptr->flag |= FLAG_FAKE ;
1555 condition_hook(TSD,SIGNAL_NOTREADY,rc+100,0,-1,Str_dupTSD(ptr->filename0),NULL);
1563 * This routine is supposed to be called when the condition is triggered
1564 * by method CALL. From the time the condition is raised until the CALL is
1565 * is triggered, I/O to the file is faked. But before the condition handler
1566 * is called, we try to tidy things up a bit.
1568 * At least, we have to clear the FAKE flag. Other 'nice' things to do
1569 * is to clear error indicator in the file pointer, and to reset the
1570 * file in general. The ERROR state is not cleared, _unless_ the file
1571 * is one of the default streams.
1574 void fixup_file( tsd_t *TSD, const streng *filename )
1576 fileboxptr ptr=NULL ;
1578 if ( filename )
1581 * filename will be NULL when condition_hook() called with a NULL description
1582 * argument. This happens when NOTREADY occurs when pulling from an external
1583 * queue with a timeout and the timeout expires.
1585 ptr = getfileptr( TSD, filename ) ;
1586 if (ptr)
1589 * If the file is open, try to clear it, first clear the error
1590 * indicator, and then try to fseek() to a 'safe' point. If the
1591 * seeking didn't work out, don't bother, it was worth a try.
1593 if (ptr->fileptr)
1595 clearerr( ptr->fileptr ) ;
1596 if ( ptr->flag & FLAG_PERSIST )
1597 fseek( ptr->fileptr, 0, SEEK_SET ) ;
1598 ptr->thispos = 0 ;
1599 ptr->oper = OPER_NONE ;
1602 if (ptr->flag & FLAG_SURVIVOR)
1604 ptr->flag &= ~(FLAG_ERROR) ;
1606 * MHES Added following 4 flag resets - 30-11-2004
1608 ptr->flag &= ~(FLAG_RDEOF) ;
1609 ptr->flag &= ~(FLAG_WREOF) ;
1610 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
1613 ptr->flag &= ~(FLAG_FAKE) ;
1622 * This is stupid ... if the file exists, but is in error mode, we
1623 * shall not close it, but leave it open, so that the rest of the
1624 * operations on this file in this statement don't trip. Same happens
1625 * if we are not able to close it properly. Oh well ...
1627 * On second thoughts ... Faking only applies for input and output.
1628 * So closing doesn't have to be faked. Remove the file, whatever
1629 * happens.
1631 void closefile( tsd_t *TSD, const streng *name )
1633 fileboxptr ptr=NULL ;
1635 /* If it isn't open, don't try to close it ... */
1636 ptr = getfileptr( TSD, name ) ;
1637 if (ptr)
1640 * If this is one of the default streams, don't let it be closed.
1641 * These file shall stay open, whatever happens.
1643 if (ptr->flag & FLAG_SURVIVOR)
1644 return ;
1647 * If the fileptr seems to point to something ... close it. We
1648 * really don't want to leak file table slots. Actually, we should
1649 * check that the close was ok, and not let the fileptr go unless
1650 * we know that it was really closed (and released for new use).
1651 * Previously, it only closed when file was not in error. I don't
1652 * know what is the correct action, but this seems to be the most
1653 * sensible ...
1655 if (ptr->fileptr)
1656 fclose( ptr->fileptr ) ;
1658 removefileptr( TSD, ptr ) ;
1660 if (ptr->errmsg)
1661 Free_stringTSD( ptr->errmsg ) ;
1663 Free_stringTSD( ptr->filename0 ) ;
1664 FreeTSD( ptr ) ;
1672 * This function is called when we need some kind of access to a file
1673 * but don't (yet) have it. It will only be called when we want to
1674 * open a file implicitly, e.g. it is open for reading, and it has then
1675 * been named in a output function.
1677 * This is rather primitive ... but this function can only be called
1678 * when the file is open for read, and we want to open it for write;
1679 * or if the file i open for write, and we want to open it for read.
1680 * So I think this will suffice. It ignores the 'access' parameter
1681 * And just assumes that the file must be opened in both read and
1682 * write.
1684 * To improve on this function, we ought to do a lot more checking,
1685 * e.g. that the 'access' wanted are required, and that the file is
1686 * already open in some kind of mode. If this don't hold, we probably
1687 * have an error condition.
1689 * We should also check another thing, that the new file which is opened
1690 * is in fact the same file that we closed. Perferably, we should open
1691 * the new file, then check the device and inode of both the old and
1692 * new file to see whether they are the same (using stat()). If they
1693 * are not the same, the reopening should fail. As it is implemented
1694 * now, the Unix method for temporary files (open it, remove it,
1695 * use it, and then close it) will fail; and we loose access to the
1696 * original file too.
1698 static void reopen_file( tsd_t *TSD, fileboxptr ptr )
1700 if (!ptr)
1701 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
1704 * We can not reopen the default streams, that makes no sence. If
1705 * tried, report an error.
1707 if (ptr->flag & FLAG_SURVIVOR)
1709 file_error( ptr, 0, "Invalid operation on default stream" ) ;
1710 return ;
1714 * Close the old file, and try to reopen the new file. There is the
1715 * same problem here as in closefile(); if closing didn't work (for
1716 * some mysterious reason), the system's file table should become
1717 * full. Better checking might be required.
1719 errno = 0 ;
1720 fclose( ptr->fileptr ) ;
1721 #ifdef VMS
1722 ptr->fileptr = fopen( ptr->filename0->value, "r+" ) ;
1723 #else
1724 ptr->fileptr = fopen( ptr->filename0->value, "r+b" ) ;
1725 #endif
1726 if (ptr->fileptr==NULL)
1728 file_error( ptr, errno, NULL ) ;
1729 return ;
1731 ptr->oper = OPER_NONE ;
1734 * We definitively want to set the close-on-exec flag. Suppose
1735 * an output file has not been flushed, and we execute a command.
1736 * This might perform an exec() and then a system(), which _will_
1737 * flush all files (close them). The result is that the file might
1738 * be flushed twice ... not good.
1740 * This don't work on VMS ... but the file system on VMS is so
1741 * different anyway, so it will probably not create any problems.
1742 * Besides, we don't do exec() and system() on VMS.
1744 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !(defined(WIN32) && defined(__IBMC__)) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__) && !defined(__LCC__) && !defined(SKYOS) && !defined(_AMIGA) && !defined(__AROS__)
1745 if (ptr && ptr->fileptr)
1747 int flags, fno ;
1748 fno = fileno( ptr->fileptr ) ;
1749 assert( fno >= -1) ;
1750 flags = fcntl( fno, F_GETFD ) ;
1751 flags |= FD_CLOEXEC ;
1752 if (fcntl( fno, F_SETFD, flags)== -1)
1753 exiterror( ERR_SYSTEM_FAILURE, 1, strerror(errno) ) ;
1755 #endif
1758 * If readposition is EOF (=illegal), then we "probably" needed to
1759 * open it in read mode. Set the current read position to the start
1760 * of the file.
1762 if (ptr->readpos == (size_t) EOF)
1764 ptr->readline = 1 ;
1765 ptr->linesleft = 0 ;
1766 ptr->readpos = 0 ;
1767 ptr->thispos = 0 ;
1768 if ( ptr->flag & FLAG_PERSIST )
1769 fseek( ptr->fileptr, 0, SEEK_SET ) ;
1773 * Then do the same thing for write access. We always set this to the
1774 * end-of-file -- the default -- even though there are other write
1775 * modes available. If the file is implicitly open in write mode,
1776 * then the current write position should be set to the default
1777 * value.
1779 if (ptr->writepos == (size_t) EOF)
1781 ptr->writeline = 0 ;
1782 if ( ptr->flag & FLAG_PERSIST )
1783 fseek( ptr->fileptr, 0, SEEK_END ) ;
1784 ptr->writepos = ftell( ptr->fileptr ) ;
1785 ptr->thispos = ptr->writepos ;
1789 * Then, at last, do some simple bookkeeping, set both read and
1790 * write access, and clear any previous problem.
1792 ptr->flag = FLAG_READ | FLAG_WRITE | FLAG_PERSIST ;
1793 ptr->error = 0 ;
1794 if (ptr->errmsg)
1795 Free_stringTSD(ptr->errmsg) ;
1797 ptr->errmsg = NULL ;
1803 * This function explicitly opens a file. It will be called if the user
1804 * has called the built-in function STREAM() in order to open a file
1805 * in a particular mode. It will also be called if the file is not
1806 * previously open, and is used in a read or write operation.
1808 * It takes two parameters, the name of the file to open, and the
1809 * mode in which it is to be opened. The mode has a value which is
1810 * matched by the ACCESS_ macros defined earlier.
1812 * If the file is actually open in advance, then we close it before we
1813 * do any other operations. If the user is interested in the file in
1814 * one particular mode, he is probably not interested in any previous
1815 * modes.
1817 static fileboxptr openfile( tsd_t *TSD, const streng *name, int faccess )
1819 fileboxptr ptr=NULL ;
1820 long lpos=0L ;
1823 * First check wether this file is already open, and use that open
1824 * file if possible. However, that may not be possible, since we
1825 * may want to use the file for another operation now. So, if the
1826 * file _is_ open, check to see if access is right.
1828 ptr = getfileptr( TSD, name ) ;
1829 if (ptr)
1831 if (ptr->flag & FLAG_SURVIVOR)
1833 file_error( ptr, 0, "Can't open a default stream" ) ;
1834 return ptr ;
1836 closefile( TSD, name ) ;
1840 * Now, get a new file table entry, and fill in the various
1841 * field with appropriate (i.e. default) values.
1843 ptr = (fileboxptr)MallocTSD( sizeof(filebox) ) ;
1844 ptr->filename0 = Str_dupstrTSD( name ) ;
1845 ptr->flag = 0 ;
1846 ptr->error = 0 ;
1847 ptr->errmsg = NULL ;
1848 ptr->readline = 0 ;
1849 ptr->linesleft = 0 ;
1850 ptr->writeline = 0 ;
1851 ptr->thispos = (size_t) EOF ;
1852 ptr->readpos = (size_t) EOF ;
1853 ptr->writepos = (size_t) EOF ;
1854 ptr->oper = OPER_NONE;
1857 * suppose we tried to open, but didn't manage, well, stuff it into
1858 * the file table, we might want to retrieve information about it
1859 * later on. _And_ we need to know about the problem if the file
1860 * I/O is to be faked later on.
1862 enterfileptr( TSD, ptr ) ;
1863 name = ptr->filename0 ;
1864 goto try_to_open ;
1866 kill_one_file:
1867 swapout_file( TSD, ptr ) ;
1869 try_to_open:
1871 * In most of these, we have to check that the file opened is really
1872 * a persistent file. We should not take that for granted.
1874 errno = 0 ;
1875 if (faccess==ACCESS_READ)
1877 #ifdef VMS
1878 if ((ptr->fileptr = fopen( name->value, "r" )) != NULL)
1879 #else
1880 if ((ptr->fileptr = fopen( name->value, "rb" )) != NULL)
1881 #endif
1883 ptr->flag = FLAG_READ | FLAG_PERSIST ;
1884 ptr->readline = 1 ;
1885 ptr->linesleft = 0 ;
1886 ptr->thispos = ptr->readpos = 0 ;
1888 else if (errno==EMFILE)
1889 goto kill_one_file ;
1890 else
1891 file_error( ptr, errno, NULL ) ;
1893 else if (faccess==ACCESS_WRITE)
1896 * This is really a problem. If opened in mode "w", it will
1897 * truncate the file if it did exist. If opened int mode "r+",
1898 * it will fail if the file did not exist. So we try to
1899 * combine the two.
1901 ptr->flag = FLAG_READ ;
1902 #ifdef VMS
1903 ptr->fileptr = fopen( name->value, "r+" ) ;
1904 #else
1905 ptr->fileptr = fopen( name->value, "r+b" ) ;
1906 #endif
1907 errno = 0 ;
1908 if (!ptr->fileptr)
1909 #ifdef VMS
1910 ptr->fileptr = fopen( name->value, "w+" ) ;
1911 #else
1912 ptr->fileptr = fopen( name->value, "w+b" ) ;
1913 #endif
1915 errno = 0 ;
1916 if (!ptr->fileptr)
1918 #ifdef SGIkludges
1919 errno = EMFILE ;
1920 #else
1921 errno = 0 ;
1922 #endif
1923 #ifdef VMS
1924 ptr->fileptr = fopen( name->value, "w" ) ;
1925 #else
1926 ptr->fileptr = fopen( name->value, "wb" ) ;
1927 #endif
1928 ptr->flag &= 0 ;
1932 * Then set the current read and write positions to the start and
1933 * the end of the file, respectively. When we first open the file
1934 * we can quickly determine readpos, writepos and readline, but
1935 * writeline is expensive to determine, because we have to read
1936 * the whole file to determine this. So we don't do this when we
1937 * open the file, because we may never use it. Instead we set the
1938 * value of writeline to 0 to indicate that the actual position
1939 * is unknown. When we do want to use writeline, we have to
1940 * determine it then.
1942 if (ptr->fileptr)
1944 ptr->flag |= FLAG_WRITE | FLAG_PERSIST ;
1945 fseek( ptr->fileptr, 0, SEEK_END ) ;
1946 lpos = ftell( ptr->fileptr ) ;
1947 ptr->thispos = ptr->writepos = lpos ;
1948 ptr->writeline = 0 ;
1949 ptr->readpos = 0 ;
1950 ptr->readline = 1 ;
1951 ptr->linesleft = 0 ;
1953 else if (errno==EMFILE)
1954 goto kill_one_file ;
1955 else
1956 file_error( ptr, errno, NULL ) ;
1958 else if (faccess==ACCESS_APPEND)
1961 * In append mode, the file is opened as a transient file, all
1962 * writing must be done at the end of the file. It is not
1963 * possible to perform reading on the file. Useful for files
1964 * to which you have write, but not read access (e.g. logfiles).
1966 #ifdef VMS
1967 if ((ptr->fileptr = fopen( name->value, "a" )) != NULL)
1968 #else
1969 if ((ptr->fileptr = fopen( name->value, "ab" )) != NULL)
1970 #endif
1972 ptr->flag = FLAG_WRITE | FLAG_WREOF ;
1974 else if (errno==EMFILE)
1975 goto kill_one_file ;
1976 else
1977 file_error( ptr, errno, NULL ) ;
1979 else if (faccess==ACCESS_STREAM_APPEND)
1982 * In "stream" append mode, the file is opened as a persistent file, all
1983 * writing must be done at the end of the file. It is not
1984 * possible to perform reading on the file. Useful for files
1985 * to which you have write, but not read access (e.g. logfiles).
1987 #ifdef VMS
1988 if ((ptr->fileptr = fopen( name->value, "a" )) != NULL)
1989 #else
1990 if ((ptr->fileptr = fopen( name->value, "ab" )) != NULL)
1991 #endif
1993 ptr->flag = FLAG_WRITE | FLAG_WREOF | FLAG_PERSIST;
1994 if ( ptr->flag & FLAG_PERSIST )
1995 fseek( ptr->fileptr, 0, SEEK_END ) ;
1996 lpos = ftell( ptr->fileptr ) ;
1997 ptr->thispos = ptr->writepos = lpos ;
1998 ptr->writeline = 0 ; /* unknown position */
1999 ptr->readpos = 0 ;
2000 ptr->readline = 1 ;
2001 ptr->linesleft = 0 ;
2003 else if (errno==EMFILE)
2004 goto kill_one_file ;
2005 else
2006 file_error( ptr, errno, NULL ) ;
2008 else if (faccess==ACCESS_STREAM_REPLACE)
2011 * The file is created if it didn't exist, and if it did exist
2012 * it is truncated and the file pointers set to the start of file.
2014 #ifdef VMS
2015 if ((ptr->fileptr = fopen( name->value, "w+" )) != NULL)
2016 #else
2017 if ((ptr->fileptr = fopen( name->value, "w+b" )) != NULL)
2018 #endif
2020 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_WREOF | FLAG_RDEOF |
2021 FLAG_PERSIST ;
2022 ptr->writeline = ptr->readline = 1 ;
2023 ptr->linesleft = 0 ;
2024 ptr->readpos = ptr->writepos = ptr->thispos = 0 ;
2026 else if (errno==EMFILE)
2027 goto kill_one_file ;
2028 else
2029 file_error( ptr, errno, NULL ) ;
2031 else if (faccess==ACCESS_UPDATE)
2034 * Like read access, but it will not create the file if it didn't
2035 * already exist. Instead, an error is reported.
2037 #ifdef VMS
2038 if ((ptr->fileptr = fopen( name->value, "r+" )) != NULL)
2039 #else
2040 if ((ptr->fileptr = fopen( name->value, "r+b" )) != NULL)
2041 #endif
2043 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_PERSIST ;
2044 ptr->readline = 0 ;
2045 ptr->linesleft = 0 ;
2046 ptr->writeline = 0 ; /* unknown */
2048 else if (errno==EMFILE)
2049 goto kill_one_file ;
2050 else
2051 file_error( ptr, errno, NULL ) ;
2053 else if (faccess==ACCESS_CREATE)
2056 * The file is created if it didn't exist, and if it did exist
2057 * it is truncated.
2059 #ifdef VMS
2060 if ((ptr->fileptr = fopen( name->value, "w+" )) != NULL)
2061 #else
2062 if ((ptr->fileptr = fopen( name->value, "w+b" )) != NULL)
2063 #endif
2065 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_WREOF | FLAG_RDEOF |
2066 FLAG_PERSIST ;
2067 ptr->writeline = ptr->readline = 1 ;
2068 ptr->linesleft = 0 ;
2069 ptr->readpos = ptr->writepos = ptr->thispos = 0 ;
2071 else if (errno==EMFILE)
2072 goto kill_one_file ;
2073 else
2074 file_error( ptr, errno, NULL ) ;
2076 else
2077 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
2079 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__) && !defined(__LCC__) && !defined(SKYOS) && !defined(_AMIGA) && !defined(__AROS__)
2081 * Then we check to see if this is a transient or persistent file.
2082 * We can remove a 'persistent' setting, but add one, since we
2083 * sometimes want to access a persistent file as transient (append).
2085 if (ptr->fileptr)
2087 int fno, rc ;
2088 struct stat statbuf ;
2089 errno = 0 ;
2090 fno = fileno(ptr->fileptr) ;
2091 rc = fstat( fno, &statbuf ) ;
2092 if (rc==0 && !S_ISREG(statbuf.st_mode))
2093 ptr->flag &= ~(FLAG_PERSIST) ;
2094 else if (rc!=0)
2095 file_error( ptr, errno, NULL ) ;
2099 * As with reopen_file(), we want to set the close-on-exec flag,
2100 * see reopen_file for more information.
2102 if (ptr->fileptr)
2104 int flags, fno ;
2105 fno = fileno(ptr->fileptr) ;
2106 assert( fno >= -1) ;
2107 flags = fcntl( fno, F_GETFD ) ;
2108 flags |= FD_CLOEXEC ;
2109 if (fcntl( fno, F_SETFD, flags)== -1)
2110 exiterror( ERR_SYSTEM_FAILURE, 1, strerror(errno) ) ;
2112 #endif
2114 return (ptr) ;
2120 /* ------------------------------------------------------------------- */
2121 /* High level utility routines */
2127 * This function is really just an interface to the function getfileptr().
2128 * It takes a (possible) filename, and retrieves the corresponding
2129 * Rexx file table entry. If the file does not exist, it is opened in
2130 * the mode indicated by 'open_mode'. If it does exist, this routine
2131 * verifies that it it has been opened in a mode corresponding to
2132 * 'access' (OPER_READ or OPER_WRITE).
2134 * If the file does not exist, it is opened in either normal read
2135 * or normal write. This correcspinds to an "implicit" file open
2136 * in Rexx.
2138 static fileboxptr get_file_ptr( tsd_t *TSD, const streng *name, int faccess, int open_mode )
2140 fileboxptr ptr=NULL ;
2142 ptr = getfileptr( TSD, name ) ;
2143 if (ptr==NULL)
2144 return openfile( TSD, name, open_mode ) ;
2146 if (ptr->flag & FLAG_ERROR)
2147 return ptr ;
2149 if (faccess==OPER_READ && (!(ptr->flag & FLAG_READ)))
2150 reopen_file( TSD, ptr ) ;
2151 else if (faccess==OPER_WRITE && (!(ptr->flag & FLAG_WRITE)))
2152 reopen_file( TSD, ptr ) ;
2154 return ptr ;
2160 * This routine reads one complete line from the file indicated by
2161 * the file table entry 'ptr'. Or rather, it read from the current
2162 * read position, until and including the first EOL mark, and returns
2163 * that. If the EOL mark is implemented as certain characters, they are
2164 * not returned. It closely corresponds to the LINEIN() built-in
2165 * function.
2167 * What is the upper limit for the size that we might read in? It's
2168 * best not to have any limit, so the method is the following: A
2169 * temporary area is used for storing the data read from the file.
2170 * We never know the size needed until the EOL mark is found. So
2171 * just read the data into the temporary area. If the EOL is found,
2172 * then we know the size, and we can transfer the data into a 'streng'
2173 * of suitable size. If the temporary area is too small, allocate
2174 * an area twice the size, and copy the data over. Afterwards, keep the
2175 * new area as the temporary area.
2177 * This way, we normally use little memory, but we are still able to
2178 * read as large lines as the memory allows, if it is needed.
2180 * No error condition is raised if noerrors is set. Instead, NULL is returned.
2182 static streng *readoneline( tsd_t *TSD, fileboxptr ptr )
2184 int i=0, j=0, eolf=0, eolchars=1 ;
2185 /*#if !defined(UNIX)*/
2186 int k=0;
2187 /*#endif*/
2188 streng *ret=NULL ;
2189 fil_tsd_t *ft;
2191 ft = (fil_tsd_t *)TSD->fil_tsd;
2194 * First verify that we actually have a file that is not in an
2195 * ERROR state. If so, don't perform any operations.
2197 if ( ptr->flag & FLAG_ERROR )
2199 if (!(ptr->flag & FLAG_FAKE))
2200 file_error( ptr, 0, NULL ) ;
2202 return nullstringptr() ;
2206 * If we have an EOF from the last linein() then cause the NOTREADY
2207 * condition now.
2209 if ( ptr->flag & FLAG_RDEOF )
2211 file_warning( ptr, 0, "EOF on line input" ) ;
2215 * If the string is not yet allocated, allocate it, and use an
2216 * initial size of 512 bytes. This can be increased during runtime.
2217 * Using higher initial sizes will waste allocation time on modern systems
2218 * with page sizes around 4KB. 512 fits most cases at its best.
2220 * MH 4 Sep 02 We should read into the ft->rol_string 512 bytes at a
2221 * time and search through the buffer for the EOL. getc() for every
2222 * character is innefficient, however (at least on Linux) getc() is
2223 * implemented as a read( 4096 ), and then returns each character
2224 * from some internal buffer.
2226 if (!ft->rol_string)
2228 ft->rol_string = (char *)MallocTSD( (ft->rol_size=512) ) ;
2229 #ifdef TRACEMEM
2230 ft->rdarea = ft->rol_string ;
2231 #endif
2235 if (ptr->fileptr==stdin)
2236 fcntl( stdin, F_SETFL, O_NONBLOCK | fcntl(stdin,F_GETFL)) ;
2238 errno = 0 ;
2239 SWITCH_OPER_READ(ptr);
2241 * Switch to the current readpos setting in case the current position
2242 * is based on the write position.
2244 ptr->thispos = ptr->readpos;
2245 if ( ptr->flag & FLAG_PERSIST )
2246 fseek(ptr->fileptr,ptr->thispos,SEEK_SET);
2247 for (i=0; ; i++)
2249 j = getc(ptr->fileptr);
2250 if (j == REGINA_EOL)
2252 eolf = REGINA_EOL;
2253 break;
2255 /*#if !defined(UNIX) && !defined(MAC)*/
2257 * MH 25042003 - all platforms can read lines with
2258 * CRLF, LF, or CR - consistent with ObjectRexx
2260 if (j == REGINA_CR)
2262 k = getc(ptr->fileptr);
2263 if (k == REGINA_EOL)
2265 eolf = REGINA_EOL;
2266 eolchars = 2;
2267 break;
2269 else
2271 ungetc(k,ptr->fileptr);
2272 eolf = REGINA_EOL;
2273 break;
2276 /*#endif*/
2278 * If we hit end-of-file, handle it carefully, and terminate the
2279 * reading. Note that this means that there we have read an
2280 * incomplete last line, so return what we've got, and report
2281 * an NOTREADY condition. (Partly, I disagree, but this is how
2282 * TRL defines it ... Case I: Programmer using Emacs forgets to
2283 * add a EOL after the last line; Rexx triggers NOTREADY when
2284 * reading that last, incomplete line.
2286 * MH 4-Sep-2002 - change in semantics happened a while ago.
2287 * We treat incomplete line as a "normal" line, and NOTREADY
2288 * is NOT raised when reading an incomplete line.
2290 if (j==EOF)
2292 ptr->flag |= FLAG_RDEOF ;
2293 /* file_warning( ptr, 0, "EOF on line input" ) ; */
2294 break ;
2298 * We are trying to avoid any limits other than memory-imposed
2299 * limits. So if the buffer size that we currently have are too
2300 * small, double it, and hide the operation from the rest of the
2301 * interpreter.
2303 if (i>=ft->rol_size)
2305 char *tmpstring ;
2307 assert( i == ft->rol_size ) ;
2308 tmpstring = (char *)MallocTSD( 2*ft->rol_size+10 ) ;
2309 memcpy( tmpstring, ft->rol_string, ft->rol_size ) ;
2310 FreeTSD( ft->rol_string ) ;
2311 ft->rol_string = tmpstring ;
2312 ft->rol_size *= 2 ;
2313 #ifdef TRACEMEM
2314 ft->rdarea = ft->rol_string ;
2315 #endif
2319 * Just an ordinary character ... append it to the buffer
2321 ft->rol_string[i] = (char) j ;
2324 * Attempt to set the read pointer and the current file
2325 * pointer based on the length of the line we just read.
2327 #if 1 /* really MH */
2328 if ( ptr->thispos == ptr->readpos )
2330 if ( ptr->thispos == (size_t) EOF )
2332 errno = 0 ;
2333 ptr->thispos = ptr->readpos = ftell( ptr->fileptr ) ;
2335 else
2337 ptr->thispos += (i - (j==EOF)) + eolchars ;
2338 ptr->readpos = ptr->thispos ;
2341 else
2343 errno = 0 ;
2344 ptr->thispos = ptr->readpos = ftell( ptr->fileptr ) ;
2346 #else
2347 if (ptr->thispos != (size_t) EOF)
2348 ptr->thispos += (i - (j==EOF)) + eolchars ;
2350 if (ptr->readpos != (size_t) EOF)
2351 ptr->readpos = ptr->thispos ;
2352 #endif
2354 * If we did read a complete line, we have to increment the line
2355 * count for the current read pointer of this file. This part of
2356 * the code is a bit Unix-ish. It will have to be reworked for
2357 * other types of operating systems.
2359 if ((eolf==REGINA_EOL) && (ptr->readline > 0))
2361 #if 1 /* really MH */
2362 ptr->readline += 1 ; /* only if we actually saw the "\n" !!! */
2363 #else
2364 ptr->readline += eolchars ; /* only if we actually saw the "\n" !!! */
2365 #endif
2366 if (ptr->linesleft)
2367 ptr->linesleft-- ;
2370 * A bit of a hack here. Because countlines() determines if any lines
2371 * are left in the stream by using the feof() function, we have to
2372 * attempt to read the EOF after each line, and set the file's state
2373 * to EOF. If the character read is not EOF, then put it back on
2374 * the stream to be read later.
2375 * Only do this for persistent streams!!
2377 if ( ptr->flag & FLAG_PERSIST
2378 && !feof( ptr->fileptr ) )
2380 int ch0;
2381 ch0 = getc(ptr->fileptr);
2382 if (feof(ptr->fileptr))
2384 ptr->flag |= FLAG_RDEOF ;
2385 /* file_warning( ptr, 0, "EOF on line input" ) ; */
2387 else
2389 ungetc(ch0,ptr->fileptr);
2394 * Wrap up the data that was read, and return it as a 'streng'.
2397 /* if (i>1000) i = 1000 ; */
2398 ret = Str_makeTSD( i ) ;
2399 memcpy( ret->value, ft->rol_string, ret->len=i ) ;
2400 return ret ;
2404 static int positionfile_SEEK_SET( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, int lineno )
2406 int ch=0x00 ;
2407 int from_line=0, tmp=0 ;
2408 long from_char=0L ;
2409 int ret;
2412 * We know the line number of at most three positions in the file:
2413 * the start of the file, the write position and the read position.
2414 * If the file is open only for reading or writing, we know at most
2415 * two positions. And in addition, the read and/or the write
2416 * position may be be invalid (i.e. previous operation was
2417 * character oriented). But at least, we know the line number of
2418 * one position, the start of the file, which is the first line.
2420 * The best method seems to be: First start with the start of file
2421 * and then see if using the read or the write position instead is
2422 * a better deal. There is one drawback ... we assume that all lines
2423 * are equally long. That assumption is probably not too bad for text
2424 * files, but it may create unnecessary overhead for 'peculiar' files
2426 from_line = 1 ;
2427 from_char = 0 ;
2429 if ( oper & OPER_READ
2430 && ptr->flag & FLAG_PERSIST )
2432 if ( fseek( ptr->fileptr, ptr->readpos, SEEK_SET ) )
2434 file_error( ptr, errno, NULL ) ;
2435 return 0;
2437 ptr->thispos = ptr->readpos ;
2439 if ( oper & OPER_WRITE
2440 && ptr->flag & FLAG_PERSIST )
2442 if ( fseek( ptr->fileptr, ptr->writepos, SEEK_SET ) )
2444 file_error( ptr, errno, NULL ) ;
2445 return 0;
2447 ptr->thispos = ptr->writepos ;
2451 * First, let's check to see if we gain anything from using the
2452 * read position instead. If the distance from the current read
2453 * position to the wanted line (counted in number of lines) is smaller
2454 * than the number of lines from the first line to the wanted line,
2455 * use the current read position in stead. But only if the current
2456 * read position is defined.
2458 if ((ptr->flag & FLAG_READ) && (ptr->readline > 0))
2460 assert( ptr->readpos != (size_t) EOF) ;
2461 tmp = ptr->readline - lineno ;
2462 if (tmp<0)
2463 tmp = (-tmp) ;
2465 if (tmp < (lineno - from_line))
2467 from_line = ptr->readline ;
2468 from_char = ptr->readpos ;
2473 * Then, we check to see whether we can gain even more if we use
2474 * the current write position of the file instead.
2476 if ((ptr->flag & FLAG_WRITE) && (ptr->writeline > 0))
2478 assert( ptr->writepos != (size_t) EOF ) ;
2479 tmp = ptr->writeline - lineno ;
2480 if (tmp<0)
2481 tmp = (-tmp) ;
2483 if (tmp < (lineno - from_line))
2485 from_line = ptr->writeline ;
2486 from_char = ptr->writepos ;
2491 * By now, the variables from_line, and from_char should contain
2492 * the optimal starting point from where a seek for the 'lineno'
2493 * line in the file can start, so first, move there. An in addition,
2494 * it should be the known position which is closest to the wanted
2495 * line.
2497 if (from_char != (long) ptr->thispos)
2499 errno = 0 ;
2500 if ( ptr->flag & FLAG_PERSIST
2501 && fseek( ptr->fileptr, from_char, SEEK_SET ))
2503 file_error( ptr, errno, NULL ) ;
2504 return 0;
2506 ptr->oper = OPER_NONE;
2507 ptr->thispos = from_char ;
2509 assert( from_char == ftell(ptr->fileptr) ) ;
2512 * Now we are positioned at the right spot, so seek forwards or
2513 * backwards until we reach the correct line. Actually, the method
2514 * we are going to use may seem a bit strange at first. First we
2515 * seek forward until we pass the line, and then we seek backwards
2516 * until we reach the line and at the end we back up to the first
2517 * preceding end-of-line marker. This may seem awkward, but it is
2518 * fairly simple. And in addition, it will always position us at
2519 * the _start_ of the wanted line.
2521 once_more:
2522 while ((lineno>from_line)) /* seek forward */
2524 SWITCH_OPER_READ(ptr);
2525 for (;((ch=getc(ptr->fileptr))!=EOF)&&(ch!=REGINA_EOL);from_char++) ;
2526 if (ch==REGINA_EOL)
2527 from_line++ ;
2528 else
2529 break ;
2533 * Then we seek backwards until we reach the line. The backwards
2534 * movement is _really_ awkward, so perhaps we should read in 512
2535 * bytes, and analyse the data in it instead? Indeed, another
2536 * algoritm should be chosen. Maybe later ...
2538 while (lineno<=from_line && from_char>0)
2540 errno = 0 ;
2541 if ( ptr->flag & FLAG_PERSIST
2542 && fseek(ptr->fileptr, -1, SEEK_CUR))
2545 * Should this happen? Only if someone overwrites EOF chars in
2546 * the file, but that _may_ happend ... Report error for
2547 * any errors from the fseek and ftell. If we hit the start of
2548 * the file, reset from_line check whether we are _below_ lineno
2549 * If so, jump back and seek from the start (then we *must*
2550 * start at line 1, since the data we've got are illegal).
2552 * It will also happen if we are seeking backwards for the
2553 * first line.
2555 errno = 0 ;
2556 if (fseek(ptr->fileptr,0,SEEK_SET))
2558 file_error( ptr, errno, NULL ) ;
2559 return 0;
2561 ptr->oper = OPER_NONE;
2563 from_line = 1 ;
2564 ptr->thispos = 0 ;
2565 if (from_line<lineno)
2567 ptr->readline = (-1);
2568 ptr->writeline = 0; /* unknown */
2569 goto once_more ;
2572 break ; /* we were looking for the first line ... how lucky :-) */
2576 * After seeking one character backwards, we must read the character
2577 * that we just skipped over. Do that, and test whether it is
2578 * a end-of-line character.
2580 SWITCH_OPER_READ(ptr);
2581 ch = getc(ptr->fileptr) ;
2582 if (ch==REGINA_EOL)
2584 if (lineno==from_line)
2585 break ;
2587 from_line-- ;
2591 * Then we move backwards once more, in order to compensate for
2592 * reading the character. Sigh, we are really doing a lot of
2593 * forward and backward reading, arn't we?
2595 errno = 0 ;
2596 if ( ptr->flag & FLAG_PERSIST
2597 && fseek(ptr->fileptr, -1, SEEK_CUR))
2599 file_error( ptr, errno, NULL ) ;
2600 return 0;
2602 ptr->oper = OPER_NONE;
2606 * Now we are almost finished. We just have to set the correct
2607 * information in the Rexx file table entry.
2609 ptr->thispos = ftell( ptr->fileptr ) ;
2610 if (oper & OPER_READ)
2612 ptr->readline = from_line ; /* was lineno */
2613 ptr->readpos = ptr->thispos ;
2614 ptr->flag &= ~(FLAG_RDEOF) ;
2615 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
2617 if (oper & OPER_WRITE)
2619 ptr->writeline = from_line ; /* was lineno */
2620 ptr->writepos = ptr->thispos ;
2621 ptr->flag &= ~(FLAG_WREOF) ;
2624 if (oper & OPER_READ)
2625 ret = ptr->readline ;
2626 else
2627 ret = ptr->writeline ;
2628 return ret;
2631 static int positionfile_SEEK_CUR( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, int lineno, int from_line, int from_char )
2633 int tmp,ret;
2636 * Do simple checks first.
2637 * If we are seeking back before the first line, then set:
2638 * READ:
2639 * ptr->readline = 1
2640 * ptr->readpos = 1
2641 * return 1
2642 * WRITE:
2643 * ptr->writeline = 1
2644 * ptr->writepos = 0
2645 * return 0
2647 tmp = from_line + lineno;
2649 if ( tmp < 1 )
2652 * We have positioned to before the first line
2655 fseek( ptr->fileptr, 0L, SEEK_SET );
2656 ptr->thispos = ftell( ptr->fileptr );
2657 if ( oper == OPER_READ )
2659 ptr->readline = 1;
2660 ptr->readpos = 1;
2661 ptr->oper = OPER_READ;
2662 return 1;
2664 else
2666 ptr->writeline = 1;
2667 ptr->writepos = 0;
2668 ptr->oper = OPER_WRITE;
2669 return 0;
2673 * We now have an absolute line number from the +ve relative line
2674 * number, so use the absolute positioning code.
2676 ret = positionfile_SEEK_SET( TSD, bif, argno, ptr, oper, tmp );
2677 return ret;
2680 static int positionfile_SEEK_END( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, int lineno )
2683 * This function does file positioning on a line basis from the
2684 * end of the file.
2685 * There is not a lot of optimisation we can do reading backwards.
2686 * We first need to determine the last line; does it end in an EOL,
2687 * or is it incomplete; ie does not end in EOL. We treat an incomplete
2688 * last line as a line.
2689 * Our initial attempt at this will position the file at the
2690 * end, and read backwards; ie getc(ptr->fileptr), seek() back 2 chars,
2691 * until the start of file, or we have the specified number of lines.
2693 long here, next, save_pos, i, ret, this_lineno, num_lines;
2694 char buf[512];
2695 int found = 0;
2697 SWITCH_OPER_READ(ptr);
2699 * First, get the size of the file. We can only do positioning on
2700 * persistant files...
2702 if ( ! (ptr->flag & FLAG_PERSIST) )
2704 file_error( ptr, 0, "Cannot position on transient stream" );
2705 return 0;
2707 if ( fseek( ptr->fileptr, 0L, SEEK_END ) )
2709 file_error( ptr, errno, NULL ) ;
2710 return 0;
2713 here = ftell( ptr->fileptr );
2715 * Seek backwards one character and read the character. If the last
2716 * character is REGINA_EOL, then DON'T treat this as a new line.
2718 if ( fseek( ptr->fileptr, -1L, SEEK_CUR ) )
2720 file_error( ptr, errno, NULL ) ;
2721 return 0;
2723 buf[0] = (char)getc( ptr->fileptr );
2724 if ( buf[0] == REGINA_EOL )
2725 num_lines = 0;
2726 else
2727 num_lines = 1;
2729 * Move the file pointer back to after the last character
2731 if ( fseek( ptr->fileptr, 0L, SEEK_END ) )
2733 file_error( ptr, errno, NULL ) ;
2734 return 0;
2738 * We have to read backwards in the file until we reach a known point.
2739 * The only point which we can guarantee is known is the start of the
2740 * file. We can't use ptr->(read/write)line as we really don't know
2741 * how many lines are in the file (we may have appended several earlier).
2742 * An incomplete last line of a file (ie no CRLF/LF) is treated as a complete
2743 * line.
2745 for ( ; ; )
2748 * Determine where we want to move the file pointer backwards
2749 * into the file, and then move the file pointer there ready for
2750 * reading a buffer.
2752 next = min( 512, here );
2753 if ( fseek( ptr->fileptr, -next, SEEK_CUR ) )
2755 file_error( ptr, errno, NULL ) ;
2756 return 0;
2759 * Save our current position; start of the buffer being read
2761 save_pos = ftell( ptr->fileptr );
2763 * Read a buffer, this moves the file pointer forward to the
2764 * end of the buffer
2767 ret = fread( buf, sizeof(char), next, ptr->fileptr );
2768 if ( ret != next
2769 && ret != EOF )
2771 file_error( ptr, errno, NULL ) ;
2772 return 0;
2775 * Count the number of lines from the end of the buffer
2777 for ( i = next-1; i >= 0; i-- )
2779 if ( buf[i] == REGINA_EOL )
2781 num_lines++;
2782 if ( num_lines > lineno
2783 && !found )
2786 * Calculate the actual char file position of the EOL
2789 * the +1 is to point at the character AFTER the EOL; the
2790 * first character of the next line.
2792 ptr->thispos = save_pos + i + 1;
2793 found = 1;
2798 * move the file pointer back to the start of the buffer just
2799 * read, so the next fseek() backwards is from the START of the
2800 * buffer.
2802 if ( fseek( ptr->fileptr, save_pos, SEEK_SET ) )
2804 file_error( ptr, errno, NULL ) ;
2805 return 0;
2808 * Calculate our own file position
2810 here -= next;
2811 if ( here == 0 )
2812 break;
2815 * We are at the start of the file. If we haven't found our lineno
2816 * (because it was greater than the number of lines in the file),
2817 * that's where we stay.
2819 if ( found )
2821 /* ptr->thispos already set */
2822 this_lineno = 1 + (num_lines - lineno);
2824 else
2826 ptr->thispos = 0;
2827 this_lineno = 1;
2831 * Now we are almost finished. We just have to set the correct
2832 * information in the Rexx file table entry.
2834 if ( fseek( ptr->fileptr, ptr->thispos, SEEK_SET ) )
2836 file_error( ptr, errno, NULL ) ;
2837 return 0;
2839 if ( oper & OPER_READ )
2841 ptr->readline = this_lineno;
2842 ptr->readpos = ptr->thispos;
2843 ptr->flag &= ~(FLAG_RDEOF);
2844 ptr->flag &= ~(FLAG_AFTER_RDEOF);
2846 if ( oper & OPER_WRITE )
2848 ptr->writeline = this_lineno;
2849 ptr->writepos = ptr->thispos ;
2850 ptr->flag &= ~(FLAG_WREOF) ;
2853 * We just counted the number of lines between the end of the file and
2854 * our current position, so we know how many lines are left.
2855 * The number of lines counted is one more than actually left in the file.
2857 ptr->linesleft = num_lines-1;
2858 if ( oper & OPER_READ )
2859 ret = ptr->readline ;
2860 else
2861 ret = ptr->writeline ;
2862 return ret;
2866 * This routine will position the current read or write position
2867 * of a file, to the start of a particular line. The file to be
2868 * operated on is 'ptr', the pointer to manipulate is indicated
2869 * by 'oper' (either OPER_READ or OPER_WRITE or both), and the linenumber
2870 * to position at is 'lineno'.
2871 * 'from' specifies if the positioning is done as an absolute position SEEK_SET,
2872 * a relative position from the current position SEEK_CUR, or relative to the
2873 * end of file: SEEK_END.
2875 * There are (at least) two ways to do the backup of the current
2876 * position in the file. First to backup to the start of the file
2877 * and then to seek forward, or to seek backwards from the current
2878 * position of the file.
2880 * Perhaps the first is best for the standard case, and the second
2881 * should be activated when the line-parameter is negative ... ?
2884 static int positionfile( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, int lineno, int from )
2886 int from_line=0;
2887 long from_char=0L ;
2888 int ret=0;
2891 * If file is in ERROR state, don't touch it.
2893 if (ptr->flag & FLAG_ERROR)
2895 if (!(ptr->flag & FLAG_FAKE))
2896 file_error( ptr, 0, NULL ) ;
2897 return 0;
2901 * If this isn't a persistent file, then report an error. We can only
2902 * perform repositioning in persistent files.
2905 if (!(ptr->flag & FLAG_PERSIST ))
2906 exiterror( ERR_INCORRECT_CALL, 42, bif, tmpstr_of( TSD, ptr->filename0 ) ) ;
2909 * If the operation is READ, but the file is not open for READ,
2910 * return an error.
2912 if ((oper&OPER_READ) && !(ptr->flag & FLAG_READ))
2913 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "READ" ) ;
2915 * If the operation is WRITE, but the file is not open for WRITE,
2916 * return an error.
2918 if ( (oper&OPER_WRITE) && !(ptr->flag & FLAG_WRITE) )
2919 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "WRITE" ) ;
2922 * If we do any repositioning, then make the old estimate of lines
2923 * left to read invalid. This is not really needed in all cases, but
2924 * it is a good start. And you _may_ even want to recalculate the
2925 * number of lines left!
2927 if ( ptr->linesleft > 0 )
2928 ptr->linesleft = 0 ;
2930 if ( ptr->thispos == (size_t) EOF )
2932 errno = 0 ;
2933 ptr->thispos = ftell( ptr->fileptr ) ;
2937 * So, what we are going to do depends partly on whether we are moving
2938 * the read or the write position of the file. We may even be as
2939 * lucky as not to have to move anything ... :-) First we can clear
2940 * the EOF flag, if set. Repositioning will clean up any EOF state.
2942 if (oper & OPER_READ)
2944 ptr->flag &= ~(FLAG_RDEOF) ;
2945 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
2947 if (oper & OPER_WRITE)
2948 ptr->flag &= ~(FLAG_WREOF) ;
2951 * Positioning by line in a forwards direction is always going to be more efficient
2952 * starting at the current line position and reading forward.
2953 * Positioning by line in a backwards direction may be more efficient to start at the
2954 * beginning of the file and read forwards, rather than reading backwards; it
2955 * depends on how far back we are positioning.
2957 switch( from )
2959 case SEEK_CUR: /* position relative to current position */
2960 if ( oper & OPER_READ )
2962 if ( ptr->readline > 0 )
2964 from_line = ptr->readline ;
2965 from_char = ptr->readpos ;
2966 ret = positionfile_SEEK_CUR( TSD, bif, argno, ptr, OPER_READ, lineno, from_line, from_char );
2968 else
2971 * If the readpos is set, then we can (inefficiently) determine the line
2972 * position by starting at the beginning of the file.
2974 if ( ptr->readpos != (size_t) -1 )
2977 * FIXME: We need a mechanism to convert to readpos into a readline;
2978 * positionfile_SEEK_SET() doesn't do it for us.
2981 errno = 2;
2982 ret = -1;
2983 break;
2985 else
2988 * ERROR:2
2989 * Can't seek lines relatively if there is no current read char position
2991 errno = 2;
2992 ret = -1;
2993 break;
2997 if ( oper & OPER_WRITE )
2999 if ( ptr->writeline > 0 )
3001 from_line = ptr->writeline ;
3002 from_char = ptr->writepos ;
3003 ret = positionfile_SEEK_CUR( TSD, bif, argno, ptr, OPER_WRITE, lineno, from_line, from_char );
3005 else
3008 * If the writepos is set, then we can (inefficiently) determine the line
3009 * position by starting at the beginning of the file.
3011 if ( ptr->writepos != (size_t) -1 )
3014 * FIXME: We need a mechanism to convert to writepos into a writeline;
3015 * positionfile_SEEK_SET() doesn't do it for us.
3018 errno = 2;
3019 ret = -1;
3020 break;
3022 else
3025 * ERROR:2
3026 * Can't seek lines relatively if there is no current write char position
3028 errno = 2;
3029 ret = -1;
3030 break;
3035 * Now we are almost finished. We just have to set the correct
3036 * information in the Rexx file table entry.
3038 if ( (oper & OPER_READ) && (oper & OPER_WRITE) )
3040 ptr->oper = OPER_NONE;
3042 if ( oper & OPER_READ )
3044 ptr->flag &= ~(FLAG_RDEOF) ;
3045 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
3047 if ( oper & OPER_WRITE )
3049 ptr->flag &= ~(FLAG_WREOF) ;
3051 break;
3052 case SEEK_END: /* position relative to end of file */
3053 ret = positionfile_SEEK_END( TSD, bif, argno, ptr, oper, lineno );
3054 break;
3055 case SEEK_SET: /* position absolute */
3056 ret = positionfile_SEEK_SET( TSD, bif, argno, ptr, oper, lineno );
3057 default: /* should not get here */
3058 break;
3060 return ret;
3066 * I wish every function would be as easy as this! Basically, it
3067 * only contain simple error checking, and a direct positioning.
3068 * it is called by the built-in function CHARIN() and CHAROUT()
3069 * in order to position the current read or write position at the
3070 * correct place in the file.
3072 static int positioncharfile( tsd_t *TSD, const char *bif, int argno, fileboxptr fileptr, int oper, long where, int from )
3074 int where_read=0,where_write=0;
3076 * If the file is in state ERROR, don't touch it! Since we are not
3077 * to return any data, don't bother about the state of FAKE.
3079 if (fileptr->flag & FLAG_ERROR)
3081 if (!(fileptr->flag & FLAG_FAKE))
3082 file_error( fileptr, 0, NULL ) ;
3083 return 0;
3087 * If the file is not persistent, then positioning is not allowed.
3088 * Give the appropriate error for this.
3090 if (!(fileptr->flag & FLAG_PERSIST))
3091 exiterror( ERR_INCORRECT_CALL, 42, bif, tmpstr_of( TSD, fileptr->filename0 ) ) ;
3093 * If the operation is READ, but the file is not open for READ,
3094 * return an error.
3096 if ((oper&OPER_READ) && !(fileptr->flag & FLAG_READ))
3097 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "READ" ) ;
3099 * If the operation is WRITE, but the file is not open for WRITE,
3100 * return an error.
3102 if ((oper&OPER_WRITE) && !(fileptr->flag & FLAG_WRITE))
3103 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "WRITE" ) ;
3105 #ifdef TRUE_TRL_IO
3107 * TRL says that positioning the read position to after the last
3108 * character in the file, is an error. Unix allows it, and gives
3109 * an EOF at the next reading. So, we have to handle this as a
3110 * special case ... Check that the new position is valid.
3112 * Should we give "Incorrect call to routine" when the character
3113 * position is greater than the size of the file? Perhaps we should
3114 * raise the NOTREADY condition instead?
3117 long oldp, endp ;
3119 oldp = ftell( fileptr->fileptr ) ;
3120 fseek(fileptr->fileptr, 0, SEEK_END) ;
3121 endp = ftell( fileptr->fileptr ) ;
3122 fseek( fileptr->fileptr, oldp, SEEK_SET ) ;
3123 fileptr->oper = OPER_NONE;
3126 * Determine the value of "where" depending on the starting
3127 * location determined by "from". "where" is passed in in an
3128 * external format; ie 1 based, internally it must be 0 based
3130 switch(from)
3132 case SEEK_CUR:
3133 if ( oper & OPER_READ )
3134 where_read = 1 + where + fileptr->readpos;
3135 if ( oper & OPER_WRITE )
3136 where_write = 1 + where + fileptr->writepos;
3137 break;
3138 case SEEK_END:
3139 if ( oper & OPER_READ )
3140 where_read = endp - where;
3141 #if SEEK_TO_EOF_FOR_WRITE_IS_AT_EOF
3142 if ( oper & OPER_WRITE )
3143 where_write = endp - where;
3144 #else
3145 if ( oper & OPER_WRITE )
3146 where_write = 1 + endp - where;
3147 #endif
3148 break;
3149 default: /* SEEK_SET */
3150 if ( oper & OPER_READ )
3151 where_read = where;
3152 if ( oper & OPER_WRITE )
3153 where_write = where;
3154 break;
3156 if ( oper & OPER_READ )
3158 if ( where_read < 1 )
3160 file_error( fileptr, 0, "Repositioning before start of file" ) ;
3161 return 0;
3163 if ( endp < where_read )
3165 file_error( fileptr, 0, "Repositioning at or after EOF" ) ;
3166 return 0;
3169 if ( oper & OPER_WRITE )
3171 if ( where_write < 1 )
3173 file_error( fileptr, 0, "Repositioning before start of file" ) ;
3174 return 0;
3176 if ( (endp+1) < where_write )
3178 file_error( fileptr, 0, "Repositioning after EOF" ) ;
3179 return 0;
3183 #endif
3186 * Then do the actual positioning. Remember to clear errno first.
3187 * Previously, this code tested afterwards to see if ftell()
3188 * returned the same position that fseek() tried to set. Surely, that
3189 * must be unnecessary?
3190 * We need to reposition using both the read and write postions (if
3191 * required).
3193 errno = 0 ;
3195 * Position the real file pointer to the write or read pointers
3196 * calculated. The "thispos" member is set to the last seek
3197 * executed. READ is done last as this is probably the most
3198 * likely use of character positioning, hence it may be slightly
3199 * more efficient.
3201 if ( oper & OPER_WRITE )
3203 if ( fseek(fileptr->fileptr,(where_write-1),SEEK_SET ) )
3205 file_error( fileptr, errno, NULL ) ;
3206 return 0;
3208 fileptr->thispos = where_write ; /* this was where-1; is that correct ?*/
3210 if ( oper & OPER_READ )
3212 if ( fseek(fileptr->fileptr,(where_read-1),SEEK_SET) )
3214 file_error( fileptr, errno, NULL ) ;
3215 return 0;
3217 fileptr->thispos = where_read ; /* this was where-1; is that correct ?*/
3219 fileptr->oper = OPER_NONE;
3222 * Then we have to update the file pointers in the entry in our
3223 * file table.
3225 * Clear the end-of-file flag. Even if we *did* position to the
3226 * end of file, we don't want to discover that until we actually
3227 * _read_ data that is _off_ the end-of-file.
3230 if (oper & OPER_READ)
3232 fileptr->readpos = where_read-1 ;
3233 fileptr->flag &= ~(FLAG_RDEOF) ;
3234 fileptr->flag &= ~(FLAG_AFTER_RDEOF) ;
3236 if (oper & OPER_WRITE)
3238 fileptr->writepos = where_write-1 ;
3239 fileptr->flag &= ~(FLAG_WREOF) ;
3241 if (oper == OPER_NONE)
3242 file_error( fileptr, 0, NULL ) ;
3245 * We have moved the file pointer by a number of characters which
3246 * may have spanned any number of lines. So we have no idea which
3247 * line we are in the file, so we need to invalidate the
3248 * read or write line position.
3250 if (oper & OPER_READ)
3251 fileptr->readline = 0 ;
3252 if (oper & OPER_WRITE)
3253 fileptr->writeline = 0 ;
3256 * Return the new position of the file pointer. If both file
3257 * pointers were set, then readpos and writepos are the same, so
3258 * the following test is valid.
3260 if (oper & OPER_READ)
3261 return fileptr->readpos + 1; /* external representation */
3262 else
3263 return fileptr->writepos + 1; /* external representation */
3269 * This routine reads a string of data from a file indicated by
3270 * the Rexx file table entry 'ptr'. The read starts at the current
3271 * read position, and the length will be 'length' characters.
3273 * Then, what if the data to be read are more than what is possible
3274 * to store in one string; let's say length=100,000, and the size of
3275 * length in a string is 16 bit. Well, That should return an error
3276 * in Str_makeTSD(), but maybe we should handle it more elegantly?
3278 * No file_error() is thrown if noerrors is set.
3280 static streng *readbytes( tsd_t *TSD, fileboxptr fileptr, int length,
3281 int noerrors )
3283 int didread=0 ;
3284 streng *retvalue=NULL ;
3287 * If state is ERROR, then refuse to handle the file further.
3288 * If the state was 'only' EOF, then don't bother, the length of
3289 * the file might have increased since last attempt to read.
3291 if (fileptr->flag & FLAG_ERROR)
3293 if (!noerrors && !(fileptr->flag & FLAG_FAKE))
3294 file_error( fileptr, 0, NULL ) ;
3295 return nullstringptr() ;
3298 assert( fileptr->flag & FLAG_READ ) ;
3301 * If we are not at the current read position, we have to
3302 * seek to the correct position, but first we have to the validity
3303 * of these positions.
3305 if (fileptr->flag & FLAG_PERSIST)
3307 if (fileptr->thispos != fileptr->readpos)
3309 errno = 0 ;
3310 if ( fileptr->flag & FLAG_PERSIST
3311 && fseek(fileptr->fileptr, fileptr->readpos, SEEK_SET ))
3313 if (!noerrors)
3314 file_error( fileptr, errno, NULL ) ;
3315 return nullstringptr() ;
3317 fileptr->thispos = fileptr->readpos ;
3318 fileptr->oper = OPER_NONE ;
3323 * The joy of POSIX ... If a file is open for input and output, it
3324 * must be flushed when changing between the two. Therefore, check
3325 * the type of the last operation. Actually, this are not very likely
3326 * since that situation would in general have been handled above.
3328 if (fileptr->oper==OPER_WRITE)
3330 errno = 0 ;
3331 if ( fileptr->flag & FLAG_PERSIST
3332 && fseek( fileptr->fileptr, 0L, SEEK_CUR ))
3334 /* Hey, how could this have happened?!?! NFS down? */
3335 if (!noerrors)
3336 file_error( fileptr, errno, NULL ) ;
3337 return nullstringptr() ;
3339 fileptr->oper = OPER_NONE ;
3343 * Lets get ready for the big event. First allocate enough space to
3344 * hold the data we are hoping to be able to read. Then read it
3345 * directly into the string.
3347 retvalue = Str_makeTSD(length+1) ;
3348 errno = 0 ;
3349 didread = fread( retvalue->value, 1, length, fileptr->fileptr ) ;
3350 fileptr->oper = OPER_READ;
3353 * Variable 'read' contains the number of items (=bytes) read, or
3354 * it contains EOF if an error occurred. Handle the error the
3355 * normal way; i.e. trigger file_error and return nothing.
3357 if (didread==EOF)
3359 if (!noerrors)
3360 file_error( fileptr, errno, NULL ) ;
3361 return nullstringptr() ;
3365 * What if we didn't manage to read all the data? Well, return what
3366 * we got, but still trigger an error, since EOF should be
3367 * considered a NOTREADY condition. However, we try to handle EOF
3368 * a bit more elegantly than other errors, since lots of programmers
3369 * are probably not bothering about EOF; an EOF condition should be
3370 * able to be reset using a file positioning.
3372 assert( 0<=didread && didread<=length ) ; /* It'd better be! */
3373 retvalue->len = didread ;
3374 if (didread<length)
3376 if (!noerrors)
3377 file_warning( fileptr, 0, "EOF on char input" ) ;
3378 fileptr->flag |= FLAG_RDEOF ;
3380 else
3382 fileptr->flag &= ~FLAG_RDEOF ;
3383 fileptr->flag &= ~FLAG_AFTER_RDEOF ;
3387 * Then, at the end, we have to set the pointers and counter to
3388 * the correct values
3390 fileptr->thispos += didread ;
3391 fileptr->readpos += didread ;
3392 fileptr->readline = (-1) ;
3393 fileptr->linesleft = 0 ;
3395 return retvalue ;
3401 * This routines write a string to a file pointed to by the Rexx file
3402 * table entry 'fileptr'. The string to be written is 'string', and the
3403 * length of the write is implicitly given as the length of 'string'
3405 * This routine is called from the Rexx built-in function CHAROUT().
3406 * It is a fairly straight forward implementation.
3408 * No file_error() is thrown if noerrors is set.
3410 static int writebytes( tsd_t *TSD, fileboxptr fileptr, const streng *string,
3411 int noerrors )
3413 #ifdef WIN32
3414 int rc=0;
3415 #endif
3416 int todo, done, written=0 ;
3417 const char *buf ;
3420 * First, if this file is in state ERROR, don't touch it, what to
3421 * return depends on whether the file is in state FAKE.
3423 if ( fileptr->flag & FLAG_ERROR )
3425 if ( fileptr->flag & FLAG_FAKE )
3426 return string->len ;
3427 else
3429 if (!noerrors)
3430 file_error( fileptr, 0, NULL ) ;
3431 if (fileptr->flag & FLAG_FAKE)
3432 return string->len ;
3434 return 0 ;
3438 * If we are not at the current write position, we have to
3439 * seek to the correct position
3441 if (fileptr->thispos != fileptr->writepos)
3443 errno = 0 ;
3444 if ( fileptr->flag & FLAG_PERSIST
3445 && fseek(fileptr->fileptr, fileptr->writepos, SEEK_SET ))
3447 if (!noerrors)
3448 file_error( fileptr, errno, NULL ) ;
3449 return 0 ;
3451 fileptr->thispos = fileptr->writepos ;
3452 fileptr->oper = OPER_NONE ;
3456 * If previous operation on this file was a read, we have to flush
3457 * the file before we can perform any write operations. This will
3458 * seldom happen, since it is in general handled above.
3460 if (fileptr->oper == OPER_READ)
3462 errno = 0 ;
3463 if ( fileptr->flag & FLAG_PERSIST
3464 && fseek(fileptr->fileptr, 0, SEEK_CUR))
3466 if (!noerrors)
3467 file_error( fileptr, errno, NULL ) ;
3468 return (fileptr->flag & FLAG_FAKE) ? string->len : 0 ;
3470 fileptr->oper = OPER_NONE ;
3474 * Here comes the actual writing. This also works when the length
3475 * of string is zero.
3477 errno = 0 ;
3478 buf = string->value ;
3479 todo = string->len ;
3480 fileptr->oper = OPER_WRITE ;
3483 #ifdef WIN32
3484 done = fwrite( buf, 1, todo, fileptr->fileptr ) ;
3485 #else
3486 done = fwrite( buf, 1, todo, fileptr->fileptr ) ;
3487 #endif
3489 * Win32 has a bug with fwrite and disk full. If the size of the
3490 * chunk to write is < 4096 and the disk fills up, then you don't get
3491 * an error indication. So flush the stream if the size of data is
3492 * < 4096 and test the result of fflush(). Bug 731664
3494 #ifdef WIN32
3495 if (string->len < 4096 )
3496 rc = fflush( fileptr->fileptr );
3497 if (done < 0 || rc != 0 )
3498 #else
3499 if (done < 0)
3500 #endif
3502 written = -1 ;
3503 break ;
3504 } else if (done == 0)
3505 break;
3506 else
3507 written += done ;
3508 buf += done ;
3509 todo -= done ;
3510 } while ( ( todo > 0 ) && noerrors ) ;
3513 * Here comes the error checking. Note that this function will
3514 * return the number of elements written, it will never return
3515 * EOF as fread can, since the problems surrounding EOF can not
3516 * occur in this operation. Therefore, report a fullfleged error
3517 * whenever rc is less than the length of string.
3519 assert( 0<=written && written<=string->len ) ;
3520 if (written < string->len )
3522 if (!noerrors)
3523 file_error( fileptr, errno, NULL ) ;
3525 else
3528 * If the operation was successful, then we set misc status
3529 * information about the file, and the counters and pointers.
3531 fileptr->writeline = 0 ;
3532 fileptr->flag &= ~FLAG_RDEOF ;
3533 fileptr->flag &= ~FLAG_AFTER_RDEOF ;
3534 fileptr->thispos += written ;
3535 fileptr->writepos += written ;
3537 fflush( fileptr->fileptr ) ;
3538 fileptr->oper = OPER_NONE;
3541 return written ;
3546 * This routine calculates the number of bytes remaining in the file,
3547 * i.e the number of bytes from the current read position until the
3548 * end-of-file. It is, of course, called from the Rexx built-in
3549 * function CHARS()
3552 static int calc_chars_left( tsd_t *TSD, fileboxptr ptr )
3554 int left=0 ;
3555 long oldpoint=0L, newpoint=0L ;
3557 if (! (ptr->flag & FLAG_READ))
3558 return 0 ;
3561 * First, determine whether this file is in ERROR state. If so, we
3562 * don't want to touch it. Whether or not the file is in FAKE state
3563 * is fairly irrelevant in this situation
3565 if ( ptr->flag & FLAG_ERROR )
3567 if (!(ptr->flag & FLAG_FAKE))
3568 file_error( ptr, 0, NULL ) ;
3569 return 0 ;
3573 * If this is not a persistent file, then we have no means of finding
3574 * out how much of the file is available. Then, return 1 if we are not
3575 * at the end-of-file, and 0 otherwise.
3577 if (!(ptr->flag & FLAG_PERSIST))
3579 #if 0
3580 left = ( !(ptr->flag & FLAG_RDEOF)) ;
3581 #else
3582 struct stat finfo;
3583 int fno;
3585 fno = fileno( ptr->fileptr ) ;
3586 fstat( fno, &finfo );
3587 left = finfo.st_size;
3588 #endif
3590 else
3593 * This is a persistent file, which is not in error state. OK, then
3594 * we must record the current point, fseek to the end-of-file,
3595 * ftell to get that position, and fseek back to where we started.
3596 * And we have to check for errors everywhere ... sigh.
3598 * First, record the current position in the file.
3600 errno = 0 ;
3601 oldpoint = ftell( ptr->fileptr ) ;
3602 if (oldpoint==EOF)
3604 file_error( ptr, errno, NULL ) ;
3605 return 0 ;
3609 * Then, move the current position to the end-of-file
3611 errno = 0 ;
3612 if (fseek(ptr->fileptr, 0L, SEEK_END))
3614 file_error( ptr, errno, NULL ) ;
3615 return 0 ;
3617 ptr->oper = OPER_NONE;
3620 * And record the position of the end-of-file
3622 errno = 0 ;
3623 newpoint = ftell( ptr->fileptr ) ;
3624 if (newpoint==EOF)
3626 file_error( ptr, errno, NULL ) ;
3627 return 0 ;
3631 * And, at last, position back to the place where we started.
3632 * Actually, this may not be necessary, since we _can_ leave the
3633 * current position at the end-of-file. After all, the next read
3634 * or write _will_ position back correctly. However, let's be
3635 * nice ...
3637 errno = 0 ;
3638 if (fseek(ptr->fileptr, oldpoint, SEEK_SET))
3640 file_error( ptr, errno, NULL ) ;
3641 return 0 ;
3645 * Then we have some accounting to do; calculate the size of the
3646 * last part of the file. And also set oper to NONE, we _have_
3647 * done a repositioning ... actually, several :-)
3649 left = newpoint - ptr->readpos ;
3650 /* left = newpoint - oldpoint ; */ /* YURI - wrong */
3651 ptr->oper = OPER_NONE ;
3654 return left ;
3659 * This routine counts the complete lines remaining in the file
3660 * pointed to by the Rexx file table entry 'ptr'. The count starts
3661 * at the current read or write position, and the current line will be counted
3662 * even if the current read position points to the middle of a line.
3663 * The last line will only be counted if it was actually terminated
3664 * by a EOL marker. If the current line is the last line, but it was
3665 * not explicitly terminated by a EOL marker, zero is returned.
3667 static int countlines( tsd_t *TSD, fileboxptr ptr, int actual, int oper )
3669 long oldpoint=0L ;
3670 int left=0, ch=0;
3671 int prevch=-1 ;
3674 * If this file is in ERROR state, we really don't want to try to
3675 * operate on it. Just report an error, and return 0.
3677 if ( ptr->flag & FLAG_ERROR )
3679 if (!(ptr->flag & FLAG_FAKE))
3680 file_error( ptr, 0, NULL ) ;
3681 return 0 ;
3685 * Counting lines requires us to reposition in the file. However,
3686 * we can not reposition in transient files. If this is not a
3687 * persistent file, don't do any repositioning, just return one
3688 * for any situation where we are not sure whether there are more
3689 * data or not (i.e. unless we are sure that there are no more data,
3690 * return "1"
3692 if (!(ptr->flag & FLAG_PERSIST) )
3694 return (!feof(ptr->fileptr)) ;
3696 else if ( !actual )
3698 return (calc_chars_left( TSD, ptr )) ? 1 : 0 ;
3700 else
3703 * Take advantage of the cached value of the lines left in the
3704 * file
3706 if (ptr->linesleft)
3707 return ptr->linesleft ;
3710 * If, however, this is a persistent file, we have to read from
3711 * the current read position to the end-of-file, and count all
3712 * the lines. First, make sure that we position at the current
3713 * read position.
3715 errno = 0 ;
3716 oldpoint = ftell( ptr->fileptr ) ;
3717 if (oldpoint==EOF)
3719 file_error( ptr, errno, NULL ) ;
3720 return 0 ;
3724 * Then read the rest of the file, and keep a count of all the files
3725 * read in the process.
3727 SWITCH_OPER_READ(ptr);
3729 * Switch to the current read or write pos setting
3731 if ( oper == OPER_READ )
3732 ptr->thispos = ptr->readpos;
3733 else
3734 ptr->thispos = ptr->writepos;
3735 fseek(ptr->fileptr,ptr->thispos,SEEK_SET);
3736 #if defined(UNIX) || defined(MAC)
3737 for(left=0;((ch=getc(ptr->fileptr))!=EOF);)
3739 if (ch==REGINA_EOL)
3740 left++ ;
3741 prevch = ch;
3743 if (prevch != REGINA_EOL
3744 && prevch != -1)
3745 left++;
3746 #else
3747 for(left=0;;)
3749 ch = getc(ptr->fileptr);
3750 if (ch == EOF)
3751 break;
3752 if ( ch == REGINA_CR)
3753 left++ ;
3754 else
3756 if ( ch == REGINA_EOL && prevch != REGINA_CR)
3757 left++ ;
3759 prevch = ch;
3761 if (prevch != REGINA_EOL
3762 && prevch != REGINA_CR
3763 && prevch != -1)
3764 left++;
3765 #endif
3768 * At the end, try to reposition back to the old current read
3769 * position, and report an error if that attempt failed.
3771 errno = 0 ;
3772 if ( ptr->flag & FLAG_PERSIST
3773 && fseek(ptr->fileptr, oldpoint, SEEK_SET))
3775 file_error( ptr, errno, NULL ) ;
3776 return 0 ;
3778 ptr->oper = OPER_NONE;
3779 ptr->linesleft = left ;
3781 return left ;
3787 * This routine writes a line to the file indicated by 'ptr'. The line
3788 * to be written is 'data', and it will be terminated by an extra
3789 * EOL marker after the charactrers in 'data'.
3791 static int writeoneline( tsd_t *TSD, fileboxptr ptr, const streng *data )
3793 const char *i=NULL ;
3794 int num_eol_chars=0;
3797 * First, make sure that the file is not in ERROR state. If it is
3798 * report an error, and return a result depending on whether this
3799 * file is to be faked.
3801 if (ptr->flag & FLAG_ERROR)
3803 if (ptr->flag & FLAG_FAKE)
3804 return 0 ;
3805 else
3807 file_error( ptr, 0, NULL ) ;
3808 if (ptr->flag & FLAG_FAKE)
3809 return 0 ;
3810 return 1 ;
3815 * If we are to write a new line, we ought to truncate the file after
3816 * that line. Or rather, we truncate the file at the start of the
3817 * new line, before we write it out. But only if we have the non-POSIX
3818 * function ftruncate() available. And not if we are already there.
3820 #if defined(HAVE_FTRUNCATE)
3821 if ( get_options_flag( TSD->currlevel, EXT_LINEOUTTRUNC ) )
3823 if (ptr->oper != OPER_WRITE && !(ptr->flag & (FLAG_WREOF)) &&
3824 (ptr->flag & FLAG_PERSIST))
3826 int fno ;
3827 errno = 0 ;
3828 SWITCH_OPER_WRITE(ptr); /* Maybe, ftruncate is a write operation in
3829 * the meaning of POSIX. This shouldn't do
3830 * any harm in other systems.
3833 fno = fileno( ptr->fileptr ) ;
3834 if (ftruncate( fno, ptr->writepos) == -1)
3836 file_error( ptr, errno, NULL ) ;
3837 return !(ptr->flag & FLAG_FAKE) ;
3839 if ( ptr->flag & FLAG_PERSIST )
3840 fseek( ptr->fileptr, 0, SEEK_END ) ;
3841 ptr->oper = OPER_NONE;
3842 ptr->thispos = ptr->writepos = ftell( ptr->fileptr ) ;
3843 if (ptr->readpos>ptr->thispos && ptr->readpos!= (size_t) EOF)
3845 ptr->readpos = ptr->thispos ;
3846 ptr->readline = 0 ;
3847 ptr->linesleft = 0 ;
3851 #endif
3854 * Then, output the characters in 'data', and sense any problem.
3855 * If there is a problem, report an error
3857 errno = 0 ;
3858 SWITCH_OPER_WRITE(ptr);
3859 for (i=data->value; i<Str_end(data); i++)
3861 if (putc( *i, ptr->fileptr)==EOF)
3863 file_error( ptr, errno, NULL ) ;
3864 return 1 ;
3869 * After all the data has been written out, we have to explicitly
3870 * terminate the file with an end-of-line marker. Under Unix this
3871 * is the single character EOL. Under Macintosh this is the single
3872 * character CR, and all others it is CR and EOL.
3874 #if !defined(UNIX)
3875 SWITCH_OPER_WRITE(ptr);
3876 if (putc( REGINA_CR, ptr->fileptr)==EOF)
3878 file_error( ptr, errno, NULL ) ;
3879 return 1 ;
3881 num_eol_chars++;
3882 #endif
3883 #if !defined(MAC)
3884 SWITCH_OPER_WRITE(ptr);
3885 if (putc( REGINA_EOL, ptr->fileptr)==EOF)
3887 file_error( ptr, errno, NULL ) ;
3888 return 1 ;
3890 num_eol_chars++;
3891 #endif
3894 * Then we have to update the counters and pointers in the Rexx
3895 * file table entry. We must do that in order to be able to keep
3896 * track of where we are.
3898 ptr->thispos += data->len + num_eol_chars ; /* fix 736578 */
3899 ptr->writepos = ptr->thispos ;
3900 ptr->oper = OPER_WRITE ;
3903 * FIXME - under what circumstances will writeline be 0 ?
3904 * If it hasn't been determined by what calls this function, the
3905 * a) the calling function(s) should determine the line number, or
3906 * b) the current line number should be determined here.
3908 if (ptr->writeline)
3909 ptr->writeline++ ;
3911 ptr->flag |= FLAG_WREOF ;
3914 * At the end, we flush the data. We do this in order to avoid
3915 * surprises later. Maybe we shouldn't do that, since it may force
3916 * a systemcall, which might give away the timeslice and decrease
3917 * system time. So you might want to remove this call ... at your
3918 * own risk :-)
3920 errno = 0 ;
3921 if (fflush( ptr->fileptr ))
3923 file_error( ptr, errno, NULL ) ;
3924 return 1 ;
3927 return 0 ;
3931 * This routine is a way of retrieving the information returned by the
3932 * standard Unix call stat(). It takes the name of a file as parameter,
3933 * and return information about that file. This is not standard Rexx,
3934 * but quite useful. It is accessed through the built-in function
3935 * STREAM(), command 'FSTAT'
3936 * This is now also used for the "standard" STREAM() options.
3937 * *Persistent will be set to 1 if the stream's type is a file. The setting
3938 * happens on success and if Persistent isn't NULL.
3940 static streng *getstatus( tsd_t *TSD, const streng *filename , int subcommand )
3942 fileboxptr ptr=NULL ;
3943 int rc=0 ;
3944 int fno=0 ;
3945 long pos_read = -2L, pos_write = -2L, line_read = -2L, line_write = -2;
3946 int streamtype = STREAMTYPE_UNKNOWN;
3947 streng *result=NULL ;
3948 struct stat buffer ;
3949 struct tm tmdata, *tmptr ;
3950 char *fn=NULL;
3951 #if 0
3952 static const char *fmt = "%02d-%02d-%02d %02d:%02d:%02d" ;
3953 static const char *iso = "%04d-%02d-%02d %02d:%02d:%02d" ;
3954 #endif
3955 static const char *streamdesc[] = { "UNKNOWN", "PERSISTENT", "TRANSIENT" };
3956 char tmppwd[50];
3957 char tmpgrp[50];
3958 char *ptmppwd=tmppwd,*ptmpgrp=tmpgrp;
3959 #if !(defined(VMS) || defined(MAC) || defined(OS2) || defined(DOS) || (defined (__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__LCC__) || defined(__AROS__))
3960 struct passwd *ppwd;
3961 struct group *pgrp;
3962 #endif
3964 memset( &buffer, 0, sizeof(buffer) );
3966 * Nul terminate the input filename string, as stat() will barf if
3967 * it isn't and other functions stuff up!
3969 fn = str_ofTSD(filename);
3971 * First get the Rexx file table entry associated with the file,
3972 * and then call stat() for that file. If the file is already open,
3973 * then call fstat, since that will in general be a 'safer' way
3974 * to be sure that it is _really_ the file that is open in Rexx.
3976 ptr = getfileptr( TSD, filename ) ;
3977 if (ptr && ptr->fileptr)
3979 fno = fileno( ptr->fileptr ) ;
3980 rc = fstat( fno, &buffer ) ;
3981 if (ptr->flag & FLAG_PERSIST)
3982 streamtype = STREAMTYPE_PERSISTENT;
3983 else
3984 streamtype = STREAMTYPE_TRANSIENT;
3985 pos_read = ptr->readpos;
3986 pos_write = ptr->writepos;
3987 line_read = ptr->readline;
3988 line_write = ptr->writeline;
3990 else
3993 * To be consistent with other functions when determining persistence,
3994 * we need to check for a "regular" file. Everything other than a
3995 * "regular" file in transient.
3996 * If we don't have S_ISREG macro, then revert to a simple check; if the
3997 * stream is a directory it is transent; ugly!
3999 rc = stat( fn, &buffer ) ;
4000 if ( rc != 0 )
4001 streamtype = STREAMTYPE_UNKNOWN;
4002 else
4005 * Resolves 802114.
4007 streamtype = stream_types[determine_stream_type( buffer.st_mode )].streamtype;
4012 * If we were able to retrieve any useful information, store it
4013 * in a string of suitable length, and return that string.
4014 * If the filename does not exist, always return an empty string.
4016 if ( rc == -1 )
4018 if ( fn )
4019 FreeTSD( fn );
4020 checkProperStreamName( TSD,
4021 NULL,
4022 (const char *) tmpstr_of( TSD, filename ),
4023 errno );
4024 return nullstringptr();
4026 switch ( subcommand )
4028 case COMMAND_FSTAT:
4029 #ifdef HAVE_LSTAT
4031 * If we have lstat(), use it to gather details, this is the only
4032 * way to determine if the file is a symlink.
4034 lstat(fn, &buffer) ;
4035 #endif
4036 #if defined(VMS) || defined(MAC) || defined(OS2) || defined(DOS) || (defined (__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__LCC__) || defined(_AMIGA) || defined(__AROS__)
4037 ptmppwd = "USER";
4038 ptmpgrp = "GROUP";
4039 #else
4040 ppwd = getpwuid( buffer.st_uid );
4041 if ( ppwd )
4042 ptmppwd = ppwd->pw_name;
4043 else
4044 sprintf( tmppwd, "%d", buffer.st_uid );
4046 pgrp = getgrgid( buffer.st_gid );
4047 if ( pgrp )
4048 ptmpgrp = pgrp->gr_name;
4049 else
4050 sprintf( tmpgrp, "%d", buffer.st_gid );
4051 #endif
4052 result = Str_makeTSD( 128 ) ;
4053 if ( sizeof(off_t) == 8 )
4054 sprintf( result->value,
4055 "%ld %ld %03o %d %s %s %lld",
4056 (long)(buffer.st_dev), (long)(buffer.st_ino),
4057 buffer.st_mode & ACCESSPERMS, buffer.st_nlink,
4058 ptmppwd, ptmpgrp,
4059 (long long)(buffer.st_size) ) ;
4060 else
4061 sprintf( result->value,
4062 "%ld %ld %03o %d %s %s %ld",
4063 (long)(buffer.st_dev), (long)(buffer.st_ino),
4064 buffer.st_mode & ACCESSPERMS, buffer.st_nlink,
4065 ptmppwd, ptmpgrp,
4066 (long)(buffer.st_size) ) ;
4068 * Append the stream type name...
4070 strcat( result->value, stream_types[determine_stream_type( buffer.st_mode )].streamname );
4071 break;
4072 case COMMAND_QUERY_EXISTS:
4073 if ( streamtype == STREAMTYPE_TRANSIENT )
4075 result = nullstringptr();
4077 else
4079 result = Str_makeTSD( REXX_PATH_MAX );
4080 my_fullpath( result->value, fn );
4081 result->len = strlen( result->value );
4083 break;
4084 case COMMAND_QUERY_SIZE:
4085 if ( streamtype == STREAMTYPE_TRANSIENT )
4087 result = nullstringptr() ;
4089 else
4091 result = Str_makeTSD( 50 ) ;
4092 if ( sizeof(off_t) == 8 )
4093 sprintf( result->value, "%lld", (long long)(buffer.st_size) ) ;
4094 else
4095 sprintf( result->value, "%ld", (long)(buffer.st_size) ) ;
4097 break;
4098 case COMMAND_QUERY_HANDLE:
4099 if (fno)
4101 result = Str_makeTSD( 10 ) ;
4102 sprintf( result->value, "%d", fno ) ;
4104 else
4105 result = nullstringptr() ;
4106 break;
4107 case COMMAND_QUERY_STREAMTYPE:
4108 result = Str_makeTSD( 12 ) ;
4109 sprintf( result->value, "%s", streamdesc[streamtype] ) ;
4110 break;
4111 case COMMAND_QUERY_DATETIME:
4112 if ( streamtype == STREAMTYPE_TRANSIENT )
4114 result = nullstringptr() ;
4116 else
4118 time_t num64;
4119 num64 = buffer.st_mtime;
4120 if ( ( tmptr = localtime( &num64 ) ) != NULL )
4121 tmdata = *tmptr;
4122 else
4123 memset(&tmdata,0,sizeof(tmdata)); /* what shall we do in this case? */
4124 result = Str_makeTSD( 20 ) ;
4125 #if 0
4126 sprintf( result->value, fmt, tmdata.tm_mon+1, tmdata.tm_mday,
4127 (tmdata.tm_year % 100), tmdata.tm_hour, tmdata.tm_min,
4128 tmdata.tm_sec ) ;
4129 #else
4130 strftime( result->value, 20, "%m-%d-%y %H:%M:%S", &tmdata );
4131 #endif
4133 break;
4134 case COMMAND_QUERY_TIMESTAMP:
4135 if ( streamtype == STREAMTYPE_TRANSIENT )
4137 result = nullstringptr() ;
4139 else
4141 time_t num64;
4142 num64 = buffer.st_mtime;
4143 if ( ( tmptr = localtime( &num64 ) ) != NULL )
4144 tmdata = *tmptr;
4145 else
4146 memset( &tmdata, 0, sizeof(tmdata) ); /* what shall we do in this case? */
4147 result = Str_makeTSD( 20 ) ;
4148 #if 0
4149 sprintf( result->value, iso, tmdata.tm_year+1900, tmdata.tm_mon+1,
4150 tmdata.tm_mday,
4151 tmdata.tm_hour, tmdata.tm_min,
4152 tmdata.tm_sec ) ;
4153 #else
4154 strftime( result->value, 20, "%Y-%m-%d %H:%M:%S", &tmdata );
4155 #endif
4157 break;
4158 case COMMAND_QUERY_POSITION_READ_CHAR:
4159 case COMMAND_QUERY_POSITION_SYS:
4160 if (pos_read != (-2))
4162 result = Str_makeTSD( 50 ) ;
4163 sprintf( result->value, "%ld", pos_read + 1) ;
4165 else
4166 result = nullstringptr() ;
4167 break;
4168 case COMMAND_QUERY_POSITION_WRITE_CHAR:
4169 if (pos_write != (-2))
4171 result = Str_makeTSD( 50 ) ;
4172 sprintf( result->value, "%ld", pos_write + 1) ;
4174 else
4175 result = nullstringptr() ;
4176 break;
4177 case COMMAND_QUERY_POSITION_READ_LINE:
4178 if (line_read != (-2))
4180 result = Str_makeTSD( 50 ) ;
4181 sprintf( result->value, "%ld", line_read ) ;
4183 else
4184 result = nullstringptr() ;
4185 break;
4186 case COMMAND_QUERY_POSITION_WRITE_LINE:
4187 if ( line_write == 0 )
4189 long here;
4190 long char_count;
4191 int ch;
4193 * When a file is first opened for both read and
4194 * write (default for implicit open), it is inexpensive
4195 * to determine pos_read, pos_write and line_read, but
4196 * is very expensive to determine line_write, so we
4197 * don't do it. It is set to 0 indicating that we don't
4198 * know the current write position. So to reduce the
4199 * cost when we may never use it, we have to pay the
4200 * price the first time we need the value; this is
4201 * one of the times we pay the price!
4203 result = Str_makeTSD( 50 ) ;
4205 * We can't use countlines(), so do our our counting
4206 * of lines form the beginning of the file to the current
4207 * write pos...
4209 here = ftell( ptr->fileptr );
4210 fseek( ptr->fileptr, 0L, SEEK_SET );
4211 SWITCH_OPER_READ(ptr);
4212 for( char_count = 0, line_write = 0; char_count < (long) ptr->writepos; char_count++ )
4214 ch = getc( ptr->fileptr );
4215 if ( ch == EOF )
4216 break;
4217 if ( ch == REGINA_EOL )
4218 line_write++;
4220 sprintf( result->value, "%ld", line_write+1 ) ;
4221 fseek( ptr->fileptr, here, SEEK_SET );
4223 else if (line_write != (-2))
4225 result = Str_makeTSD( 50 ) ;
4226 sprintf( result->value, "%ld", line_write ) ;
4228 else
4229 result = nullstringptr() ;
4230 break;
4232 result->len = strlen( result->value ) ;
4234 if ( fn )
4235 FreeTSD( fn );
4236 return result ;
4241 * This little sweet routine returns information stored in the Rexx
4242 * file table entry about the named file 'filename'. It is perhaps more
4243 * of a debugging function than a Rexx function. It is accessed by the
4244 * Rexx built-in function STREAM(), command 'STATUS'. One of the nice
4245 * pieces of information this function returns is whether a file is
4246 * transient or persistent.
4248 * This is really a simple function, just retrieve the Rexx file
4249 * table entry, and store the information in that entry into a string
4250 * and return that string.
4252 * The difference between getrexxstatus() and getstatus() is that
4253 * that former returns information stored in Rexx's datastructures,
4254 * while the latter return information about the file stored in and
4255 * managed by the operating system
4257 static streng *getrexxstatus( const tsd_t *TSD, cfileboxptr ptr )
4259 streng *result=NULL ;
4261 if (ptr==NULL)
4262 return nullstringptr() ;
4264 result = Str_makeTSD(64) ; /* Ought to be enough */
4265 result->value[0] = 0x00 ;
4267 if ((ptr->flag & FLAG_READ) && (ptr->flag & FLAG_WRITE))
4268 strcat( result->value, "READ/WRITE" ) ;
4269 else if (ptr->flag & FLAG_READ)
4270 strcat( result->value, "READ" ) ;
4271 else if (ptr->flag & FLAG_WRITE)
4272 strcat( result->value, "WRITE" ) ;
4273 else
4274 strcat( result->value, "NONE" ) ;
4276 sprintf( result->value + strlen(result->value),
4277 " READ: char=%ld line=%d WRITE: char=%ld line=%d %s",
4278 (long)(ptr->readpos+1), ptr->readline,
4279 (long)(ptr->writepos+1), ptr->writeline,
4280 (ptr->flag & FLAG_PERSIST) ? "PERSISTENT" : "TRANSIENT" ) ;
4282 result->len = strlen(result->value) ;
4283 return result ;
4288 * This routine parses the remainder of the parameters passed to the
4289 * Stream(,'C','QUERY...') function.
4291 static streng *getquery( tsd_t *TSD, const streng *filename , const streng *subcommand)
4293 streng *result=NULL, *psub=NULL, *psubsub=NULL ;
4294 char oper = 0;
4295 char seek_oper = 0;
4298 * Get the subcommand to QUERY
4300 oper = get_querycommand( subcommand );
4301 switch ( oper )
4303 case COMMAND_QUERY_DATETIME :
4304 case COMMAND_QUERY_TIMESTAMP :
4305 case COMMAND_QUERY_EXISTS :
4306 case COMMAND_QUERY_HANDLE :
4307 case COMMAND_QUERY_SIZE :
4308 case COMMAND_QUERY_STREAMTYPE :
4309 result = getstatus( TSD, filename, oper );
4310 break;
4311 case COMMAND_QUERY_SEEK :
4312 case COMMAND_QUERY_POSITION :
4313 if ( oper == COMMAND_QUERY_SEEK )
4315 psub = Str_nodupTSD( subcommand, 4, subcommand->len - 4 );
4316 seek_oper = 1;
4318 else
4320 psub = Str_nodupTSD( subcommand, 8, subcommand->len - 8 );
4321 seek_oper = 0;
4323 psub = Str_strp( psub, ' ', STRIP_LEADING);
4324 oper = get_querypositioncommand( psub );
4325 switch ( oper )
4327 case COMMAND_QUERY_POSITION_SYS :
4328 result = getstatus(TSD, filename, oper );
4329 break;
4330 case COMMAND_QUERY_POSITION_READ :
4331 psubsub = Str_nodupTSD( psub, 4, psub->len - 4 );
4332 psubsub = Str_strp( psubsub, ' ', STRIP_LEADING);
4333 oper = get_querypositionreadcommand( psubsub );
4334 switch( oper )
4336 case COMMAND_QUERY_POSITION_READ_CHAR:
4337 case COMMAND_QUERY_POSITION_READ_LINE:
4338 result = getstatus( TSD, filename, oper );
4339 break;
4340 default:
4341 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK READ":"QUERY POSITION READ", "CHAR LINE ''", tmpstr_of( TSD, psubsub ) ) ;
4342 break;
4344 break;
4345 case COMMAND_QUERY_POSITION_WRITE :
4346 psubsub = Str_nodupTSD( psub, 5, psub->len - 5 );
4347 psubsub = Str_strp( psubsub, ' ', STRIP_LEADING);
4348 oper = get_querypositionwritecommand( psubsub );
4349 switch( oper )
4351 case COMMAND_QUERY_POSITION_WRITE_CHAR:
4352 case COMMAND_QUERY_POSITION_WRITE_LINE:
4353 result = getstatus( TSD, filename, oper );
4354 break;
4355 default:
4356 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK WRITE":"QUERY POSITION WRITE", "CHAR LINE ''", tmpstr_of( TSD, psubsub ) ) ;
4357 break;
4359 break;
4360 default:
4361 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK":"QUERY POSITION", "READ WRITE SYS", tmpstr_of( TSD, psub ) ) ;
4362 break;
4364 Free_stringTSD(psub);
4365 break;
4366 default:
4367 exiterror( ERR_STREAM_COMMAND, 1, "QUERY", "DATETIME TIMESTAMP EXISTS HANDLE SIZE STREAMTYPE SEEK POSITION", tmpstr_of( TSD, subcommand ) ) ;
4368 break;
4371 return result ;
4375 * This routine parses the remainder of the parameters passed to the
4376 * Stream(,'C','OPEN...') function.
4378 static streng *getopen( tsd_t *TSD, const streng *filename , const streng *subcommand)
4380 fileboxptr ptr=NULL ;
4381 streng *result=NULL, *psub=NULL ;
4382 char oper = 0;
4383 char buf[20];
4386 * Get the subcommand to OPEN
4388 oper = get_opencommand( subcommand );
4389 switch ( oper )
4391 case COMMAND_OPEN_BOTH :
4392 if ( subcommand->len >= 4
4393 && memcmp(subcommand->value, "BOTH", 4) == 0 )
4394 psub = Str_nodupTSD( subcommand, 4, subcommand->len - 4 );
4395 else
4396 psub = Str_dupTSD( subcommand );
4397 psub = Str_strp( psub, ' ', STRIP_LEADING);
4398 oper = get_opencommandboth( psub );
4399 if ( TSD->restricted )
4400 exiterror( ERR_RESTRICTED, 4 ) ;
4401 switch ( oper )
4403 case COMMAND_OPEN_BOTH :
4404 closefile( TSD, filename ) ;
4405 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
4406 break;
4407 case COMMAND_OPEN_BOTH_APPEND :
4408 closefile( TSD, filename ) ;
4409 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND ) ;
4410 break;
4411 case COMMAND_OPEN_BOTH_REPLACE :
4412 closefile( TSD, filename ) ;
4413 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE ) ;
4414 break;
4415 default:
4416 exiterror( ERR_STREAM_COMMAND, 1, "OPEN BOTH", "APPEND REPLACE ''", tmpstr_of( TSD, psub ) ) ;
4417 break;
4419 Free_stringTSD(psub);
4420 if (ptr->fileptr)
4421 result = Str_creTSD( "READY:" ) ;
4422 else
4424 sprintf(buf,"ERROR:%d",errno);
4425 result = Str_creTSD( buf ) ;
4427 break;
4428 case COMMAND_OPEN_READ :
4429 closefile( TSD, filename ) ;
4430 ptr = openfile( TSD, filename, ACCESS_READ ) ;
4431 if (ptr->fileptr)
4432 result = Str_creTSD( "READY:" ) ;
4433 else
4435 sprintf(buf,"ERROR:%d",errno);
4436 result = Str_creTSD( buf ) ;
4438 break;
4439 case COMMAND_OPEN_WRITE :
4440 if ( TSD->restricted )
4441 exiterror( ERR_RESTRICTED, 4 ) ;
4442 psub = Str_nodupTSD( subcommand, 5, subcommand->len - 5 );
4443 psub = Str_strp( psub, ' ', STRIP_LEADING);
4444 oper = get_opencommandwrite( psub );
4446 switch ( oper )
4448 case COMMAND_OPEN_WRITE :
4449 closefile( TSD, filename ) ;
4450 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
4451 break;
4452 case COMMAND_OPEN_WRITE_APPEND :
4453 closefile( TSD, filename ) ;
4454 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND ) ;
4455 break;
4456 case COMMAND_OPEN_WRITE_REPLACE :
4457 closefile( TSD, filename ) ;
4458 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE ) ;
4459 break;
4460 default:
4461 exiterror( ERR_STREAM_COMMAND, 1, "OPEN WRITE", "APPEND REPLACE ''", tmpstr_of( TSD, psub ) ) ;
4462 break;
4464 Free_stringTSD(psub);
4465 if (ptr->fileptr)
4466 result = Str_creTSD( "READY:" ) ;
4467 else
4469 sprintf(buf,"ERROR:%d",errno);
4470 result = Str_creTSD( buf ) ;
4472 break;
4473 default:
4474 exiterror( ERR_STREAM_COMMAND, 1, "OPEN", "BOTH READ WRITE ''", tmpstr_of( TSD, subcommand ) ) ;
4475 break;
4478 return result ;
4482 static streng *getseek( tsd_t *TSD, const streng *filename, const streng *cmd )
4484 #define STATE_START 0
4485 #define STATE_WORD 1
4486 #define STATE_DELIM 2
4487 char *word[5] = {NULL,NULL,NULL,NULL};
4488 char *str;
4489 char *offset=NULL;
4490 int i,j=0;
4491 int state=STATE_START;
4492 int seek_by_line=0;
4493 int seek_type=0;
4494 int seek_sign=0;
4495 long seek_offset=0,pos=0;
4496 int pos_type=OPER_NONE,num_params=0;
4497 int str_start=0,str_end=(-1);
4498 fileboxptr ptr;
4499 streng *result=NULL;
4500 char buf[20];
4502 str = str_ofTSD(cmd);
4503 for (i=0;i<Str_len(cmd);i++)
4505 switch(state)
4507 case STATE_START:
4508 if (*(str+i) == ' ')
4510 state = STATE_DELIM;
4511 break;
4513 if ( j < 3 )
4514 word[j] = str+str_start;
4515 j++;
4516 if (str_end != (-1))
4518 *(str+str_end) = '\0';
4520 state = STATE_WORD;
4521 break;
4522 case STATE_WORD:
4523 if (*(str+i) == ' ')
4525 state = STATE_DELIM;
4526 str_end = i;
4527 str_start = str_end + 1;
4528 break;
4530 break;
4531 case STATE_DELIM:
4532 state = STATE_WORD;
4533 if (*(str+i) == ' ')
4535 state = STATE_DELIM;
4537 if (state == STATE_WORD)
4539 if ( j < 3 )
4540 word[j] = str+str_start;
4541 j++;
4542 if (str_end != (-1))
4544 *(str+str_end) = '\0';
4547 break;
4550 num_params = j;
4551 if (num_params < 1)
4552 exiterror( ERR_INCORRECT_CALL, 922, "STREAM", 3, 2, num_params+1 );
4553 if (num_params > 3)
4554 exiterror( ERR_INCORRECT_CALL, 923, "STREAM", 3, 4, num_params+1 );
4556 switch( num_params )
4558 case 3:
4559 if (strcmp(word[2],"CHAR") == 0)
4560 seek_by_line = 0;
4561 else
4563 if (strcmp(word[2],"LINE") == 0)
4564 seek_by_line = 1;
4565 else
4566 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "CHAR LINE", word[2] );
4568 /* meant to fall through */
4569 case 2:
4571 * 2 params(to SEEK), last one (word[1]) could be READ/WRITE or CHAR/LINE
4573 if (strcmp(word[1],"READ") == 0)
4574 pos_type = OPER_READ;
4575 else if (strcmp(word[1],"WRITE") == 0)
4576 pos_type = OPER_WRITE;
4577 else if (strcmp(word[1],"CHAR") == 0)
4578 seek_by_line = 0;
4579 else if (strcmp(word[1],"LINE") == 0)
4580 seek_by_line = 1;
4581 else
4582 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "READ WRITE CHAR LINE", word[1] );
4585 * Determine the position type if not supplied prior
4587 if ( pos_type == OPER_NONE )
4589 ptr = getfileptr( TSD, filename ) ;
4590 if ( ptr != NULL )
4592 if ( ptr->flag & FLAG_READ )
4593 pos_type |= OPER_READ;
4594 if ( ptr->flag & FLAG_WRITE )
4595 pos_type |= OPER_WRITE;
4598 offset = word[0];
4599 switch(*offset)
4601 case '=':
4602 seek_type = SEEK_SET;
4603 offset++;
4604 break;
4605 case '-':
4606 seek_type = SEEK_CUR;
4607 seek_sign = 1;
4608 offset++;
4609 break;
4610 case '+':
4611 seek_type = SEEK_CUR;
4612 seek_sign = 0;
4613 offset++;
4614 break;
4615 case '<':
4616 seek_type = SEEK_END;
4617 offset++;
4618 break;
4619 default:
4620 seek_type = SEEK_SET;
4621 break;
4623 for (i=0;i<(int)strlen(offset);i++)
4625 if (!rx_isdigit(*(offset+i)))
4626 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "n, +n, -n, =n or <n", word[0] );
4628 seek_offset = atol(offset);
4629 if (seek_sign) /* negative */
4630 seek_offset *= -1;
4631 ptr = get_file_ptr( TSD, filename, pos_type, (pos_type&OPER_WRITE) ? ACCESS_WRITE : ACCESS_READ ) ;
4632 if (!ptr)
4634 sprintf(buf,"ERROR:%d",errno);
4635 result = Str_creTSD( buf ) ;
4637 if (seek_by_line) /* position by line */
4638 pos = positionfile( TSD, "STREAM", 3, ptr, pos_type, seek_offset, seek_type ) ;
4639 else
4640 pos = positioncharfile( TSD, "STREAM", 3, ptr, pos_type, seek_offset, seek_type ) ;
4641 if ( pos >= 0 )
4643 result = Str_makeTSD( 20 ) ; /* should be enough digits */
4644 sprintf(result->value, "%ld", pos );
4645 Str_len( result ) = strlen( result->value );
4647 else
4649 sprintf(buf,"ERROR:%d",errno);
4650 result = Str_creTSD( buf ) ;
4652 FreeTSD(str);
4653 return result ;
4658 /* ------------------------------------------------------------------- */
4659 /* Rexx builtin functions (level 3) */
4661 * This part consists of one function for each of the Rexx builtin
4662 * functions that operates on filesystem I/O
4667 * This routine implements the Rexx built-in function CHARS(). It is
4668 * really quite simple, little more than a wrap-around to the
4669 * function calc_chars_left.
4671 streng *std_chars( tsd_t *TSD, cparamboxptr parms )
4673 char opt = 'N';
4674 streng *string=NULL ;
4675 fileboxptr ptr=NULL ;
4676 int was_closed=0, result=0 ;
4677 fil_tsd_t *ft;
4679 ft = (fil_tsd_t *)TSD->fil_tsd;
4681 /* Syntax: chars([filename]) */
4682 checkparam( parms, 0, 2 , "CHARS" ) ;
4684 if (parms&&parms->next&&parms->next->value)
4685 opt = getoptionchar( TSD, parms->next->value, "CHARS", 2, "CN", "" ) ;
4687 (void)opt; // Unused for now
4689 string = (parms->value && parms->value->len) ? parms->value : ft->stdio_ptr[0]->filename0 ;
4691 * Get a pointer to the Rexx file table entry of the file, and
4692 * calculate the number of characters left.
4694 ptr = getfileptr( TSD, string ) ;
4695 was_closed = (ptr==NULL) ;
4696 if (!ptr)
4697 ptr = get_file_ptr( TSD, string, OPER_READ, ACCESS_READ ) ;
4699 result = calc_chars_left( TSD, ptr ) ;
4700 if (was_closed)
4701 closefile( TSD, string ) ;
4703 return int_to_streng( TSD, result ) ;
4709 * Implements the Rexx builtin function charin(). This function takes
4710 * three parameters, and they are treated pretty straight forward
4711 * according to TRL. If called with no start position, and a length of
4712 * zero, it may be used to do some fancy work (flushing I/O?), although
4713 * that is probably more needed for output :-) Note that the file in
4714 * entered into the file table in this case, so it might be used to
4715 * explicitly open a file for reading. However, consider using stream()
4716 * to do this, it's a much cleaner approach!
4718 streng *std_charin( tsd_t *TSD, cparamboxptr parms )
4720 streng *filename=NULL, *result=NULL ;
4721 fileboxptr ptr=NULL ;
4722 int length=0 ;
4723 long start=0 ;
4724 fil_tsd_t *ft;
4726 ft = (fil_tsd_t *)TSD->fil_tsd;
4728 /* Syntax: charin([filename][,[start][,length]]) */
4729 checkparam( parms, 0, 3 , "CHARIN" ) ;
4732 * First, let's get the information about the file from the
4733 * file table, and open it in the correct mode if is not already
4734 * availble as such.
4736 filename = (parms->value && parms->value->len) ? (parms->value) : ft->stdio_ptr[0]->filename0 ;
4737 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
4740 * Then, get the starting point, or set it to zero.
4742 parms = parms->next ;
4743 if ((parms)&&(parms->value))
4744 start = atopos( TSD, parms->value, "CHARIN", 2 ) ;
4745 else
4746 start = 0 ;
4749 * At last, get the length, or use the default value one.
4751 if (parms)
4752 parms = parms->next ;
4754 if ((parms)&&(parms->value))
4755 length = atozpos( TSD, parms->value, "CHARIN", 3 ) ;
4756 else
4757 length = 1 ;
4760 * Position current position in file if necessary
4762 if (start)
4763 positioncharfile( TSD, "CHARIN", 2, ptr, OPER_READ, start, SEEK_SET ) ;
4765 if (length)
4766 result = readbytes( TSD, ptr, length, 0 ) ;
4767 else
4769 if (!start)
4770 flush_input( ptr ) ; /* Whatever happens ... */
4771 result = nullstringptr() ;
4774 return result ;
4780 * This function implements the Rexx built-in function CHAROUT(). It
4781 * is basically a wrap-around for the two functions that perform
4782 * character repositioning in a file; and writes out characters.
4785 streng *std_charout( tsd_t *TSD, cparamboxptr parms )
4787 streng *filename=NULL, *string=NULL ;
4788 int length=0 ;
4789 long pos=0 ;
4790 fileboxptr ptr=NULL ;
4791 fil_tsd_t *ft;
4793 ft = (fil_tsd_t *)TSD->fil_tsd;
4795 if ( TSD->restricted )
4796 exiterror( ERR_RESTRICTED, 1, "CHAROUT" ) ;
4798 /* Syntax: charout([filename][,[string][,start]]) */
4799 checkparam( parms, 0, 3 , "CHAROUT" ) ;
4801 filename = (parms->value && parms->value->len) ? (parms->value) : ft->stdio_ptr[1]->filename0 ;
4803 /* Read the data to be written, if any */
4804 parms = parms->next ;
4805 if (parms && parms->value )
4806 string = parms->value ;
4807 else
4808 string = NULL ;
4810 /* Read the position to start writing, is any */
4811 if (parms)
4812 parms = parms->next ;
4814 if ( parms && parms->value )
4815 pos = atopos( TSD, parms->value, "CHAROUT", 3 ) ;
4816 else
4817 pos = 0 ;
4819 ptr = get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE ) ;
4822 * If we are to position the write position somewhere, do that first.
4824 if (pos)
4825 positioncharfile( TSD, "CHAROUT", 3, ptr, OPER_WRITE, pos, SEEK_SET ) ;
4828 * Then, write the actual data, or flush output if neither data nor
4829 * position was given.
4831 if (string)
4832 length = string->len - writebytes( TSD, ptr, string, 0 ) ;
4833 else
4835 length = 0;
4836 if ( !pos )
4839 * flush_output() will swap out the file and close it, but leave ALL positions
4840 * intact
4841 * We need to set the write positions to end of file (NOT EOF)
4842 * See ANSI 9.7.2, 9.7.5, A.5.8.9
4843 * For efficiency sake, we will have to set writeline = 0 :-(
4844 * We do this BEFORE flush_output() otherwise we won't have a ptr->fileptr!
4846 if ( ptr->flag & FLAG_PERSIST )
4848 fseek( ptr->fileptr, 0, SEEK_END ) ;
4849 ptr->writepos = ftell( ptr->fileptr ) ;
4851 else
4852 ptr->writepos = 0;
4853 ptr->writeline = 0;
4855 if ( flush_output( TSD, ptr ) == -1 )
4856 length = 1; /* simulate (at least) 1 byte not written */
4860 return int_to_streng( TSD, length ) ;
4866 * Simple routine that implements the Rexx built-in function LINES().
4867 * Really just a wrap-around to the countlines() routine.
4870 streng *std_lines( tsd_t *TSD, cparamboxptr parms )
4872 char opt = 'N';
4873 fileboxptr ptr=NULL ;
4874 streng *filename=NULL ;
4875 int was_closed=0, result=0 ;
4876 int actual;
4877 fil_tsd_t *ft;
4879 ft = (fil_tsd_t *)TSD->fil_tsd;
4881 /* Syntax: lines([filename][,C|N]) */
4882 checkparam( parms, 0, 2 , "LINES" ) ;
4884 if (parms&&parms->next&&parms->next->value)
4885 opt = getoptionchar( TSD, parms->next->value, "LINES", 2, "CN", "" ) ;
4888 * Get the name of the file (use defaults if necessary), and get
4889 * a pointer to the entry of that file from the file table
4891 if (parms->value
4892 && parms->value->len)
4893 filename = parms->value ;
4894 else
4895 filename = ft->stdio_ptr[0]->filename0 ;
4898 * Try to get the Rexx file table entry, if it doesn't work, then
4899 * try again ... and a bit harder
4901 ptr = getfileptr( TSD, filename ) ;
4902 was_closed = (ptr==NULL) ;
4903 if (!ptr)
4904 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
4907 * If 'C' is passed, we ALWAYS get the actual number of lines
4908 * If 'N' is passed (or unset), we get the actual number of lines
4909 * unless FAST_LINES_BIF is set (default is set)
4910 * Bug: 1000227
4912 if ( opt == 'C' )
4913 actual = 1;
4914 else
4916 if ( get_options_flag( TSD->currlevel, EXT_FAST_LINES_BIF_DEFAULT ) )
4917 actual = 0;
4918 else
4919 actual = 1;
4921 result = countlines( TSD, ptr, actual, OPER_READ ) ;
4923 if (was_closed)
4924 closefile( TSD, filename ) ;
4927 return int_to_streng( TSD, result ) ;
4933 * The Rexx built-in function LINEIN() reads a line from a file.
4934 * The actual reading is performed in 'readoneline', while this routine
4935 * takes care of range checking of parameters, and decides which
4936 * lower level routines to call.
4939 streng *std_linein( tsd_t *TSD, cparamboxptr parms )
4941 streng *filename=NULL, *res=NULL ;
4942 fileboxptr ptr=NULL ;
4943 int count=0, line=0 ;
4944 fil_tsd_t *ft;
4946 ft = (fil_tsd_t *)TSD->fil_tsd;
4948 /* Syntax: linein([filename][,[line][,count]]) */
4949 checkparam( parms, 0, 3 , "LINEIN" ) ;
4952 * First get the name of the file, or use the appropriate default
4954 if (parms->value
4955 && parms->value->len)
4956 filename = parms->value ;
4957 else
4958 filename = ft->stdio_ptr[0]->filename0 ;
4961 * Then get the line number at which the read it to start, or set
4962 * set it to zero if none was specified.
4964 if (parms)
4965 parms = parms->next ;
4967 if (parms && parms->value)
4968 line = atopos( TSD, parms->value, "LINEIN", 2 ) ;
4969 else
4970 line = 0 ; /* Illegal value */
4973 * And at last, read the count, which can be only 0 or 1, and which
4974 * is the number of lines to read.
4976 if (parms)
4977 parms = parms->next ;
4979 if (parms && parms->value)
4981 count = atozpos( TSD, parms->value, "LINEIN", 3 ) ;
4982 if (count!=0 && count!=1)
4983 exiterror( ERR_INCORRECT_CALL, 39, "LINEIN", tmpstr_of( TSD, parms->value ) ) ;
4985 else
4986 count = 1 ; /* The default */
4989 * Now, get the pointer to the entry in the file table that contains
4990 * information about this file, or make it automatically create
4991 * an entry if one didn't exist.
4993 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
4996 * If line was specified, we must reposition the current read
4997 * position of the file.
4999 if (line)
5000 positionfile( TSD, "LINEIN", 2, ptr, OPER_READ, line, SEEK_SET ) ;
5003 * As the last thing, read in the data. If no data was wanted, skip it
5004 * but call flushing if line wasn't specified either.
5006 if (count)
5007 res = readoneline( TSD, ptr ) ;
5008 else
5010 if (!line)
5011 flush_input( ptr ) ;
5012 res = nullstringptr() ;
5015 return res ;
5022 * This function is a wrap-around for the Rexx built-in function
5023 * LINEOUT(). It performs parameter checking and decides which lower
5024 * level routines to call.
5027 streng *std_lineout( tsd_t *TSD, cparamboxptr parms )
5029 streng *string=NULL, *file=NULL ;
5030 int lineno=0, result=0 ;
5031 fileboxptr ptr=NULL ;
5032 fil_tsd_t *ft;
5034 ft = (fil_tsd_t *)TSD->fil_tsd;
5036 if ( TSD->restricted )
5037 exiterror( ERR_RESTRICTED, 1, "LINEOUT" ) ;
5039 /* Syntax: lineout([filename][,[string][,line]]) */
5040 checkparam( parms, 0, 3 , "LINEOUT" ) ;
5043 * First get the pointer for the file to operate on. If omitted,
5044 * use the standard output stream
5046 if (parms->value
5047 && parms->value->len)
5048 file = parms->value ;
5049 else
5050 file = ft->stdio_ptr[1]->filename0 ;
5052 * The file pointer is needed in ALL circumstances!
5054 ptr = get_file_ptr( TSD, file, OPER_WRITE, ACCESS_WRITE ) ;
5057 * Then, get the data to be written, if any.
5059 if (parms)
5060 parms = parms->next ;
5062 if (parms && parms->value)
5063 string = parms->value ;
5064 else
5065 string = NULL ;
5068 * At last, we must find the line number of the file to write. We
5069 * must position the file at this line before the write.
5071 if (parms)
5072 parms = parms->next ;
5074 if (parms && parms->value)
5075 lineno = atopos( TSD, parms->value, "LINEOUT", 3 ) ;
5076 else
5077 lineno = 0 ; /* illegal value */
5080 * First, let's reposition the file if necessary.
5082 if (lineno)
5083 positionfile( TSD, "LINEOUT", 2, ptr, OPER_WRITE, lineno, SEEK_SET ) ;
5086 * And then, we write out the data. If there are not data, it may have
5087 * been just positioning. However, if there are neither data nor
5088 * a linenumber, something magic may happen.
5090 if (string)
5091 result = writeoneline( TSD, ptr, string ) ;
5092 else
5094 if ( !lineno )
5097 * flush_output() will swap out the file and close it, but leave ALL positions
5098 * intact
5099 * We need to set the write positions to end of file (NOT EOF)
5100 * See ANSI 9.7.2, 9.7.5, A.5.8.9
5101 * For efficiency sake, we will have to set writeline = 0 :-(
5102 * We do this BEFORE flush_output() otherwise we won't have a ptr->fileptr!
5104 if ( ptr->flag & FLAG_PERSIST )
5106 fseek( ptr->fileptr, 0, SEEK_END ) ;
5107 ptr->writepos = ftell( ptr->fileptr ) ;
5109 else
5110 ptr->writepos = 0;
5111 ptr->writeline = 0;
5113 * ANSI states that a file is not necessarily closed in this case.
5114 * Position of file pointers is explicitly stated in ANSI and if the
5115 * file is NOT closed they cause breakage.
5116 * Therefore implement ANSI in STRICT_ANSI mode and normal behaviour
5117 * (that does not cause breakage) in "regina" mode.
5118 * MH 22/06/2004 - after non-conclusive discussions on ANSI mailing list
5120 if ( get_options_flag( TSD->currlevel, EXT_STRICT_ANSI ) )
5121 flush_output( TSD, ptr );
5122 else
5123 closefile( TSD, file ) ;
5125 result = 0;
5128 return int_to_streng( TSD, result ) ;
5135 * This function checks whether a particular file is accessable by
5136 * the user in a certain mode, which may be read, write or execute.
5137 * Unfortunately, this function differs a bit from the functionality
5138 * of several others. It explicitly checks a file, so that if the
5139 * file didn't exist in advance, it is _not_ opened. And even _if_
5140 * the file existed, the file in the file system is checked, not the
5141 * file opened by Regina. The two may differ slightly under certain
5142 * circumstances.
5145 static int is_accessable( const tsd_t *TSD, const streng *filename, int mode )
5147 int res=0 ;
5148 char *fn ;
5150 fn = str_ofTSD( filename ) ;
5152 * First, call access() with the 'correct' parameters, and store
5153 * the result in 'res'. If 'mode' had an "impossible" value, give
5154 * an error.
5156 #if defined(WIN32) && ( defined(__IBMC__) || defined(__LCC__) )
5158 DWORD Attrib;
5159 res=-1;
5160 Attrib=GetFileAttributes(fn);
5161 if (Attrib==(DWORD)-1)
5162 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5163 if ((Attrib&FILE_ATTRIBUTE_DIRECTORY)!=FILE_ATTRIBUTE_DIRECTORY)
5165 if ((mode == COMMAND_READABLE) && ((Attrib&FILE_ATTRIBUTE_READONLY)==FILE_ATTRIBUTE_READONLY))
5166 res = 0 ;
5167 else if ((mode == COMMAND_WRITEABLE) || (mode == COMMAND_EXECUTABLE))
5168 res = 0 ;
5171 #else
5172 if (mode == COMMAND_READABLE)
5173 res = access( fn, R_OK ) ;
5174 else if (mode == COMMAND_WRITEABLE)
5175 res = access( fn, W_OK ) ;
5176 else if (mode == COMMAND_EXECUTABLE)
5177 res = access( fn, X_OK ) ;
5178 else
5179 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5180 #endif
5183 * Perhaps we should analyze the output a bit before returning?
5184 * If res==EACCES, that is not really an error, while other errno
5185 * code _do_ signify an error. However ... since the return code
5186 * a boolean variable, just return it.
5188 FreeTSD(fn) ;
5189 return (res==0) ;
5195 * This little function implements the RESET command of the Rexx
5196 * built-in function STREAM(). Basically, most of the job is done in
5197 * the function 'fixup_file()'. Except from removing the ERROR flag.
5198 * The 'fixup_file()' function is intended for fixing the file at the
5199 * start of a condition handler for the NOTREADY condition.
5201 * The value returned from this function is either "READY" or "UNKNOWN",
5202 * and reflects the philosophy that the file _is_ fixed, unless it
5203 * is impossible to open it. Of course, that may be a false READY,
5204 * since the actual _problem_ might not have been fixed, but at least
5205 * you have another try at the problem.
5208 static streng *reset_file( tsd_t *TSD, fileboxptr fileptr )
5210 if (!fileptr)
5211 return nullstringptr() ;
5213 fixup_file( TSD, fileptr->filename0 ) ;
5214 fileptr->flag &= ~(FLAG_ERROR | FLAG_FAKE) ;
5216 if (fileptr->fileptr)
5217 return Str_creTSD( "READY" ) ; /* Per definition */
5218 else
5219 return Str_creTSD( "UNKNOWN" ) ;
5225 * The built-in function STREAM() is new in TRL2. It is supposed to be
5226 * a sort of all-round function for just about anything having to do with
5227 * files. The details of its specification in TRL2 leaves a lot of room
5228 * for the implementors. Two of the options to this command -- the Status
5229 * and Description options are treated as defined by TRL, the Command
5230 * option takes several command, defined by the COMMAND_ macros.
5232 streng* std_stream( tsd_t *TSD, cparamboxptr parms )
5234 char oper=' ' ;
5235 streng *command=NULL, *result=NULL, *filename=NULL, *psub=NULL ;
5236 fileboxptr ptr=NULL ;
5238 /* Syntax: stream(filename[,[oper][,command ...]]) */
5239 if ((!parms)||(!parms->value))
5240 exiterror( ERR_INCORRECT_CALL, 5, "STREAM", 1 ) ;
5241 checkparam( parms, 1, 3 , "STREAM" ) ;
5244 * Get the filepointer to Rexx's file table, but make sure that the
5245 * file is not in any way created if it didn't exist.
5247 filename = Str_dupstrTSD( parms->value );
5248 ptr = getfileptr( TSD, filename ) ;
5250 * Read the 'operation'. This is really just an 'option'. The
5251 * default option is 'S'.
5253 parms = parms->next ;
5254 if (parms && parms->value)
5255 oper = getoptionchar( TSD, parms->value, "STREAM", 2, "CSD", "" ) ;
5256 else
5257 oper = 'S' ;
5260 * If the operation was 'C', we _must_ have a third parameter, on the
5261 * other hand, if it was not 'C', we must never have a third parameter.
5262 * Make sure that these rules are followed.
5264 command = NULL ;
5265 if (oper=='C')
5267 parms = parms->next ;
5268 if (parms && parms->value)
5269 command = parms->value ;
5270 else
5271 exiterror( ERR_INCORRECT_CALL, 3, "STREAM", 3 ) ;
5273 else
5274 if (parms && parms->next && parms->next->value)
5275 exiterror( ERR_INCORRECT_CALL, 4, "STREAM", 2 ) ;
5278 * Here comes the main loop.
5280 result = NULL ;
5281 switch ( oper )
5283 case 'C':
5285 * Read the command, and 'translate' it into an integer which
5286 * describes it, see the implementation of get_command(), and
5287 * the COMMAND_ macros. The first of these are rather simple,
5288 * in fact, they could probably be compressed to save some
5289 * space.
5291 command = Str_strp( command, ' ', STRIP_BOTH );
5292 oper = get_command( command ) ;
5293 switch(oper)
5295 case COMMAND_READ:
5296 closefile( TSD, filename ) ;
5297 ptr = openfile( TSD, filename, ACCESS_READ ) ;
5298 break;
5299 case COMMAND_WRITE:
5300 closefile( TSD, filename ) ;
5301 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
5302 break;
5303 case COMMAND_APPEND:
5304 closefile( TSD, filename ) ;
5305 ptr = openfile( TSD, filename, ACCESS_APPEND ) ;
5306 break;
5307 case COMMAND_UPDATE:
5308 closefile( TSD, filename ) ;
5309 ptr = openfile( TSD, filename, ACCESS_UPDATE ) ;
5310 break;
5311 case COMMAND_CREATE:
5312 closefile( TSD, filename ) ;
5313 ptr = openfile( TSD, filename, ACCESS_CREATE ) ;
5314 break;
5315 case COMMAND_CLOSE:
5317 * The file is always unknown after is has been closed. Does
5318 * that sound convincing, or does it sound like I didn't feel
5319 * to implement the rest of this ... ?
5321 closefile( TSD, filename ) ;
5322 result = Str_creTSD( "UNKNOWN" ) ;
5323 break ;
5324 case COMMAND_FLUSH:
5326 * Flush the file. Actually, this might not be needed, since
5327 * the functions that write out data may contain explicit
5328 * calls to fflush()
5330 ptr = getfileptr( TSD, filename ) ;
5331 if (ptr && ptr->fileptr)
5333 errno = 0 ;
5334 if (fflush( ptr->fileptr))
5336 file_error( ptr, errno, NULL ) ;
5337 result = Str_creTSD( "ERROR" ) ;
5339 else
5340 result = Str_creTSD( "READY" ) ;
5342 else if (ptr)
5343 result = Str_creTSD( "ERROR" ) ;
5344 else
5345 result = Str_creTSD( "UNKNOWN" ) ;
5346 break ;
5347 case COMMAND_STATUS:
5348 ptr = getfileptr( TSD, filename ) ;
5349 result = getrexxstatus( TSD, ptr ) ;
5350 break;
5351 case COMMAND_FSTAT:
5352 result = getstatus( TSD, filename, COMMAND_FSTAT );
5353 break;
5354 case COMMAND_RESET:
5355 ptr = getfileptr( TSD, filename ) ;
5356 result = reset_file( TSD, ptr ) ;
5357 break;
5358 case COMMAND_READABLE:
5359 case COMMAND_WRITEABLE:
5360 case COMMAND_EXECUTABLE:
5361 result = int_to_streng( TSD, is_accessable( TSD, filename, oper )) ;
5362 break;
5363 case COMMAND_QUERY:
5365 * We have to further parse the remainder of the command
5366 * to determine what sub-command has been passed.
5368 psub = Str_nodupTSD( command , 5, command->len - 5);
5369 psub = Str_strp( psub, ' ', STRIP_LEADING);
5370 result = getquery( TSD, filename, psub ) ;
5371 Free_stringTSD(psub);
5372 break;
5373 case COMMAND_OPEN:
5375 * We have to further parse the remainder of the command
5376 * to determine what sub-command has been passed.
5378 psub = Str_nodupTSD( command , 4, command->len - 4);
5379 psub = Str_strp( psub, ' ', STRIP_LEADING);
5380 result = getopen( TSD, filename, psub ) ;
5381 Free_stringTSD(psub);
5382 break;
5383 case COMMAND_SEEK:
5384 psub = Str_nodupTSD( command , 4, command->len - 4);
5385 psub = Str_strp( psub, ' ', STRIP_LEADING);
5386 result = getseek( TSD, filename, psub ) ;
5387 Free_stringTSD(psub);
5388 break;
5389 case COMMAND_POSITION:
5390 psub = Str_nodupTSD( command , 8, command->len - 8);
5391 psub = Str_strp( psub, ' ', STRIP_LEADING);
5392 result = getseek( TSD, filename, psub ) ;
5393 Free_stringTSD(psub);
5394 break;
5395 default:
5396 exiterror( ERR_STREAM_COMMAND, 3, "CLOSE FLUSH OPEN POSITION QUERY SEEK", tmpstr_of( TSD, command ) ) ;
5397 break;
5399 break ;
5401 case 'D':
5403 * Get a description of the most recent error for this file
5405 if (ptr)
5407 if (ptr->errmsg)
5408 result = Str_dupTSD(ptr->errmsg) ;
5409 else if (ptr->error)
5410 result = Str_creTSD( strerror(ptr->error) ) ;
5412 break ;
5414 case 'S':
5416 * Get a simple status for the file in question. If the file
5417 * doesn't exist in Rexx's tables, UNKNOWN is returned. If the
5418 * file is in error state, return ERROR, else return READY,
5419 * unless current read position is at EOF, in which case
5420 * NOTREADY is return. Note that ERROR and NOTREADY are the
5421 * two states that will raise the NOTREADY condition.
5423 if (ptr)
5425 if (ptr->flag & FLAG_ERROR)
5427 result = Str_creTSD( "ERROR" ) ;
5429 #if 1 /* really MH */
5430 else if (ptr->flag & FLAG_AFTER_RDEOF)
5432 result = Str_creTSD( "NOTREADY" ) ;
5434 #else
5435 else if (ptr->flag & FLAG_RDEOF)
5437 result = Str_creTSD( "NOTREADY" ) ;
5439 #endif
5440 else
5442 result = Str_creTSD( "READY" ) ;
5445 else
5446 result = Str_creTSD( "UNKNOWN" ) ;
5448 break ;
5450 default:
5451 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5454 if (result==NULL)
5455 result = nullstringptr() ;
5457 Free_stringTSD(filename);
5458 return result ;
5464 * This routine will traverse the list of open files, and dump relevant
5465 * information about each of them. Really a debugging routine. It is
5466 * not available when Regina is compiled with optimalization.
5468 #ifndef NDEBUG
5469 streng *dbg_dumpfiles( tsd_t *TSD, cparamboxptr parms )
5471 fileboxptr ptr ;
5472 int i=0, fno ;
5473 char string[11] ;
5474 fil_tsd_t *ft;
5475 char opt='N' ;
5476 int slot;
5478 ft = (fil_tsd_t *)TSD->fil_tsd;
5480 checkparam( parms, 0, 2 , "DUMPFILES" ) ;
5482 if ( parms && parms->value )
5483 opt = getoptionchar( TSD, parms->value, "DUMPFILES", 1, "LN", "" ) ;
5485 if ( parms )
5486 parms = parms->next ;
5488 if ( parms && parms->value )
5489 slot = atopos( TSD, parms->value, "DUMPFILES", 2 ) ;
5490 else
5491 slot = -1 ; /* dump all slots */
5493 if (TSD->stddump == NULL)
5494 return nullstringptr() ;
5496 if ( opt == 'N' )
5498 fprintf(TSD->stddump,
5499 " Read Write\n" ) ;
5500 fprintf(TSD->stddump,
5501 "File Filename Flags line char line char\n");
5503 for ( ptr = ft->mrufile; ptr ; ptr = ptr->older )
5505 fno = fileno( ptr->fileptr ) ;
5506 fprintf( TSD->stddump,"%4d %-30s", fno, ptr->filename0->value);
5507 i = 0 ;
5509 string[0] = (char) (( ptr->flag & FLAG_READ ) ? 'r' : ' ') ;
5510 string[1] = (char) (( ptr->flag & FLAG_WRITE ) ? 'w' : ' ') ;
5511 string[2] = (char) (( ptr->flag & FLAG_PERSIST ) ? 'p' : 't') ;
5512 string[3] = (char) (( ptr->flag & FLAG_RDEOF ) ? 'R' : ' ') ;
5513 string[4] = (char) (( ptr->flag & FLAG_AFTER_RDEOF ) ? 'A' : ' ') ;
5514 string[5] = (char) (( ptr->flag & FLAG_WREOF ) ? 'W' : ' ') ;
5515 string[6] = (char) (( ptr->flag & FLAG_SURVIVOR ) ? 'S' : ' ') ;
5516 string[7] = (char) (( ptr->flag & FLAG_ERROR ) ? 'E' : ' ') ;
5517 string[8] = (char) (((ptr->flag & FLAG_FAKE) && (ptr->flag & FLAG_ERROR) ) ? 'F' : ' ') ;
5518 string[9] = 0x00 ;
5520 fprintf( TSD->stddump, " %8s %4d %4ld %4d %4ld\n", string,
5521 ptr->readline, (long)(ptr->readpos),
5522 ptr->writeline,(long)(ptr->writepos) ) ;
5524 if (ptr->flag & FLAG_ERROR)
5526 if (ptr->errmsg)
5527 fprintf(TSD->stddump, " ==> %s\n", ptr->errmsg->value ) ;
5528 else if (ptr->error)
5529 fprintf(TSD->stddump, " ==> %s\n", strerror( ptr->error )) ;
5532 fprintf( TSD->stddump," r=read, w=write, p=persistent, t=transient, e=eof\n");
5533 fprintf( TSD->stddump," R=read-eof, W=write-eof, S=special, E=error, F=fake\n");
5535 else
5537 int start, end;
5538 if ( slot == -1 )
5540 start = 0;
5541 end = 131;
5543 else
5545 start = slot;
5546 end = start + 1;
5548 for ( i = start; i < end; i++ )
5550 ptr = ft->filehash[i];
5551 if ( ptr )
5553 fno = 0;
5554 if ( ptr->fileptr )
5555 fno = fileno( ptr->fileptr ) ;
5556 fprintf( TSD->stddump,"Slot: %3d: %4d %8lx prev: %8lx next: %8lx %-30s\n",
5557 i, fno, (long)ptr, (long)ptr->prev, (long)ptr->next, ptr->filename0->value);
5558 for ( ptr = ptr->next; ptr; ptr = ptr->next )
5560 fno = 0;
5561 if ( ptr->fileptr )
5562 fno = fileno( ptr->fileptr ) ;
5563 fprintf( TSD->stddump," %4d %8lx prev: %8lx next: %8lx %-30s\n",
5564 fno, (long)ptr, (long)ptr->prev, (long)ptr->next, ptr->filename0->value );
5567 else
5569 fprintf( TSD->stddump,"Slot: %3d is empty\n", i );
5574 return nullstringptr() ;
5576 #endif
5582 * Read from stdin using readoneline()
5584 streng *readkbdline( tsd_t *TSD )
5586 fil_tsd_t *ft;
5588 ft = (fil_tsd_t *)TSD->fil_tsd;
5589 return readoneline( TSD, ft->stdio_ptr[DEFAULT_STDIN_INDEX] );
5592 void *addr_reopen_file( tsd_t *TSD, const streng *filename, char code,
5593 int iserror )
5594 /* This is the open routine for the ADDRESS WITH-redirection. filename is
5595 * the name of the file. code is either 'r' for "READ",
5596 * 'A' for "WRITE APPEND", 'R' for "WRITE REPLACE". In case of READ
5597 * already opened files will be reused. In case of APPEND or REPLACE the
5598 * files are (re-)opened. An internal structure for files is returned and
5599 * should be used for calls to addr_io_file.
5600 * An already opened file for write can't be used. See J18PUB.pdf, 5.5.1.
5601 * The return value may be NULL in case of an error. A NOTREADY condition
5602 * may have been raised in this case.
5603 * filename may be NULL for a default file.
5604 * iserror can be set or not. If set, stderr instead of stdout should be used.
5607 fileboxptr ptr;
5608 fil_tsd_t *ft;
5610 ft = (fil_tsd_t *)TSD->fil_tsd;
5612 iserror = ( iserror ) ? 1 : 0;
5613 switch ( code )
5615 case 'r':
5616 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5617 return ft->stdio_ptr[DEFAULT_STDIN_INDEX];
5618 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ );
5619 if ( ptr != NULL )
5620 ptr->readpos = 0;
5621 break;
5623 case 'A':
5624 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5625 return ft->stdio_ptr[DEFAULT_STDOUT_INDEX + iserror];
5626 if ( ( ptr = getfileptr( TSD, filename ) ) != NULL )
5628 if ( ptr->flag & FLAG_SURVIVOR )
5629 return get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE );
5631 closefile( TSD, filename );
5632 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND );
5633 break;
5635 case 'R':
5636 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5637 return ft->stdio_ptr[DEFAULT_STDOUT_INDEX + iserror];
5638 if ( ( ptr = getfileptr( TSD, filename ) ) != NULL )
5640 if ( ptr->flag & FLAG_SURVIVOR )
5641 return get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE );
5643 closefile( TSD, filename );
5644 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE );
5645 break;
5647 default:
5648 ptr = NULL;
5649 break;
5652 if ( ( ptr != NULL ) && ( ptr->fileptr == NULL ) )
5653 ptr = NULL;
5655 return ptr;
5658 streng *addr_io_file( tsd_t *TSD, void *fileptr, const streng *buffer )
5659 /* This is the working routine for the ADDRESS WITH-redirection. fileptr is
5660 * the return value of addr_reopen_file. buffer must be NULL for a read
5661 * operation or a filled buffer.
5662 * The return value is NULL in case of a write operation or in case of EOF
5663 * while reading.
5664 * All IO is done by charin/charout.
5665 * A NOTREADY condition won't be raised.
5668 streng *retval = NULL ;
5670 if ( fileptr == NULL ) /* fixes bug 806948 */
5671 return retval;
5673 if ( buffer == NULL )
5674 retval = readbytes( TSD, (fileboxptr)fileptr, 0x1000, 1 ) ;
5675 else
5676 writebytes( TSD, (fileboxptr)fileptr, buffer, 1 ) ;
5678 return( retval ) ;
5681 void addr_reset_file( tsd_t *TSD, void *fileptr )
5682 /* This is the "close" routine for the ADDRESS WITH-redirection. We don't close
5683 * the stream, we simply perform a reset, that's enough to restart reading
5684 * or writing.
5687 fileboxptr ptr = (fileboxptr)fileptr;
5689 if ( fileptr == NULL ) /* fixes bug 806948 */
5690 return;
5692 if ( ptr->fileptr )
5694 clearerr( ptr->fileptr );
5695 if ( ptr->flag & FLAG_PERSIST )
5696 fseek( ptr->fileptr, 0, SEEK_SET );
5697 ptr->thispos = 0;
5698 ptr->oper = OPER_NONE;
5701 if ( ptr->flag & FLAG_SURVIVOR )
5702 ptr->flag &= ~FLAG_ERROR;
5704 ptr->flag &= ~FLAG_FAKE;
5708 streng *addr_file_info( tsd_t *TSD, const streng *source, int defchannel )
5710 * addr_file_info is a helper for the ADDRESS WITH-redirection. source is the
5711 * name of the file and may be NULL for a default channel. The channel's number
5712 * is used then, 0 = stdin, 1 = stdout, 2 = stderr.
5713 * The return name is a fresh copy of the qualified name of the file.
5716 fil_tsd_t *ft;
5717 streng *result;
5718 fileboxptr p;
5720 ft = (fil_tsd_t *)TSD->fil_tsd;
5723 * We don't know anything about the default channels. So just return our
5724 * internal name.
5726 if ( source == NULL )
5727 return Str_dupstrTSD( ft->stdio_ptr[ defchannel ]->filename0 );
5730 * Check for well known devices, the SURVIVORs. Return a comparable name
5731 * which makes sure "<stdin>" is equal to "stdin".
5733 if ( ( p = getfileptr( TSD, source ) ) != NULL )
5735 if ( p->flag & FLAG_SURVIVOR )
5737 if ( p->fileptr == stdin )
5738 defchannel = 0;
5739 else if ( p->fileptr == stdout )
5740 defchannel = 1;
5741 else
5742 defchannel = 2;
5744 return Str_dupstrTSD( ft->stdio_ptr[ defchannel ]->filename0 );
5749 * Even in case of character devices a name resolution makes sense, e.g.
5750 * "tty" may expand to "/dev/tty" or the fifo "local" may expand to
5751 * "/somethere/local".
5753 result = Str_makeTSD( REXX_PATH_MAX );
5754 my_fullpathstreng( TSD, result->value, source );
5755 result->len = strlen( result->value );
5757 return result;
5762 * This routine is not really interesting. You should use the STREAM()
5763 * built-in function for greater portability and functionality. It is
5764 * left in the code for portability reasons.
5766 streng *unx_open( tsd_t *TSD, cparamboxptr parms )
5768 fileboxptr ptr=NULL ;
5769 char ch=' ' ;
5770 int iaccess=ACCESS_NONE ;
5772 checkparam( parms, 1, 2 , "OPEN" ) ;
5774 if ((parms->next)&&(parms->next->value))
5776 ch = getoptionchar( TSD, parms->next->value, "OPEN", 2, "RW", "" ) ;
5777 if ( ch == 'R' ) /* bja */
5778 iaccess = ACCESS_READ ;
5779 else if ( ch == 'W' ) /* bja */
5780 iaccess = ACCESS_WRITE ;
5781 else
5782 assert( 0 ) ;
5784 else
5785 iaccess = ACCESS_READ ;
5787 ptr = openfile( TSD, parms->value, iaccess ) ;
5789 return int_to_streng( TSD,( ptr && ptr->fileptr )) ;
5794 * This routine is not really interesting. You should use the CLOSE
5795 * command of the STREAM() built-in function for greater portability
5796 * and compatibility. It is left in the code only for compatibility
5797 * reasons.
5799 streng *unx_close( tsd_t *TSD, cparamboxptr parms )
5801 fileboxptr ptr=NULL ;
5803 checkparam( parms, 1, 1 , "CLOSE" ) ;
5804 ptr = getfileptr( TSD, parms->value ) ;
5805 closefile( TSD, parms->value ) ;
5807 return int_to_streng( TSD, ptr!=NULL ) ;
5812 * a function called exists that checks if a file with a certain name
5813 * exists. This function was taken from the ARexx API.
5815 streng *arexx_exists( tsd_t *TSD, cparamboxptr parms )
5817 char *name;
5818 streng *retval;
5819 struct stat st;
5821 checkparam( parms, 1, 1, "EXISTS" ) ;
5823 name = str_of( TSD, parms->value ) ;
5824 retval = int_to_streng( TSD, stat( name, &st ) != -1 ) ;
5825 Free_TSD( TSD, name ) ;
5827 return retval;
5831 * get_external_routine_file opens a file in binary mode and returns the
5832 * fully qualified path name on success. NULL is returned otherwise.
5833 * The opened file pointer is returned in *fp.
5835 static streng *get_external_routine_file( const tsd_t *TSD,
5836 const char *inname, FILE **fp )
5838 char buf[3 * REXX_PATH_MAX + 1];
5840 #ifdef VMS
5841 *fp = fopen( inname, "r" );
5842 #else
5843 *fp = fopen( inname, "rb" );
5844 #endif
5845 if ( *fp == NULL )
5846 return NULL;
5848 my_fullpath( buf, inname );
5850 return Str_crestrTSD( buf );
5855 * See get_external_routine for comments. This function processes one path
5856 * element which is passed in path.
5857 * suffixes is either NULL or the list of extra suffixes which should be
5858 * tested. *fp must be NULL on entry.
5859 * path may be NULL if no further directory processing shall happen.
5861 static streng *get_external_routine_path( const tsd_t *TSD,
5862 const char *inname, FILE **fp,
5863 const char *path,
5864 const char *suffixes,
5865 int emptySuffixAllowed )
5867 char outname[REXX_PATH_MAX+1];
5868 int i,ilen,hlen;
5869 streng *retval;
5870 static const char *default_suffixes = "rexx,rex,cmd,rx";
5871 const char *suffixlist[2];
5872 const char *suffix;
5873 int suffixlen;
5875 ilen = strlen( inname );
5876 if ( !path )
5877 hlen = 0;
5878 else
5879 hlen = strlen( path );
5880 if ( !hlen )
5882 if ( ilen > REXX_PATH_MAX )
5883 return NULL;
5884 strcpy( outname, inname );
5886 else
5888 if ( ( strchr( FILE_SEPARATORS, inname[0] ) == NULL ) &&
5889 ( strchr( FILE_SEPARATORS, path[hlen - 1] ) == NULL ) )
5891 if ( ilen + hlen + 1 > REXX_PATH_MAX )
5892 return NULL;
5893 strcpy( outname, path );
5894 strcat( outname, FILE_SEPARATOR_STR );
5895 strcat( outname, inname );
5897 else
5899 if ( ilen + hlen > REXX_PATH_MAX )
5900 return NULL;
5901 strcpy( outname, path );
5902 strcat( outname, inname );
5907 * The filename is constructed. Try without fiddling with suffixes first.
5909 if ( emptySuffixAllowed )
5911 if ( ( retval = get_external_routine_file( TSD, outname, fp ) ) != NULL )
5912 return retval;
5916 * Next try the supplied suffix list, then try the default list.
5917 * First check if a known extension exists, after every check do the
5918 * application.
5921 suffixlist[0] = suffixes;
5922 suffixlist[1] = default_suffixes;
5923 ilen = strlen( outname );
5924 #define IsDelim(c) ( ( (c) == ',' ) || ( (c) == '.' ) || \
5925 ( (c) == PATH_SEPARATOR ) || rx_isspace(c) )
5926 for ( i = 0; i < 2; i++ )
5928 suffixes = suffixlist[i];
5930 while ( suffixes )
5932 while ( IsDelim(*suffixes) )
5933 suffixes++;
5934 if ( *suffixes == '\0' )
5935 break;
5937 for ( suffixlen = 1; !IsDelim(suffixes[suffixlen]); suffixlen++ )
5938 if ( suffixes[suffixlen] == '\0' )
5939 break;
5941 suffix = suffixes;
5942 suffixes += suffixlen;
5944 if ( suffixlen + 1 > ilen )
5945 continue;
5946 if ( outname[ ilen - suffixlen - 1 ] != '.' )
5947 continue;
5948 #ifdef CASE_SENSITIVE_FILENAMES
5949 if ( memcmp( suffix, outname + ilen - suffixlen, suffixlen - 1 ) )
5950 #else
5951 if ( mem_cmpic( suffix, outname + ilen - suffixlen, suffixlen - 1 ) )
5952 #endif
5953 continue;
5956 * A matching suffix forces us to terminate every further seeking a
5957 * proper file.
5959 if ( !emptySuffixAllowed )
5960 return get_external_routine_file( TSD, outname, fp );
5961 return NULL;
5966 * Try the extensions.
5968 for ( i = 0; i < 2; i++ )
5970 suffixes = suffixlist[i];
5972 while ( suffixes )
5974 while ( IsDelim(*suffixes) )
5975 suffixes++;
5976 if ( *suffixes == '\0' )
5977 break;
5979 for ( suffixlen = 1; !IsDelim(suffixes[suffixlen]); suffixlen++ )
5980 if ( suffixes[suffixlen] == '\0' )
5981 break;
5983 suffix = suffixes;
5984 suffixes += suffixlen;
5986 if ( suffixlen + 1 + ilen > REXX_PATH_MAX )
5987 continue;
5988 outname[ ilen ] = '.';
5989 memcpy( outname + ilen + 1, suffix, suffixlen );
5990 outname[ilen + 1 + suffixlen] = '\0';
5991 if ( ( retval = get_external_routine_file( TSD, outname, fp ) ) !=
5992 NULL )
5993 return retval;
5997 #undef Delim
5998 return NULL;
6002 * See get_external_routine for comments. This function processes a list of
6003 * path elements delimited by the path separator which is passed in paths.
6004 * suffixes is either NULL or the list of extra suffixes which should be
6005 * tested. *fp must be NULL on entry.
6006 * paths will be destroyed.
6008 static streng *get_external_routine_paths( const tsd_t *TSD,
6009 const char *inname, FILE **fp,
6010 char *paths, const char *suffixes,
6011 int emptySuffixAllowed )
6013 char *path;
6014 streng *retval;
6016 if ( *paths == '\0' )
6017 return NULL;
6019 while ( paths )
6021 path = paths;
6022 paths = strchr( paths, PATH_SEPARATOR );
6023 if ( paths != NULL )
6024 *paths++ = '\0';
6026 if ( *path == '\0')
6029 * An empty string is counted as "." in unix systems and ignored in
6030 * all other systems.
6032 #ifdef UNIX
6033 path = ".";
6034 #else
6035 continue;
6036 #endif
6039 retval = get_external_routine_path( TSD, inname, fp, path, suffixes,
6040 emptySuffixAllowed );
6042 if ( retval )
6043 return retval;
6046 return NULL;
6050 * get_external_routine searches for a script called inname. Some paths are
6051 * search if the file is not found and an extension may be added if no file is
6052 * found.
6054 * On success *fp is set to the opened (binary) file and the return value is
6055 * the fully qualified file name. If no file was found the return value is
6056 * NULL and *fp will be NULL, too.
6057 * The returned file name is extended by a terminating '\0' without counting
6058 * is in the string's length.
6060 * This is the search algorithm:
6062 * First of all we process the environment variable REGINA_MACROS. If no file
6063 * is found we proceed with the current directory and then with the environment
6064 * variable PATH. The semantics of the use of REGINA_MACROS and PATH are the
6065 * same, and the search in the current directory is omitted for the superuser
6066 * in unix systems for security reasons. The current directory must be
6067 * specified explicitely by the superuser.
6068 * When processing an environment variable the content is split into the
6069 * different paths and each path is processed separately.
6070 * Note that the search algorithm to this point is ignored if the script name
6071 * contains a file path specification. eg. If "CALL .\MYPROG" is called, then
6072 * no searching of REGINA_MACROS or PATH is done; only the concatenation of
6073 * suffixes is carried out.
6075 * For each file name and path element a concatenated file name is created. If
6076 * a known file extension is part of the file name only this file is searched,
6077 * otherwise the file name is extended by the extensions "<empty>", ".rexx",
6078 * ".rex", ".cmd", ".rx" in this order. The file name case is ignored on
6079 * systems that ignore the character case for normal file operations like DOS,
6080 * Windows, OS/2.
6082 * The first matching file terminates the whole algorithm and the found file
6083 * is returned.
6085 * The environment variable REGINA_SUFFIXES extends the list of known suffixes
6086 * as specified above, and is inserted after the "<empty"> extension in the
6087 * process. REGINA_SUFFIXES has to contain a space or comma separated list of
6088 * extensions, a dot in front of each entry is allowed, e.g.
6089 * ".macro,.mac,regina" or "macro mac regina"
6091 * Note that it is planned to extend the list of known suffixes by ".rxc" in
6092 * version 3.4 to allow for seemless integration of precompiled macros.
6094 streng *get_external_routine( const tsd_t *TSD, const char *inname, FILE **fp )
6096 streng *retval=NULL;
6097 char *paths;
6098 char *suffixes;
6100 *fp = NULL;
6102 suffixes = mygetenv( TSD, "REGINA_SUFFIXES", NULL, 0 );
6104 /* Always try without path added to the beginning on Amiga */
6105 #if !defined(_AMIGA) && !defined(__AROS__)
6106 if ( strpbrk( inname, FILE_SEPARATORS ) != NULL )
6107 #endif
6109 retval = get_external_routine_path( TSD, inname, fp, NULL, suffixes, 1 );
6110 if ( retval )
6112 if ( suffixes )
6113 FreeTSD( suffixes );
6114 return retval;
6116 return NULL;
6119 if ( ( paths = mygetenv( TSD, "REGINA_MACROS", NULL, 0 ) ) != NULL )
6121 retval = get_external_routine_paths( TSD, inname, fp, paths, suffixes, 1 );
6122 FreeTSD( paths );
6123 if ( retval )
6125 if ( suffixes )
6126 FreeTSD( suffixes );
6127 return retval;
6131 paths = ".";
6132 #ifdef UNIX
6133 if ( geteuid() == 0 )
6134 paths = NULL;
6135 #elif defined(_AMIGA) || defined(__AROS__)
6136 paths = "REXX:";
6137 #endif
6138 if ( paths )
6140 retval = get_external_routine_path( TSD, inname, fp, paths, suffixes, 1 );
6141 if ( retval )
6143 if ( suffixes )
6144 FreeTSD( suffixes );
6145 return retval;
6149 if ( ( paths = mygetenv( TSD, "PATH", NULL, 0 ) ) != NULL )
6151 retval = get_external_routine_paths( TSD, inname, fp, paths, suffixes, 0 );
6152 FreeTSD( paths );
6155 if ( suffixes )
6156 FreeTSD( suffixes );
6157 return retval;
6161 * find_shared_library is used for HP/UX purpose only.
6162 * It looks for the file inname in the content of the environment variable
6163 * inenv and puts the result into retname. retname has to have a size of
6164 * at least REXX_PATH_MAX+1.
6165 * retname becomes inname if no other file is found.
6167 void find_shared_library(const tsd_t *TSD, const char *inname, const char *inenv, char *retname)
6169 char *paths;
6170 char outname[REXX_PATH_MAX+1];
6171 char *env_path;
6173 strcpy( retname, inname );
6174 env_path = mygetenv( TSD, inenv, NULL, 0 ); /* fixes bug 595293 */
6175 if ( !env_path )
6176 return;
6177 paths = env_path;
6178 while ( paths && *paths )
6180 int pathlen;
6181 char *sep;
6183 sep = strchr( paths, PATH_SEPARATOR );
6184 pathlen = sep ? sep-paths : strlen( paths );
6185 strncpy( outname, paths, pathlen );
6186 outname[pathlen] = 0;
6188 if ( ( pathlen > 0 ) && ( outname[pathlen-1] != FILE_SEPARATOR ) )
6189 strcat( outname, FILE_SEPARATOR_STR );
6190 strcat( outname, inname );
6191 paths = sep ? sep+1 : 0; /* set up for next pass */
6192 if ( access( outname,F_OK ) == 0)
6194 strcpy( retname,outname );
6195 break;
6198 FreeTSD( env_path );
6199 return;
6202 /* CloseOpenFiles closes all scripting input files and it closes all opened
6203 * STREAM files without destroying the associated informations.
6204 * Bug 982062: Added FilePtrDisposition to allow for purging fileptr
6205 * table if requested.
6207 void CloseOpenFiles( const tsd_t *TSD, FilePtrDisposition fpd )
6209 sysinfobox *ptr;
6211 if ( fpd == fpdRETAIN )
6213 ptr = TSD->systeminfo;
6214 while (ptr)
6216 if (ptr->input_fp)
6218 fclose(ptr->input_fp);
6219 ptr->input_fp = NULL;
6221 ptr = ptr->previous;
6224 * Cheat about the const-state.
6226 swapout_all( ( tsd_t *) TSD );
6228 else
6230 purge_filetable( ( tsd_t *) TSD );
6232 return;
6235 streng *ConfigStreamQualified( tsd_t *TSD, const streng *name )
6237 char *fn=NULL;
6238 streng *result=NULL ;
6241 * Nul terminate the input filename string, as stat() will barf if
6242 * it isn't and other functions stuff up!
6244 fn = str_ofTSD(name);
6246 result = Str_makeTSD( REXX_PATH_MAX );
6247 if ( my_fullpath( result->value, fn ) == -1 )
6250 * my_fullpath failed, so split the supplied file into filename
6251 * and directory. Then look for directory and append the filename
6252 * TODO -
6255 result->len = strlen( result->value );
6256 return( result );
6259 #if defined(HAVE__FULLPATH) || defined(__EMX__)
6261 * my_fullpath tries to get the fully qualified name of a file or directory
6262 * even if it doesn't exist. It tries to return a reasonable value even if
6263 * a path element is missing.
6264 * The return value is 0 on success, -1 in case of a severe error.
6266 int my_fullpath( char *dst, const char *src )
6268 int rc = 0;
6269 # if defined(__EMX__)
6270 int i, len;
6272 if ( _fullpath( dst, src, REXX_PATH_MAX ) == -1)
6273 strcpy( dst, src );
6275 * Convert / back to \.
6277 len = strlen( dst );
6278 for ( i = 0; i < len; i++ )
6280 if ( dst[i] == '/' )
6281 dst[i] = '\\';
6283 # else
6284 if ( _fullpath( dst, src, REXX_PATH_MAX ) == NULL )
6286 strcpy( dst, src );
6287 rc = -1;
6289 # endif
6291 return rc;
6293 #elif defined(HAVE__TRUENAME)
6295 int my_fullpath( char *dst, const char *src )
6297 _truename( src, dst );
6299 return 0;
6301 #elif defined(HAVE_REALPATH)
6303 int my_fullpath( char *dst, const char *src )
6305 realpath( src, dst );
6307 return 0;
6309 #elif defined(VMS)
6310 # include <ssdef.h>
6311 # include <rmsdef.h>
6312 # include <descrip.h>
6314 int my_fullpath( char *dst, const char *src )
6316 char *s;
6317 int status, context = 0;
6318 struct dsc$descriptor_d result_dx = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0};
6319 struct dsc$descriptor_d finddesc_dx = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0};
6321 finddesc_dx.dsc$a_pointer = (char *)src; /* You may need to cast this */
6322 finddesc_dx.dsc$w_length = strlen(src);
6323 status = lib$find_file( &finddesc_dx, &result_dx, &context, 0, 0, 0, 0 );
6324 if ( status == RMS$_NORMAL )
6326 memcpy(dst,result_dx.dsc$a_pointer,result_dx.dsc$w_length);
6327 *(dst+result_dx.dsc$w_length) = '\0';
6329 else
6330 strcpy(dst,src);
6331 lib$find_file_end(&context);
6332 str$free1_dx(&result_dx);
6333 return(0);
6335 #else /* neither _FULLPATH, _TRUENAME, REALNAME, VMS */
6337 int my_fullpath( char *dst, const char *src )
6339 char tmp[REXX_PATH_MAX+1];
6340 char curr_path[REXX_PATH_MAX+1];
6341 char path[REXX_PATH_MAX+1];
6342 char fname[REXX_PATH_MAX+1];
6343 int i = 0, len = -1, retval;
6344 struct stat stat_buf;
6346 getcwd(curr_path,REXX_PATH_MAX);
6347 strcpy(tmp,src);
6349 * First determine if the supplied filename is a directory.
6351 # if defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
6352 for ( i = 0; i < strlen( tmp ); i++ )
6353 if ( tmp[ i ] == '\\' )
6354 tmp[ i ] = '/';
6355 # endif
6356 if ((stat(tmp,&stat_buf) == 0)
6357 && (stat_buf.st_mode & S_IFMT) == S_IFDIR)
6359 strcpy(path,tmp);
6360 strcpy(fname,"");
6362 else /* here if the file doesn't exist or is not a directory */
6364 for (i=strlen(tmp),len=-1;i>-1;i--)
6366 if (tmp[i] == '/')
6368 len = i;
6369 break;
6372 switch(len)
6374 case (-1):
6375 getcwd(path,REXX_PATH_MAX);
6376 strcpy(fname,tmp);
6377 break;
6378 case 0:
6379 strcpy(path,tmp);
6380 path[1] = '\0';
6381 strcpy(fname,tmp+1+len);
6382 break;
6383 default:
6384 strcpy(path,tmp);
6385 path[len] = '\0';
6386 strcpy(fname,tmp+1+len);
6387 break;
6391 * Change directory to the supplied path, if possible and store the
6392 * expanded path.
6393 * If an error, restore the current path.
6395 if (chdir(path) != 0)
6397 retval = -1;
6399 else
6401 getcwd(path,REXX_PATH_MAX);
6402 retval = 0;
6404 chdir(curr_path);
6406 * Append the OS directory character to the path if it doesn't already
6407 * end in the character.
6409 len = strlen(path);
6410 if (len > 0)
6412 # if defined(__WINS__) || defined(__EPOC32__)
6413 if ( path[ len - 1 ] != '\\'
6414 # else
6415 if ( path[ len - 1 ] != '/'
6416 # endif
6417 && strlen( fname ) != 0 )
6419 strcat(path,"/");
6420 len++;
6422 # if defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
6423 for ( i = 0; i < len; i++ )
6424 if ( path[ i ] == '/' )
6425 path[ i ] = '\\';
6426 # endif
6428 strcpy(dst,path);
6429 strcat(dst,fname);
6431 return retval;
6433 #endif
6435 int my_fullpathstreng( const tsd_t *TSD, char *dst, const streng *src )
6437 char *copy;
6438 int retval;
6440 copy = str_ofTSD( src );
6441 retval = my_fullpath( dst, copy );
6442 FreeTSD( copy );
6444 return retval;
6447 #if !defined(HAVE__SPLITPATH2) && !defined(HAVE__SPLITPATH) && !defined(__EMX__) && !defined(DJGPP)
6448 int my_splitpath2( const char *in, char *out, char **drive, char **dir, char **name, char **ext )
6450 int inlen = strlen(in);
6451 int last_slash_pos=-1,last_dot_pos=-1,last_pos=0,i=0;
6453 for (i=0;i<inlen;i++)
6455 if ( *(in+i) == '/' || *(in+i) == '\\' )
6456 last_slash_pos = i;
6457 else if ( *(in+i) == '.' )
6458 last_dot_pos = i;
6461 * drive is always empty !
6463 out[0] = '\0';
6464 *drive = out;
6466 *ext = out+1;
6467 if (last_dot_pos > last_slash_pos)
6469 strcpy(*ext,in+last_dot_pos);
6470 last_pos = 2 + (inlen - last_dot_pos);
6471 inlen = last_dot_pos;
6473 else
6475 **ext = '\0';
6476 last_pos = 2;
6478 *dir = out+last_pos;
6480 * If there is a path component (last_slash_pos not -1), then copy
6481 * from the start of the in string to the last_slash_pos to out[1]
6483 if (last_slash_pos != -1)
6485 memcpy(*dir, in, last_slash_pos + 1);
6486 last_pos += last_slash_pos + 1;
6487 out[last_pos++] = '\0';
6488 *name = out+last_pos;
6489 memcpy(*name, in+last_slash_pos+1,(inlen - last_slash_pos - 1) );
6490 out[last_pos + (inlen - last_slash_pos - 1)] = '\0';
6492 else
6494 **dir = '\0';
6495 last_pos++;
6496 *name = out+last_pos;
6497 memcpy(*name, in, inlen);
6498 *(*name+inlen) = '\0';
6500 return(0);
6502 #endif