2 static char *RCSid
= "$Id$";
6 * The Regina Rexx Interpreter
7 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * This module is a real pain, since file I/O is one of the features
26 * that varies most between different platforms. And what makes it
27 * even more of a pain, is the fact that it must be coordinated with
28 * the handling of the condition NOTREADY. Anyway, here are the
29 * decisions set up before (well ... during) the implementation that
30 * guide how this this thing is supposed to work.
32 * There are four kind of routines, structured in four levels:
34 * (1)---------+ (2)--------+
35 * | builtin | ----> | general | B C library
36 * | functions | A | routines | ----> Routines
37 * +-----------+ +----------+
41 * +----------------->+---> | Error |
45 * 1) Builtin functions, these has the "std_" prefix which is standard
46 * for all buildin functions. The task for these functions are to
47 * process parameters, call (2) which specializes on operations (like
48 * read, write, position etc), and return a decent answer back to its
49 * caller. There is one routine in this level for each of the
50 * functions in the library of built-in functions. Most of them are
51 * std_* functions, but there are a few others too.
53 * 2) These are general operations for reading, writing, positioning,
54 * etc. They may call the C library routines directly, or
55 * indirectly, through calls to (3). The interface (A) between (1)
56 * and (2) is based on the local structure fileboxptr and strengs.
57 * There are one function in this level for each of the basic
58 * operations needed to be performed on a file. Opening, closing,
59 * reading a line, writing a line, line positioning, reading chars,
60 * writing chars, positioning chars, counting lines, counting
61 * chars, etc. The interface (B) to the C library routines uses
62 * FILE* and char* for its operations.
64 * 3) General routines to perform 'trivial' tasks. In this level,
65 * things like retriving Rexx's file table entries are implemented,
66 * and all the errorhandling. These are called from both the two
69 * There are three standard files, called "<stdin>", "<stdout>" and
70 * "<stderr>" (note that the "<" and ">" are part of the filename.)
71 * These are handles for the equivalent Unix standard files. This
72 * might cause problems if you actually do want a file calls that, or
73 * if one of these files is closed, and the more information is
74 * written to it (I can easily visulize Users trying to delete such a
75 * file :-)) So the standard files -- having set flag SURVIVOR -- will
76 * never be closed or reopened.
78 * Error_file is called by that routine which actually discovers the
79 * problem. If it is an CALL ON condition, it will set the FLAG_FAKE
80 * flag, which all other routines will check for.
98 # include <sys/stat.h>
102 #elif defined(__WATCOMC__) || defined(_MSC_VER) /* MH 10-06-96 */
103 # include <sys/stat.h> /* MH 10-06-96 */
104 # include <fcntl.h> /* MH 10-06-96 */
105 # ifdef HAVE_UNISTD_H
106 # include <unistd.h> /* MH 10-06-96 */
108 # if defined(_MSC_VER) && !defined(__WINS__)
111 #elif defined(WIN32) && defined(__IBMC__) /* LM 26-02-99 */
113 # include <sys/stat.h>
118 # include <sys/stat.h>
126 # ifdef HAVE_UNISTD_H
137 # if _MSC_VER >= 1100
138 /* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
139 # pragma warning(disable: 4115 4201 4214)
142 # include <windows.h>
144 # if _MSC_VER >= 1100
145 # pragma warning(default: 4115 4201 4214)
148 # if defined(__WATCOMC__) || defined(__BORLANDC__)
153 #if (defined(_AMIGA) || defined(__AROS__)) && defined(ACCESS_READ)
156 #if (defined(_AMIGA) || defined(__AROS__)) && defined(ACCESS_WRITE)
161 * The macrodefinition below defines the various modes in which a
162 * file may be opened. These modes are:
164 * READ - Open for readonly access. The current read position
165 * is set to the the start of the file. If the file does
166 * not exist, report an error.
168 * WRITE - Open for read and write. The current read position is
169 * set to the start of the file, while the current write
170 * position is set to EOF. If file does not exist, create
171 * it. If file does exist, use existing data.
173 * UPDATE - The combined operation of READ and WRITE, but if file
174 * does not exist, issue an error.
176 * APPEND - Open in APPEND mode, i.e. open for writeonly, position
177 * at the End-Of-File, and (if possible) open in a mode
178 * that disallows positioning. The file will be a transient
179 * file. If the file does not exist, create it.
181 * CREATE - Open for write, but if the file does exist, truncate
182 * it at the beginning after opening it.
184 #define ACCESS_NONE 0
185 #define ACCESS_READ 1
186 #define ACCESS_WRITE 2
187 #define ACCESS_UPDATE 3
188 #define ACCESS_APPEND 4
189 #define ACCESS_CREATE 5
190 #define ACCESS_STREAM_APPEND 6
191 #define ACCESS_STREAM_REPLACE 7
194 * These macros is used to set the value of the 'oper' in the filebox
195 * data structure. If last operation on a file was a read or a write,
196 * set 'oper' to OPER_READ or OPER_WRITE, respectively. If last
197 * operation was repositioning or flushing, use OPER_NONE. See
198 * description of 'oper' field in definition of 'filebox'.
205 * Flags, carrying information about files. The 'flag' field in the
206 * 'filebox' structure is set to values matching these defintions. The
207 * meaning of each of these flags is:
209 * PERSIST - Set if file is persistent, if unset, file is treated
210 * as a transient file.
211 * EOF - Currently not in use
212 * READ - File has been opened for read access.
213 * WRITE - File has been opened for write access.
214 * CREATE - Currently not in use
215 * ERROR - Set if the file is in error state. If operations are
216 * attempted performed on files in state error, the
217 * NOTREADY condition will in general be raised, and the
218 * operation will fail.
219 * SURVIVOR - Set for certain special files; the default streams, which
220 * is not really to be closed or reopened.
221 * FAKE - Meaningful only if ERROR is set. If FAKE is set, and
222 * an operation on the file is attempted, the operation is
223 * 'faked' (NOTREADY is not triggered, and the result returned
224 * for write operations does not report that the output was
226 * WREOF - Current write position is at EOF. If line output is
227 * performed, there is no need to truncate the file.
228 * RDEOF - Current read position is at EOF. Reading EOF raises the
229 * NOTREADY condition, but does not put the file into error
231 * AFTER_RDEOF - Bit of a hack here. This flag is set after an attempt
232 * (Added by MH) is made to read a stream once the RDEOF flag is set.
233 * The reason for this is that all the "read" stream
234 * functions; LINEIN, LINES, CHARIN, etc set the RDEOF
235 * flag at the point that they determine a RDEOF has
236 * occurred. This is usually at the end of the function.
237 * Therefore a LINEIN that reads EOF sets RDEOF and a
238 * subsequent call to STREAM(stream,'S') will return
239 * NOTREADY. This to me is logical, but the behaviour
240 * of other interpreters is that the first call to
241 * STREAM(stream,'S') after reaching EOF should still return
242 * READY. Only when ANOTHER "read" stream function is
243 * called does STREAM(stream,'S') return NOTREADY.
244 * SWAPPED - This flag is set if the file is currently swapped out, that
245 * is, the file is closed in order to let another file use
246 * the system's file table sloth freed when the file was
247 * temporarily closed.
249 #define FLAG_PERSIST 0x0001
250 #define FLAG_EOF 0x0002
251 #define FLAG_READ 0x0004
252 #define FLAG_WRITE 0x0008
253 #define FLAG_CREATE 0x0010
254 #define FLAG_ERROR 0x0020
255 #define FLAG_SURVIVOR 0x0040
256 #define FLAG_FAKE 0x0080
257 #define FLAG_WREOF 0x0100
258 #define FLAG_RDEOF 0x0200
259 #define FLAG_SWAPPED 0x0400
260 #define FLAG_AFTER_RDEOF 0x0800
263 * So, what is the big difference between FAKE and ERROR. Well, when a
264 * file gets it ERROR flag set, it signalizes that the file is in
265 * error state, and that no fileoperations should be performed on it.
266 * The FAKE flag is only meaningful when the ERROR flag is set. If set
267 * the FAKE flag tells that file operations should be faked in order to
268 * give the user the impression that everything is OK, while if FAKE is
269 * not set, errors are returned.
271 * The clue is that if a statement contains several operations on one
272 * file, and the first operation bombs, CALL ON NOTREADY will not take
273 * effect before the next statement boundary at the same procedural
274 * level So, for the rest of the file operations until that statement
275 * has finished, the FAKE flag is set, and signalizes that OK result
276 * should be returned whenever positioning or write is performed, and
277 * that NOTREADY should not be raised again.
279 * The reason for the RDEOF flag is that reading beyond EOF is not really
280 * a capital crime, and a lot of programmers are likely to do that, and
281 * expect things to be OK after repositioning current read position to
282 * another part of the file. If a file is put into ERROR state, it has
283 * to be explicitly reset in order to do any useful to it afterwards.
284 * Therefore, if EOF is seen on input, RDEOF is set, and NOTREADY is
285 * raised, but the file is not put into ERROR state.
289 * The following macros defines symbolic names to the commands available
290 * in the Rexx built-in function STREAM(). The meaning of each of these
293 * READ - Opens the file with the corresponding mode. For a deeper
294 * WRITE description of each of these modes, see the defininition
295 * APPEND of the ACCESS_* macros. STREAM() is used to explicitly
296 * UPDATE open a file, while Rexx is totally happy with the
297 * CREATE traditional implicit opening, i.e. that the file is
298 * opened for the needed access at the time when it is
299 * first used. If the file to be opened is already open,
300 * it will first be closed, and then opened in the
303 * CLOSE - Closes a file, works for any type of access. But if
304 * the file is a default stream, it will not be closed.
305 * Default streams should not be closed.
307 * FLUSH - Performs flushing on the file. Actually, I'm not so
308 * sure whether that is very interesting, since flushing
309 * is always performed after a write, anyway. Though, it
310 * might become an important function if the automatic
311 * flushing after write is removed (e.g. to improve speed).
313 * STATUS - Returns status information assiciated with the file as
314 * a human readable string. The information returned is the
315 * internal information that Rexx stores in the Rexx file
316 * table entry for that file. Use FSTAT to get information
317 * about the file from the operating system. See the
318 * function 'getrexxstatus()' for more information about
319 * the layout of the returned string.
321 * FSTAT - Returns status information associated with the file as
322 * a human readable string. The information returned is the
323 * information normally returned by the stat() system call
324 * under Unix (i.e. size, dates, access modes, etc). Use
325 * STATUS to get Rexx's information about the file. See
326 * the function 'getstatus()' for more information about
327 * the layout of the string returned.
329 * RESET - Resets the file after an error. Of course, this will
330 * only work for files which are 'resettable'. If the error
331 * is too serious, resetting will help little to fix the
332 * problem. E.g. writing beyond end-of-file can easily be
333 * fixed by RESET, trying to use a file which is named
334 * by an invalid syntax can not be correctly reset.
336 * READABLE - Checks that the file in question is available in the
337 * WRITABLE mode given, for the user that is executing the script.
338 * EXECUTABLE I.e. READABLE will return '1' for a file, if the file
339 * is readable for the user, else '0' is returned. Note
340 * that FSTAT returns the information about the accessmodes
341 * for a file, these returns the 'accessmode' which is
342 * relevant for a particular user. Also note that if your
343 * machine are using suid-bit (i.e. Unix), this function
344 * will check for the real uid, not the effective uid.
345 * Consequently, it may not give the wanted result for
346 * suid rexx scripts, see the Unix access() function. (And
347 * anyway, suid scripts are a _very_ bad idea under Unix,
348 * so this is probably not a problem ... :-)
350 #define COMMAND_NONE 0
351 #define COMMAND_READ 1
352 #define COMMAND_WRITE 2
353 #define COMMAND_APPEND 3
354 #define COMMAND_UPDATE 4
355 #define COMMAND_CREATE 5
356 #define COMMAND_CLOSE 6
357 #define COMMAND_FLUSH 7
358 #define COMMAND_STATUS 8
359 #define COMMAND_FSTAT 9
360 #define COMMAND_RESET 10
361 #define COMMAND_READABLE 11
362 #define COMMAND_WRITEABLE 12
363 #define COMMAND_EXECUTABLE 13
364 #define COMMAND_LIST 14
365 #define COMMAND_QUERY_DATETIME 15
366 #define COMMAND_QUERY_EXISTS 16
367 #define COMMAND_QUERY_HANDLE 17
368 #define COMMAND_QUERY_SEEK 18
369 #define COMMAND_QUERY_SIZE 19
370 #define COMMAND_QUERY_STREAMTYPE 20
371 #define COMMAND_QUERY_TIMESTAMP 21
372 #define COMMAND_QUERY_POSITION 23
373 #define COMMAND_QUERY 24
374 #define COMMAND_QUERY_POSITION_READ 25
375 #define COMMAND_QUERY_POSITION_WRITE 26
376 #define COMMAND_QUERY_POSITION_SYS 27
377 #define COMMAND_QUERY_POSITION_READ_CHAR 28
378 #define COMMAND_QUERY_POSITION_READ_LINE 29
379 #define COMMAND_QUERY_POSITION_WRITE_CHAR 30
380 #define COMMAND_QUERY_POSITION_WRITE_LINE 31
381 #define COMMAND_OPEN 32
382 #define COMMAND_OPEN_READ 33
383 #define COMMAND_OPEN_WRITE 34
384 #define COMMAND_OPEN_BOTH 35
385 #define COMMAND_OPEN_BOTH_APPEND 36
386 #define COMMAND_OPEN_BOTH_REPLACE 37
387 #define COMMAND_OPEN_WRITE_APPEND 38
388 #define COMMAND_OPEN_WRITE_REPLACE 39
389 #define COMMAND_SEEK 40
390 #define COMMAND_POSITION 41
393 * Define TRUE_TRL_IO, if you want the I/O system to be even more like
394 * TRL. It will try to mimic the behaviour in TRL exactly. Note that if
395 * you _do_ define this, you might experience a degrade in runtime
401 * There are two ways to report an error for file I/O operations. Either
402 * as an "error" or as a "warning". Both will raise the NOTREADY
403 * condition, but only ERROR will actually put the file into ERROR mode.
404 * Warnings are used for e.g. EOF while reading. Both are implemented
405 * by the same routine.
407 #define file_error(a,b,c) handle_file_error(TSD,a,b,c,1)
408 #define file_warning(a,b,c) handle_file_error(TSD,a,b,c,0)
411 * CASE_SENSITIVE_FILENAMES is used to determine if internal file
412 * pointers respect the case of files and treat "ABC" as a different
416 # define CASE_SENSITIVE_FILENAMES
419 * Regina truncates a file when repositioning by the use of a line
420 * count. That is, if the file has ten lines, and you use the BIF
421 * lineout(file,,4), it will be truncated after the fourth line.
422 * Truncating is not performed for character repositioning.
424 * If you don't want truncating after line repositioning, undefine
425 * the macro HAVE_FTRUNCATE in config.h. Also, if your system doesn't
426 * have ftruncate(), undefine HAVE_FTRUNCATE, and survive without the
429 * The function ftruncate() is a BSDism; if you have trouble finding
430 * it, try linking with -lbsd or -lucb or something like that. Since
431 * it is not a standard POSIX feature, some machines may generate
432 * warnings during compilation. Let's help these machines ...
434 #if defined(FIX_PROTOS) && defined(HAVE_FTRUNCATE)
436 int ftruncate( int fd
, int length
) ;
441 * Since development of Ultrix has ceased, and they never managed to
442 * fix a few things, we want to define a few things, just in order
443 * to kill a few warnings ...
445 #if defined(FIX_PROTOS) && defined(FIX_ALL_PROTOS) && defined(ultrix)
446 int fstat( int fd
, struct stat
*buf
) ;
447 int stat( char *path
, struct stat
*buf
) ;
452 * Here comes another 'sunshine-story' ... Since SunOS don't have
453 * a decent set of include-files in the standard version of the OS,
454 * their <stdio.h> don't define these macros. Instead, Sun seems to
455 * survive with the old custom of using the numberic values of these
456 * macros directly. If compiled with "SunKlugdes" defined, try to
459 * If you are using gcc on a Sun, you may want to run the program
460 * fixincludes that comes with gcc. It will fix this more permanently.
461 * At least one recent version of GCC for VMS doesn't have this
464 #if defined(SunKludges) || (defined(__GNUC__) && defined(VMS))
471 * Some machines don't defined these ... they should!
473 #if defined(VMS) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || (defined(WIN32) && defined(__BORLANDC__))
481 * Here is the datastructure in which to store the information about
482 * open files. The storage format of the file table is discussed
483 * elsewhere. The fields used to handle storing are 'next' and 'prev'
484 * which is used to implement a double linked list of files having
485 * the same hashfunc; and 'newer' and 'older' which are used to maintain
486 * a large double linked list of all files in order of the most
487 * recently used file.
489 * The other fields are:
491 * fileptr - Pointer to the filehandle use by the system when
492 * accessing the file through the normal I/O calls.
493 * If this pointer is NULL, it means that the file is
494 * not currently open.
495 * oper - Holds the value that tells whether the most recent
496 * operation on this file was a read or a write. This has
497 * importance for flushing, since a read can't imediately
498 * follow a write (or vice versa) without a flush (or
499 * a repositioning) inbetween. Takes the values OPER_READ,
500 * OPER_WRITE or OPER_NONE (signalizes that most recent
501 * operation can be followed by either read or write).
502 * flag - Bitfield that holds information about the file. The
503 * significance of the various fields are described by
505 * error - Most recently 'errno' code for this file. It could have
506 * been stored into 'errmsg' instead, but that would require
507 * copying of data which might not be used. If undefined,
508 * it will have the value 0.
509 * readpos - The current read position in the file, as a character
510 * position. Note that this is in 'C-syntax', i.e. the
511 * first character in the file is numbered "0". A value of
512 * -1 means that the value is unknown or undefined.
513 * readline - The line number of the current read position, which must
514 * be positive if define. A value of zero means that the
515 * line number is undefined or unknown. If current read
516 * position is at an EOL, 'readline' refers to the line
518 * writepos - Similar to 'readpos' but for current write position.
519 * writeline - Similar to 'readline' but for current write position.
520 * filename - Pointer to string containing the filename assiciated
521 * with this file. This string is garanteed to have an
522 * ASCII NUL following the last character, so it can be
523 * used directly in file operations. This field *must*
525 * errmsg - Error message associated with the file. Some errors are
526 * not trapped during call to system routines, and these
527 * does not have an error message defined by the opsys.
528 * E.g. when positioning current read position after EOF.
529 * This field stores errormessages for these situations.
530 * If undefined, it will be a NULL pointer.
532 * Both errmsg and error can not be defined simultaneously.
535 typedef struct fileboxtype
*fileboxptr
;
536 typedef const struct fileboxtype
*cfileboxptr
;
537 typedef struct fileboxtype
{
540 size_t readpos
, writepos
, thispos
;
541 int flag
, error
, readline
, writeline
, linesleft
;
542 fileboxptr prev
, next
, newer
, older
;
547 /* POSIX denies read and write operations on streams without intermediate
548 * fflush, fseek, fsetpos or rewind (list from EMX). We use the following
549 * macros to switch directly before an I/O operation. "Useful" fseeks should
550 * be error checked. This is not necessary here since the following operation
551 * will fault in case of an error.
553 #define SWITCH_OPER_READ(fptr) {if (fptr->oper==OPER_WRITE) \
554 fseek(fptr->fileptr,0l,SEEK_CUR); \
555 fptr->oper=OPER_READ;}
556 #define SWITCH_OPER_WRITE(fptr) {if (fptr->oper==OPER_READ) \
557 fseek(fptr->fileptr,0l,SEEK_CUR); \
558 fptr->oper=OPER_WRITE;}
560 typedef struct { /* fil_tsd: static variables of this module (thread-safe) */
562 * The following two pointers are pointers into the doble linked list
563 * of all files in the file table. They points to the most recently
564 * used file, and the least recently used open file. Note that the latter
565 * of these are _not_ the same as the last file in the list. If the
566 * Rexx' file table contains more files than the system's file table
567 * can contain, 'lrufile' will point to the last open file in the double
568 * linked list. Files further out in the list are 'swapped' out.
571 fileboxptr swappoint
;
573 fileboxptr stdio_ptr
[6];
575 fileboxptr filehash
[131];
576 int rol_size
; /* readoneline() */
577 char * rol_string
; /* readoneline() */
578 int got_eof
; /* readkbdline() */
579 } fil_tsd_t
; /* thread-specific but only needed by this module. see
583 static int positioncharfile( tsd_t
*TSD
, const char *bif
, int argno
, fileboxptr fileptr
, int oper
, long where
, int from
);
584 static int positionfile( tsd_t
*TSD
, const char *bif
, int argno
, fileboxptr ptr
, int oper
, int lineno
, int from
);
585 static void handle_file_error( tsd_t
*TSD
, fileboxptr ptr
, int rc
, const char *errmsg
, int level
) ;
588 * Marks all entries in the filetable. Used only by the memory
589 * management. Does not really change anything, so you can in general
590 * forget this one. This routine is called from memory.c in order to
591 * mark all statically defined data in this file.
594 void mark_filetable( const tsd_t
*TSD
)
596 fileboxptr ptr
=NULL
;
600 for (ptr
=ft
->mrufile
; ptr
; ptr
=ptr
->older
)
602 markmemory( ptr
, TRC_FILEPTR
) ;
603 markmemory( ptr
->filename0
, TRC_FILEPTR
) ;
605 markmemory( ptr
->errmsg
, TRC_FILEPTR
) ;
609 markmemory( ft
->rdarea
, TRC_FILEPTR
) ;
612 #endif /* TRACEMEM */
614 #if defined(WIN32) && defined(_MSC_VER)
616 * This is a replacement fo the BSD ftruncate() function.
617 * The code in this function was written by Les Moull.
620 int ftruncate( int fd
, long pos
)
622 HANDLE h
= (HANDLE
)_get_osfhandle( fd
) ;
624 if (SetFilePointer( h
, pos
, NULL
, FILE_BEGIN
) == 0xFFFFFFFF)
627 if ( !SetEndOfFile( h
) )
634 #if defined(__WATCOMC__) && defined(__QNX__)
635 # define ftruncate( fd, pos ) ltrunc( fd, pos, SEEK_SET )
639 * This command maps the string 'cmd' into a number which is to be
640 * interpreted according to the settings of the COMMAND_ macros.
641 * The input strings must be one of the valid command, or else the
642 * COMMAND_NONE value is returned.
644 * Well, this routine should really have been implemented differently,
645 * since sequential searching through a list of strings is not very
646 * efficient. But still, it is not so many entries in the list, and
647 * this function is not going to be called often, so I suppose it
648 * doesn't matter too much. Ideallistic, it should be rewritten to
652 static char get_command( streng
*cmd
)
656 if (cmd
->len
==4 && !memcmp(cmd
->value
, "READ", 4))
657 return COMMAND_READ
;
658 if (cmd
->len
==5 && !memcmp(cmd
->value
, "WRITE", 5))
659 return COMMAND_WRITE
;
660 if (cmd
->len
==6 && !memcmp(cmd
->value
, "APPEND", 6))
661 return COMMAND_APPEND
;
662 if (cmd
->len
==6 && !memcmp(cmd
->value
, "UPDATE", 6))
663 return COMMAND_UPDATE
;
664 if (cmd
->len
==6 && !memcmp(cmd
->value
, "CREATE", 6))
665 return COMMAND_CREATE
;
666 if (cmd
->len
==5 && !memcmp(cmd
->value
, "CLOSE", 5))
667 return COMMAND_CLOSE
;
668 if (cmd
->len
==5 && !memcmp(cmd
->value
, "FLUSH", 5))
669 return COMMAND_FLUSH
;
670 if (cmd
->len
==6 && !memcmp(cmd
->value
, "STATUS", 6))
671 return COMMAND_STATUS
;
672 if (cmd
->len
==5 && !memcmp(cmd
->value
, "FSTAT", 5))
673 return COMMAND_FSTAT
;
674 if (cmd
->len
==5 && !memcmp(cmd
->value
, "RESET", 5))
675 return COMMAND_RESET
;
676 if (cmd
->len
==8 && !memcmp(cmd
->value
, "READABLE", 8))
677 return COMMAND_READABLE
;
678 if (cmd
->len
==8 && !memcmp(cmd
->value
, "WRITABLE", 8))
679 return COMMAND_WRITEABLE
;
680 if (cmd
->len
==10 && !memcmp(cmd
->value
, "EXECUTABLE", 10))
681 return COMMAND_EXECUTABLE
;
682 if (cmd
->len
==4 && !memcmp(cmd
->value
, "LIST", 4))
683 return COMMAND_LIST
;
684 if (cmd
->len
>=4 && !memcmp(cmd
->value
, "OPEN", 4))
685 return COMMAND_OPEN
;
686 if (cmd
->len
>=5 && !memcmp(cmd
->value
, "QUERY", 5))
687 return COMMAND_QUERY
;
688 if (cmd
->len
>=4 && !memcmp(cmd
->value
, "SEEK", 4))
689 return COMMAND_SEEK
;
690 if (cmd
->len
>=8 && !memcmp(cmd
->value
, "POSITION", 8))
691 return COMMAND_POSITION
;
692 return COMMAND_NONE
;
695 static char get_querycommand( const streng
*cmd
)
697 if (cmd
->len
==8 && !memcmp(cmd
->value
, "DATETIME", 8))
698 return COMMAND_QUERY_DATETIME
;
699 if (cmd
->len
==6 && !memcmp(cmd
->value
, "EXISTS", 6))
700 return COMMAND_QUERY_EXISTS
;
701 if (cmd
->len
==6 && !memcmp(cmd
->value
, "HANDLE", 6))
702 return COMMAND_QUERY_HANDLE
;
703 if (cmd
->len
>=4 && !memcmp(cmd
->value
, "SEEK", 4))
704 return COMMAND_QUERY_SEEK
;
705 if (cmd
->len
>=8 && !memcmp(cmd
->value
, "POSITION", 8))
706 return COMMAND_QUERY_POSITION
;
707 if (cmd
->len
==4 && !memcmp(cmd
->value
, "SIZE", 4))
708 return COMMAND_QUERY_SIZE
;
709 if (cmd
->len
==10 && !memcmp(cmd
->value
, "STREAMTYPE", 10))
710 return COMMAND_QUERY_STREAMTYPE
;
711 if (cmd
->len
==9 && !memcmp(cmd
->value
, "TIMESTAMP", 9))
712 return COMMAND_QUERY_TIMESTAMP
;
713 return COMMAND_NONE
;
716 static char get_querypositioncommand( const streng
*cmd
)
718 if (cmd
->len
>=4 && !memcmp(cmd
->value
, "READ", 4))
719 return COMMAND_QUERY_POSITION_READ
;
720 if (cmd
->len
>=5 && !memcmp(cmd
->value
, "WRITE", 5))
721 return COMMAND_QUERY_POSITION_WRITE
;
722 if (cmd
->len
==3 && !memcmp(cmd
->value
, "SYS", 3))
723 return COMMAND_QUERY_POSITION_SYS
;
724 return COMMAND_NONE
;
727 static char get_querypositionreadcommand( const streng
*cmd
)
729 if (cmd
->len
==4 && !memcmp(cmd
->value
, "CHAR", 4))
730 return COMMAND_QUERY_POSITION_READ_CHAR
;
731 if (cmd
->len
==4 && !memcmp(cmd
->value
, "LINE", 4))
732 return COMMAND_QUERY_POSITION_READ_LINE
;
734 return COMMAND_QUERY_POSITION_READ_CHAR
;
735 return COMMAND_NONE
;
738 static char get_querypositionwritecommand( const streng
*cmd
)
740 if (cmd
->len
==4 && !memcmp(cmd
->value
, "CHAR", 4))
741 return COMMAND_QUERY_POSITION_WRITE_CHAR
;
742 if (cmd
->len
==4 && !memcmp(cmd
->value
, "LINE", 4))
743 return COMMAND_QUERY_POSITION_WRITE_LINE
;
745 return COMMAND_QUERY_POSITION_WRITE_CHAR
;
746 return COMMAND_NONE
;
749 static char get_opencommand( const streng
*cmd
)
751 if (cmd
->len
>=4 && !memcmp(cmd
->value
, "BOTH", 4))
752 return COMMAND_OPEN_BOTH
;
753 if (cmd
->len
==4 && !memcmp(cmd
->value
, "READ", 4))
754 return COMMAND_OPEN_READ
;
755 if (cmd
->len
>=5 && !memcmp(cmd
->value
, "WRITE", 5))
756 return COMMAND_OPEN_WRITE
;
758 return COMMAND_OPEN_BOTH
;
759 return COMMAND_NONE
;
762 static char get_opencommandboth( const streng
*cmd
)
764 if (cmd
->len
==6 && !memcmp(cmd
->value
, "APPEND", 6))
765 return COMMAND_OPEN_BOTH_APPEND
;
766 if (cmd
->len
==7 && !memcmp(cmd
->value
, "REPLACE", 7))
767 return COMMAND_OPEN_BOTH_REPLACE
;
769 return COMMAND_OPEN_BOTH
;
770 return COMMAND_NONE
;
773 static char get_opencommandwrite( const streng
*cmd
)
775 if (cmd
->len
==6 && !memcmp(cmd
->value
, "APPEND", 6))
776 return COMMAND_OPEN_WRITE_APPEND
;
777 if (cmd
->len
==7 && !memcmp(cmd
->value
, "REPLACE", 7))
778 return COMMAND_OPEN_WRITE_REPLACE
;
780 return COMMAND_OPEN_WRITE
;
781 return COMMAND_NONE
;
785 /* ==================================================================== */
786 /* level 3 routines */
789 * Returns the fileboxptr of a file, if is has already been opened.
790 * If it does not exist in Rexx's file table, a NULL pointer is
791 * returned in stead. It is easy to change the datastruction in
792 * which the file table is stored.
794 * If using VMS, or another opsys that has a caseinsensitive file
795 * system, maybe it should disregard the case of the filename. In
796 * general, maybe it should 'normalize' the file name before storing
797 * it in the file table (do we sence an upcoming namei() :-)
800 #define FILEHASH_SIZE (sizeof(((fil_tsd_t*)0)->filehash) / \
801 sizeof(((fil_tsd_t*)0)->filehash[0]))
803 #ifdef CASE_SENSITIVE_FILENAMES
804 #define filehashvalue(strng) (hashvalue(strng->value, strng->len) % FILEHASH_SIZE)
806 #define filehashvalue(strng) (hashvalue_ic(strng->value, strng->len) % FILEHASH_SIZE)
809 static void removefileptr( const tsd_t
*TSD
, cfileboxptr ptr
)
814 if (ft
->swappoint
== ptr
)
815 ft
->swappoint
= ptr
->newer
;
817 if (ft
->mrufile
==ptr
)
818 ft
->mrufile
= ptr
->older
;
821 ptr
->older
->newer
= ptr
->newer
;
824 ptr
->newer
->older
= ptr
->older
;
827 ptr
->next
->prev
= ptr
->prev
;
830 ptr
->prev
->next
= ptr
->next
;
832 ft
->filehash
[filehashvalue(ptr
->filename0
)] = ptr
->next
;
835 /* enterfileptr initializes a fileboxptr. It must be allocated and the
836 * following fields must already been set:
837 * errmsg, error, fileptr, flag, filename0
839 static void enterfileptr( const tsd_t
*TSD
, fileboxptr ptr
)
847 * First, get the magic number for this file. Note that when we're
848 * doing hashing like this, we *may* get trouble on machines that
849 * don't differ between upper and lower case letters in filenames.
851 hashval
= filehashvalue(ptr
->filename0
) ;
854 * Then, link it into the list of values having the same hashvalue
856 ptr
->next
= ft
->filehash
[hashval
] ;
858 ptr
->next
->prev
= ptr
;
859 ft
->filehash
[hashval
] = ptr
;
863 * Then, link it into the 'global' list of files, sorted by how
864 * recently they have been used.
866 ptr
->older
= ft
->mrufile
;
868 ptr
->older
->newer
= ptr
;
873 ft
->swappoint
= ptr
;
878 ptr
->thispos
= (size_t) EOF
;
879 ptr
->readpos
= (size_t) EOF
;
880 ptr
->writepos
= (size_t) EOF
;
881 ptr
->oper
= OPER_NONE
;
885 static void swapout_file( const tsd_t
*TSD
)
891 * Too many open files simultaneously, we have to close one down
892 * in order to free one file descriptor, but only if there actually
893 * are some files that can be closed down.
896 if (ft
->swappoint
==NULL
)
898 ft
->swappoint
= ft
->mrufile
;
899 for (; ft
->swappoint
&& ft
->swappoint
->older
; ft
->swappoint
=ft
->swappoint
->older
) ;
902 if (ft
->swappoint
==NULL
)
903 exiterror( ERR_SYSTEM_FAILURE
, 0 ) ;
905 if ((ft
->swappoint
->flag
& FLAG_SURVIVOR
) ||
906 (ft
->swappoint
->flag
& FLAG_SWAPPED
) ||
907 (ft
->swappoint
->fileptr
==NULL
))
909 ft
->swappoint
= ft
->swappoint
->newer
;
914 if (fclose( ft
->swappoint
->fileptr
)==EOF
)
915 exiterror( ERR_SYSTEM_FAILURE
, 1, strerror(errno
) ) ;
917 ft
->swappoint
->fileptr
= NULL
;
918 ft
->swappoint
->flag
|= FLAG_SWAPPED
;
919 ft
->swappoint
->thispos
= (size_t) EOF
;
920 ft
->swappoint
= ft
->swappoint
->newer
;
924 static const char *acc_mode
[] = { "r", "r+", "a" } ;
926 static const char *acc_mode
[] = { "rb", "r+b", "ab" } ;
929 #define ACCMODE_READ 0
930 #define ACCMODE_RDWRT 1
931 #define ACCMODE_WRITE 2
932 #define ACCMODE_NONE 3
934 static void swapin_file( tsd_t
*TSD
, fileboxptr ptr
)
936 int faccess
=0, itmp
=0 ;
939 * First, just try to reopen the file, we _might_ have a vacant
940 * entry in the system file table, so, use that.
942 itmp
= (ptr
->flag
& (FLAG_READ
| FLAG_WRITE
)) ;
943 if (itmp
==(FLAG_READ
| FLAG_WRITE
))
944 faccess
= ACCMODE_RDWRT
;
945 else if (itmp
==(FLAG_READ
))
946 faccess
= ACCMODE_READ
;
947 else if (itmp
==(FLAG_WRITE
))
948 faccess
= ACCMODE_WRITE
;
950 faccess
= ACCMODE_NONE
;
952 if (faccess
== ACCMODE_NONE
)
953 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
961 ptr
->fileptr
= fopen( ptr
->filename0
->value
, acc_mode
[faccess
] ) ;
962 if ((!ptr
->fileptr
) && (errno
== EMFILE
))
964 swapout_file( TSD
) ;
968 ptr
->flag
&= ~(FLAG_SWAPPED
) ;
969 if (ptr
->fileptr
==NULL
)
970 file_error( ptr
, errno
, NULL
) ;
972 fseek( ptr
->fileptr
, 0, SEEK_SET
) ;
975 ptr
->readline
= ptr
->writeline
= 0 ;
982 static fileboxptr
getfileptr( tsd_t
*TSD
, const streng
*name
)
990 * First, try to find the correct file in this sloth of the
991 * hash table. If one is found, ptr points to it, else, ptr is set
994 for (ptr
=ft
->filehash
[filehashvalue(name
)];ptr
;ptr
=ptr
->next
)
995 #ifdef CASE_SENSITIVE_FILENAMES
996 if (!Str_cmp(name
,ptr
->filename0
))
998 if (!Str_ccmp(name
,ptr
->filename0
))
1003 * In order not to create any problems later, just return with NULL
1004 * (signifying that no file was found) if that is the case. Then we may
1005 * able to assume that ptr _is_ set later.
1011 * Now, put the file in front of the list of files storted by how
1012 * recently they were used. We assume that any access to a file is
1013 * equivalent to the file being used.
1017 if (ft
->swappoint
==ptr
)
1018 ft
->swappoint
= ptr
->newer
;
1019 ptr
->newer
->older
= ptr
->older
;
1021 ptr
->older
->newer
= ptr
->newer
;
1022 ptr
->older
= ft
->mrufile
;
1024 ft
->mrufile
->newer
= ptr
;
1029 * If this file has been swapped out, we have to reopen it, so we can
1030 * continue to access it. First we verify that ft->swappoint is set; if it
1031 * isn't that means that
1033 if (ptr
->flag
& FLAG_SWAPPED
)
1034 swapin_file( TSD
, ptr
) ;
1040 static void flush_input( cfileboxptr dummy
)
1042 dummy
= dummy
; /* keep compiler happy */
1048 * This closes the file ... actually, I'm not sure whether that is the
1049 * correct thing to do, but lots of other rexx interpreters seem to do
1050 * exactly that. Maybe a feature for the 'traditional' mode?
1052 static void flush_output( tsd_t
*TSD
, const streng
*ptr
)
1054 closefile( TSD
, ptr
) ;
1059 * Sets up the internal filetable for REXX, and initializes it with
1060 * the three standard files under Unix, stderr, stdout og and stdin.
1061 * Should only be called once, from the main routine. We should also
1062 * add code to register the routine for marking memory from this
1065 * As a shortcut to access these three default files, there is a
1066 * variable 'stdio_ptr' which contains pointers to them. This allows
1067 * for quick access to the default streams.
1068 * The function returns 1 on success, 0 if memory is short.
1071 * The entry for stdin must be the same as the following #define for
1072 * DEFAULT_STDIN_INDEX below.
1073 * This assumption is used in readkbdline().
1075 #define DEFAULT_STDIN_INDEX 0
1076 int init_filetable( tsd_t
*TSD
)
1081 if (TSD
->fil_tsd
!= NULL
)
1084 if ((ft
= TSD
->fil_tsd
= MallocTSD(sizeof(fil_tsd_t
))) == NULL
)
1086 memset(ft
,0,sizeof(fil_tsd_t
));
1090 ft
->stdio_ptr
[i
] = MallocTSD( sizeof( filebox
)) ;
1091 ft
->stdio_ptr
[i
]->errmsg
= NULL
;
1092 ft
->stdio_ptr
[i
]->error
= 0 ;
1095 ft
->stdio_ptr
[0]->fileptr
= ft
->stdio_ptr
[3]->fileptr
= stdin
;
1096 ft
->stdio_ptr
[1]->fileptr
= ft
->stdio_ptr
[4]->fileptr
= stdout
;
1097 ft
->stdio_ptr
[2]->fileptr
= ft
->stdio_ptr
[5]->fileptr
= stderr
;
1099 ft
->stdio_ptr
[0]->flag
= ft
->stdio_ptr
[3]->flag
= ( FLAG_SURVIVOR
+ FLAG_READ
) ;
1100 ft
->stdio_ptr
[1]->flag
= ft
->stdio_ptr
[4]->flag
= ( FLAG_SURVIVOR
+ FLAG_WRITE
) ;
1101 ft
->stdio_ptr
[2]->flag
= ft
->stdio_ptr
[5]->flag
= ( FLAG_SURVIVOR
+ FLAG_WRITE
) ;
1103 ft
->stdio_ptr
[0]->filename0
= Str_crestrTSD( "<stdin>" ) ;
1104 ft
->stdio_ptr
[1]->filename0
= Str_crestrTSD( "<stdout>" ) ;
1105 ft
->stdio_ptr
[2]->filename0
= Str_crestrTSD( "<stderr>" ) ;
1106 ft
->stdio_ptr
[3]->filename0
= Str_crestrTSD( "stdin" ) ;
1107 ft
->stdio_ptr
[4]->filename0
= Str_crestrTSD( "stdout" ) ;
1108 ft
->stdio_ptr
[5]->filename0
= Str_crestrTSD( "stderr" ) ;
1111 enterfileptr( TSD
, ft
->stdio_ptr
[i
] ) ;
1116 void purge_filetable( tsd_t
*TSD
)
1118 fileboxptr ptr1
, ptr2
, save_ptr1
, save_ptr2
;
1123 /* Naming this the "removal loop". */
1124 for ( ptr1
=ft
->mrufile
; ptr1
; )
1126 save_ptr1
= ptr1
->older
;
1127 for ( ptr2
=ptr1
; ptr2
; )
1129 save_ptr2
= ptr2
->next
; /* this was moved from third parm of loop
1130 so that it did not address the free'd
1131 memory. See if statement below. */
1133 * If this is one of the default streams, don't let it be closed.
1134 * These file shall stay open, whatever happens.
1137 * JH 19991105 if was modified to include the next 5 statements. Originally,
1138 * the file was not closed, but all other references to it were deleted. In
1139 * situations where one *.exe invokes Rexx mutiple times, subsequent calls to
1140 * the standard streams caused an error. (getfileptr() failed, the file name
1141 * for stdio_ptr[?] comes up blank.)
1143 if (!(ptr2
->flag
& FLAG_SURVIVOR
)
1146 fclose( ptr2
->fileptr
) ;
1148 removefileptr( TSD
, ptr2
) ;
1151 Free_stringTSD( ptr2
->errmsg
) ;
1153 Free_stringTSD( ptr2
->filename0
) ;
1161 ft
->mrufile
= ft
->swappoint
= NULL
;
1164 * Now lets be absolutely paranoid, and remove all entries from the
1167 memset( ft
->filehash
, 0, sizeof(ft
->filehash
) );
1169 * JH 19991105 The following loop was added to re-instate the std streams into the
1170 * hash table. It seems easier to do this then to muck around with reseting the pointers
1171 * as the fileboxptr's are deleted. Cannot modify the loop above to look at filenames
1172 * before removing from filehas table, it might be pointing to a fileboxptr that got removed
1173 * by the "removal loop".
1177 enterfileptr( TSD
, ft
->stdio_ptr
[i
] ) ;
1183 * Sets the proper error conditions for the file, including providing a
1184 * a hook into the CALL/SIGNAL ON system. Now, we also like to set some
1185 * other information, like the status of the file (taken from rc).
1187 * First parameter is the file to operate on, the second and third
1188 * parameters are the error message to set (they can't both be defined),
1189 * and the last parameter is the level of 'severity'. If set, the file
1190 * is thrown into error state.
1192 static void handle_file_error( tsd_t
*TSD
, fileboxptr ptr
, int rc
, const char *errmsg
, int level
)
1196 assert( !(rc
&& errmsg
) ) ;
1198 if ((ptr
->flag
& FLAG_ERROR
) && (ptr
->flag
& FLAG_FAKE
))
1201 * If we are faking for this file already, don't bother to do anything
1202 * more. In particular, we do not want to set a new error, since that
1203 * will in general only overwrite the old (and probably more relevant)
1204 * error message. However, faking are _only_ done when NOTREADY is
1212 * If the file is not already in error, set the ERROR flag, and record
1213 * the error message. Also, clear the FAKE flag. This flag is only
1214 * defined when the ERROR flag is set, and we don't want any old
1215 * values laying around (it will be set later if needed).
1219 ptr
->flag
&= ~FLAG_FAKE
;
1220 ptr
->flag
|= FLAG_ERROR
;
1222 else if (ptr
->flag
& FLAG_RDEOF
)
1225 * If the file was in RDEOF state; ie EOF was read on the file
1226 * set the AFTER_RDEOF flag to ensure STREAM(stream,'S') works
1227 * like other interpreters.
1229 ptr
->flag
|= FLAG_AFTER_RDEOF
;
1233 * Set the error message, but only if one was given. This routine
1234 * can be called _without_ any errormessage, and if so, keep the
1240 Free_stringTSD( ptr
->errmsg
) ;
1244 ptr
->errmsg
= Str_creTSD( errmsg
) ;
1246 ptr
->errmsg
= NULL
;
1250 * OK, the file has been put into ERROR state, now we must check
1251 * to see if we should raise the NOTREADY condition. If NOTREADY
1252 * is not currently enabled, don't bother to try to raise it.
1254 traps
= gettraps( TSD
, TSD
->currlevel
) ;
1255 if (traps
[SIGNAL_NOTREADY
].on_off
)
1258 * The NOTREADY condition is being trapped; set the FAKE flag
1259 * so that we don't create more errors for this file. But _only_
1260 * set the FAKE flag if NOTREADY is trapped by method CALL.
1261 * Then raise the condition ...
1263 if (!traps
[SIGNAL_NOTREADY
].invoked
)
1264 ptr
->flag
|= FLAG_FAKE
;
1266 condition_hook(TSD
,SIGNAL_NOTREADY
,rc
+100,0,-1,Str_dupTSD(ptr
->filename0
),NULL
);
1274 * This routine is supposed to be called when the condition is triggered
1275 * by method CALL. From the time the condition is raised until the CALL is
1276 * is triggered, I/O to the file is faked. But before the condition handler
1277 * is called, we try to tidy things up a bit.
1279 * At least, we have to clear the FAKE flag. Other 'nice' things to do
1280 * is to clear error indicator in the file pointer, and to reset the
1281 * file in general. The ERROR state is not cleared, _unless_ the file
1282 * is one of the default streams.
1285 void fixup_file( tsd_t
*TSD
, const streng
*filename
)
1287 fileboxptr ptr
=NULL
;
1289 ptr
= getfileptr( TSD
, filename
) ;
1293 * If the file is open, try to clear it, first clear the error
1294 * indicator, and then try to fseek() to a 'safe' point. If the
1295 * seeking didn't work out, don't bother, it was worth a try.
1299 clearerr( ptr
->fileptr
) ;
1300 if ( ptr
->flag
& FLAG_PERSIST
)
1301 fseek( ptr
->fileptr
, 0, SEEK_SET
) ;
1303 ptr
->oper
= OPER_NONE
;
1306 if (ptr
->flag
& FLAG_SURVIVOR
)
1307 ptr
->flag
&= ~(FLAG_ERROR
) ;
1309 ptr
->flag
&= ~(FLAG_FAKE
) ;
1317 * This is stupid ... if the file exists, but is in error mode, we
1318 * shall not close it, but leave it open, so that the rest of the
1319 * operations on this file in this statement don't trip. Same happens
1320 * if we are not able to close it properly. Oh well ...
1322 * On second thoughts ... Faking only applies for input and output.
1323 * So closing doesn't have to be faked. Remove the file, whatever
1326 void closefile( tsd_t
*TSD
, const streng
*name
)
1328 fileboxptr ptr
=NULL
;
1330 /* If it isn't open, don't try to close it ... */
1331 ptr
= getfileptr( TSD
, name
) ;
1335 * If this is one of the default streams, don't let it be closed.
1336 * These file shall stay open, whatever happens.
1338 if (ptr
->flag
& FLAG_SURVIVOR
)
1342 * If the fileptr seems to point to something ... close it. We
1343 * really don't want to leak file table sloths. Actually, we should
1344 * check that the close was ok, and not let the fileptr go unless
1345 * we know that it was really closed (and released for new use).
1346 * Previously, it only closed when file was not in error. I don't
1347 * know what is the correct action, but this seems to be the most
1351 fclose( ptr
->fileptr
) ;
1353 removefileptr( TSD
, ptr
) ;
1356 Free_stringTSD( ptr
->errmsg
) ;
1358 Free_stringTSD( ptr
->filename0
) ;
1367 * This function is called when we need some kind of access to a file
1368 * but don't (yet) have it. It will only be called when we want to
1369 * open a file implicitly, e.g. it is open for reading, and it has then
1370 * been named in a output function.
1372 * This is rather primitive ... but this function can only be called
1373 * when the file is open for read, and we want to open it for write;
1374 * or if the file i open for write, and we want to open it for read.
1375 * So I think this will suffice. It ignores the 'access' parameter
1376 * And just assumes that the file must be opened in both read and
1379 * To improve on this function, we ought to do a lot more checking,
1380 * e.g. that the 'access' wanted are required, and that the file is
1381 * already open in some kind of mode. If this don't hold, we probably
1382 * have an error condition.
1384 * We should also check another thing, that the new file which is opened
1385 * is in fact the same file that we closed. Perferably, we should open
1386 * the new file, then check the device and inode of both the old and
1387 * new file to see whether they are the same (using stat()). If they
1388 * are not the same, the reopening should fail. As it is implemented
1389 * now, the Unix method for temporary files (open it, remove it,
1390 * use it, and then close it) will fail; and we loose access to the
1391 * original file too.
1393 static void reopen_file( tsd_t
*TSD
, fileboxptr ptr
)
1396 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
1399 * We can not reopen the default streams, that makes no sence. If
1400 * tried, report an error.
1402 if (ptr
->flag
& FLAG_SURVIVOR
)
1404 file_error( ptr
, 0, "Invalid operation on default stream" ) ;
1409 * Close the old file, and try to reopen the new file. There is the
1410 * same problem here as in closefile(); if closing didn't work (for
1411 * some mysterious reason), the system's file table should become
1412 * full. Better checking might be required.
1415 fclose( ptr
->fileptr
) ;
1417 ptr
->fileptr
= fopen( ptr
->filename0
->value
, "r+" ) ;
1419 ptr
->fileptr
= fopen( ptr
->filename0
->value
, "r+b" ) ;
1421 if (ptr
->fileptr
==NULL
)
1423 file_error( ptr
, errno
, NULL
) ;
1426 ptr
->oper
= OPER_NONE
;
1429 * We definitively want to set the close-on-exec flag. Suppose
1430 * an output file has not been flushed, and we execute a command.
1431 * This might perform an exec() and then a system(), which _will_
1432 * flush all files (close them). The result is that the file might
1433 * be flushed twice ... not good.
1435 * This don't work on VMS ... but the file system on VMS is so
1436 * different anyway, so it will probably not create any problems.
1437 * Besides, we don't do exec() and system() on VMS.
1439 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !(defined(WIN32) && defined(__IBMC__)) && !defined(_AMIGA) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__) && !defined(__AROS__)
1440 if (ptr
&& ptr
->fileptr
)
1443 fno
= fileno( ptr
->fileptr
) ;
1444 assert( fno
>= -1) ;
1445 flags
= fcntl( fno
, F_GETFD
) ;
1446 flags
|= FD_CLOEXEC
;
1447 if (fcntl( fno
, F_SETFD
, flags
)== -1)
1448 exiterror( ERR_SYSTEM_FAILURE
, 1, strerror(errno
) ) ;
1453 * If readposition is EOF (=illegal), then we "probably" needed to
1454 * open it in read mode. Set the current read position to the start
1457 if (ptr
->readpos
==EOF
)
1460 ptr
->linesleft
= 0 ;
1463 if ( ptr
->flag
& FLAG_PERSIST
)
1464 fseek( ptr
->fileptr
, 0, SEEK_SET
) ;
1468 * Then do the same thing for write access. We always set this to the
1469 * end-of-file -- the default -- even though there are other write
1470 * modes available. If the file is implicitly open in write mode,
1471 * then the current write position should be set to the default
1474 if (ptr
->writepos
==EOF
)
1476 ptr
->writeline
= 0 ;
1477 if ( ptr
->flag
& FLAG_PERSIST
)
1478 fseek( ptr
->fileptr
, 0, SEEK_END
) ;
1479 ptr
->writepos
= ftell( ptr
->fileptr
) ;
1480 ptr
->thispos
= ptr
->writepos
;
1484 * Then, at last, do some simple bookkeeping, set both read and
1485 * write access, and clear any previous problem.
1487 ptr
->flag
= FLAG_READ
| FLAG_WRITE
| FLAG_PERSIST
;
1490 Free_stringTSD(ptr
->errmsg
) ;
1492 ptr
->errmsg
= NULL
;
1498 * This function explicitly opens a file. It will be called if the user
1499 * has called the built-in function STREAM() in order to open a file
1500 * in a particular mode. It will also be called if the file is not
1501 * previously open, and is used in a read or write operation.
1503 * It takes two parameters, the name of the file to open, and the
1504 * mode in which it is to be opened. The mode has a value which is
1505 * matched by the ACCESS_ macros defined earlier.
1507 * If the file is actually open in advance, then we close it before we
1508 * do any other operations. If the user is interested in the file in
1509 * one particular mode, he is probably not interested in any previous
1512 static fileboxptr
openfile( tsd_t
*TSD
, const streng
*name
, int faccess
)
1514 fileboxptr ptr
=NULL
;
1518 * First check wether this file is already open, and use that open
1519 * file if possible. However, that may not be possible, since we
1520 * may want to use the file for another operation now. So, if the
1521 * file _is_ open, check to see if access is right.
1523 ptr
= getfileptr( TSD
, name
) ;
1526 if (ptr
->flag
& FLAG_SURVIVOR
)
1528 file_error( ptr
, 0, "Can't open a default stream" ) ;
1531 closefile( TSD
, name
) ;
1535 * Now, get a new file table entry, and fill in the various
1536 * field with appropriate (i.e. default) values.
1538 ptr
= MallocTSD( sizeof(filebox
) ) ;
1539 ptr
->filename0
= Str_dupstrTSD( name
) ;
1542 ptr
->errmsg
= NULL
;
1544 ptr
->linesleft
= 0 ;
1545 ptr
->writeline
= 0 ;
1546 ptr
->thispos
= (size_t) EOF
;
1547 ptr
->readpos
= (size_t) EOF
;
1548 ptr
->writepos
= (size_t) EOF
;
1549 ptr
->oper
= OPER_NONE
;
1552 * suppose we tried to open, but didn't manage, well, stuff it into
1553 * the file table, we might want to retrieve information about it
1554 * later on. _And_ we need to know about the problem if the file
1555 * I/O is to be faked later on.
1557 enterfileptr( TSD
, ptr
) ;
1558 name
= ptr
->filename0
;
1562 swapout_file( TSD
) ;
1566 * In most of these, we have to check that the file opened is really
1567 * a persistent file. We should not take that for granted.
1570 if (faccess
==ACCESS_READ
)
1573 if ((ptr
->fileptr
= fopen( name
->value
, "r" )) != NULL
)
1575 if ((ptr
->fileptr
= fopen( name
->value
, "rb" )) != NULL
)
1578 ptr
->flag
= FLAG_READ
| FLAG_PERSIST
;
1580 ptr
->linesleft
= 0 ;
1581 ptr
->thispos
= ptr
->readpos
= 0 ;
1583 else if (errno
==EMFILE
)
1584 goto kill_one_file
;
1586 file_error( ptr
, errno
, NULL
) ;
1588 else if (faccess
==ACCESS_WRITE
)
1591 * This is really a problem. If opened in mode "w", it will
1592 * truncate the file if it did exist. If opened int mode "r+",
1593 * it will fail if the file did not exist. So we try to
1596 ptr
->flag
= FLAG_READ
;
1598 ptr
->fileptr
= fopen( name
->value
, "r+" ) ;
1600 ptr
->fileptr
= fopen( name
->value
, "r+b" ) ;
1605 ptr
->fileptr
= fopen( name
->value
, "w+" ) ;
1607 ptr
->fileptr
= fopen( name
->value
, "w+b" ) ;
1619 ptr
->fileptr
= fopen( name
->value
, "w" ) ;
1621 ptr
->fileptr
= fopen( name
->value
, "wb" ) ;
1627 * Then set the current read and write positions to the start and
1628 * the end of the file, respectively.
1632 ptr
->flag
|= FLAG_WRITE
| FLAG_PERSIST
;
1633 fseek( ptr
->fileptr
, 0, SEEK_END
) ;
1634 lpos
= ftell( ptr
->fileptr
) ;
1635 ptr
->thispos
= ptr
->writepos
= lpos
;
1636 ptr
->writeline
= 0 ;
1639 ptr
->linesleft
= 0 ;
1641 else if (errno
==EMFILE
)
1642 goto kill_one_file
;
1644 file_error( ptr
, errno
, NULL
) ;
1646 else if (faccess
==ACCESS_APPEND
)
1649 * In append mode, the file is opened as a transient file, all
1650 * writing must be done at the end of the file. It is not
1651 * possible to perform reading on the file. Useful for files
1652 * to which you have write, but not read access (e.g. logfiles).
1655 if ((ptr
->fileptr
= fopen( name
->value
, "a" )) != NULL
)
1657 if ((ptr
->fileptr
= fopen( name
->value
, "ab" )) != NULL
)
1660 ptr
->flag
= FLAG_WRITE
| FLAG_WREOF
;
1662 else if (errno
==EMFILE
)
1663 goto kill_one_file
;
1665 file_error( ptr
, errno
, NULL
) ;
1667 else if (faccess
==ACCESS_STREAM_APPEND
)
1670 * In "stream" append mode, the file is opened as a persistent file, all
1671 * writing must be done at the end of the file. It is not
1672 * possible to perform reading on the file. Useful for files
1673 * to which you have write, but not read access (e.g. logfiles).
1676 if ((ptr
->fileptr
= fopen( name
->value
, "a" )) != NULL
)
1678 if ((ptr
->fileptr
= fopen( name
->value
, "ab" )) != NULL
)
1681 ptr
->flag
= FLAG_WRITE
| FLAG_WREOF
| FLAG_PERSIST
;
1682 if ( ptr
->flag
& FLAG_PERSIST
)
1683 fseek( ptr
->fileptr
, 0, SEEK_END
) ;
1684 lpos
= ftell( ptr
->fileptr
) ;
1685 ptr
->thispos
= ptr
->writepos
= lpos
;
1686 ptr
->writeline
= 0 ;
1689 ptr
->linesleft
= 0 ;
1691 else if (errno
==EMFILE
)
1692 goto kill_one_file
;
1694 file_error( ptr
, errno
, NULL
) ;
1696 else if (faccess
==ACCESS_STREAM_REPLACE
)
1699 * The file is created if it didn't exist, and if it did exist
1700 * it is truncated and the file pointers set to the start of file.
1703 if ((ptr
->fileptr
= fopen( name
->value
, "w+" )) != NULL
)
1705 if ((ptr
->fileptr
= fopen( name
->value
, "w+b" )) != NULL
)
1708 ptr
->flag
= FLAG_WRITE
| FLAG_READ
| FLAG_WREOF
| FLAG_RDEOF
|
1710 ptr
->writeline
= ptr
->readline
= 1 ;
1711 ptr
->linesleft
= 0 ;
1712 ptr
->readpos
= ptr
->writepos
= ptr
->thispos
= 0 ;
1714 else if (errno
==EMFILE
)
1715 goto kill_one_file
;
1717 file_error( ptr
, errno
, NULL
) ;
1719 else if (faccess
==ACCESS_UPDATE
)
1722 * Like read access, but it will not create the file if it didn't
1723 * already exist. Instead, an error is reported.
1726 if ((ptr
->fileptr
= fopen( name
->value
, "r+" )) != NULL
)
1728 if ((ptr
->fileptr
= fopen( name
->value
, "r+b" )) != NULL
)
1731 ptr
->flag
= FLAG_WRITE
| FLAG_READ
| FLAG_PERSIST
;
1733 ptr
->linesleft
= 0 ;
1734 ptr
->writeline
= 0 ;
1736 else if (errno
==EMFILE
)
1737 goto kill_one_file
;
1739 file_error( ptr
, errno
, NULL
) ;
1741 else if (faccess
==ACCESS_CREATE
)
1744 * The file is created if it didn't exist, and if it did exist
1748 if ((ptr
->fileptr
= fopen( name
->value
, "w+" )) != NULL
)
1750 if ((ptr
->fileptr
= fopen( name
->value
, "w+b" )) != NULL
)
1753 ptr
->flag
= FLAG_WRITE
| FLAG_READ
| FLAG_WREOF
| FLAG_RDEOF
|
1755 ptr
->writeline
= ptr
->readline
= 1 ;
1756 ptr
->linesleft
= 0 ;
1757 ptr
->readpos
= ptr
->writepos
= ptr
->thispos
= 0 ;
1759 else if (errno
==EMFILE
)
1760 goto kill_one_file
;
1762 file_error( ptr
, errno
, NULL
) ;
1765 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
1767 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(_AMIGA) && !defined(__AROS__) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__)
1769 * Then we check to see if this is a transient or persistent file.
1770 * We can remove a 'persistent' setting, but add one, since we
1771 * sometimes want to access a persistent file as transient (append).
1776 struct stat statbuf
;
1778 fno
= fileno(ptr
->fileptr
) ;
1779 rc
= fstat( fno
, &statbuf
) ;
1780 if (rc
==0 && !S_ISREG(statbuf
.st_mode
))
1781 ptr
->flag
&= ~(FLAG_PERSIST
) ;
1783 file_error( ptr
, errno
, NULL
) ;
1787 * As with reopen_file(), we want to set the close-on-exec flag,
1788 * se reopen_file for more information.
1793 fno
= fileno(ptr
->fileptr
) ;
1794 assert( fno
>= -1) ;
1795 flags
= fcntl( fno
, F_GETFD
) ;
1796 flags
|= FD_CLOEXEC
;
1797 if (fcntl( fno
, F_SETFD
, flags
)== -1)
1798 exiterror( ERR_SYSTEM_FAILURE
, 1, strerror(errno
) ) ;
1808 /* ------------------------------------------------------------------- */
1809 /* High level utility routines */
1815 * This function is really just an interface to the function getfileptr().
1816 * It takes a (possible) filename, and retrieves the corresponding
1817 * Rexx file table entry. If the file does not exist, it is opened in
1818 * the mode indicated by 'open_mode'. If it does exist, this routine
1819 * verifies that it it has been opened in a mode corresponding to
1820 * 'access' (OPER_READ or OPER_WRITE).
1822 * If the file does not exist, it is opened in either normal read
1823 * or normal write. This correcspinds to an "implicit" file open
1826 static fileboxptr
get_file_ptr( tsd_t
*TSD
, const streng
*name
, int faccess
, int open_mode
)
1828 fileboxptr ptr
=NULL
;
1830 ptr
= getfileptr( TSD
, name
) ;
1832 return openfile( TSD
, name
, open_mode
) ;
1834 if (ptr
->flag
& FLAG_ERROR
)
1837 if (faccess
==OPER_READ
&& (!(ptr
->flag
& FLAG_READ
)))
1838 reopen_file( TSD
, ptr
) ;
1839 else if (faccess
==OPER_WRITE
&& (!(ptr
->flag
& FLAG_WRITE
)))
1840 reopen_file( TSD
, ptr
) ;
1848 * This routine reads one complete line from the file indicated by
1849 * the file table entry 'ptr'. Or rather, it read from the current
1850 * read position, until and including the first EOL mark, and returns
1851 * that. If the EOL mark is implemented as certain characters, they are
1852 * not returned. It closely corresponds to the LINEIN() built-in
1855 * What is the upper limit for the size that we might read in? It's
1856 * best not to have any limit, so the method is the following: A
1857 * temporary area is used for storing the data read from the file.
1858 * We never know the size needed until the EOL mark is found. So
1859 * just read the data into the temporary area. If the EOL is found,
1860 * then we know the size, and we can transfer the data into a 'streng'
1861 * of suitable size. If the temporary area is too small, allocate
1862 * an area twice the size, and copy the data over. Afterwards, keep the
1863 * new area as the temporary area.
1865 * This way, be normally use little memory, but we are still able to
1866 * read as large lines as the memory allows, if it is needed.
1868 * No error condition is raised if noerrors is set. Instead, NULL is returned.
1870 static streng
*readoneline( tsd_t
*TSD
, fileboxptr ptr
, int noerrors
)
1872 int i
=0, j
=0, eolf
=0, eolchars
=1 ;
1882 * First verify that we actually have a file that is not in an
1883 * ERROR state. If so, don't perform any operations.
1885 if ( ptr
->flag
& FLAG_ERROR
)
1889 if (!(ptr
->flag
& FLAG_FAKE
))
1890 file_error( ptr
, 0, NULL
) ;
1892 return nullstringptr() ;
1896 * If we have an EOF from the last linein() then cause the NOTREADY
1899 if ( ptr
->flag
& FLAG_RDEOF
)
1903 file_warning( ptr
, 0, "EOF on line input" ) ;
1907 * If the string is not yet allocated, allocate it, and use an
1908 * initial size of 512 bytes. This can be increased during runtime.
1909 * Using higher initial sizes will waste allocation time on modern systems
1910 * with page sizes around 4KB. 512 fits most cases at its best.
1912 if (!ft
->rol_string
)
1914 ft
->rol_string
= MallocTSD( (ft
->rol_size
=512) ) ;
1916 ft
->rdarea
= ft
->rol_string
;
1921 if (ptr->fileptr==stdin)
1922 fcntl( stdin, F_SETFL, O_NONBLOCK | fcntl(stdin,F_GETFL)) ;
1925 SWITCH_OPER_READ(ptr
);
1928 j
= getc(ptr
->fileptr
);
1929 if (j
== REGINA_EOL
)
1934 #if !defined(UNIX) && !defined(MAC)
1937 k
= getc(ptr
->fileptr
);
1938 if (k
== REGINA_EOL
)
1946 ungetc(k
,ptr
->fileptr
);
1953 * If we hit end-of-file, handle it carefully, and terminate the
1954 * reading. Note that this means that there we have read an
1955 * incomplete last line, so return what we've got, and report
1956 * an NOTREADY condition. (Partly, I disagree, but this is how
1957 * TRL defines it ... Case I: Programmer using Emacs forgets to
1958 * add a EOL after the last line; Rexx triggers NOTREADY when
1959 * reading that last, incomplete line.
1963 ptr
->flag
|= FLAG_RDEOF
;
1964 /* file_warning( ptr, 0, "EOF on line input" ) ; */
1969 * We are trying to avoid any limits other than memory-imposed
1970 * limits. So if the buffer size that we currently have are too
1971 * small, double it, and hide the operation from the rest of the
1974 if (i
>=ft
->rol_size
)
1978 assert( i
== ft
->rol_size
) ;
1979 tmpstring
= MallocTSD( 2*ft
->rol_size
+10 ) ;
1980 memcpy( tmpstring
, ft
->rol_string
, ft
->rol_size
) ;
1981 FreeTSD( ft
->rol_string
) ;
1982 ft
->rol_string
= tmpstring
;
1985 ft
->rdarea
= ft
->rol_string
;
1990 * Just an ordinary character ... append it to the buffer
1992 ft
->rol_string
[i
] = (char) j
;
1995 * Attempt to set the read pointer and the current file
1996 * pointer based on the lenght of the line we just read.
1998 #if 1 /* really MH */
1999 if ( ptr
->thispos
== ptr
->readpos
)
2001 if ( ptr
->thispos
== EOF
)
2004 ptr
->thispos
= ptr
->readpos
= ftell( ptr
->fileptr
) ;
2008 ptr
->thispos
+= (i
- (j
==EOF
)) + eolchars
;
2009 ptr
->readpos
= ptr
->thispos
;
2015 ptr
->thispos
= ptr
->readpos
= ftell( ptr
->fileptr
) ;
2018 if (ptr
->thispos
!= EOF
)
2019 ptr
->thispos
+= (i
- (j
==EOF
)) + eolchars
;
2021 if (ptr
->readpos
!= EOF
)
2022 ptr
->readpos
= ptr
->thispos
;
2025 * If we did read a complete line, we have to increment the line
2026 * count for the current read pointer of this file. This part of
2027 * the code is a bit Unix-ish. It will have to be reworked for
2028 * other types of operating systems.
2030 if ((eolf
==REGINA_EOL
) && (ptr
->readline
> 0))
2032 #if 1 /* really MH */
2033 ptr
->readline
+= 1 ; /* only if we actually saw the "\n" !!! */
2035 ptr
->readline
+= eolchars
; /* only if we actually saw the "\n" !!! */
2041 * A bit of a hack here. Because countlines() determines if any lines
2042 * are left in the stream by using the feof() function, we have to
2043 * attempt to read the EOF after each line, and set the file's state
2044 * to EOF. If the character read is not EOF, then put it back on
2045 * the stream to be read later.
2046 * Only do this for persistent streams!!
2048 if ( ptr
->flag
& FLAG_PERSIST
2049 && !feof( ptr
->fileptr
) )
2052 ch0
= getc(ptr
->fileptr
);
2053 if (feof(ptr
->fileptr
))
2055 ptr
->flag
|= FLAG_RDEOF
;
2056 /* file_warning( ptr, 0, "EOF on line input" ) ; */
2060 ungetc(ch0
,ptr
->fileptr
);
2065 * Wrap up the data that was read, and return it as a 'streng'.
2068 /* if (i>1000) i = 1000 ; */
2069 if (noerrors
&& (i
== 0) && (ptr
->flag
| FLAG_RDEOF
))
2071 ret
= Str_makeTSD( i
) ;
2072 memcpy( ret
->value
, ft
->rol_string
, ret
->len
=i
) ;
2080 * This routine will position the current read or write position
2081 * of a file, to the start of a particular line. The file to be
2082 * operated on is 'ptr', the pointer to manipulate is indicated
2083 * by 'oper' (either OPER_READ or OPER_WRITE or both), and the linenumber
2084 * to position at is 'lineno'.
2086 * There are (at least) two ways to do the backup of the current
2087 * position in the file. First to backup to the start of the file
2088 * and then to seek forward, or to seek backwards from the current
2089 * position of the file.
2091 * Perhaps the first is best for the standard case, and the second
2092 * should be activated when the line-parameter is negative ... ?
2095 static int positionfile( tsd_t
*TSD
, const char *bif
, int argno
, fileboxptr ptr
, int oper
, int lineno
, int from
)
2098 int from_line
=0, old_errno
=0, tmp
=0 ;
2101 from
= from
; /* keep compiler happy - FIXME why is this not used ? */
2103 * If file is in ERROR state, don't touch it.
2105 if (ptr
->flag
& FLAG_ERROR
)
2107 if (!(ptr
->flag
& FLAG_FAKE
))
2108 file_error( ptr
, 0, NULL
) ;
2113 * If this isn't a persistent file, then report an error. We can only
2114 * perform repositioning in persistent files.
2117 if (!(ptr
->flag
& FLAG_PERSIST
))
2119 exiterror( ERR_INCORRECT_CALL
, 42, bif
, tmpstr_of( TSD
, ptr
->filename0
) ) ;
2123 * If the operation is READ, but the file is not open for READ,
2126 if ((oper
&OPER_READ
) && !(ptr
->flag
& FLAG_READ
))
2127 exiterror( ERR_INCORRECT_CALL
, 921, bif
, argno
, "READ" ) ;
2129 * If the operation is WRITE, but the file is not open for WRITE,
2132 if ( (oper
&OPER_WRITE
) && !(ptr
->flag
& FLAG_WRITE
) )
2133 exiterror( ERR_INCORRECT_CALL
, 921, bif
, argno
, "WRITE" ) ;
2136 * If we do any repositioning, then make the old estimate of lines
2137 * left to read invalid. This is not really needed in all cases, but
2138 * it is a good start. And you _may_ even want to recalculate the
2139 * number of lines left!
2141 if ( ptr
->linesleft
> 0 )
2142 ptr
->linesleft
= 0 ;
2144 if ( ptr
->thispos
== EOF
)
2147 ptr
->thispos
= ftell( ptr
->fileptr
) ;
2151 * So, what we are going to do depends partly on wheter we are moving
2152 * the read or the write position of the file. We may even be as
2153 * lucky as not to have to move anything ... :-) First we can clear
2154 * the EOF flag, if set. Repositioning will clean up any EOF state.
2156 if (oper
& OPER_READ
)
2158 ptr
->flag
&= ~(FLAG_RDEOF
) ;
2159 ptr
->flag
&= ~(FLAG_AFTER_RDEOF
) ;
2161 if (oper
& OPER_WRITE
)
2162 ptr
->flag
&= ~(FLAG_WREOF
) ;
2165 * We know the line number of at most three positions in the file:
2166 * the start of the file, the write position and the read position.
2167 * If the file is open only for reading or writing, we know at most
2168 * two positions. And in addition, the read and/or the write
2169 * position may be be invalid (i.e. previous operation was
2170 * character oriented). But at least, we know the line number of
2171 * one position, the start of the file, which is the first line.
2173 * The best method seems to be: First start with the start of file
2174 * and then see if using the read or the write position instead is
2175 * a better deal. There is one drawback ... we assume that all lines
2176 * are equally long. That assumption is probably not too bad for text
2177 * files, but it may create unnecessary overhead for 'peculiar' files
2183 * First, let's check to see if we gain anything from using the
2184 * read position instead. If the distance from the current read
2185 * position to the wanted line (counted in number of lines) is smaller
2186 * than the number of lines from the first line to the wanted line,
2187 * use the current read position in stead. But only if the current
2188 * read position is defined.
2190 if ((ptr
->flag
& FLAG_READ
) && (ptr
->readline
> 0))
2192 assert( ptr
->readpos
!= EOF
) ;
2193 tmp
= ptr
->readline
- lineno
;
2197 if (tmp
< (lineno
- from_line
))
2199 from_line
= ptr
->readline
;
2200 from_char
= ptr
->readpos
;
2205 * Then, we check to see whether we can gain even more if we use
2206 * the current write position of the file instead.
2208 if ((ptr
->flag
& FLAG_WRITE
) && (ptr
->writeline
> 0))
2210 assert( ptr
->writepos
!= EOF
) ;
2211 tmp
= ptr
->writeline
- lineno
;
2215 if (tmp
< (lineno
- from_line
))
2217 from_line
= ptr
->writeline
;
2218 from_char
= ptr
->writepos
;
2223 * By now, the variables from_line, and from_char should contain
2224 * the optimal starting point from where a seek for the 'lineno'
2225 * line in the file can start, so first, move there. An in addition,
2226 * it should be the known position which is closest to the wanted
2229 if (from_char
!= (long) ptr
->thispos
)
2232 if ( ptr
->flag
& FLAG_PERSIST
2233 && fseek( ptr
->fileptr
, from_char
, SEEK_SET
))
2235 file_error( ptr
, errno
, NULL
) ;
2238 ptr
->oper
= OPER_NONE
;
2239 ptr
->thispos
= from_char
;
2241 assert( from_char
== ftell(ptr
->fileptr
) ) ;
2244 * Now we are positioned at the right spot, so seek forwards or
2245 * backwards until we reach the correct line. Actually, the method
2246 * we are going to use may seem a bit strange at first. First we
2247 * seek forward until we pass the line, and then we seek backwards
2248 * until we reach the line and at the end we back up to the first
2249 * preceding end-of-line marker. This may seem awkward, but it is
2250 * fairly simple. And in addition, it will always position us at
2251 * the _start_ of the wanted line.
2254 while ((lineno
>from_line
)) /* seek forward */
2256 SWITCH_OPER_READ(ptr
);
2257 for (;((ch
=getc(ptr
->fileptr
))!=EOF
)&&(ch
!=REGINA_EOL
);from_char
++) ;
2265 * Then we seek backwards until we reach the line. The backwards
2266 * movement is _really_ awkward, so perhaps we should read in 512
2267 * bytes, and analyse the data in it instead? Indeed, another
2268 * algoritm should be chosen. Maybe later ...
2270 while (lineno
<=from_line
&& from_char
>0)
2273 if ( ptr
->flag
& FLAG_PERSIST
2274 && fseek(ptr
->fileptr
, -1, SEEK_CUR
))
2277 * Should this happen? Only if someone overwrites EOF chars in
2278 * the file, but that _may_ happend ... Report error for
2279 * any errors from the fseek and ftell. If we hit the start of
2280 * the file, reset from_line check whether we are _below_ lineno
2281 * If so, jump back and seek from the start (then we *must*
2282 * start at line 1, since the data we've got are illegal).
2284 * It will also happen if we are seeking backwards for the
2289 if (fseek(ptr
->fileptr
,0,SEEK_SET
))
2291 file_error( ptr
, errno
, NULL
) ;
2294 ptr
->oper
= OPER_NONE
;
2298 if (from_line
<lineno
)
2300 ptr
->readline
= ptr
->writeline
= (-1) ;
2304 break ; /* we were looking for the first line ... how lucky :-) */
2308 * After seeking one character backwards, we must read the character
2309 * that we just skipped over. Do that, and test whether it is
2310 * a end-of-line character.
2312 SWITCH_OPER_READ(ptr
);
2313 ch
= getc(ptr
->fileptr
) ;
2316 if (lineno
==from_line
)
2323 * Then we move backwards once more, in order to compensate for
2324 * reading the character. Sigh, we are really doing a lot of
2325 * forward and backward reading, arn't we?
2328 if ( ptr
->flag
& FLAG_PERSIST
2329 && fseek(ptr
->fileptr
, -1, SEEK_CUR
))
2331 file_error( ptr
, errno
, NULL
) ;
2334 ptr
->oper
= OPER_NONE
;
2338 * Now we are almost finished. We just have to set the correct
2339 * information in the Rexx file table entry.
2341 ptr
->thispos
= ftell( ptr
->fileptr
) ;
2342 if (oper
& OPER_READ
)
2344 ptr
->readline
= lineno
;
2345 ptr
->readpos
= ptr
->thispos
;
2346 ptr
->flag
&= ~(FLAG_RDEOF
) ;
2347 ptr
->flag
&= ~(FLAG_AFTER_RDEOF
) ;
2349 if (oper
& OPER_WRITE
)
2351 ptr
->writeline
= lineno
;
2352 ptr
->writepos
= ptr
->thispos
;
2353 ptr
->flag
&= ~(FLAG_WREOF
) ;
2356 if (oper
& OPER_READ
)
2357 return ptr
->readline
+ 1; /* external representation */
2359 return ptr
->writeline
+ 1; /* external representation */
2366 * I wish every function would be as easy as this! Basically, it
2367 * only contain simple error checking, and a direct positioning.
2368 * it is called by the built-in function CHARIN() and CHAROUT()
2369 * in order to position the current read or write position at the
2370 * correct place in the file.
2372 static int positioncharfile( tsd_t
*TSD
, const char *bif
, int argno
, fileboxptr fileptr
, int oper
, long where
, int from
)
2374 int where_read
=0,where_write
=0;
2376 * If the file is in state ERROR, don't touch it! Since we are not
2377 * to return any data, don't bother about the state of FAKE.
2379 if (fileptr
->flag
& FLAG_ERROR
)
2381 if (!(fileptr
->flag
& FLAG_FAKE
))
2382 file_error( fileptr
, 0, NULL
) ;
2387 * If the file is not persistent, then positioning is not allowed.
2388 * Give the appropriate error for this.
2390 if (!(fileptr
->flag
& FLAG_PERSIST
))
2391 exiterror( ERR_INCORRECT_CALL
, 42, bif
, tmpstr_of( TSD
, fileptr
->filename0
) ) ;
2393 * If the operation is READ, but the file is not open for READ,
2396 if ((oper
&OPER_READ
) && !(fileptr
->flag
& FLAG_READ
))
2397 exiterror( ERR_INCORRECT_CALL
, 921, bif
, argno
, "READ" ) ;
2399 * If the operation is WRITE, but the file is not open for WRITE,
2402 if ((oper
&OPER_WRITE
) && !(fileptr
->flag
& FLAG_WRITE
))
2403 exiterror( ERR_INCORRECT_CALL
, 921, bif
, argno
, "WRITE" ) ;
2407 * TRL says that positioning the read position to after the last
2408 * character in the file, is an error. Unix allows it, and gives
2409 * an EOF at the next reading. So, we have to handle this as a
2410 * special case ... Check that the new position is valid.
2412 * Should we give "Incorrect call to routine" when the character
2413 * position is greater than the size of the file? Perhaps we should
2414 * raise the NOTREADY condition instead?
2419 oldp
= ftell( fileptr
->fileptr
) ;
2420 fseek(fileptr
->fileptr
, 0, SEEK_END
) ;
2421 endp
= ftell( fileptr
->fileptr
) ;
2422 fseek( fileptr
->fileptr
, oldp
, SEEK_SET
) ;
2423 fileptr
->oper
= OPER_NONE
;
2426 * Determine the value of "where" depending on the starting
2427 * location determined by "from". "where" is passed in in an
2428 * external format; ie 1 based, internally it must be 0 based
2433 if ( oper
& OPER_READ
)
2434 where_read
= 1 + where
+ fileptr
->readpos
;
2435 if ( oper
& OPER_WRITE
)
2436 where_write
= 1 + where
+ fileptr
->writepos
;
2439 if ( oper
& OPER_READ
)
2440 where_read
= endp
- where
;
2441 #if SEEK_TO_EOF_FOR_WRITE_IS_AT_EOF
2442 if ( oper
& OPER_WRITE
)
2443 where_write
= endp
- where
;
2445 if ( oper
& OPER_WRITE
)
2446 where_write
= 1 + endp
- where
;
2449 default: /* SEEK_SET */
2450 if ( oper
& OPER_READ
)
2452 if ( oper
& OPER_WRITE
)
2453 where_write
= where
;
2456 if ( oper
& OPER_READ
)
2458 if ( where_read
< 1 )
2460 file_error( fileptr
, 0, "Repositioning before start of file" ) ;
2463 if ( endp
< where_read
)
2465 file_error( fileptr
, 0, "Repositioning at or after EOF" ) ;
2469 if ( oper
& OPER_WRITE
)
2471 if ( where_write
< 1 )
2473 file_error( fileptr
, 0, "Repositioning before start of file" ) ;
2476 if ( (endp
+1) < where_write
)
2478 file_error( fileptr
, 0, "Repositioning after EOF" ) ;
2486 * Then do the actual positioning. Remember to clear errno first.
2487 * Previously, this code tested afterwards to see if ftell()
2488 * returned the same position that fseek() tried to set. Surely, that
2489 * must be unnecessary?
2490 * We need to reposition using both the read and write postions (if
2495 * Position the real file pointer to the write or read pointers
2496 * calculated. The "thispos" member is set to the last seek
2497 * executed. READ is done last as this is probably the most
2498 * likely use of character positioning, hence it may be slightly
2501 if ( oper
& OPER_WRITE
)
2503 if ( fseek(fileptr
->fileptr
,(where_write
-1),SEEK_SET
) )
2505 file_error( fileptr
, errno
, NULL
) ;
2508 fileptr
->thispos
= where_write
; /* this was where-1; is that correct ?*/
2510 if ( oper
& OPER_READ
)
2512 if ( fseek(fileptr
->fileptr
,(where_read
-1),SEEK_SET
) )
2514 file_error( fileptr
, errno
, NULL
) ;
2517 fileptr
->thispos
= where_read
; /* this was where-1; is that correct ?*/
2519 fileptr
->oper
= OPER_NONE
;
2522 * Then we have to update the file pointers in the entry in our
2525 * Clear the end-of-file flag. Even if we *did* position to the
2526 * end of file, we don't want to discover that until we actually
2527 * _read_ data that is _off_ the end-of-file.
2530 if (oper
& OPER_READ
)
2532 fileptr
->readpos
= where_read
-1 ;
2533 fileptr
->flag
&= ~(FLAG_RDEOF
) ;
2534 fileptr
->flag
&= ~(FLAG_AFTER_RDEOF
) ;
2536 if (oper
& OPER_WRITE
)
2538 fileptr
->writepos
= where_write
-1 ;
2539 fileptr
->flag
&= ~(FLAG_WREOF
) ;
2541 if (oper
== OPER_NONE
)
2542 file_error( fileptr
, 0, NULL
) ;
2545 * And then, set the linenr field to a value signifying that we
2546 * have no good idea about which lines are current.
2548 if (oper
& OPER_READ
)
2549 fileptr
->readline
= 0 ;
2550 if (oper
& OPER_WRITE
)
2551 fileptr
->writeline
= 0 ;
2554 * Return the new position of the file pointer. If both file
2555 * pointers were set, then readpos and writepos are the same, so
2556 * the following test is valid.
2558 if (oper
& OPER_READ
)
2559 return fileptr
->readpos
+ 1; /* external representation */
2561 return fileptr
->writepos
+ 1; /* external representation */
2567 * This routine reads a string of data from a file indicated by
2568 * the Rexx file table entry 'ptr'. The read starts at the current
2569 * read position, and the length will be 'length' characters.
2571 * Then, what if the data to be read are more than what is possible
2572 * to store in one string; let's say length=100,000, and the size of
2573 * length in a string is 16 bit. Well, That should return an error
2574 * in Str_makeTSD(), but maybe we should handle it more elegantly?
2576 static streng
*readbytes( tsd_t
*TSD
, fileboxptr fileptr
, int length
)
2579 streng
*retvalue
=NULL
;
2582 * If state is ERROR, then refuse to handle the file further.
2583 * If the state was 'only' EOF, then don't bother, the length of
2584 * the file might have increased since last attempt to read.
2586 if (fileptr
->flag
& FLAG_ERROR
)
2588 if (!(fileptr
->flag
& FLAG_FAKE
))
2589 file_error( fileptr
, 0, NULL
) ;
2590 return nullstringptr() ;
2593 assert( fileptr
->flag
& FLAG_READ
) ;
2596 * If we are not at the current read position, we have to
2597 * seek to the correct position, but first we have to the validity
2598 * of these positions.
2600 if (fileptr
->flag
& FLAG_PERSIST
)
2602 if (fileptr
->thispos
!= fileptr
->readpos
)
2605 if ( fileptr
->flag
& FLAG_PERSIST
2606 && fseek(fileptr
->fileptr
, fileptr
->readpos
, SEEK_SET
))
2608 file_error( fileptr
, errno
, NULL
) ;
2609 return nullstringptr() ;
2611 fileptr
->thispos
= fileptr
->readpos
;
2612 fileptr
->oper
= OPER_NONE
;
2617 * The joy of POSIX ... If a file is open for input and output, it
2618 * must be flushed when changing between the two. Therefore, check
2619 * the type of the last operation. Actually, this are not very likely
2620 * since that situation would in general have been handled above.
2622 if (fileptr
->oper
==OPER_WRITE
)
2625 if ( fileptr
->flag
& FLAG_PERSIST
2626 && fseek( fileptr
->fileptr
, 0L, SEEK_CUR
))
2628 /* Hey, how could this have happened?!?! NFS down? */
2629 file_error( fileptr
, errno
, NULL
) ;
2630 return nullstringptr() ;
2632 fileptr
->oper
= OPER_NONE
;
2636 * Lets get ready for the big event. First allocate enough space to
2637 * hold the data we are hoping to be able to read. Then read it
2638 * directly into the string.
2640 retvalue
= Str_makeTSD(length
+1) ;
2642 didread
= fread( retvalue
->value
, 1, length
, fileptr
->fileptr
) ;
2643 fileptr
->oper
= OPER_READ
;
2646 * Variable 'read' contains the number of items (=bytes) read, or
2647 * it contains EOF if an error occurred. Handle the error the
2648 * normal way; i.e. trigger file_error and return nothing.
2652 file_error( fileptr
, errno
, NULL
) ;
2653 return nullstringptr() ;
2657 * What if we didn't manage to read all the data? Well, return what
2658 * we got, but still trigger an error, since EOF should be
2659 * considered a NOTREADY condition. However, we try to handle EOF
2660 * a bit more elegantly than other errors, since lots of programmers
2661 * are probably not bothering about EOF; an EOF condition should be
2662 * able to be reset using a file positioning.
2664 assert( 0<=didread
&& didread
<=length
) ; /* It'd better be! */
2665 retvalue
->len
= didread
;
2668 file_warning( fileptr
, 0, "EOF on char input" ) ;
2669 fileptr
->flag
|= FLAG_RDEOF
;
2673 fileptr
->flag
&= ~FLAG_RDEOF
;
2674 fileptr
->flag
&= ~FLAG_AFTER_RDEOF
;
2678 * Then, at the end, we have to set the pointers and counter to
2679 * the correct values
2681 fileptr
->thispos
+= didread
;
2682 fileptr
->readpos
+= didread
;
2683 fileptr
->readline
= (-1) ;
2684 fileptr
->linesleft
= 0 ;
2692 * This routines write a string to a file pointed to by the Rexx file
2693 * table entry 'fileptr'. The string to be written is 'string', and the
2694 * length of the write is implicitly given avs the length of 'string'
2696 * This routine is called from the Rexx built-in function CHAROUT().
2697 * It is a fairly streight forward implementation.
2700 static int writebytes( tsd_t
*TSD
, fileboxptr fileptr
, const streng
*string
)
2705 * First, if this file is in state ERROR, don't touch it, what to
2706 * return depends on whether the file is in state FAKE.
2708 if ( fileptr
->flag
& FLAG_ERROR
)
2710 if ( fileptr
->flag
& FLAG_FAKE
)
2711 return string
->len
;
2714 file_error( fileptr
, 0, NULL
) ;
2715 if (fileptr
->flag
& FLAG_FAKE
)
2716 return string
->len
;
2722 * If we are not at the current write position, we have to
2723 * seek to the correct position
2725 if (fileptr
->thispos
!= fileptr
->writepos
)
2728 if ( fileptr
->flag
& FLAG_PERSIST
2729 && fseek(fileptr
->fileptr
, fileptr
->writepos
, SEEK_SET
))
2731 file_error( fileptr
, errno
, NULL
) ;
2734 fileptr
->thispos
= fileptr
->writepos
;
2735 fileptr
->oper
= OPER_NONE
;
2739 * If previous operation on this file was a read, we have to flush
2740 * the file before we can perform any write operations. This will
2741 * seldom happen, since it is in general handled above.
2743 if (fileptr
->oper
== OPER_READ
)
2746 if ( fileptr
->flag
& FLAG_PERSIST
2747 && fseek(fileptr
->fileptr
, 0, SEEK_CUR
))
2749 file_error( fileptr
, errno
, NULL
) ;
2750 return (fileptr
->flag
& FLAG_FAKE
) ? string
->len
: 0 ;
2752 fileptr
->oper
= OPER_NONE
;
2756 * Here comes the actual writing. This also works when the length
2757 * of string is zero.
2760 written
= fwrite( string
->value
, 1, string
->len
, fileptr
->fileptr
) ;
2761 fileptr
->oper
= OPER_WRITE
;
2764 * Here comes the error checking. Note that this function will
2765 * return the number of elements written, it will never return
2766 * EOF as fread can, since the problems surrounding EOF can not
2767 * occur in this operation. Therefore, report a fullfleged error
2768 * whenever rc is less than the length of string.
2770 assert( 0<=written
&& written
<=string
->len
) ;
2771 if (written
< string
->len
)
2772 file_error( fileptr
, errno
, NULL
) ;
2776 * If the operation was successful, then we set misc status
2777 * information about the file, and the counters and pointers.
2779 fileptr
->writeline
= 0 ;
2780 fileptr
->flag
&= ~FLAG_RDEOF
;
2781 fileptr
->flag
&= ~FLAG_AFTER_RDEOF
;
2782 fileptr
->thispos
+= written
;
2783 fileptr
->writepos
+= written
;
2785 fflush( fileptr
->fileptr
) ;
2786 fileptr
->oper
= OPER_NONE
;
2795 * This routine counts the complete lines remaining in the file
2796 * pointed to by the Rexx file table entry 'ptr'. The count starts
2797 * at the current read position, and the current line will be counted
2798 * even if the current read position points to the middle of a line.
2799 * The last line will only be counted if it was actually terminated
2800 * by a EOL marker. If the current line is the last line, but it was
2801 * not explicitly terminated by a EOL marker, zero is returned.
2803 static int countlines( tsd_t
*TSD
, fileboxptr ptr
, int actual
)
2810 * If this file is in ERROR state, we really don't want to try to
2811 * operate on it. Just report an error, and return 0.
2813 if ( ptr
->flag
& FLAG_ERROR
)
2815 if (!(ptr
->flag
& FLAG_FAKE
))
2816 file_error( ptr
, 0, NULL
) ;
2821 * Counting lines requires us to reposition in the file. However,
2822 * we can not reposition in transient files. If this is not a
2823 * persistent file, don't do any repositioning, just return one
2824 * for any situation where we are not sure whether there are more
2825 * data or not (i.e. unless we are sure that there are no more data,
2828 if (!(ptr
->flag
& FLAG_PERSIST
)
2831 return (!feof(ptr
->fileptr
)) ;
2836 * Take advantage of the cached value of the lines left in the
2840 return ptr
->linesleft
;
2843 * If, however, this is a persistent file, we have to read from
2844 * the current read position to the end-of-file, and count all
2845 * the lines. First, make sure that wse position at the current
2849 oldpoint
= ftell( ptr
->fileptr
) ;
2852 file_error( ptr
, errno
, NULL
) ;
2857 * Then read the rest of the file, and keep a count of all the files
2858 * read in the process.
2860 SWITCH_OPER_READ(ptr
);
2861 #if defined(UNIX) || defined(MAC)
2862 for(left
=0;((ch
=getc(ptr
->fileptr
))!=EOF
);)
2868 if (prevch
!= REGINA_EOL
2874 ch
= getc(ptr
->fileptr
);
2877 if ( ch
== REGINA_CR
)
2881 if ( ch
== REGINA_EOL
&& prevch
!= REGINA_CR
)
2886 if (prevch
!= REGINA_EOL
2887 && prevch
!= REGINA_CR
2893 * At the end, try to reposition back to the old current read
2894 * position, and report an error if that attempt failed.
2897 if ( ptr
->flag
& FLAG_PERSIST
2898 && fseek(ptr
->fileptr
, oldpoint
, SEEK_SET
))
2900 file_error( ptr
, errno
, NULL
) ;
2903 ptr
->oper
= OPER_NONE
;
2904 ptr
->linesleft
= left
;
2912 * This routine calculates the number of bytes remaining in the file,
2913 * i.e the number of bytes from the current read position until the
2914 * end-of-file. It is, of course, called from the Rexx built-in
2918 static int calc_chars_left( tsd_t
*TSD
, fileboxptr ptr
)
2921 long oldpoint
=0L, newpoint
=0L ;
2923 if (! ptr
->flag
& FLAG_READ
)
2927 * First, determine whether this file is in ERROR state. If so, we
2928 * don't want to touch it. Whether or not the file is in FAKE state
2929 * is fairly irrelevant in this situation
2931 if ( ptr
->flag
& FLAG_ERROR
)
2933 if (!(ptr
->flag
& FLAG_FAKE
))
2934 file_error( ptr
, 0, NULL
) ;
2939 * If this is not a persistent file, then we have no means of finding
2940 * out how much of the file is available. Then, return 1 if we are not
2941 * at the end-of-file, and 0 otherwise.
2943 if (!(ptr
->flag
& FLAG_PERSIST
))
2944 left
= ( !(ptr
->flag
& FLAG_RDEOF
)) ;
2948 * This is a persistent file, which is not in error state. OK, then
2949 * we must record the current point, fseek to the end-of-file,
2950 * ftell to get that position, and fseek back to where we started.
2951 * And we have to check for errors everywhere ... sigh.
2953 * First, record the current position in the file.
2956 oldpoint
= ftell( ptr
->fileptr
) ;
2959 file_error( ptr
, errno
, NULL
) ;
2964 * Then, move the current position to the end-of-file
2967 if (fseek(ptr
->fileptr
, 0L, SEEK_END
))
2969 file_error( ptr
, errno
, NULL
) ;
2972 ptr
->oper
= OPER_NONE
;
2975 * And record the position of the end-of-file
2978 newpoint
= ftell( ptr
->fileptr
) ;
2981 file_error( ptr
, errno
, NULL
) ;
2986 * And, at last, position back to the place where we started.
2987 * Actually, this may not be necessary, since we _can_ leave the
2988 * current position at the end-of-file. After all, the next read
2989 * or write _will_ position back correctly. However, let's be
2993 if (fseek(ptr
->fileptr
, oldpoint
, SEEK_SET
))
2995 file_error( ptr
, errno
, NULL
) ;
3000 * Then we have some accounting to do; calculate the size of the
3001 * last part of the file. And also set oper to NONE, we _have_
3002 * done a repositioning ... actually, several :-)
3004 left
= newpoint
- ptr
->readpos
;
3005 /* left = newpoint - oldpoint ; */ /* YURI - wrong */
3006 ptr
->oper
= OPER_NONE
;
3018 * This routine writes a line to the file indicated by 'ptr'. The line
3019 * to be written is 'data', and it will be terminated by an extra
3020 * EOL marker after the charactrers in 'data'.
3022 * No error condition is raised if noerrors is set.
3024 static int writeoneline( tsd_t
*TSD
, fileboxptr ptr
, const streng
*data
,
3027 const char *i
=NULL
;
3030 * First, make sure that the file is not in ERROR state. If it is
3031 * report an error, and return a result depending on whether this
3032 * file is to be faked.
3034 if (ptr
->flag
& FLAG_ERROR
)
3036 if (ptr
->flag
& FLAG_FAKE
)
3041 file_error( ptr
, 0, NULL
) ;
3042 if (ptr
->flag
& FLAG_FAKE
)
3049 * If we are to write a new line, we ought to truncate the file after
3050 * that line. Or rather, we truncate the file at the start of the
3051 * new line, before we write it out. But only if we have the non-POSIX
3052 * function ftruncate() available. And not if we are already there.
3054 #if defined(HAVE_FTRUNCATE)
3055 if ( get_options_flag( TSD
->currlevel
, EXT_LINEOUTTRUNC
) )
3057 if (ptr
->oper
!= OPER_WRITE
&& !(ptr
->flag
& (FLAG_WREOF
)) &&
3058 (ptr
->flag
& FLAG_PERSIST
))
3062 SWITCH_OPER_WRITE(ptr
); /* Maybe, ftruncate is a write operation in
3063 * the meaning of POSIX. This shouldn't do
3064 * any harm in other systems.
3067 fno
= fileno( ptr
->fileptr
) ;
3068 if (ftruncate( fno
, ptr
->writepos
) == -1)
3071 file_error( ptr
, errno
, NULL
) ;
3072 return !(ptr
->flag
& FLAG_FAKE
) ;
3074 if ( ptr
->flag
& FLAG_PERSIST
)
3075 fseek( ptr
->fileptr
, 0, SEEK_END
) ;
3076 ptr
->oper
= OPER_NONE
;
3077 ptr
->thispos
= ptr
->writepos
= ftell( ptr
->fileptr
) ;
3078 if (ptr
->readpos
>ptr
->thispos
&& ptr
->readpos
!=EOF
)
3080 ptr
->readpos
= ptr
->thispos
;
3082 ptr
->linesleft
= 0 ;
3089 * Then, output the characters in 'data', and sense any problem.
3090 * If there is a problem, report an error
3093 SWITCH_OPER_WRITE(ptr
);
3094 for (i
=data
->value
; i
<Str_end(data
); i
++)
3096 if (putc( *i
, ptr
->fileptr
)==EOF
)
3099 file_error( ptr
, errno
, NULL
) ;
3105 * After all the data has been written out, we have to explicitly
3106 * terminate the file with an end-of-line marker. Under Unix this
3107 * is the single character EOL. Under Macintosh this is the single
3108 * character CR, and all others it is CR and EOL.
3111 SWITCH_OPER_WRITE(ptr
);
3112 if (putc( REGINA_CR
, ptr
->fileptr
)==EOF
)
3115 file_error( ptr
, errno
, NULL
) ;
3120 SWITCH_OPER_WRITE(ptr
);
3121 if (putc( REGINA_EOL
, ptr
->fileptr
)==EOF
)
3124 file_error( ptr
, errno
, NULL
) ;
3130 * Then we have to update the counters and pointers in the Rexx
3131 * file table entry. We must do that in order to be able to keep
3132 * track of where we are.
3134 ptr
->thispos
+= data
->len
+ 1 ;
3135 ptr
->writepos
= ptr
->thispos
;
3136 ptr
->oper
= OPER_WRITE
;
3141 ptr
->flag
|= FLAG_WREOF
;
3144 * At the end, we flush the data. We do this in order to avoid
3145 * surprises later. Maybe we shouldn't do that, since it may force
3146 * a systemcall, which might give away the timeslice and decrease
3147 * system time. So you might want to remove this call ... at your
3151 if (fflush( ptr
->fileptr
))
3154 file_error( ptr
, errno
, NULL
) ;
3162 * This routine is a way of retrieving the information returned by the
3163 * standard Unix call stat(). It takes the name of a file as parameter,
3164 * and return information about that file. This is not standard Rexx,
3165 * but quite useful. It is accessed through the built-in function
3166 * STREAM(), command 'FSTAT'
3167 * This is now also used for the "standard" STREAM() options.
3169 static streng
*getstatus( tsd_t
*TSD
, const streng
*filename
, int subcommand
)
3171 fileboxptr ptr
=NULL
;
3174 #if defined(__EMX__) || defined(__WINS__) || defined(__EPOC32__)
3177 long pos_read
= -2L, pos_write
= -2L, pos_line
= -2L;
3178 int streamtype
= 0; /* unknown */
3179 streng
*result
=NULL
;
3180 struct stat buffer
;
3181 struct tm tmdata
, *tmptr
;
3183 static const char *fmt
= "%02d-%02d-%02d %02d:%02d:%02d" ;
3184 static const char *iso
= "%04d-%02d-%02d %02d:%02d:%02d" ;
3185 static const char *streamdesc
[] = { "UNKNOWN", "PERSISTENT", "TRANSIENT" };
3188 * Nul terminate the input filename string, as stat() will barf if
3189 * it isn't and other functions stuff up!
3191 fn
= str_ofTSD(filename
);
3193 * First get the Rexx file table entry associated with the file,
3194 * and then call stat() for that file. If the file is already open,
3195 * then call fstat, since that will in general be a 'safer' way
3196 * to be sure that it is _really_ the file that is open in Rexx.
3198 ptr
= getfileptr( TSD
, filename
) ;
3199 if (ptr
&& ptr
->fileptr
)
3201 fno
= fileno( ptr
->fileptr
) ;
3202 rc
= fstat( fno
, &buffer
) ;
3203 if (ptr
->flag
& FLAG_PERSIST
)
3207 pos_read
= ptr
->readpos
;
3208 pos_write
= ptr
->writepos
;
3209 pos_line
= ptr
->readline
;
3213 rc
= stat( fn
, &buffer
) ;
3216 if ( (buffer
.st_mode
& S_IFMT
) == S_IFDIR
)
3226 * If we were able to retrieve any useful information, store it
3227 * in a string of suitable length, and return that string.
3228 * If the filename does not exist, always return an empty string.
3231 result
= nullstringptr() ;
3234 switch ( subcommand
)
3237 if ( streamtype
== 2 )
3239 result
= nullstringptr() ;
3243 result
= Str_makeTSD( 128 ) ;
3244 sprintf( result
->value
,
3245 "%ld %ld %03o %d %s %s %ld",
3246 (long)(buffer
.st_dev
), (long)(buffer
.st_ino
),
3247 buffer
.st_mode
& 0x7f, buffer
.st_nlink
,
3248 #if defined(VMS) || defined(MAC) || defined(OS2) || defined(DOS) || defined (__WATCOMC__) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || defined(_AMIGA) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__AROS__)
3251 getpwuid( buffer
.st_uid
)->pw_name
,
3252 getgrgid( buffer
.st_gid
)->gr_name
,
3254 (long)(buffer
.st_size
) ) ;
3257 case COMMAND_QUERY_EXISTS
:
3258 if ( streamtype
== 2 ) /* transient file */
3260 result
= nullstringptr() ;
3264 #if defined(HAVE__FULLPATH)
3265 result
= Str_makeTSD( REXX_PATH_MAX
);
3266 _fullpath(result
->value
, fn
, REXX_PATH_MAX
);
3267 # if defined(__EMX__)
3269 * Convert / to \ as the API call doesn't do this for us
3271 result
->len
= strlen( result
->value
) ;
3272 for ( i
=0; i
< result
->len
; i
++)
3274 if ( result
->value
[i
] == '/' )
3275 result
->value
[i
] = '\\';
3278 #elif defined(HAVE__TRUENAME)
3279 result
= Str_makeTSD( REXX_PATH_MAX
) ;
3280 _truename(fn
, result
->value
);
3282 result
= Str_makeTSD( REXX_PATH_MAX
) ;
3283 if (my_fullpath(result
->value
, fn
, REXX_PATH_MAX
) == -1)
3284 result
= nullstringptr() ;
3285 # if defined(__WINS__) || defined(__EPOC32__)
3289 * Convert / to \ as the API call doesn't do this for us
3291 result
->len
= strlen( result
->value
) ;
3292 for ( i
=0; i
< result
->len
; i
++)
3294 if ( result
->value
[i
] == '/' )
3295 result
->value
[i
] = '\\';
3302 case COMMAND_QUERY_SIZE
:
3303 if ( streamtype
== 2 ) /* transient file */
3305 result
= nullstringptr() ;
3309 result
= Str_makeTSD( 50 ) ;
3310 sprintf( result
->value
, "%ld", (long)(buffer
.st_size
) ) ;
3313 case COMMAND_QUERY_HANDLE
:
3316 result
= Str_makeTSD( 10 ) ;
3317 sprintf( result
->value
, "%d", fno
) ;
3320 result
= nullstringptr() ;
3322 case COMMAND_QUERY_STREAMTYPE
:
3323 result
= Str_makeTSD( 12 ) ;
3324 sprintf( result
->value
, "%s", streamdesc
[streamtype
] ) ;
3326 case COMMAND_QUERY_DATETIME
:
3327 if ( streamtype
== 2 ) /* transient file */
3329 result
= nullstringptr() ;
3333 if ((tmptr
= localtime(&buffer
.st_mtime
)) != NULL
)
3336 memset(&tmdata
,0,sizeof(tmdata
)); /* what shall we do in this case? */
3337 result
= Str_makeTSD( 20 ) ;
3338 sprintf( result
->value
, fmt
, tmdata
.tm_mon
+1, tmdata
.tm_mday
,
3339 (tmdata
.tm_year
% 100), tmdata
.tm_hour
, tmdata
.tm_min
,
3343 case COMMAND_QUERY_TIMESTAMP
:
3344 if ( streamtype
== 2 ) /* transient file */
3346 result
= nullstringptr() ;
3350 if ((tmptr
= localtime(&buffer
.st_mtime
)) != NULL
)
3353 memset(&tmdata
,0,sizeof(tmdata
)); /* what shall we do in this case? */
3354 result
= Str_makeTSD( 20 ) ;
3355 sprintf( result
->value
, iso
, tmdata
.tm_year
+1900, tmdata
.tm_mon
+1,
3357 tmdata
.tm_hour
, tmdata
.tm_min
,
3361 case COMMAND_QUERY_POSITION_READ_CHAR
:
3362 case COMMAND_QUERY_POSITION_SYS
:
3363 if (pos_read
!= (-2))
3365 result
= Str_makeTSD( 50 ) ;
3366 sprintf( result
->value
, "%ld", pos_read
+ 1) ;
3369 result
= nullstringptr() ;
3371 case COMMAND_QUERY_POSITION_WRITE_CHAR
:
3372 if (pos_write
!= (-2))
3374 result
= Str_makeTSD( 50 ) ;
3375 sprintf( result
->value
, "%ld", pos_write
+ 1) ;
3378 result
= nullstringptr() ;
3380 case COMMAND_QUERY_POSITION_READ_LINE
:
3381 case COMMAND_QUERY_POSITION_WRITE_LINE
:
3382 if (pos_line
!= (-2))
3384 result
= Str_makeTSD( 50 ) ;
3385 sprintf( result
->value
, "%ld", pos_line
) ;
3388 result
= nullstringptr() ;
3391 result
->len
= strlen( result
->value
) ;
3394 if ( fn
) FreeTSD(fn
);
3400 * This little sweet routine returns information stored in the Rexx
3401 * file table entry about the named file 'filename'. It is perhaps more
3402 * of a debugging function than a Rexx function. It is accessed by the
3403 * Rexx built-in function STREAM(), command 'STATUS'. One of the nice
3404 * pieces of information this function returns is whether a file is
3405 * transient or persistent.
3407 * This is really a simple function, just retrieve the Rexx file
3408 * table entry, and store the information in that entry into a string
3409 * and return that string.
3411 * The difference between getrexxstatus() and getstatus() is that
3412 * that former returns information stored in Rexx's datastructures,
3413 * while the latter return information about the file stored in and
3414 * managed by the operating system
3416 static streng
*getrexxstatus( const tsd_t
*TSD
, cfileboxptr ptr
)
3418 streng
*result
=NULL
;
3421 return nullstringptr() ;
3423 result
= Str_makeTSD(64) ; /* Ought to be enough */
3424 result
->value
[0] = 0x00 ;
3426 if ((ptr
->flag
& FLAG_READ
) && (ptr
->flag
& FLAG_WRITE
))
3427 strcat( result
->value
, "READ/WRITE" ) ;
3428 else if (ptr
->flag
& FLAG_READ
)
3429 strcat( result
->value
, "READ" ) ;
3430 else if (ptr
->flag
& FLAG_WRITE
)
3431 strcat( result
->value
, "WRITE" ) ;
3433 strcat( result
->value
, "NONE" ) ;
3435 sprintf( result
->value
+ strlen(result
->value
),
3436 " READ: char=%ld line=%d WRITE: char=%ld line=%d %s",
3437 (long)(ptr
->readpos
+1), ptr
->readline
,
3438 (long)(ptr
->writepos
+1), ptr
->writeline
,
3439 (ptr
->flag
& FLAG_PERSIST
) ? "PERSISTENT" : "TRANSIENT" ) ;
3441 result
->len
= strlen(result
->value
) ;
3447 * This routine parses the remainder of the parameters passed to the
3448 * Stream(,'C','QUERY...') function.
3450 static streng
*getquery( tsd_t
*TSD
, const streng
*filename
, const streng
*subcommand
)
3452 streng
*result
=NULL
, *psub
=NULL
, *psubsub
=NULL
;
3457 * Get the subcommand to QUERY
3459 oper
= get_querycommand( subcommand
);
3462 case COMMAND_QUERY_DATETIME
:
3463 case COMMAND_QUERY_TIMESTAMP
:
3464 case COMMAND_QUERY_EXISTS
:
3465 case COMMAND_QUERY_HANDLE
:
3466 case COMMAND_QUERY_SIZE
:
3467 case COMMAND_QUERY_STREAMTYPE
:
3468 result
= getstatus( TSD
, filename
, oper
);
3470 case COMMAND_QUERY_SEEK
:
3471 case COMMAND_QUERY_POSITION
:
3472 if ( oper
== COMMAND_QUERY_SEEK
)
3474 psub
= Str_nodupTSD( subcommand
, 4, subcommand
->len
- 4 );
3479 psub
= Str_nodupTSD( subcommand
, 8, subcommand
->len
- 8 );
3482 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
3483 oper
= get_querypositioncommand( psub
);
3486 case COMMAND_QUERY_POSITION_SYS
:
3487 result
= getstatus(TSD
, filename
, oper
);
3489 case COMMAND_QUERY_POSITION_READ
:
3490 psubsub
= Str_nodupTSD( psub
, 4, psub
->len
- 4 );
3491 psubsub
= Str_strp( psubsub
, ' ', STRIP_LEADING
);
3492 oper
= get_querypositionreadcommand( psubsub
);
3495 case COMMAND_QUERY_POSITION_READ_CHAR
:
3496 case COMMAND_QUERY_POSITION_READ_LINE
:
3497 result
= getstatus( TSD
, filename
, oper
);
3500 exiterror( ERR_STREAM_COMMAND
, 1, (seek_oper
)?"QUERY SEEK READ":"QUERY POSITION READ", "CHAR LINE ''", tmpstr_of( TSD
, psubsub
) ) ;
3504 case COMMAND_QUERY_POSITION_WRITE
:
3505 psubsub
= Str_nodupTSD( psub
, 5, psub
->len
- 5 );
3506 psubsub
= Str_strp( psubsub
, ' ', STRIP_LEADING
);
3507 oper
= get_querypositionwritecommand( psubsub
);
3510 case COMMAND_QUERY_POSITION_WRITE_CHAR
:
3511 case COMMAND_QUERY_POSITION_WRITE_LINE
:
3512 result
= getstatus( TSD
, filename
, oper
);
3515 exiterror( ERR_STREAM_COMMAND
, 1, (seek_oper
)?"QUERY SEEK WRITE":"QUERY POSITION WRITE", "CHAR LINE ''", tmpstr_of( TSD
, psubsub
) ) ;
3520 exiterror( ERR_STREAM_COMMAND
, 1, (seek_oper
)?"QUERY SEEK":"QUERY POSITION", "READ WRITE SYS", tmpstr_of( TSD
, psub
) ) ;
3523 Free_stringTSD(psub
);
3526 exiterror( ERR_STREAM_COMMAND
, 1, "QUERY", "DATETIME TIMESTAMP EXISTS HANDLE SIZE STREAMTYPE SEEK POSITION", tmpstr_of( TSD
, subcommand
) ) ;
3534 * This routine parses the remainder of the parameters passed to the
3535 * Stream(,'C','OPEN...') function.
3537 static streng
*getopen( tsd_t
*TSD
, const streng
*filename
, const streng
*subcommand
)
3539 fileboxptr ptr
=NULL
;
3540 streng
*result
=NULL
, *psub
=NULL
;
3545 * Get the subcommand to OPEN
3547 oper
= get_opencommand( subcommand
);
3550 case COMMAND_OPEN_BOTH
:
3551 if ( subcommand
->len
>= 4
3552 && memcmp(subcommand
->value
, "BOTH", 4) == 0 )
3553 psub
= Str_nodupTSD( subcommand
, 4, subcommand
->len
- 4 );
3555 psub
= Str_dupTSD( subcommand
);
3556 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
3557 oper
= get_opencommandboth( psub
);
3558 if ( TSD
->restricted
)
3559 exiterror( ERR_RESTRICTED
, 4 ) ;
3562 case COMMAND_OPEN_BOTH
:
3563 closefile( TSD
, filename
) ;
3564 ptr
= openfile( TSD
, filename
, ACCESS_WRITE
) ;
3566 case COMMAND_OPEN_BOTH_APPEND
:
3567 closefile( TSD
, filename
) ;
3568 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_APPEND
) ;
3570 case COMMAND_OPEN_BOTH_REPLACE
:
3571 closefile( TSD
, filename
) ;
3572 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_REPLACE
) ;
3575 exiterror( ERR_STREAM_COMMAND
, 1, "OPEN BOTH", "APPEND REPLACE ''", tmpstr_of( TSD
, psub
) ) ;
3578 Free_stringTSD(psub
);
3580 result
= Str_creTSD( "READY:" ) ;
3583 sprintf(buf
,"ERROR:%d",errno
);
3584 result
= Str_creTSD( buf
) ;
3587 case COMMAND_OPEN_READ
:
3588 closefile( TSD
, filename
) ;
3589 ptr
= openfile( TSD
, filename
, ACCESS_READ
) ;
3591 result
= Str_creTSD( "READY:" ) ;
3594 sprintf(buf
,"ERROR:%d",errno
);
3595 result
= Str_creTSD( buf
) ;
3598 case COMMAND_OPEN_WRITE
:
3599 if ( TSD
->restricted
)
3600 exiterror( ERR_RESTRICTED
, 4 ) ;
3601 psub
= Str_nodupTSD( subcommand
, 5, subcommand
->len
- 5 );
3602 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
3603 oper
= get_opencommandwrite( psub
);
3607 case COMMAND_OPEN_WRITE
:
3608 closefile( TSD
, filename
) ;
3609 ptr
= openfile( TSD
, filename
, ACCESS_WRITE
) ;
3611 case COMMAND_OPEN_WRITE_APPEND
:
3612 closefile( TSD
, filename
) ;
3613 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_APPEND
) ;
3615 case COMMAND_OPEN_WRITE_REPLACE
:
3616 closefile( TSD
, filename
) ;
3617 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_REPLACE
) ;
3620 exiterror( ERR_STREAM_COMMAND
, 1, "OPEN WRITE", "APPEND REPLACE ''", tmpstr_of( TSD
, psub
) ) ;
3623 Free_stringTSD(psub
);
3625 result
= Str_creTSD( "READY:" ) ;
3628 sprintf(buf
,"ERROR:%d",errno
);
3629 result
= Str_creTSD( buf
) ;
3633 exiterror( ERR_STREAM_COMMAND
, 1, "OPEN", "BOTH READ WRITE ''", tmpstr_of( TSD
, subcommand
) ) ;
3641 static streng
*getseek( tsd_t
*TSD
, const streng
*filename
, const streng
*cmd
)
3643 #define STATE_START 0
3644 #define STATE_WORD 1
3645 #define STATE_DELIM 2
3646 char *word
[5] = {NULL
,NULL
,NULL
,NULL
};
3650 int state
=STATE_START
;
3654 long seek_offset
=0,pos
=0;
3655 int pos_type
=OPER_NONE
,num_params
=0;
3656 int str_start
=0,str_end
=(-1),words
;
3658 streng
*result
=NULL
;
3661 str
= str_ofTSD(cmd
);
3663 for (i
=0;i
<Str_len(cmd
);i
++)
3668 if (*(str
+i
) == ' ')
3670 state
= STATE_DELIM
;
3674 word
[j
] = str
+str_start
;
3676 if (str_end
!= (-1))
3678 *(str
+str_end
) = '\0';
3683 if (*(str
+i
) == ' ')
3685 state
= STATE_DELIM
;
3687 str_start
= str_end
+ 1;
3693 if (*(str
+i
) == ' ')
3695 state
= STATE_DELIM
;
3697 if (state
== STATE_WORD
)
3700 word
[j
] = str
+str_start
;
3702 if (str_end
!= (-1))
3704 *(str
+str_end
) = '\0';
3712 exiterror( ERR_INCORRECT_CALL
, 922, "STREAM", 3, 2, num_params
+1 );
3714 exiterror( ERR_INCORRECT_CALL
, 923, "STREAM", 3, 4, num_params
+1 );
3716 switch( num_params
)
3719 if (strcmp(word
[2],"CHAR") == 0)
3723 if (strcmp(word
[2],"LINE") == 0)
3726 exiterror( ERR_INCORRECT_CALL
, 924, "STREAM", 3, "CHAR LINE", word
[2] );
3728 /* meant to fall through */
3731 * 2 params(to SEEK), last one (word[1]) could be READ/WRITE or CHAR/LINE
3733 if (strcmp(word
[1],"READ") == 0)
3734 pos_type
= OPER_READ
;
3735 else if (strcmp(word
[1],"WRITE") == 0)
3736 pos_type
= OPER_WRITE
;
3737 else if (strcmp(word
[1],"CHAR") == 0)
3739 else if (strcmp(word
[1],"LINE") == 0)
3742 exiterror( ERR_INCORRECT_CALL
, 924, "STREAM", 3, "READ WRITE CHAR LINE", word
[1] );
3745 * Determine the position type if not supplied prior
3747 if ( pos_type
== OPER_NONE
)
3749 ptr
= getfileptr( TSD
, filename
) ;
3752 if ( ptr
->flag
& FLAG_READ
)
3753 pos_type
|= OPER_READ
;
3754 if ( ptr
->flag
& FLAG_WRITE
)
3755 pos_type
|= OPER_WRITE
;
3762 seek_type
= SEEK_SET
;
3766 seek_type
= SEEK_CUR
;
3771 seek_type
= SEEK_CUR
;
3776 seek_type
= SEEK_END
;
3780 seek_type
= SEEK_SET
;
3783 for (i
=0;i
<(int)strlen(offset
);i
++)
3785 if (!isdigit(*(offset
+i
)))
3786 exiterror( ERR_INCORRECT_CALL
, 924, "STREAM", 3, "n, +n, -n, =n or <n", word
[0] );
3788 seek_offset
= atol(offset
);
3789 if (seek_sign
) /* negative */
3791 ptr
= get_file_ptr( TSD
, filename
, pos_type
, (pos_type
&OPER_WRITE
) ? ACCESS_WRITE
: ACCESS_READ
) ;
3794 sprintf(buf
,"ERROR:%d",errno
);
3795 result
= Str_creTSD( buf
) ;
3797 if (seek_on
) /* position by line */
3798 pos
= positionfile( TSD
, "STREAM", 3, ptr
, pos_type
, seek_offset
, seek_type
) ;
3800 pos
= positioncharfile( TSD
, "STREAM", 3, ptr
, pos_type
, seek_offset
, seek_type
) ;
3803 result
= Str_makeTSD( 20 ) ; /* should be enough digits */
3804 sprintf(result
->value
, "%ld", pos
);
3805 Str_len( result
) = strlen( result
->value
);
3809 sprintf(buf
,"ERROR:%d",errno
);
3810 result
= Str_creTSD( buf
) ;
3818 /* ------------------------------------------------------------------- */
3819 /* Rexx builtin functions (level 3) */
3821 * This part consists of one function for each of the Rexx builtin
3822 * functions that operates on filesystem I/O
3827 * This routine implements the Rexx built-in function CHARS(). It is
3828 * really quite simple, little more than a wrap-around to the
3829 * function calc_chars_left.
3831 streng
*std_chars( tsd_t
*TSD
, cparamboxptr parms
)
3834 streng
*string
=NULL
;
3835 fileboxptr ptr
=NULL
;
3836 int was_closed
=0, result
=0 ;
3841 /* Syntax: chars([filename]) */
3842 checkparam( parms
, 0, 2 , "CHARS" ) ;
3844 if (parms
&&parms
->next
&&parms
->next
->value
)
3845 opt
= getoptionchar( TSD
, parms
->next
->value
, "CHARS", 2, "CN", "" ) ;
3847 string
= (parms
->value
&& parms
->value
->len
) ? parms
->value
: ft
->stdio_ptr
[0]->filename0
;
3849 * Get a pointer to the Rexx file table entry of the file, and
3850 * calculate the number of characters left.
3852 ptr
= getfileptr( TSD
, string
) ;
3853 was_closed
= (ptr
==NULL
) ;
3855 ptr
= get_file_ptr( TSD
, string
, OPER_READ
, ACCESS_READ
) ;
3857 result
= calc_chars_left( TSD
, ptr
) ;
3859 closefile( TSD
, string
) ;
3861 return int_to_streng( TSD
, result
) ;
3867 * Implements the Rexx builtin function charin(). This function takes
3868 * three parameters, and they are treated pretty straight forward
3869 * according to TRL. If called with no start position, and a length of
3870 * zero, it may be used to do some fancy work (flushing I/O?), although
3871 * that is probably more needed for output :-) Note that the file in
3872 * entered into the file table in this case, so it might be used to
3873 * explicitly open a file for reading. However, consider using stream()
3874 * to do this, it's a much cleaner approach!
3876 streng
*std_charin( tsd_t
*TSD
, cparamboxptr parms
)
3878 streng
*filename
=NULL
, *result
=NULL
;
3879 fileboxptr ptr
=NULL
;
3886 /* Syntax: charin([filename][,[start][,length]]) */
3887 checkparam( parms
, 0, 3 , "CHARIN" ) ;
3890 * First, let's get the information about the file from the
3891 * file table, and open it in the correct mode if is not already
3894 filename
= (parms
->value
&& parms
->value
->len
) ? (parms
->value
) : ft
->stdio_ptr
[0]->filename0
;
3895 ptr
= get_file_ptr( TSD
, filename
, OPER_READ
, ACCESS_READ
) ;
3898 * Then, get the starting point, or set it to zero.
3900 parms
= parms
->next
;
3901 if ((parms
)&&(parms
->value
))
3902 start
= atopos( TSD
, parms
->value
, "CHARIN", 2 ) ;
3907 * At last, get the length, or use the default value one.
3910 parms
= parms
->next
;
3912 if ((parms
)&&(parms
->value
))
3913 length
= atozpos( TSD
, parms
->value
, "CHARIN", 3 ) ;
3918 * Position current position in file if necessary
3921 positioncharfile( TSD
, "CHARIN", 2, ptr
, OPER_READ
, start
, SEEK_SET
) ;
3924 result
= readbytes( TSD
, ptr
, length
) ;
3928 flush_input( ptr
) ; /* Whatever happens ... */
3929 result
= nullstringptr() ;
3938 * This function implements the Rexx built-in function CHAROUT(). It
3939 * is basically a wrap-around for the two functions that perform
3940 * character repositioning in a file; and writes out characters.
3943 streng
*std_charout( tsd_t
*TSD
, cparamboxptr parms
)
3945 streng
*filename
=NULL
, *string
=NULL
;
3948 fileboxptr ptr
=NULL
;
3953 if ( TSD
->restricted
)
3954 exiterror( ERR_RESTRICTED
, 1, "CHAROUT" ) ;
3956 /* Syntax: charout([filename][,[string][,start]]) */
3957 checkparam( parms
, 0, 3 , "CHAROUT" ) ;
3959 filename
= (parms
->value
&& parms
->value
->len
) ? (parms
->value
) : ft
->stdio_ptr
[1]->filename0
;
3961 /* Read the data to be written, if any */
3962 parms
= parms
->next
;
3963 if (parms
&& parms
->value
)
3964 string
= parms
->value
;
3968 /* Read the position to start writing, is any */
3970 parms
= parms
->next
;
3972 if ( parms
&& parms
->value
)
3973 pos
= atopos( TSD
, parms
->value
, "CHAROUT", 3 ) ;
3978 * Get the filepointer, if necessary, open in in the right mode
3981 ptr
= get_file_ptr( TSD
, filename
, OPER_WRITE
, ACCESS_WRITE
) ;
3988 * If we are to position the write position somewhere, do that first.
3991 positioncharfile( TSD
, "CHAROUT", 3, ptr
, OPER_WRITE
, pos
, SEEK_SET
) ;
3994 * Then, write the actual data, or flush output if neither data nor
3995 * position was given.
3998 length
= string
->len
- writebytes( TSD
, ptr
, string
) ;
4003 flush_output( TSD
, filename
) ; /* Whatever that may mean */
4006 return int_to_streng( TSD
, length
) ;
4012 * Simple routine that implements the Rexx built-in function LINES().
4013 * Really just a wrap-around to the countlines() routine.
4016 streng
*std_lines( tsd_t
*TSD
, cparamboxptr parms
)
4019 fileboxptr ptr
=NULL
;
4020 streng
*filename
=NULL
;
4021 int was_closed
=0, result
=0 ;
4027 /* Syntax: lines([filename][,C|N]) */
4028 checkparam( parms
, 0, 2 , "LINES" ) ;
4030 if (parms
&&parms
->next
&&parms
->next
->value
)
4031 opt
= getoptionchar( TSD
, parms
->next
->value
, "LINES", 2, "CN", "" ) ;
4034 * Get the name of the file (use defaults if necessary), and get
4035 * a pointer to the entry of that file from the file table
4038 && parms
->value
->len
)
4039 filename
= parms
->value
;
4041 filename
= ft
->stdio_ptr
[0]->filename0
;
4044 * Try to get the Rexx file table entry, if it doesn't work, then
4045 * try again ... and a bit harder
4047 ptr
= getfileptr( TSD
, filename
) ;
4048 was_closed
= (ptr
==NULL
) ;
4050 ptr
= get_file_ptr( TSD
, filename
, OPER_READ
, ACCESS_READ
) ;
4053 * It's rather simple ... all the work has already been done in
4054 * the function countlines()
4056 if ( get_options_flag( TSD
->currlevel
, EXT_FAST_LINES_BIF_DEFAULT
) )
4057 actual
= (opt
== 'C') ? 1 : 0;
4059 actual
= (opt
== 'C') ? 0 : 1;
4060 result
= countlines( TSD
, ptr
, actual
) ;
4063 closefile( TSD
, filename
) ;
4066 return int_to_streng( TSD
, result
) ;
4072 * The Rexx built-in function LINEIN() reads a line from a file.
4073 * The actual reading is performed in 'readoneline', while this routine
4074 * takes care of range checking of parameters, and decides which
4075 * lower level routines to call.
4078 streng
*std_linein( tsd_t
*TSD
, cparamboxptr parms
)
4080 streng
*filename
=NULL
, *res
=NULL
;
4081 fileboxptr ptr
=NULL
;
4082 int count
=0, line
=0 ;
4087 /* Syntax: linein([filename][,[line][,count]]) */
4088 checkparam( parms
, 0, 3 , "LINEIN" ) ;
4091 * First get the name of the file, or use the appropriate default
4094 && parms
->value
->len
)
4095 filename
= parms
->value
;
4097 filename
= ft
->stdio_ptr
[0]->filename0
;
4100 * Then get the line number at which the read it to start, or set
4101 * set it to zero if none was specified.
4104 parms
= parms
->next
;
4106 if (parms
&& parms
->value
)
4107 line
= atopos( TSD
, parms
->value
, "LINEIN", 2 ) ;
4109 line
= 0 ; /* Illegal value */
4112 * And at last, read the count, which can be only 0 or 1, and which
4113 * is the number of lines to read.
4116 parms
= parms
->next
;
4118 if (parms
&& parms
->value
)
4120 count
= atozpos( TSD
, parms
->value
, "LINEIN", 3 ) ;
4121 if (count
!=0 && count
!=1)
4122 exiterror( ERR_INCORRECT_CALL
, 39, "LINEIN", tmpstr_of( TSD
, parms
->value
) ) ;
4125 count
= 1 ; /* The default */
4128 * Now, get the pointer to the entry in the file table that contains
4129 * information about this file, or make it automatically create
4130 * an entry if one didn't exist.
4132 ptr
= get_file_ptr( TSD
, filename
, OPER_READ
, ACCESS_READ
) ;
4135 * If line was specified, we must reposition the current read
4136 * position of the file.
4139 positionfile( TSD
, "LINEIN", 2, ptr
, OPER_READ
, line
, SEEK_SET
) ;
4142 * As the last thing, read in the data. If no data was wanted, skip it
4143 * but call flushing if line wasn't specified either.
4146 res
= readoneline( TSD
, ptr
, 0 ) ;
4150 flush_input( ptr
) ;
4151 res
= nullstringptr() ;
4161 * This function is a wrap-around for the Rexx built-in function
4162 * LINEOUT(). It performs parameter checking and decides which lower
4163 * level routines to call.
4166 streng
*std_lineout( tsd_t
*TSD
, cparamboxptr parms
)
4168 streng
*string
=NULL
, *file
=NULL
;
4169 int lineno
=0, result
=0 ;
4170 fileboxptr ptr
=NULL
;
4175 if ( TSD
->restricted
)
4176 exiterror( ERR_RESTRICTED
, 1, "LINEOUT" ) ;
4178 /* Syntax: lineout([filename][,[string][,line]]) */
4179 checkparam( parms
, 0, 3 , "LINEOUT" ) ;
4182 * First get the pointer for the file to operate on. If omitted,
4183 * use the standard output stream
4186 && parms
->value
->len
)
4187 file
= parms
->value
;
4189 file
= ft
->stdio_ptr
[1]->filename0
;
4191 ptr = get_file_ptr( TSD, file, OPER_WRITE, ACCESS_WRITE ) ;
4195 * Then, get the data to be written, if any.
4198 parms
= parms
->next
;
4200 if (parms
&& parms
->value
)
4201 string
= parms
->value
;
4206 * At last, we must find the line number of the file to write. We
4207 * must position the file at this line before the write.
4210 parms
= parms
->next
;
4212 if (parms
&& parms
->value
)
4213 lineno
= atopos( TSD
, parms
->value
, "LINEOUT", 3 ) ;
4215 lineno
= 0 ; /* illegal value */
4217 if (string
|| lineno
)
4218 ptr
= get_file_ptr( TSD
, file
, OPER_WRITE
, ACCESS_WRITE
) ;
4221 * First, let's reposition the file if necessary.
4224 positionfile( TSD
, "LINEOUT", 2, ptr
, OPER_WRITE
, lineno
, SEEK_SET
) ;
4227 * And then, we write out the data. If there are not data, it may have
4228 * been just positioning. However, if there are neither data nor
4229 * a linenumber, something magic may happen.
4232 result
= writeoneline( TSD
, ptr
, string
, 0 ) ;
4236 flush_output( TSD
, file
) ;
4240 return int_to_streng( TSD
, result
) ;
4247 * This function checks whether a particular file is accessable by
4248 * the user in a certain mode, which may be read, write or execute.
4249 * Unforunately, this function differs a bit from the functionality
4250 * of several others. It explicitly checks a file, so that if the
4251 * file didn't exist in advance, it is _not_ opened. And even _if_
4252 * the file existed, the file in the file system is checked, not the
4253 * file opened by Regina. The two may differ slightly under certain
4257 static int is_accessable( const tsd_t
*TSD
, const streng
*filename
, int mode
)
4262 fn
= str_ofTSD( filename
) ;
4264 * First, call access() with the 'correct' parameters, and store
4265 * the result in 'res'. If 'mode' had an "impossible" value, give
4268 #if defined(WIN32) && defined(__IBMC__)
4272 Attrib
=GetFileAttributes(fn
);
4273 if (Attrib
==(DWORD
)-1)
4274 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
4275 if ((Attrib
&FILE_ATTRIBUTE_DIRECTORY
)!=FILE_ATTRIBUTE_DIRECTORY
)
4277 if ((mode
== COMMAND_READABLE
) && ((Attrib
&FILE_ATTRIBUTE_READONLY
)==FILE_ATTRIBUTE_READONLY
))
4279 else if ((mode
== COMMAND_WRITEABLE
) || (mode
== COMMAND_EXECUTABLE
))
4284 if (mode
== COMMAND_READABLE
)
4285 res
= access( fn
, R_OK
) ;
4286 else if (mode
== COMMAND_WRITEABLE
)
4287 res
= access( fn
, W_OK
) ;
4288 else if (mode
== COMMAND_EXECUTABLE
)
4289 res
= access( fn
, X_OK
) ;
4291 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
4295 * Perhaps we should analyze the output a bit before returning?
4296 * If res==EACCES, that is not really an error, while other errno
4297 * code _do_ signify an error. However ... since the return code
4298 * a boolean variable, just return it.
4307 * This little function implements the RESET command of the Rexx
4308 * built-in function STREAM(). Basically, most of the job is done in
4309 * the function 'fixup_file()'. Except from removing the ERROR flag.
4310 * The 'fixup_file()' function is intended for fixing the file at the
4311 * start of a condition handler for the NOTREADY condition.
4313 * The value returned from this function is either "READY" or "UNKNOWN",
4314 * and reflects the philosophy that the file _is_ fixed, unless it
4315 * is impossible to open it. Of course, that may be a false READY,
4316 * since the actual _problem_ might not have been fixed, but at least
4317 * you have another try at the problem.
4320 static streng
*reset_file( tsd_t
*TSD
, fileboxptr fileptr
)
4323 return nullstringptr() ;
4325 fixup_file( TSD
, fileptr
->filename0
) ;
4326 fileptr
->flag
&= ~(FLAG_ERROR
| FLAG_FAKE
) ;
4328 if (fileptr
->fileptr
)
4329 return Str_creTSD( "READY" ) ; /* Per definition */
4331 return Str_creTSD( "UNKNOWN" ) ;
4337 * The built-in function STREAM() is new in TRL2. It is supposed to be
4338 * a sort of all-round function for just about anything having to do with
4339 * files. The details of its specification in TRL2 leaves a lot of room
4340 * for the implementors. Two of the options to this command -- the Status
4341 * and Description options are treated as defined by TRL, the Command
4342 * option takes several command, defined by the COMMAND_ macros.
4344 streng
* std_stream( tsd_t
*TSD
, cparamboxptr parms
)
4347 streng
*command
=NULL
, *result
=NULL
, *filename
=NULL
, *psub
=NULL
;
4348 fileboxptr ptr
=NULL
;
4350 /* Syntax: stream(filename[,[oper][,command ...]]) */
4351 if ((!parms
)||(!parms
->value
))
4352 exiterror( ERR_INCORRECT_CALL
, 5, "STREAM", 1 ) ;
4353 checkparam( parms
, 1, 3 , "STREAM" ) ;
4356 * Get the filepointer to Rexx's file table, but make sure that the
4357 * file is not in any way created if it didn't exist.
4359 filename
= Str_dupstrTSD( parms
->value
);
4360 ptr
= getfileptr( TSD
, filename
) ;
4363 * Read the 'operation'. This is really just an 'option'. The
4364 * default option is 'S'.
4366 parms
= parms
->next
;
4367 if (parms
&& parms
->value
)
4368 oper
= getoptionchar( TSD
, parms
->value
, "STREAM", 2, "CSD", "" ) ;
4373 * If the operation was 'C', we _must_ have a third parameter, on the
4374 * other hand, if it was not 'C', we must never have a third parameter.
4375 * Make sure that these rules are followed.
4380 parms
= parms
->next
;
4381 if (parms
&& parms
->value
)
4382 command
= parms
->value
;
4384 exiterror( ERR_INCORRECT_CALL
, 3, "STREAM", 3 ) ;
4387 if (parms
&& parms
->next
&& parms
->next
->value
)
4388 exiterror( ERR_INCORRECT_CALL
, 4, "STREAM", 2 ) ;
4391 * Here comes the main loop.
4398 * Read the command, and 'translate' it into an integer which
4399 * describes it, see the implementation of get_command(), and
4400 * the COMMAND_ macros. The first of these are rather simple,
4401 * in fact, they could probably be compressed to save some
4404 command
= Str_strp( command
, ' ', STRIP_BOTH
);
4405 oper
= get_command( command
) ;
4409 closefile( TSD
, filename
) ;
4410 ptr
= openfile( TSD
, filename
, ACCESS_READ
) ;
4413 closefile( TSD
, filename
) ;
4414 ptr
= openfile( TSD
, filename
, ACCESS_WRITE
) ;
4416 case COMMAND_APPEND
:
4417 closefile( TSD
, filename
) ;
4418 ptr
= openfile( TSD
, filename
, ACCESS_APPEND
) ;
4420 case COMMAND_UPDATE
:
4421 closefile( TSD
, filename
) ;
4422 ptr
= openfile( TSD
, filename
, ACCESS_UPDATE
) ;
4424 case COMMAND_CREATE
:
4425 closefile( TSD
, filename
) ;
4426 ptr
= openfile( TSD
, filename
, ACCESS_CREATE
) ;
4430 * The file is always unknown after is has been closed. Does
4431 * that sound convincing, or does it sound like I didn't feel
4432 * to implement the rest of this ... ?
4434 closefile( TSD
, filename
) ;
4435 result
= Str_creTSD( "UNKNOWN" ) ;
4439 * Flush the file. Actually, this might not be needed, since
4440 * the functions that write out data may contain explicit
4443 ptr
= getfileptr( TSD
, filename
) ;
4444 if (ptr
&& ptr
->fileptr
)
4447 if (fflush( ptr
->fileptr
))
4449 file_error( ptr
, errno
, NULL
) ;
4450 result
= Str_creTSD( "ERROR" ) ;
4453 result
= Str_creTSD( "READY" ) ;
4456 result
= Str_creTSD( "ERROR" ) ;
4458 result
= Str_creTSD( "UNKNOWN" ) ;
4460 case COMMAND_STATUS
:
4461 ptr
= getfileptr( TSD
, filename
) ;
4462 result
= getrexxstatus( TSD
, ptr
) ;
4465 result
= getstatus( TSD
, filename
, COMMAND_FSTAT
) ;
4468 ptr
= getfileptr( TSD
, filename
) ;
4469 result
= reset_file( TSD
, ptr
) ;
4471 case COMMAND_READABLE
:
4472 case COMMAND_WRITEABLE
:
4473 case COMMAND_EXECUTABLE
:
4474 result
= int_to_streng( TSD
, is_accessable( TSD
, filename
, oper
)) ;
4478 * We have to further parse the remainder of the command
4479 * to determine what sub-command has been passed.
4481 psub
= Str_nodupTSD( command
, 5, command
->len
- 5);
4482 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
4483 result
= getquery( TSD
, filename
, psub
) ;
4484 Free_stringTSD(psub
);
4488 * We have to further parse the remainder of the command
4489 * to determine what sub-command has been passed.
4491 psub
= Str_nodupTSD( command
, 4, command
->len
- 4);
4492 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
4493 result
= getopen( TSD
, filename
, psub
) ;
4494 Free_stringTSD(psub
);
4497 psub
= Str_nodupTSD( command
, 4, command
->len
- 4);
4498 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
4499 result
= getseek( TSD
, filename
, psub
) ;
4500 Free_stringTSD(psub
);
4502 case COMMAND_POSITION
:
4503 psub
= Str_nodupTSD( command
, 8, command
->len
- 8);
4504 psub
= Str_strp( psub
, ' ', STRIP_LEADING
);
4505 result
= getseek( TSD
, filename
, psub
) ;
4506 Free_stringTSD(psub
);
4509 exiterror( ERR_STREAM_COMMAND
, 3, "CLOSE FLUSH OPEN POSITION QUERY SEEK", tmpstr_of( TSD
, command
) ) ;
4516 * Get a description of the most recent error for this file
4521 result
= Str_dupTSD(ptr
->errmsg
) ;
4522 else if (ptr
->error
)
4523 result
= Str_creTSD( strerror(ptr
->error
) ) ;
4529 * Get a simple status for the file in question. If the file
4530 * doesn't exist in Rexx's tables, UNKNOWN is returned. If the
4531 * file is in error state, return ERROR, else return READY,
4532 * unless current read position is at EOF, in which case
4533 * NOTREADY is return. Note that ERROR and NOTREADY are the
4534 * two states that will raise the NOTREADY condition.
4538 if (ptr
->flag
& FLAG_ERROR
)
4540 result
= Str_creTSD( "ERROR" ) ;
4542 #if 1 /* really MH */
4543 else if (ptr
->flag
& FLAG_AFTER_RDEOF
)
4545 result
= Str_creTSD( "NOTREADY" ) ;
4548 else if (ptr
->flag
& FLAG_RDEOF
)
4550 result
= Str_creTSD( "NOTREADY" ) ;
4555 result
= Str_creTSD( "READY" ) ;
4559 result
= Str_creTSD( "UNKNOWN" ) ;
4564 exiterror( ERR_INTERPRETER_FAILURE
, 1, __FILE__
, __LINE__
, "" ) ;
4568 result
= nullstringptr() ;
4570 Free_stringTSD(filename
);
4577 * This routine will traverse the list of open files, and dump relevant
4578 * information about each of them. Really a debugging routine. It is
4579 * not available when Regina is compiled with optimalization.
4582 streng
*dbg_dumpfiles( tsd_t
*TSD
, cparamboxptr parms
)
4584 fileboxptr ptr1
=NULL
, ptr2
=NULL
;
4591 checkparam( parms
, 0, 0 , "DUMPFILES" ) ;
4593 if (TSD
->stddump
== NULL
)
4594 return nullstringptr() ;
4596 fprintf(TSD
->stddump
,
4598 fprintf(TSD
->stddump
,
4599 "File Filename Flags line char line char\n");
4601 for (ptr1
=ft
->mrufile
;ptr1
;ptr1
=ptr1
->older
)
4603 for (ptr2
=ptr1
;ptr2
;ptr2
=ptr1
->next
)
4606 fno
= fileno( ptr2
->fileptr
) ;
4607 fprintf( TSD
->stddump
,"%4d %-30s", fno
, ptr2
->filename0
->value
);
4610 string
[0] = (char) (( ptr2
->flag
& FLAG_READ
) ? 'r' : ' ') ;
4611 string
[1] = (char) (( ptr2
->flag
& FLAG_WRITE
) ? 'w' : ' ') ;
4612 string
[2] = (char) (( ptr2
->flag
& FLAG_PERSIST
) ? 'p' : 't') ;
4613 string
[3] = (char) (( ptr2
->flag
& FLAG_RDEOF
) ? 'R' : ' ') ;
4614 string
[4] = (char) (( ptr2
->flag
& FLAG_AFTER_RDEOF
) ? 'A' : ' ') ;
4615 string
[5] = (char) (( ptr2
->flag
& FLAG_WREOF
) ? 'W' : ' ') ;
4616 string
[6] = (char) (( ptr2
->flag
& FLAG_SURVIVOR
) ? 'S' : ' ') ;
4617 string
[7] = (char) (( ptr2
->flag
& FLAG_ERROR
) ? 'E' : ' ') ;
4618 string
[8] = (char) (((ptr2
->flag
& FLAG_FAKE
) && (ptr2
->flag
& FLAG_ERROR
) ) ? 'F' : ' ') ;
4621 fprintf( TSD
->stddump
, " %8s %4d %4ld %4d %4ld\n", string
,
4622 ptr2
->readline
, (long)(ptr2
->readpos
),
4623 ptr2
->writeline
,(long)(ptr2
->writepos
) ) ;
4625 if (ptr2
->flag
& FLAG_ERROR
)
4628 fprintf(TSD
->stddump
, " ==> %s\n", ptr2
->errmsg
->value
) ;
4629 else if (ptr2
->error
)
4630 fprintf(TSD
->stddump
, " ==> %s\n", strerror( ptr2
->error
)) ;
4634 fprintf( TSD
->stddump
," r=read, w=write, p=persistent, t=transient, e=eof\n");
4635 fprintf( TSD
->stddump
," R=read-eof, W=write-eof, S=special, E=error, F=fake\n");
4637 return nullstringptr() ;
4646 * This should really go out .... We definitively don't want to do this.
4647 * we want to go through readoneline() for the default input stream.
4649 #if 1 /* really MH */
4650 streng
*readkbdline( tsd_t
*TSD
)
4655 return readoneline( TSD
, ft
->stdio_ptr
[DEFAULT_STDIN_INDEX
], 0 );
4658 streng
*readkbdline( const tsd_t
*TSD
)
4660 streng
*source
=NULL
;
4666 source
= Str_makeTSD(BUFFERSIZE
) ;
4668 (void)fseek(stdin
,SEEK_SET
,0) ;
4673 source
->value
[i
++] = (char) (ch
= getc(stdin
)) ;
4674 } while((ch
!='\012')&&(ch
!=EOF
)) ;
4676 ft
->got_eof
= (ch
==EOF
) ;
4680 if (source
->value
[i
-2] == '\015')
4688 void *addr_reopen_file( tsd_t
*TSD
, const streng
*filename
, char code
)
4689 /* This is the open routine for the ADDRESS WITH-redirection. filename is
4690 * the name of the file. code is either 'r' for "READ",
4691 * 'A' for "WRITE APPEND", 'R' for "WRITE REPLACE". In case of READ
4692 * already opened files will be reused. In case of APPEND or REPLACE the
4693 * files are (re-)opened. An internal structure for files is returned and
4694 * should be used for calls to addr_io_file.
4695 * An already opened file for write can't be used. See J18PUB.pdf, 5.5.1.
4696 * The return value may be NULL in case of an error. A NOTREADY condition
4697 * may have been raised in this case.
4704 ptr
= get_file_ptr( TSD
, filename
, OPER_READ
, ACCESS_READ
) ;
4708 closefile( TSD
, filename
) ;
4709 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_APPEND
) ;
4713 closefile( TSD
, filename
) ;
4714 ptr
= openfile( TSD
, filename
, ACCESS_STREAM_REPLACE
) ;
4722 if ((ptr
!= NULL
) && (ptr
->fileptr
== NULL
))
4728 streng
*addr_io_file( tsd_t
*TSD
, void *fileptr
, const streng
*line
)
4729 /* This is the working routine for the ADDRESS WITH-redirection. fileptr is
4730 * the return value of addr_reopen_file. line must be NULL for a read
4731 * operation or a filled string.
4732 * The return value is NULL in case of a write operation or in case of EOF
4734 * A NOTREADY condition won't be raised.
4737 streng
*retval
= NULL
;
4740 retval
= readoneline( TSD
, fileptr
, 1 ) ;
4742 writeoneline( TSD
, fileptr
, line
, 1 ) ;
4749 * This routine is not really interesting. You should use the STREAM()
4750 * built-in function for greater portability and functionality. It is
4751 * left in the code for portability reasons.
4753 #ifdef OLD_REGINA_FEATURES
4754 streng
*unx_open( tsd_t
*TSD
, cparamboxptr parms
)
4756 fileboxptr ptr
=NULL
;
4758 int iaccess
=ACCESS_NONE
;
4760 checkparam( parms
, 1, 2 , "OPEN" ) ;
4762 if ((parms
->next
)&&(parms
->next
->value
))
4764 ch
= getoptionchar( TSD
, parms
->next
->value
, "OPEN", 2, "RW", "" ) ;
4765 if ( ch
== 'R' ) /* bja */
4766 iaccess
= ACCESS_READ
;
4767 else if ( ch
== 'W' ) /* bja */
4768 iaccess
= ACCESS_WRITE
;
4773 iaccess
= ACCESS_READ
;
4775 ptr
= openfile( TSD
, parms
->value
, iaccess
) ;
4777 return int_to_streng( TSD
,( ptr
&& ptr
->fileptr
)) ;
4783 * This routine is not really interesting. You should use the CLOSE
4784 * command of the STREAM() built-in function for greater portability
4785 * and compatibility. It is left in the code only for compatibility
4788 #ifdef OLD_REGINA_FEATURES
4789 streng
*unx_close( tsd_t
*TSD
, cparamboxptr parms
)
4791 fileboxptr ptr
=NULL
;
4793 checkparam( parms
, 1, 1 , "CLOSE" ) ;
4794 ptr
= getfileptr( TSD
, parms
->value
) ;
4795 closefile( TSD
, parms
->value
) ;
4797 return int_to_streng( TSD
, ptr
!=NULL
) ;
4803 * a function called exists that checks if a file with a certain name
4804 * exists. This function was taken from the ARexx API.
4806 streng
*arexx_exists( tsd_t
*TSD
, cparamboxptr parms
)
4812 checkparam( parms
, 1, 1, "EXISTS" ) ;
4814 name
= str_of( TSD
, parms
->value
) ;
4815 retval
= int_to_streng( TSD
, stat( name
, &st
) != -1 ) ;
4816 Free_TSD( TSD
, name
) ;
4824 * The code in this function borrows heavily from code supplied by
4825 * Keith Patton (keith,patton@dssi-jcl.com)
4827 /* FIXME, FGC:Nothing will happen here if *fp != NULL, is this a wanted side
4829 void get_external_routine(const tsd_t
*TSD
,const char *env
, const char *inname
, FILE **fp
, char *retname
, int startup
)
4831 static const char *extensions
[] = {"",".rexx",".rex",".cmd",".rx",NULL
};
4834 char outname
[REXX_PATH_MAX
+1];
4835 char buf
[REXX_PATH_MAX
+1];
4840 * If we are searching PATH for Rexx programs, don't look for files
4841 * without an extension.
4843 if ( strcmp( env
, "PATH" ) == 0 )
4847 env_path
= mygetenv( TSD
, env
, buf
, sizeof(buf
) );
4849 for (i
=start_ext
;extensions
[i
]!=NULL
&& *fp
== NULL
;i
++)
4852 * Try the filename without any path first
4854 strcpy(outname
,inname
);
4855 strcat(outname
,extensions
[i
]);
4857 *fp
= fopen(outname
, "r");
4859 *fp
= fopen(outname
, "rb");
4865 #if defined(HAVE__FULLPATH)
4866 _fullpath(retname
, outname
, REXX_PATH_MAX
);
4867 #elif defined(HAVE__TRUENAME)
4868 _truename(outname
, retname
);
4870 if (my_fullpath(retname
, outname
, REXX_PATH_MAX
) == -1)
4878 while (paths
&& !*fp
)
4883 while (*paths
== PATH_SEPARATOR
)
4885 sep
= strchr(paths
, PATH_SEPARATOR
);
4886 pathlen
= sep
? sep
-paths
: strlen(paths
);
4888 break; /* no more paths! */
4889 strncpy(outname
, paths
, pathlen
);
4890 outname
[pathlen
] = 0;
4892 if (outname
[pathlen
-1] != FILE_SEPARATOR
)
4893 strcat(outname
, FILE_SEPARATOR_STR
);
4894 strcat(outname
, inname
);
4895 strcat(outname
, extensions
[i
]);
4896 paths
= sep
? sep
+1 : 0; /* set up for next pass */
4898 *fp
= fopen(outname
, "r");
4900 *fp
= fopen(outname
, "rb");
4906 #if defined(HAVE__FULLPATH)
4907 _fullpath(retname
, outname
, REXX_PATH_MAX
);
4908 #elif defined(HAVE__TRUENAME)
4909 _truename(outname
, retname
);
4911 if (my_fullpath(retname
, outname
, REXX_PATH_MAX
) == -1)
4923 void find_shared_library(const tsd_t
*TSD
, const char *inname
, const char *inenv
, char *retname
)
4926 char outname
[REXX_PATH_MAX
+1];
4927 char buf
[REXX_PATH_MAX
+1];
4930 env_path
= mygetenv( TSD
, inenv
, buf
, sizeof(buf
) );
4931 strcpy(retname
,inname
);
4939 while (*paths
== PATH_SEPARATOR
)
4941 sep
= strchr(paths
, PATH_SEPARATOR
);
4942 pathlen
= sep
? sep
-paths
: strlen(paths
);
4944 break; /* no more paths! */
4945 strncpy(outname
, paths
, pathlen
);
4946 outname
[pathlen
] = 0;
4948 if (outname
[pathlen
-1] != FILE_SEPARATOR
)
4949 strcat(outname
, FILE_SEPARATOR_STR
);
4950 strcat(outname
, inname
);
4951 paths
= sep
? sep
+1 : 0; /* set up for next pass */
4952 if (access(outname
,F_OK
) == 0)
4954 strcpy(retname
,outname
);
4961 void CloseOpenFiles( const tsd_t
*TSD
)
4965 ptr
= TSD
->systeminfo
;
4968 if (TSD
->systeminfo
->input_fp
)
4970 fclose(TSD
->systeminfo
->input_fp
);
4971 TSD
->systeminfo
->input_fp
= NULL
;
4973 ptr
= TSD
->systeminfo
->previous
;
4978 streng
*ConfigStreamQualified( tsd_t
*TSD
, const streng
*name
)
4980 return( getstatus( TSD
, name
, COMMAND_QUERY_EXISTS
) );
4983 #if !defined(HAVE__FULLPATH) && !defined(HAVE__TRUENAME)
4985 * This function builds up the full pathname of a file
4986 * It is based heavily on code from splitpath() defined in
4987 * nonansi.c of Mark Hessling's THE
4991 # include <rmsdef.h>
4992 # include <descrip.h>
4994 int my_fullpath( char *dst
, const char *src
, int size
)
4997 int status
, context
= 0;
4998 struct dsc$descriptor_d result_dx
= {0, DSC$K_DTYPE_T
, DSC$K_CLASS_D
, 0};
4999 struct dsc$descriptor_d finddesc_dx
= {0, DSC$K_DTYPE_T
, DSC$K_CLASS_D
, 0};
5001 finddesc_dx
.dsc$a_pointer
= src
; /* You may need to cast this */
5002 finddesc_dx
.dsc$w_length
= strlen(src
);
5003 status
= lib$
find_file(&finddesc_dx
,&result_dx
,&context
,0,0,0,0);
5004 if (status
== RMS$_NORMAL
)
5006 memcpy(dst
,result_dx
.dsc$a_pointer
,result_dx
.dsc$w_length
);
5007 *(dst
+result_dx
.dsc$w_length
) = '\0';
5011 lib$
find_file_end(&context
);
5012 str$
free1_dx(&result_dx
);
5017 int my_fullpath( char *dst
, const char *src
, int size
)
5019 char tmp
[REXX_PATH_MAX
+1];
5020 char curr_path
[REXX_PATH_MAX
+1];
5021 char path
[REXX_PATH_MAX
+1];
5022 char fname
[REXX_PATH_MAX
+1];
5023 int i
= 0, len
= -1;
5024 struct stat stat_buf
;
5027 _getcwd2(curr_path
,REXX_PATH_MAX
);
5029 getcwd(curr_path
,REXX_PATH_MAX
);
5034 * First determine if the supplied filename is a directory.
5036 # if defined(__EMX__) || defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
5037 for ( i
= 0; i
< strlen( tmp
); i
++ )
5038 if ( tmp
[ i
] == '\\' )
5041 if ((stat(tmp
,&stat_buf
) == 0)
5042 && (stat_buf
.st_mode
& S_IFMT
) == S_IFDIR
)
5047 else /* here if the file doesn't exist or is not a directory */
5049 for (i
=strlen(tmp
),len
=-1;i
>-1;i
--)
5061 _getcwd2(path
,REXX_PATH_MAX
);
5063 getcwd(path
,REXX_PATH_MAX
);
5070 strcpy(fname
,tmp
+1+len
);
5075 strcpy(fname
,tmp
+1+len
);
5080 * Change directory to the supplied path, if possible and store the
5082 * If an error, restore the current path.
5085 if (_chdir2(path
) != 0)
5090 _getcwd2(path
,REXX_PATH_MAX
);
5093 if (chdir(path
) != 0)
5098 getcwd(path
,REXX_PATH_MAX
);
5102 * Append the OS directory character to the path if it doesn't already
5103 * end in the character.
5108 # if defined(__WINS__) || defined(__EPOC32__)
5109 if ( path
[ len
- 1 ] != '\\'
5111 if ( path
[ len
- 1 ] != '/'
5113 && strlen( fname
) != 0 )
5118 # if defined(__EMX__) || defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
5119 for ( i
= 0; i
< len
; i
++ )
5120 if ( path
[ i
] == '/' )
5126 size
= size
; /* keep compiler happy */
5132 #if !defined(HAVE__SPLITPATH2) && !defined(HAVE__SPLITPATH) && !defined(__EMX__) && !defined(DJGPP)
5133 int my_splitpath2( const char *in
, char *out
, char **drive
, char **dir
, char **name
, char **ext
)
5135 int inlen
= strlen(in
);
5136 int last_slash_pos
=-1,last_dot_pos
=-1,last_pos
=0,i
=0;
5138 for (i
=0;i
<inlen
;i
++)
5140 if ( *(in
+i
) == '/' || *(in
+i
) == '\\' )
5142 else if ( *(in
+i
) == '.' )
5146 * drive is always empty !
5152 if (last_dot_pos
> last_slash_pos
)
5154 strcpy(*ext
,in
+last_dot_pos
);
5155 last_pos
= 2 + (inlen
- last_dot_pos
);
5156 inlen
= last_dot_pos
;
5163 *dir
= out
+last_pos
;
5165 * If there is a path componenet (last_slash_pos not -1), then copy
5166 * from the start of the in string to the last_slash_pos to out[1]
5168 if (last_slash_pos
!= -1)
5170 memcpy(*dir
, in
, last_slash_pos
+ 1);
5171 last_pos
+= last_slash_pos
+ 1;
5172 out
[last_pos
++] = '\0';
5173 *name
= out
+last_pos
;
5174 memcpy(*name
, in
+last_slash_pos
+1,(inlen
- last_slash_pos
- 1) );
5175 out
[last_pos
+ (inlen
- last_slash_pos
- 1)] = '\0';
5181 *name
= out
+last_pos
;
5182 memcpy(*name
, in
, inlen
);
5183 *(name
+inlen
) = '\0';