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