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