1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 1992-1994 Anders Christensen <anders@pvv.unit.no>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /*
21 * This module is a real pain, since file I/O is one of the features
22 * that varies most between different platforms. And what makes it
23 * even more of a pain, is the fact that it must be coordinated with
24 * the handling of the condition NOTREADY. Anyway, here are the
25 * decisions set up before (well ... during) the implementation that
26 * guide how this this thing is supposed to work.
27 *
28 * There are four kind of routines, structured in four levels:
29 *
30 * (1)---------+ (2)--------+
31 * | builtin | ----> | general | B C library
32 * | functions | A | routines | ----> Routines
33 * +-----------+ +----------+
34 * | |
35 * | |
36 * | V (3)--------+
37 * +----------------->+---> | Error |
38 * | routines |
39 * +----------+
40 *
41 * 1) Builtin functions, these has the "std_" prefix which is standard
42 * for all buildin functions. The task for these functions are to
43 * process parameters, call (2) which specializes on operations (like
44 * read, write, position etc), and return a decent answer back to its
45 * caller. There is one routine in this level for each of the
46 * functions in the library of built-in functions. Most of them are
47 * std_* functions, but there are a few others too.
48 *
49 * 2) These are general operations for reading, writing, positioning,
50 * etc. They may call the C library routines directly, or
51 * indirectly, through calls to (3). The interface (A) between (1)
52 * and (2) is based on the local structure fileboxptr and strengs.
53 * There are one function in this level for each of the basic
54 * operations needed to be performed on a file. Opening, closing,
55 * reading a line, writing a line, line positioning, reading chars,
56 * writing chars, positioning chars, counting lines, counting
57 * chars, etc. The interface (B) to the C library routines uses
58 * FILE* and char* for its operations.
59 *
60 * 3) General routines to perform 'trivial' tasks. In this level,
61 * things like retriving Rexx's file table entries are implemented,
62 * and all the errorhandling. These are called from both the two
63 * previous levels.
64 *
65 * There are three standard files, called "<stdin>", "<stdout>" and
66 * "<stderr>" (note that the "<" and ">" are part of the filename.)
67 * These are handles for the equivalent Unix standard files. This
68 * might cause problems if you actually do want a file calls that, or
69 * if one of these files is closed, and the more information is
70 * written to it (I can easily visulize Users trying to delete such a
71 * file :-)) So the standard files -- having set flag SURVIVOR -- will
72 * never be closed or reopened.
73 *
74 * Error_file is called by that routine which actually discovers the
75 * problem. If it is an CALL ON condition, it will set the FLAG_FAKE
76 * flag, which all other routines will check for.
77 */
78
79 #pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
80
81 /*
82 * Bug in LCC complier wchar.h that incorrectly says it defines stat struct
83 * but doesn't
84 */
85 #if defined(__LCC__)
86 # include <sys/stat.h>
87 #endif
88
89 #include "rexx.h"
90 #include <errno.h>
91 #include <stdio.h>
92 #include <string.h>
93 #ifdef HAVE_ASSERT_H
94 # include <assert.h>
95 #endif
96 #ifdef HAVE_LIMITS_H
97 # include <limits.h>
98 #endif
99 #include <time.h>
100 #if defined(VMS)
101 # include <stat.h>
102 #elif defined(OS2)
103 # include <sys/stat.h>
104 # ifdef HAVE_UNISTD_H
105 # include <unistd.h>
106 # endif
107 #elif (defined(__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || defined(__LCC__)
108 # include <sys/stat.h> /* MH 10-06-96 */
109 # include <fcntl.h> /* MH 10-06-96 */
110 # ifdef HAVE_UNISTD_H
111 # include <unistd.h> /* MH 10-06-96 */
112 # endif
113 # if defined(_MSC_VER) && !defined(__WINS__)
114 # include <io.h>
115 # endif
116 #elif defined(WIN32) && defined(__IBMC__) /* LM 26-02-99 */
117 # include <io.h>
118 # include <sys/stat.h>
119 # include <fcntl.h>
120 #elif defined(MAC)
121 # include "mac.h"
122 #else
123 # include <sys/stat.h>
124 # ifdef HAVE_PWD_H
125 # include <pwd.h>
126 #endif
127 # ifdef HAVE_GRP_H
128 # include <grp.h>
129 # endif
130 # include <fcntl.h>
131 # ifdef HAVE_UNISTD_H
132 # include <unistd.h>
133 # endif
134 #endif
135
136 #ifdef HAVE_DIRECT_H
137 # include <direct.h>
138 #endif
139
140 #ifdef __EMX__
141 # include <io.h>
142 #endif
143
144 #ifdef WIN32
145 # ifdef _MSC_VER
146 # if _MSC_VER >= 1100
147 /* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
148 # pragma warning(disable: 4115 4201 4214 4514)
149 # endif
150 # endif
151 # include <windows.h>
152 # ifdef _MSC_VER
153 # if _MSC_VER >= 1100
154 # pragma warning(default: 4115 4201 4214)
155 # endif
156 # endif
157 # if defined(__WATCOMC__) || defined(__BORLANDC__)
158 # include <io.h>
159 # endif
160 #endif
161
162 #ifndef min
163 # define min(a,b) (((a) < (b)) ? (a) : (b))
164 #endif
165
166 /*
167 * Stuff for FSTAT
168 */
169 #if defined(S_IFDIR) && !defined(S_ISDIR)
170 # define S_ISDIR(mode) (((mode) & S_IFDIR)==S_IFDIR)
171 #endif
172 #if defined(S_IFREG) && !defined(S_ISREG)
173 # define S_ISREG(mode) (((mode) & S_IFREG)==S_IFREG)
174 #endif
175 #if defined(S_IFCHR) && !defined(S_ISCHR)
176 # define S_ISCHR(mode) (((mode) & S_IFCHR)==S_IFCHR)
177 #endif
178 #if defined(S_IFBLK) && !defined(S_ISBLK)
179 # define S_ISBLK(mode) (((mode) & S_IFBLK)==S_IFBLK)
180 #endif
181 #if defined(S_IFLNK) && !defined(S_ISLNK)
182 # define S_ISLNK(mode) (((mode) & S_IFLNK)==S_IFLNK)
183 #endif
184 #if defined(S_IFFIFO) && !defined(S_ISFIFO)
185 # define S_ISFIFO(mode) (((mode) & S_IFFIFO)==S_IFFIFO)
186 #endif
187 #if defined(S_IFSOCK) && !defined(S_ISSOCK)
188 # define S_ISSOCK(mode) (((mode) & S_IFSOCK)==S_IFSOCK)
189 #endif
190 #if defined(S_IFNAM) && !defined(S_ISNAM)
191 # define S_ISNAM(mode) (((mode) & S_IFNAM)==S_IFNAM)
192 #endif
193
194 #if !defined(ACCESSPERMS)
195 # if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
196 # define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
197 # else
198 # if defined(S_IREAD) && defined(S_IWRITE) && defined(S_IEXEC)
199 # define ACCESSPERMS (S_IREAD|S_IWRITE|S_IEXEC)
200 # else
201 # define ACCESSPERMS (0xfff)
202 # endif
203 # endif
204 #endif
205 /*
206 * The macrodefinition below defines the various modes in which a
207 * file may be opened. These modes are:
208 *
209 * READ - Open for readonly access. The current read position
210 * is set to the the start of the file. If the file does
211 * not exist, report an error.
212 *
213 * WRITE - Open for read and write. The current read position is
214 * set to the start of the file, while the current write
215 * position is set to EOF. If file does not exist, create
216 * it. If file does exist, use existing data.
217 *
218 * UPDATE - The combined operation of READ and WRITE, but if file
219 * does not exist, issue an error.
220 *
221 * APPEND - Open in APPEND mode, i.e. open for writeonly, position
222 * at the End-Of-File, and (if possible) open in a mode
223 * that disallows positioning. The file will be a transient
224 * file. If the file does not exist, create it.
225 *
226 * CREATE - Open for write, but if the file does exist, truncate
227 * it at the beginning after opening it.
228 */
229 #define ACCESS_NONE 0
230 #define ACCESS_READ 1
231 #define ACCESS_WRITE 2
232 #define ACCESS_UPDATE 3
233 #define ACCESS_APPEND 4
234 #define ACCESS_CREATE 5
235 #define ACCESS_STREAM_APPEND 6
236 #define ACCESS_STREAM_REPLACE 7
237
238 /*
239 * These macros is used to set the value of the 'oper' in the filebox
240 * data structure. If last operation on a file was a read or a write,
241 * set 'oper' to OPER_READ or OPER_WRITE, respectively. If last
242 * operation was repositioning or flushing, use OPER_NONE. See
243 * description of 'oper' field in definition of 'filebox'.
244 */
245 #define OPER_NONE 0
246 #define OPER_READ 1
247 #define OPER_WRITE 2
248
249 /*
250 * Flags, carrying information about files. The 'flag' field in the
251 * 'filebox' structure is set to values matching these defintions. The
252 * meaning of each of these flags is:
253 *
254 * PERSIST - Set if file is persistent, if unset, file is treated
255 * as a transient file.
256 * EOF - Currently not in use
257 * READ - File has been opened for read access.
258 * WRITE - File has been opened for write access.
259 * CREATE - Currently not in use
260 * ERROR - Set if the file is in error state. If operations are
261 * attempted performed on files in state error, the
262 * NOTREADY condition will in general be raised, and the
263 * operation will fail.
264 * SURVIVOR - Set for certain special files; the default streams, which
265 * is not really to be closed or reopened.
266 * FAKE - Meaningful only if ERROR is set. If FAKE is set, and
267 * an operation on the file is attempted, the operation is
268 * 'faked' (NOTREADY is not triggered, and the result returned
269 * for write operations does not report that the output was
270 * not written.
271 * WREOF - Current write position is at EOF. If line output is
272 * performed, there is no need to truncate the file.
273 * RDEOF - Current read position is at EOF. Reading EOF raises the
274 * NOTREADY condition, but does not put the file into error
275 * state.
276 * AFTER_RDEOF - Bit of a hack here. This flag is set after an attempt
277 * (Added by MH) is made to read a stream once the RDEOF flag is set.
278 * The reason for this is that all the "read" stream
279 * functions; LINEIN, LINES, CHARIN, etc set the RDEOF
280 * flag at the point that they determine a RDEOF has
281 * occurred. This is usually at the end of the function.
282 * Therefore a LINEIN that reads EOF sets RDEOF and a
283 * subsequent call to STREAM(stream,'S') will return
284 * NOTREADY. This to me is logical, but the behaviour
285 * of other interpreters is that the first call to
286 * STREAM(stream,'S') after reaching EOF should still return
287 * READY. Only when ANOTHER "read" stream function is
288 * called does STREAM(stream,'S') return NOTREADY.
289 * SWAPPED - This flag is set if the file is currently swapped out, that
290 * is, the file is closed in order to let another file use
291 * the system's file table sloth freed when the file was
292 * temporarily closed.
293 */
294 #define FLAG_PERSIST 0x0001
295 #define FLAG_EOF 0x0002
296 #define FLAG_READ 0x0004
297 #define FLAG_WRITE 0x0008
298 #define FLAG_CREATE 0x0010
299 #define FLAG_ERROR 0x0020
300 #define FLAG_SURVIVOR 0x0040
301 #define FLAG_FAKE 0x0080
302 #define FLAG_WREOF 0x0100
303 #define FLAG_RDEOF 0x0200
304 #define FLAG_SWAPPED 0x0400
305 #define FLAG_AFTER_RDEOF 0x0800
306
307 /*
308 * So, what is the big difference between FAKE and ERROR. Well, when a
309 * file gets it ERROR flag set, it signalizes that the file is in
310 * error state, and that no fileoperations should be performed on it.
311 * The FAKE flag is only meaningful when the ERROR flag is set. If set
312 * the FAKE flag tells that file operations should be faked in order to
313 * give the user the impression that everything is OK, while if FAKE is
314 * not set, errors are returned.
315 *
316 * The clue is that if a statement contains several operations on one
317 * file, and the first operation bombs, CALL ON NOTREADY will not take
318 * effect before the next statement boundary at the same procedural
319 * level So, for the rest of the file operations until that statement
320 * has finished, the FAKE flag is set, and signalizes that OK result
321 * should be returned whenever positioning or write is performed, and
322 * that NOTREADY should not be raised again.
323 *
324 * The reason for the RDEOF flag is that reading beyond EOF is not really
325 * a capital crime, and a lot of programmers are likely to do that, and
326 * expect things to be OK after repositioning current read position to
327 * another part of the file. If a file is put into ERROR state, it has
328 * to be explicitly reset in order to do any useful to it afterwards.
329 * Therefore, if EOF is seen on input, RDEOF is set, and NOTREADY is
330 * raised, but the file is not put into ERROR state.
331 */
332
333 /*
334 * The following macros defines symbolic names to the commands available
335 * in the Rexx built-in function STREAM(). The meaning of each of these
336 * commands are:
337 *
338 * READ - Opens the file with the corresponding mode. For a deeper
339 * WRITE description of each of these modes, see the defininition
340 * APPEND of the ACCESS_* macros. STREAM() is used to explicitly
341 * UPDATE open a file, while Rexx is totally happy with the
342 * CREATE traditional implicit opening, i.e. that the file is
343 * opened for the needed access at the time when it is
344 * first used. If the file to be opened is already open,
345 * it will first be closed, and then opened in the
346 * specified mode.
347 *
348 * CLOSE - Closes a file, works for any type of access. But if
349 * the file is a default stream, it will not be closed.
350 * Default streams should not be closed.
351 *
352 * FLUSH - Performs flushing on the file. Actually, I'm not so
353 * sure whether that is very interesting, since flushing
354 * is always performed after a write, anyway. Though, it
355 * might become an important function if the automatic
356 * flushing after write is removed (e.g. to improve speed).
357 *
358 * STATUS - Returns status information assiciated with the file as
359 * a human readable string. The information returned is the
360 * internal information that Rexx stores in the Rexx file
361 * table entry for that file. Use FSTAT to get information
362 * about the file from the operating system. See the
363 * function 'getrexxstatus()' for more information about
364 * the layout of the returned string.
365 *
366 * FSTAT - Returns status information associated with the file as
367 * a human readable string. The information returned is the
368 * information normally returned by the stat() system call
369 * under Unix (i.e. size, dates, access modes, etc). Use
370 * STATUS to get Rexx's information about the file. See
371 * the function 'getstatus()' for more information about
372 * the layout of the string returned.
373 *
374 * RESET - Resets the file after an error. Of course, this will
375 * only work for files which are 'resettable'. If the error
376 * is too serious, resetting will help little to fix the
377 * problem. E.g. writing beyond end-of-file can easily be
378 * fixed by RESET, trying to use a file which is named
379 * by an invalid syntax can not be correctly reset.
380 *
381 * READABLE - Checks that the file in question is available in the
382 * WRITABLE mode given, for the user that is executing the script.
383 * EXECUTABLE I.e. READABLE will return '1' for a file, if the file
384 * is readable for the user, else '0' is returned. Note
385 * that FSTAT returns the information about the accessmodes
386 * for a file, these returns the 'accessmode' which is
387 * relevant for a particular user. Also note that if your
388 * machine are using suid-bit (i.e. Unix), this function
389 * will check for the real uid, not the effective uid.
390 * Consequently, it may not give the wanted result for
391 * suid rexx scripts, see the Unix access() function. (And
392 * anyway, suid scripts are a _very_ bad idea under Unix,
393 * so this is probably not a problem ... :-)
394 */
395 #define COMMAND_NONE 0
396 #define COMMAND_READ 1
397 #define COMMAND_WRITE 2
398 #define COMMAND_APPEND 3
399 #define COMMAND_UPDATE 4
400 #define COMMAND_CREATE 5
401 #define COMMAND_CLOSE 6
402 #define COMMAND_FLUSH 7
403 #define COMMAND_STATUS 8
404 #define COMMAND_FSTAT 9
405 #define COMMAND_RESET 10
406 #define COMMAND_READABLE 11
407 #define COMMAND_WRITEABLE 12
408 #define COMMAND_EXECUTABLE 13
409 #define COMMAND_LIST 14
410 #define COMMAND_QUERY_DATETIME 15
411 #define COMMAND_QUERY_EXISTS 16
412 #define COMMAND_QUERY_HANDLE 17
413 #define COMMAND_QUERY_SEEK 18
414 #define COMMAND_QUERY_SIZE 19
415 #define COMMAND_QUERY_STREAMTYPE 20
416 #define COMMAND_QUERY_TIMESTAMP 21
417 #define COMMAND_QUERY_CREATETIME 22
418 #define COMMAND_QUERY_MODIFYTIME 23
419 #define COMMAND_QUERY_ACCESSTIME 24
420 #define COMMAND_QUERY_POSITION 25
421 #define COMMAND_QUERY 26
422 #define COMMAND_QUERY_POSITION_READ 27
423 #define COMMAND_QUERY_POSITION_WRITE 28
424 #define COMMAND_QUERY_POSITION_SYS 29
425 #define COMMAND_QUERY_POSITION_READ_CHAR 30
426 #define COMMAND_QUERY_POSITION_READ_LINE 31
427 #define COMMAND_QUERY_POSITION_WRITE_CHAR 32
428 #define COMMAND_QUERY_POSITION_WRITE_LINE 33
429 #define COMMAND_OPEN 34
430 #define COMMAND_OPEN_READ 35
431 #define COMMAND_OPEN_WRITE 36
432 #define COMMAND_OPEN_BOTH 37
433 #define COMMAND_OPEN_BOTH_APPEND 38
434 #define COMMAND_OPEN_BOTH_REPLACE 39
435 #define COMMAND_OPEN_WRITE_APPEND 40
436 #define COMMAND_OPEN_WRITE_REPLACE 41
437 #define COMMAND_SEEK 42
438 #define COMMAND_POSITION 43
439
440 #define STREAMTYPE_UNKNOWN 0
441 #define STREAMTYPE_PERSISTENT 1
442 #define STREAMTYPE_TRANSIENT 2
443 /*
444 * Define TRUE_TRL_IO, if you want the I/O system to be even more like
445 * TRL. It will try to mimic the behaviour in TRL exactly. Note that if
446 * you _do_ define this, you might experience a degrade in runtime
447 * performance.
448 */
449 #define TRUE_TRL_IO
450
451 /*
452 * There are two ways to report an error for file I/O operations. Either
453 * as an "error" or as a "warning". Both will raise the NOTREADY
454 * condition, but only ERROR will actually put the file into ERROR mode.
455 * Warnings are used for e.g. EOF while reading. Both are implemented
456 * by the same routine.
457 */
458 #define file_error(a,b,c) handle_file_error(TSD,a,b,c,1)
459 #define file_warning(a,b,c) handle_file_error(TSD,a,b,c,0)
460
461 /*
462 * CASE_SENSITIVE_FILENAMES is used to determine if internal file
463 * pointers respect the case of files and treat "ABC" as a different
464 * file to "abc".
465 */
466 #ifdef UNIX
467 # define CASE_SENSITIVE_FILENAMES
468 #endif
469 /*
470 * Regina truncates a file when repositioning by the use of a line
471 * count. That is, if the file has ten lines, and you use the BIF
472 * lineout(file,,4), it will be truncated after the fourth line.
473 * Truncating is not performed for character repositioning.
474 *
475 * If you don't want truncating after line repositioning, undefine
476 * the macro HAVE_FTRUNCATE in config.h. Also, if your system doesn't
477 * have ftruncate(), undefine HAVE_FTRUNCATE, and survive without the
478 * truncating.
479 *
480 * The function ftruncate() is a BSDism; if you have trouble finding
481 * it, try linking with -lbsd or -lucb or something like that. Since
482 * it is not a standard POSIX feature, some machines may generate
483 * warnings during compilation. Let's help these machines ...
484 */
485 #if defined(FIX_PROTOS) && defined(HAVE_FTRUNCATE)
486 # if defined(ultrix)
487 int ftruncate( int fd, int length ) ;
488 # endif
489 #endif
490
491 /*
492 * Since development of Ultrix has ceased, and they never managed to
493 * fix a few things, we want to define a few things, just in order
494 * to kill a few warnings ...
495 */
496 #if defined(FIX_PROTOS) && defined(FIX_ALL_PROTOS) && defined(ultrix)
497 int fstat( int fd, struct stat *buf ) ;
498 int stat( char *path, struct stat *buf ) ;
499 #endif
500
501 #include "regina64.h"
502
503 #if defined(WIN32)
504 /*
505 * Work around for bug in WIN32 stat() function; can't have trailing slash
506 */
507 # if defined(HAVE__STATI64)
rx_w32_stat(const char * path,struct _stati64 * buffer)508 static int rx_w32_stat( const char *path, struct _stati64 *buffer )
509 {
510 int lastpos;
511 int rc;
512 char tmpstring[REXX_PATH_MAX ];
513
514 rc = _stati64( path, buffer );
515 if ( rc != 0 && errno == ENOENT )
516 {
517 if ( path )
518 {
519 lastpos = strlen( path ) - 1;
520 if ( path[lastpos] == '\\' || path[lastpos] == '/')
521 {
522 memcpy( tmpstring, path, lastpos ) ;
523 tmpstring[lastpos] = '\0';
524 rc = _stati64( tmpstring, buffer );
525 }
526 }
527 }
528 return rc;
529 }
530 # else
rx_w32_stat(const char * path,struct _stat * buffer)531 static int rx_w32_stat( const char *path, struct _stat *buffer )
532 {
533 int lastpos;
534 int rc;
535 char tmpstring[REXX_PATH_MAX ];
536
537 rc = stat( path, buffer );
538 if ( rc != 0 && errno == ENOENT )
539 {
540 if ( path )
541 {
542 lastpos = strlen( path ) - 1;
543 if ( path[lastpos] == '\\'|| path[lastpos] == '/')
544 {
545 memcpy( tmpstring, path, lastpos ) ;
546 tmpstring[lastpos+1] = '\0';
547 rc = stat( tmpstring, buffer );
548 }
549 }
550 }
551 return rc;
552 }
553 # endif
554 #endif
555
556 /*
557 * Here comes another 'sunshine-story' ... Since SunOS don't have
558 * a decent set of include-files in the standard version of the OS,
559 * their <stdio.h> don't define these macros. Instead, Sun seems to
560 * survive with the old custom of using the numberic values of these
561 * macros directly. If compiled with "SunKlugdes" defined, try to
562 * fix this.
563 *
564 * If you are using gcc on a Sun, you may want to run the program
565 * fixincludes that comes with gcc. It will fix this more permanently.
566 * At least one recent version of GCC for VMS doesn't have this
567
568 */
569 #if defined(SunKludges) || (defined(__GNUC__) && defined(VMS))
570 # define SEEK_SET 0
571 # define SEEK_CUR 1
572 # define SEEK_END 2
573 #endif
574
575 /*
576 * Some machines don't defined these ... they should!
577 */
578 #if defined(VMS) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || (defined(WIN32) && defined(__BORLANDC__)) || defined(__LCC__)
579 # define F_OK 0
580 # define X_OK 1
581 # define W_OK 2
582 # define R_OK 4
583 #endif
584
585 /*
586 * Here is the datastructure in which to store the information about
587 * open files. The storage format of the file table is discussed
588 * elsewhere. The fields used to handle storing are 'next' and 'prev'
589 * which is used to implement a double linked list of files having
590 * the same hashfunc; and 'newer' and 'older' which are used to maintain
591 * a large double linked list of all files in order of the most
592 * recently used file.
593 *
594 * The other fields are:
595 *
596 * fileptr - Pointer to the filehandle use by the system when
597 * accessing the file through the normal I/O calls.
598 * If this pointer is NULL, it means that the file is
599 * not currently open.
600 * oper - Holds the value that tells whether the most recent
601 * operation on this file was a read or a write. This has
602 * importance for flushing, since a read can't imediately
603 * follow a write (or vice versa) without a flush (or
604 * a repositioning) inbetween. Takes the values OPER_READ,
605 * OPER_WRITE or OPER_NONE (signalizes that most recent
606 * operation can be followed by either read or write).
607 * flag - Bitfield that holds information about the file. The
608 * significance of the various fields are described by
609 * the FLAG_* macros.
610 * error - Most recently 'errno' code for this file. It could have
611 * been stored into 'errmsg' instead, but that would require
612 * copying of data which might not be used. If undefined,
613 * it will have the value 0.
614 * readpos - The current read position in the file, as a character
615 * position. Note that this is in 'C-syntax', i.e. the
616 * first character in the file is numbered "0". A value of
617 * -1 means that the value is unknown or undefined.
618 * readline - The line number of the current read position, which must
619 * be positive if define. A value of zero means that the
620 * line number is undefined or unknown. If current read
621 * position is at an EOL, 'readline' refers to the line
622 * preceding the EOL.
623 * writepos - Similar to 'readpos' but for current write position.
624 * writeline - Similar to 'readline' but for current write position.
625 * filename0 - Pointer to string containing the filename assiciated
626 * with this file. This string is garanteed to have an
627 * ASCII NUL following the last character, so it can be
628 * used directly in file operations. This field *must*
629 * be defined.
630 * errmsg - Error message associated with the file. Some errors are
631 * not trapped during call to system routines, and these
632 * does not have an error message defined by the opsys.
633 * E.g. when positioning current read position after EOF.
634 * This field stores errormessages for these situations.
635 * If undefined, it will be a NULL pointer.
636 *
637 * Both errmsg and error can not be defined simultaneously.
638 */
639
640 typedef struct fileboxtype *fileboxptr ;
641 typedef const struct fileboxtype *cfileboxptr ;
642 typedef struct fileboxtype {
643 FILE *fileptr ;
644 unsigned char oper ;
645 // size_t readpos, writepos, thispos ;
646 rx_64 readpos, writepos, thispos ;
647 rx_64 readline, writeline, linesleft ;
648 int flag, error;
649 fileboxptr prev, next ; /* within a filehash entry */
650 fileboxptr newer, older ;
651 streng *filename0 ;
652 streng *errmsg ;
653 } filebox ;
654
655 /* POSIX denies read and write operations on streams without intermediate
656 * fflush, fseek, fsetpos or rewind (list from EMX). We use the following
657 * macros to switch directly before an I/O operation. "Useful" fseeks should
658 * be error checked. This is not necessary here since the following operation
659 * will fault in case of an error.
660 */
661
662 #define SWITCH_OPER_READ(fptr) {if (fptr->oper==OPER_WRITE) \
663 rx_fseek(fptr->fileptr,0l,SEEK_CUR); \
664 fptr->oper=OPER_READ;}
665 #define SWITCH_OPER_WRITE(fptr) {if (fptr->oper==OPER_READ) \
666 rx_fseek(fptr->fileptr,0l,SEEK_CUR); \
667 fptr->oper=OPER_WRITE;}
668
669 typedef struct
670 { /* fil_tsd: static variables of this module (thread-safe) */
671 /*
672 * The following two pointers are pointers into the doble linked list
673 * of all files in the file table. They points to the most recently
674 * used file, and the least recently used open file. Note that the latter
675 * of these are _not_ the same as the last file in the list. If the
676 * Rexx' file table contains more files than the system's file table
677 * can contain, 'lrufile' will point to the last open file in the double
678 * linked list. Files further out in the list are 'swapped' out.
679 */
680 fileboxptr mrufile;
681
682 fileboxptr stdio_ptr[6];
683 void * rdarea;
684 fileboxptr filehash[131];
685 int rol_size ; /* readoneline() */
686 char * rol_string ; /* readoneline() */
687 int got_eof ; /* readkbdline() */
688 } fil_tsd_t; /* thread-specific but only needed by this module. see
689 * init_filetable
690 */
691 /*
692 * Structure to define stream types; names and whether persistent,transient or unknown
693 */
694 typedef struct
695 {
696 int streamtype;
697 char *streamname;
698 } stream_type_t;
699
700 #define STREAMTYPE_DIRECTORY 1
701 #define STREAMTYPE_CHARACTERSPECIAL 2
702 #define STREAMTYPE_BLOCKSPECIAL 3
703 #define STREAMTYPE_REGULARFILE 4
704 #define STREAMTYPE_FIFO 5
705 #define STREAMTYPE_SYMBOLICLINK 6
706 #define STREAMTYPE_SOCKET 7
707 #define STREAMTYPE_SPECIALNAME 8
708
709 static const stream_type_t stream_types[] =
710 {
711 { STREAMTYPE_UNKNOWN , "" },
712 { STREAMTYPE_UNKNOWN , " Directory" },
713 { STREAMTYPE_PERSISTENT," CharacterSpecial" },
714 { STREAMTYPE_PERSISTENT," BlockSpecial" },
715 { STREAMTYPE_PERSISTENT," RegularFile" },
716 { STREAMTYPE_UNKNOWN , " FIFO" },
717 { STREAMTYPE_UNKNOWN , " SymbolicLink" },
718 { STREAMTYPE_UNKNOWN , " Socket" },
719 { STREAMTYPE_UNKNOWN , " SpecialName" },
720 };
721
722 static rx_64 positioncharfile( tsd_t *TSD, const char *bif, int argno, fileboxptr fileptr, int oper, rx_64 where, int from );
723 static rx_64 positionfile( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, rx_64 lineno, int from );
724 static void handle_file_error( tsd_t *TSD, fileboxptr ptr, int rc, const char *errmsg, int level) ;
725 static int flush_output( tsd_t *TSD, fileboxptr ptr );
726
727 /*
728 * Based on the st_mode filed returned from stat(), determine the Regina "stream type".
729 * The returned value is an index into stream_types array, which allows the caller to
730 * determine if the stream is persistent, transient or unknown, and also allows the
731 * user to look up the stream type name.
732 * Added to resolve 802114.
733 */
determine_stream_type(int mode)734 static int determine_stream_type( int mode )
735 {
736 #ifdef S_ISDIR
737 if ( S_ISDIR(mode) )
738 return STREAMTYPE_DIRECTORY;
739 #endif
740 #ifdef S_ISCHR
741 if ( S_ISCHR(mode) )
742 return STREAMTYPE_CHARACTERSPECIAL;
743 #endif
744 #ifdef S_ISBLK
745 if ( S_ISBLK(mode) )
746 return STREAMTYPE_BLOCKSPECIAL;
747 #endif
748 #ifdef S_ISREG
749 if ( S_ISREG(mode) )
750 return STREAMTYPE_REGULARFILE;
751 #endif
752 #ifdef S_ISFIFO
753 if ( S_ISFIFO(mode) )
754 return STREAMTYPE_FIFO;
755 #endif
756 #ifdef S_ISLNK
757 if ( S_ISLNK(mode) )
758 return STREAMTYPE_SYMBOLICLINK;
759 #endif
760 #ifdef S_ISSOCK
761 if ( S_ISSOCK(mode) )
762 return STREAMTYPE_SOCKET;
763 #endif
764 #ifdef S_ISNAM
765 if ( S_ISNAM(mode) )
766 return STREAMTYPE_SPECIALNAME;
767 #endif
768 return STREAMTYPE_UNKNOWN;
769 }
770
771 /*
772 * Marks all entries in the filetable. Used only by the memory
773 * management. Does not really change anything, so you can in general
774 * forget this one. This routine is called from memory.c in order to
775 * mark all statically defined data in this file.
776 */
777 #ifdef TRACEMEM
mark_filetable(const tsd_t * TSD)778 void mark_filetable( const tsd_t *TSD )
779 {
780 fileboxptr ptr=NULL ;
781 fil_tsd_t *ft;
782
783 ft = (fil_tsd_t *)TSD->fil_tsd;
784 for (ptr=ft->mrufile; ptr; ptr=ptr->older)
785 {
786 markmemory( ptr, TRC_FILEPTR ) ;
787 markmemory( ptr->filename0, TRC_FILEPTR ) ;
788 if (ptr->errmsg)
789 markmemory( ptr->errmsg, TRC_FILEPTR ) ;
790 }
791
792 if (ft->rdarea)
793 markmemory( ft->rdarea, TRC_FILEPTR ) ;
794
795 }
796 #endif /* TRACEMEM */
797
798 #if defined(WIN32) && defined(_MSC_VER)
799 /*
800 * This is a replacement fo the BSD ftruncate() function.
801 * The code in this function was written by Les Moull.
802 */
803
ftruncate(int fd,long pos)804 int ftruncate( int fd, long pos )
805 {
806 HANDLE h = (HANDLE)_get_osfhandle( fd ) ;
807
808 if (SetFilePointer( h, pos, NULL, FILE_BEGIN) == 0xFFFFFFFF)
809 return -1;
810
811 if ( !SetEndOfFile( h ) )
812 return -1;
813
814 return 0;
815 }
816 #endif
817
818 #if defined(__WATCOMC__) && defined(__QNX__)
819 # define ftruncate( fd, pos ) ltrunc( fd, pos, SEEK_SET )
820 #endif
821
822 /*
823 * This command maps the string 'cmd' into a number which is to be
824 * interpreted according to the settings of the COMMAND_ macros.
825 * The input strings must be one of the valid command, or else the
826 * COMMAND_NONE value is returned.
827 *
828 * Well, this routine should really have been implemented differently,
829 * since sequential searching through a list of strings is not very
830 * efficient. But still, it is not so many entries in the list, and
831 * this function is not going to be called often, so I suppose it
832 * doesn't matter too much. Ideallistic, it should be rewritten to
833 * a binary search.
834 */
835
get_command(streng * cmd)836 static char get_command( streng *cmd )
837 {
838 Str_upper(cmd);
839
840 if (cmd->len==4 && !memcmp(cmd->value, "READ", 4))
841 return COMMAND_READ ;
842 if (cmd->len==5 && !memcmp(cmd->value, "WRITE", 5))
843 return COMMAND_WRITE ;
844 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
845 return COMMAND_APPEND ;
846 if (cmd->len==6 && !memcmp(cmd->value, "UPDATE", 6))
847 return COMMAND_UPDATE ;
848 if (cmd->len==6 && !memcmp(cmd->value, "CREATE", 6))
849 return COMMAND_CREATE ;
850 if (cmd->len==5 && !memcmp(cmd->value, "CLOSE", 5))
851 return COMMAND_CLOSE ;
852 if (cmd->len==5 && !memcmp(cmd->value, "FLUSH", 5))
853 return COMMAND_FLUSH ;
854 if (cmd->len==6 && !memcmp(cmd->value, "STATUS", 6))
855 return COMMAND_STATUS ;
856 if (cmd->len==5 && !memcmp(cmd->value, "FSTAT", 5))
857 return COMMAND_FSTAT ;
858 if (cmd->len==5 && !memcmp(cmd->value, "RESET", 5))
859 return COMMAND_RESET ;
860 if (cmd->len==8 && !memcmp(cmd->value, "READABLE", 8))
861 return COMMAND_READABLE ;
862 if (cmd->len==8 && !memcmp(cmd->value, "WRITABLE", 8))
863 return COMMAND_WRITEABLE ;
864 if (cmd->len==10 && !memcmp(cmd->value, "EXECUTABLE", 10))
865 return COMMAND_EXECUTABLE ;
866 if (cmd->len==4 && !memcmp(cmd->value, "LIST", 4))
867 return COMMAND_LIST ;
868 if (cmd->len>=4 && !memcmp(cmd->value, "OPEN", 4))
869 return COMMAND_OPEN ;
870 if (cmd->len>=5 && !memcmp(cmd->value, "QUERY", 5))
871 return COMMAND_QUERY ;
872 if (cmd->len>=4 && !memcmp(cmd->value, "SEEK", 4))
873 return COMMAND_SEEK ;
874 if (cmd->len>=8 && !memcmp(cmd->value, "POSITION", 8))
875 return COMMAND_POSITION ;
876 return COMMAND_NONE ;
877 }
878
get_querycommand(const streng * cmd)879 static char get_querycommand( const streng *cmd )
880 {
881 if (cmd->len==8 && !memcmp(cmd->value, "DATETIME", 8))
882 return COMMAND_QUERY_DATETIME ;
883 if (cmd->len==6 && !memcmp(cmd->value, "EXISTS", 6))
884 return COMMAND_QUERY_EXISTS ;
885 if (cmd->len==6 && !memcmp(cmd->value, "HANDLE", 6))
886 return COMMAND_QUERY_HANDLE ;
887 if (cmd->len>=4 && !memcmp(cmd->value, "SEEK", 4))
888 return COMMAND_QUERY_SEEK ;
889 if (cmd->len>=8 && !memcmp(cmd->value, "POSITION", 8))
890 return COMMAND_QUERY_POSITION ;
891 if (cmd->len==4 && !memcmp(cmd->value, "SIZE", 4))
892 return COMMAND_QUERY_SIZE ;
893 if (cmd->len==10 && !memcmp(cmd->value, "STREAMTYPE", 10))
894 return COMMAND_QUERY_STREAMTYPE ;
895 if (cmd->len==9 && !memcmp(cmd->value, "TIMESTAMP", 9))
896 return COMMAND_QUERY_TIMESTAMP ;
897 if (cmd->len==10 && !memcmp(cmd->value, "CREATETIME", 10))
898 return COMMAND_QUERY_CREATETIME;
899 if (cmd->len==10 && !memcmp(cmd->value, "MODIFYTIME", 10))
900 return COMMAND_QUERY_MODIFYTIME;
901 if (cmd->len==10 && !memcmp(cmd->value, "ACCESSTIME", 10))
902 return COMMAND_QUERY_ACCESSTIME;
903 return COMMAND_NONE ;
904 }
905
get_querypositioncommand(const streng * cmd)906 static char get_querypositioncommand( const streng *cmd )
907 {
908 if (cmd->len>=4 && !memcmp(cmd->value, "READ", 4))
909 return COMMAND_QUERY_POSITION_READ ;
910 if (cmd->len>=5 && !memcmp(cmd->value, "WRITE", 5))
911 return COMMAND_QUERY_POSITION_WRITE ;
912 if (cmd->len==3 && !memcmp(cmd->value, "SYS", 3))
913 return COMMAND_QUERY_POSITION_SYS ;
914 return COMMAND_NONE ;
915 }
916
get_querypositionreadcommand(const streng * cmd)917 static char get_querypositionreadcommand( const streng *cmd )
918 {
919 if (cmd->len==4 && !memcmp(cmd->value, "CHAR", 4))
920 return COMMAND_QUERY_POSITION_READ_CHAR ;
921 if (cmd->len==4 && !memcmp(cmd->value, "LINE", 4))
922 return COMMAND_QUERY_POSITION_READ_LINE ;
923 if (cmd->len==0)
924 return COMMAND_QUERY_POSITION_READ_CHAR ;
925 return COMMAND_NONE ;
926 }
927
get_querypositionwritecommand(const streng * cmd)928 static char get_querypositionwritecommand( const streng *cmd )
929 {
930 if (cmd->len==4 && !memcmp(cmd->value, "CHAR", 4))
931 return COMMAND_QUERY_POSITION_WRITE_CHAR ;
932 if (cmd->len==4 && !memcmp(cmd->value, "LINE", 4))
933 return COMMAND_QUERY_POSITION_WRITE_LINE ;
934 if (cmd->len==0)
935 return COMMAND_QUERY_POSITION_WRITE_CHAR ;
936 return COMMAND_NONE ;
937 }
938
get_opencommand(const streng * cmd)939 static char get_opencommand( const streng *cmd )
940 {
941 if (cmd->len>=4 && !memcmp(cmd->value, "BOTH", 4))
942 return COMMAND_OPEN_BOTH ;
943 if (cmd->len==4 && !memcmp(cmd->value, "READ", 4))
944 return COMMAND_OPEN_READ ;
945 if (cmd->len>=5 && !memcmp(cmd->value, "WRITE", 5))
946 return COMMAND_OPEN_WRITE ;
947 if (cmd->len==0)
948 return COMMAND_OPEN_BOTH ;
949 return COMMAND_NONE ;
950 }
951
get_opencommandboth(const streng * cmd)952 static char get_opencommandboth( const streng *cmd )
953 {
954 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
955 return COMMAND_OPEN_BOTH_APPEND ;
956 if (cmd->len==7 && !memcmp(cmd->value, "REPLACE", 7))
957 return COMMAND_OPEN_BOTH_REPLACE ;
958 if (cmd->len==0)
959 return COMMAND_OPEN_BOTH ;
960 return COMMAND_NONE ;
961 }
962
get_opencommandwrite(const streng * cmd)963 static char get_opencommandwrite( const streng *cmd )
964 {
965 if (cmd->len==6 && !memcmp(cmd->value, "APPEND", 6))
966 return COMMAND_OPEN_WRITE_APPEND ;
967 if (cmd->len==7 && !memcmp(cmd->value, "REPLACE", 7))
968 return COMMAND_OPEN_WRITE_REPLACE ;
969 if (cmd->len==0)
970 return COMMAND_OPEN_WRITE ;
971 return COMMAND_NONE ;
972 }
973
974
975 /* ==================================================================== */
976 /* level 3 routines */
977
978 /*
979 * Returns the fileboxptr of a file, if is has already been opened.
980 * If it does not exist in Rexx's file table, a NULL pointer is
981 * returned in stead. It is easy to change the datastruction in
982 * which the file table is stored.
983 *
984 * If using VMS, or another opsys that has a caseinsensitive file
985 * system, maybe it should disregard the case of the filename. In
986 * general, maybe it should 'normalize' the file name before storing
987 * it in the file table (do we sence an upcoming namei() :-)
988 */
989
990 #define FILEHASH_SIZE (sizeof(((fil_tsd_t*)0)->filehash) / \
991 sizeof(((fil_tsd_t*)0)->filehash[0]))
992
993 #ifdef CASE_SENSITIVE_FILENAMES
994 #define filehashvalue(strng) (hashvalue(strng->value, strng->len) % FILEHASH_SIZE)
995 #else
996 #define filehashvalue(strng) (hashvalue_ic(strng->value, strng->len) % FILEHASH_SIZE)
997 #endif
998
removefileptr(const tsd_t * TSD,cfileboxptr ptr)999 static void removefileptr( const tsd_t *TSD, cfileboxptr ptr )
1000 {
1001 fil_tsd_t *ft;
1002
1003 ft = (fil_tsd_t *)TSD->fil_tsd;
1004
1005 if (ft->mrufile==ptr)
1006 ft->mrufile = ptr->older ;
1007
1008 if (ptr->older)
1009 ptr->older->newer = ptr->newer ;
1010
1011 if (ptr->newer)
1012 ptr->newer->older = ptr->older ;
1013
1014 if (ptr->next)
1015 ptr->next->prev = ptr->prev ;
1016
1017 if (ptr->prev)
1018 ptr->prev->next = ptr->next ;
1019 else
1020 ft->filehash[filehashvalue(ptr->filename0)] = ptr->next ;
1021 }
1022
1023 /* enterfileptr initializes a fileboxptr. It must be allocated and the
1024 * following fields must already been set:
1025 * errmsg, error, fileptr, flag, filename0
1026 */
enterfileptr(const tsd_t * TSD,fileboxptr ptr)1027 static void enterfileptr( const tsd_t *TSD, fileboxptr ptr )
1028 {
1029 int hashval=0 ;
1030 fil_tsd_t *ft;
1031
1032 ft = (fil_tsd_t *)TSD->fil_tsd;
1033
1034 /*
1035 * First, get the magic number for this file. Note that when we're
1036 * doing hashing like this, we *may* get trouble on machines that
1037 * don't differ between upper and lower case letters in filenames.
1038 */
1039 hashval = filehashvalue(ptr->filename0) ;
1040 /*
1041 * Then, link it into the list of values having the same hashvalue
1042 */
1043 ptr->next = ft->filehash[hashval] ;
1044 if (ptr->next)
1045 ptr->next->prev = ptr ;
1046 ft->filehash[hashval] = ptr ;
1047 ptr->prev = NULL ;
1048
1049 /*
1050 * Then, link it into the 'global' list of files, sorted by how
1051 * recently they have been used.
1052 */
1053 ptr->older = ft->mrufile ;
1054 if (ptr->older)
1055 ptr->older->newer = ptr ;
1056 ptr->newer = NULL ;
1057 ft->mrufile = ptr ;
1058
1059 ptr->readline = 0 ;
1060 ptr->linesleft = 0 ;
1061 ptr->writeline = 0 ;
1062 ptr->thispos = (size_t) EOF ;
1063 ptr->readpos = (size_t) EOF ;
1064 ptr->writepos = (size_t) EOF ;
1065 ptr->oper = OPER_NONE;
1066 }
1067
1068 /* swapout_file swaps out one closeable file. The state persists. Use
1069 * swapin_file to reuse it.
1070 * The given fileboxptr MUST NOT be swapped out. It indicates a file which
1071 * should be swapped in after this operation. dont_swap may be NULL.
1072 */
swapout_file(tsd_t * TSD,fileboxptr dont_swap)1073 static void swapout_file( tsd_t *TSD, fileboxptr dont_swap )
1074 {
1075 fil_tsd_t *ft;
1076 fileboxptr start, run, found;
1077
1078 ft = (fil_tsd_t *)TSD->fil_tsd;
1079 /*
1080 * Too many open files simultaneously, we have to close one down
1081 * in order to free one file descriptor, but only if there actually
1082 * are some files that can be closed down.
1083 */
1084 found = NULL;
1085
1086 if ( ( start = dont_swap ) == NULL ) /* any start point is better than */
1087 start = ft->mrufile ; /* mru head. We need the opposite */
1088
1089 /* first try finding an older file */
1090 for ( run = start ; run ; run = run->older )
1091 {
1092 if ( ( (run->flag & ( FLAG_SURVIVOR | FLAG_SWAPPED ) ) == 0 ) &&
1093 ( run->fileptr != NULL ) &&
1094 ( run != dont_swap ) )
1095 found = run ; /* continue looking for an older file */
1096 }
1097
1098 /* if !found, try finding a more recent swapable file */
1099 if ( found == NULL )
1100 {
1101 for ( run = start ; run ; run = run->newer )
1102 {
1103 if ( ( (run->flag & ( FLAG_SURVIVOR | FLAG_SWAPPED ) ) == 0 ) &&
1104 ( run->fileptr != NULL ) &&
1105 ( run != dont_swap ) )
1106 {
1107 found = run ; /* least newer swapable file */
1108 break;
1109 }
1110 }
1111 }
1112 if ( found == NULL )
1113 exiterror( ERR_SYSTEM_FAILURE, 0 ) ;
1114
1115 flush_output( TSD, found );
1116 }
1117
1118
1119 /* swapout_all swaps out all closeable files. The states persist. Use
1120 * swapin_file to reuse them.
1121 * This function is useful when exiting the external's interface.
1122 */
swapout_all(tsd_t * TSD)1123 static void swapout_all( tsd_t *TSD )
1124 {
1125 fil_tsd_t *ft;
1126 fileboxptr run;
1127
1128 ft = (fil_tsd_t *)TSD->fil_tsd;
1129
1130 for ( run = ft->mrufile ; run ; run = run->older )
1131 {
1132 flush_output( TSD, run );
1133 }
1134 }
1135
1136 #ifdef VMS
1137 static const char *acc_mode[] = { "r", "r+", "a" } ;
1138 #else
1139 static const char *acc_mode[] = { "rb", "r+b", "ab" } ;
1140 #endif
1141
1142 #define ACCMODE_READ 0
1143 #define ACCMODE_RDWRT 1
1144 #define ACCMODE_WRITE 2
1145 #define ACCMODE_NONE 3
1146
swapin_file(tsd_t * TSD,fileboxptr ptr)1147 static void swapin_file( tsd_t *TSD, fileboxptr ptr )
1148 {
1149 int faccess=0, itmp=0 ;
1150
1151 /*
1152 * First, just try to reopen the file, we _might_ have a vacant
1153 * entry in the system file table, so, use that.
1154 */
1155 itmp = (ptr->flag & (FLAG_READ | FLAG_WRITE)) ;
1156 if (itmp==(FLAG_READ | FLAG_WRITE))
1157 faccess = ACCMODE_RDWRT ;
1158 else if (itmp==(FLAG_READ))
1159 faccess = ACCMODE_READ ;
1160 else if (itmp==(FLAG_WRITE))
1161 faccess = ACCMODE_WRITE ;
1162 else
1163 faccess = ACCMODE_NONE ;
1164
1165 if (faccess == ACCMODE_NONE)
1166 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
1167
1168 tryagain:
1169 #ifdef SGIkludges
1170 errno = EMFILE ;
1171 #else
1172 errno = 0 ;
1173 #endif
1174 ptr->fileptr = fopen( ptr->filename0->value, acc_mode[faccess] ) ;
1175 if ((!ptr->fileptr) && (errno == EMFILE))
1176 {
1177 swapout_file( TSD, ptr ) ;
1178 goto tryagain ;
1179 }
1180
1181 ptr->flag &= ~(FLAG_SWAPPED) ;
1182 if (ptr->fileptr==NULL)
1183 file_error( ptr, errno, NULL ) ;
1184 else
1185 {
1186 if ( ptr->thispos == (size_t) EOF )
1187 rx_fseek( ptr->fileptr, 0, SEEK_SET ) ;
1188 else
1189 rx_fseek( ptr->fileptr, ptr->thispos, SEEK_SET ) ;
1190 /*
1191 * If the swapped-in file was already after EOF; ie
1192 * ptr->flag contained FLAG_RDEOF, then force eof()
1193 * to return true.
1194 */
1195 if ( ptr->flag & FLAG_RDEOF )
1196 {
1197 rx_fseek( ptr->fileptr, 0, SEEK_END );
1198 fgetc( ptr->fileptr );
1199 }
1200 }
1201 }
1202
1203 /*
1204 * Compares two filesnames, either with ignoring or respecting the letter case.
1205 */
filename_cmp(const streng * name1,const streng * name2)1206 int filename_cmp( const streng *name1, const streng *name2 )
1207 {
1208 #ifdef CASE_SENSITIVE_FILENAMES
1209 return Str_cmp( name1, name2 );
1210 #else
1211 return Str_ccmp( name1, name2 );
1212 #endif
1213 }
1214
getfileptr(tsd_t * TSD,const streng * name)1215 static fileboxptr getfileptr( tsd_t *TSD, const streng *name )
1216 {
1217 int hashval ;
1218 fileboxptr ptr ;
1219 fil_tsd_t *ft;
1220
1221 ft = (fil_tsd_t *)TSD->fil_tsd;
1222
1223 hashval = filehashvalue( name ) ;
1224 /*
1225 * First, try to find the correct file in this slot of the
1226 * hash table. If one is found, ptr points to it, else, ptr is set
1227 * to NULL
1228 */
1229 for (ptr=ft->filehash[hashval];ptr;ptr=ptr->next)
1230 {
1231 if ( !filename_cmp( name, ptr->filename0 ) )
1232 break ;
1233 }
1234 /*
1235 * In order not to create any problems later, just return with NULL
1236 * (signifying that no file was found) if that is the case. Then we may
1237 * able to assume that ptr _is_ set later.
1238 */
1239 if (!ptr)
1240 return NULL ;
1241
1242 /*
1243 * Now, put the file in front of the list of files stored by how
1244 * recently they were used. We assume that any access to a file is
1245 * equivalent to the file being used.
1246 */
1247 if ( ptr != ft->mrufile )
1248 {
1249 if ( ptr->newer )
1250 {
1251 ptr->newer->older = ptr->older ;
1252 }
1253 if ( ptr->older )
1254 {
1255 ptr->older->newer = ptr->newer ;
1256 }
1257
1258 ptr->older = ft->mrufile ;
1259 ptr->newer = NULL ;
1260 ft->mrufile->newer = ptr ;
1261 ft->mrufile = ptr ;
1262 }
1263 if ( ptr != ft->filehash[ hashval ] )
1264 {
1265 /* Why the hell do we use ft->mrufile? Is it useful for anything?
1266 * The following code speeds up getfileptr much more.
1267 * If the current file pointer (ptr) is not the first file pointer
1268 * in the list for this hash value; reposition it so that it is
1269 * at the front of the list, and move the current file pointer
1270 * that is at the front of the list after ptr.
1271 * Fixes bug 594553
1272 */
1273 if ( ptr->next )
1274 ptr->next->prev = ptr->prev ;
1275 if ( ptr->prev )
1276 ptr->prev->next = ptr->next ;
1277
1278 ptr->prev = NULL;
1279 ptr->next = ft->filehash[ hashval ];
1280 ft->filehash[ hashval ]->prev = ptr;
1281 ft->filehash[ hashval ] = ptr ;
1282 }
1283
1284 /*
1285 * If this file has been swapped out, we have to reopen it, so we can
1286 * continue to access it.
1287 */
1288 if (ptr->flag & FLAG_SWAPPED)
1289 swapin_file( TSD, ptr ) ;
1290
1291 return ptr ;
1292 }
1293
1294
flush_input(cfileboxptr dummy)1295 static void flush_input( cfileboxptr dummy )
1296 {
1297 dummy = dummy; /* keep compiler happy */
1298 return ;
1299 }
1300
1301
1302 /*
1303 * flush_output does close the file. The purpose of this function is to free
1304 * up space for opening another file while maintaining all state information about
1305 * this file if/when it is needed again.
1306 * Returns -1 in case of an error, 0 on success.
1307 */
flush_output(tsd_t * TSD,fileboxptr ptr)1308 static int flush_output( tsd_t *TSD, fileboxptr ptr )
1309 {
1310 int h;
1311
1312 errno = 0;
1313
1314 if ( ptr->fileptr == NULL || ptr->flag & FLAG_SWAPPED )
1315 return 0;
1316
1317 if ( ptr->flag & FLAG_SURVIVOR )
1318 {
1319 if ( ptr->flag & FLAG_WRITE )
1320 {
1321 if ( fflush( ptr->fileptr ) != 0 )
1322 {
1323 file_error( ptr, errno, NULL );
1324 return -1;
1325 }
1326 }
1327 return 0;
1328 }
1329
1330 if ( fflush( ptr->fileptr ) != 0 )
1331 {
1332 h = errno;
1333 fclose( ptr->fileptr );
1334 ptr->fileptr = NULL;
1335 ptr->flag |= FLAG_SWAPPED;
1336 file_error( ptr, h, NULL );
1337 return -1;
1338 }
1339 if ( fclose( ptr->fileptr ) == EOF )
1340 {
1341 h = errno;
1342 ptr->fileptr = NULL;
1343 ptr->flag |= FLAG_SWAPPED;
1344 file_error( ptr, h, NULL );
1345 return -1;
1346 }
1347
1348 ptr->fileptr = NULL;
1349 ptr->flag |= FLAG_SWAPPED;
1350 return 0;
1351 }
1352
1353 /*
1354 * Sets up the internal filetable for REXX, and initializes it with
1355 * the three standard files under Unix, stderr, stdout og and stdin.
1356 * Should only be called once, from the main routine. We should also
1357 * add code to register the routine for marking memory from this
1358 * routine.
1359 *
1360 * As a shortcut to access these three default files, there is a
1361 * variable 'stdio_ptr' which contains pointers to them. This allows
1362 * for quick access to the default streams.
1363 * The function returns 1 on success, 0 if memory is short.
1364 *
1365 * IMPORTANT!
1366 * The entry for stdin must be the same as the following #define for
1367 * DEFAULT_STDIN_INDEX below. Never changes it.
1368 * This assumption is used in readkbdline().
1369 */
1370 #define DEFAULT_STDIN_INDEX 0
1371 #define DEFAULT_STDOUT_INDEX 1
1372 #define DEFAULT_STDERR_INDEX 2
init_filetable(tsd_t * TSD)1373 int init_filetable( tsd_t *TSD )
1374 {
1375 int i=0 ;
1376 fil_tsd_t *ft;
1377
1378 if (TSD->fil_tsd != NULL)
1379 return(1);
1380
1381 if ( ( TSD->fil_tsd = MallocTSD( sizeof(fil_tsd_t) ) ) == NULL )
1382 return(0);
1383 ft = (fil_tsd_t *)TSD->fil_tsd;
1384 memset( ft, 0, sizeof(fil_tsd_t) );
1385
1386 for ( i = 0; i < 6; i++ )
1387 {
1388 ft->stdio_ptr[i] = (fileboxptr)MallocTSD( sizeof( filebox )) ;
1389 ft->stdio_ptr[i]->errmsg = NULL ;
1390 ft->stdio_ptr[i]->error = 0 ;
1391 }
1392
1393 ft->stdio_ptr[0]->fileptr = ft->stdio_ptr[3]->fileptr = stdin ;
1394 ft->stdio_ptr[1]->fileptr = ft->stdio_ptr[4]->fileptr = stdout ;
1395 ft->stdio_ptr[2]->fileptr = ft->stdio_ptr[5]->fileptr = stderr ;
1396
1397 ft->stdio_ptr[0]->flag = ft->stdio_ptr[3]->flag = ( FLAG_SURVIVOR + FLAG_READ ) ;
1398 ft->stdio_ptr[1]->flag = ft->stdio_ptr[4]->flag = ( FLAG_SURVIVOR + FLAG_WRITE ) ;
1399 ft->stdio_ptr[2]->flag = ft->stdio_ptr[5]->flag = ( FLAG_SURVIVOR + FLAG_WRITE ) ;
1400
1401 ft->stdio_ptr[0]->filename0 = Str_crestrTSD( "<stdin>" ) ;
1402 ft->stdio_ptr[1]->filename0 = Str_crestrTSD( "<stdout>" ) ;
1403 ft->stdio_ptr[2]->filename0 = Str_crestrTSD( "<stderr>" ) ;
1404 ft->stdio_ptr[3]->filename0 = Str_crestrTSD( "stdin" ) ;
1405 ft->stdio_ptr[4]->filename0 = Str_crestrTSD( "stdout" ) ;
1406 ft->stdio_ptr[5]->filename0 = Str_crestrTSD( "stderr" ) ;
1407
1408 for (i=0; i<6; i++)
1409 enterfileptr( TSD, ft->stdio_ptr[i] ) ;
1410
1411 return(1);
1412 }
1413
purge_filetable(tsd_t * TSD)1414 void purge_filetable( tsd_t *TSD )
1415 {
1416 fileboxptr ptr1, ptr2, save_ptr1, save_ptr2 ;
1417 int i;
1418 fil_tsd_t *ft;
1419
1420 ft = (fil_tsd_t *)TSD->fil_tsd;
1421 /* Naming this the "removal loop". */
1422 for ( ptr1=ft->mrufile; ptr1; )
1423 {
1424 save_ptr1 = ptr1->older ;
1425 for ( ptr2=ptr1; ptr2; )
1426 {
1427 save_ptr2 = ptr2->next ; /* this was moved from third parm of loop
1428 so that it did not address the free'd
1429 memory. See if statement below. */
1430 /*
1431 * If this is one of the default streams, don't let it be closed.
1432 * These file shall stay open, whatever happens.
1433 */
1434 /*
1435 * JH 19991105 if was modified to include the next 5 statements. Originally,
1436 * the file was not closed, but all other references to it were deleted. In
1437 * situations where one *.exe invokes Rexx mutiple times, subsequent calls to
1438 * the standard streams caused an error. (getfileptr() failed, the file name
1439 * for stdio_ptr[?] comes up blank.)
1440 */
1441 if (!(ptr2->flag & FLAG_SURVIVOR)
1442 && ptr2->fileptr)
1443 {
1444 fclose( ptr2->fileptr ) ;
1445
1446 removefileptr( TSD, ptr2 ) ;
1447
1448 if (ptr2->errmsg)
1449 Free_stringTSD( ptr2->errmsg ) ;
1450
1451 Free_stringTSD( ptr2->filename0 ) ;
1452 FreeTSD( ptr2 ) ;
1453 }
1454 ptr2 = save_ptr2 ;
1455 }
1456 ptr1 = save_ptr1 ;
1457 }
1458
1459 ft->mrufile = NULL;
1460
1461 /*
1462 * Now lets be absolutely paranoid, and remove all entries from the
1463 * filehash table...
1464 */
1465 memset( ft->filehash, 0, sizeof(ft->filehash) );
1466 /*
1467 * JH 19991105 The following loop was added to re-instate the std streams into the
1468 * hash table. It seems easier to do this then to muck around with reseting the pointers
1469 * as the fileboxptr's are deleted. Cannot modify the loop above to look at filenames
1470 * before removing from filehas table, it might be pointing to a fileboxptr that got removed
1471 * by the "removal loop".
1472 */
1473 for (i=0; i<6; i++)
1474 {
1475 enterfileptr( TSD, ft->stdio_ptr[i] ) ;
1476 }
1477
1478 #if 0
1479 // can't free this as the next call to RexxStart() expects this memory to be here
1480 if ( TSD->fil_tsd )
1481 {
1482 FreeTSD( TSD->fil_tsd );
1483 TSD->fil_tsd = NULL;
1484 }
1485 #endif
1486 }
1487
1488 /*
1489 * checkProperStreamName raises 40.27 if errno describes an error leading
1490 * to the assumption that the filename was malformed according to ANSI 9.2.1.
1491 */
checkProperStreamName(tsd_t * TSD,streng * kill,const char * fn,int eno)1492 static void checkProperStreamName( tsd_t *TSD, streng *kill, const char *fn,
1493 int eno )
1494 {
1495 static const int bad[] = {
1496 #if defined(ENAMETOOLONG)
1497 ENAMETOOLONG,
1498 #endif
1499 0
1500 };
1501
1502 int i;
1503
1504 for ( i = 0; bad[i] != 0; i++ )
1505 {
1506 if ( eno == bad[i] )
1507 {
1508 /*
1509 * ANSI 9.2.1 wants us to raise 40.27 if stream is malformed.
1510 * Feel free to provide more errno values describing this situation.
1511 */
1512 if ( kill )
1513 Free_stringTSD( kill ) ;
1514 exiterror( ERR_INCORRECT_CALL, 27, BIFname( TSD ), fn );
1515 }
1516 }
1517 }
1518
1519 /*
1520 * Sets the proper error conditions for the file, including providing a
1521 * a hook into the CALL/SIGNAL ON system. Now, we also like to set some
1522 * other information, like the status of the file (taken from rc).
1523 *
1524 * First parameter is the file to operate on, the second and third
1525 * parameters are the error message to set (they can't both be defined),
1526 * and the last parameter is the level of 'severity'. If set, the file
1527 * is thrown into error state.
1528 */
handle_file_error(tsd_t * TSD,fileboxptr ptr,int rc,const char * errmsg,int level)1529 static void handle_file_error( tsd_t *TSD, fileboxptr ptr, int rc, const char *errmsg, int level)
1530 {
1531 trap *traps=NULL ;
1532
1533 assert( !(rc && errmsg) ) ;
1534
1535 if ((ptr->flag & FLAG_ERROR) && (ptr->flag & FLAG_FAKE))
1536 {
1537 /*
1538 * If we are faking for this file already, don't bother to do anything
1539 * more. In particular, we do not want to set a new error, since that
1540 * will in general only overwrite the old (and probably more relevant)
1541 * error message. However, faking are _only_ done when NOTREADY is
1542 * being trapped.
1543 */
1544 return ;
1545 }
1546 else
1547 {
1548 /*
1549 * If the file is not already in error, set the ERROR flag, and record
1550 * the error message. Also, clear the FAKE flag. This flag is only
1551 * defined when the ERROR flag is set, and we don't want any old
1552 * values laying around (it will be set later if needed).
1553 */
1554 if (level)
1555 {
1556 ptr->flag &= ~FLAG_FAKE ;
1557 ptr->flag |= FLAG_ERROR ;
1558 }
1559 else if (ptr->flag & FLAG_RDEOF)
1560 {
1561 /*
1562 * If the file was in RDEOF state; ie EOF was read on the file
1563 * set the AFTER_RDEOF flag to ensure STREAM(stream,'S') works
1564 * like other interpreters.
1565 */
1566 ptr->flag |= FLAG_AFTER_RDEOF;
1567 }
1568
1569 checkProperStreamName( TSD, ptr->errmsg, Str_val( ptr->filename0 ), rc );
1570
1571 /*
1572 * Set the error message, but only if one was given. This routine
1573 * can be called _without_ any errormessage, and if so, keep the
1574 * old one (if any)
1575 */
1576 if (rc || errmsg)
1577 {
1578 if (ptr->errmsg)
1579 Free_stringTSD( ptr->errmsg ) ;
1580
1581 ptr->error = rc ;
1582 if ( errmsg )
1583 ptr->errmsg = Str_creTSD( errmsg ) ;
1584 else
1585 #ifdef WIN32
1586 {
1587 /*
1588 * For Win32 always get the last error, and store that;
1589 * it seems to be more meaningful than strerror( errno )
1590 * Address bug ???? FIXME
1591 */
1592 CHAR LastError[256];
1593 ULONG last_error = GetLastError();
1594 if ( last_error )
1595 {
1596 FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), LastError, 256, NULL ) ;
1597 ptr->errmsg = Str_creTSD( LastError ) ;
1598 }
1599 else
1600 ptr->errmsg = NULL;
1601 }
1602 #else
1603 ptr->errmsg = NULL;
1604 #endif
1605 }
1606
1607 /*
1608 * OK, the file has been put into ERROR state, now we must check
1609 * to see if we should raise the NOTREADY condition. If NOTREADY
1610 * is not currently enabled, don't bother to try to raise it.
1611 */
1612 traps = gettraps( TSD, TSD->currlevel ) ;
1613 if (traps[SIGNAL_NOTREADY].on_off)
1614 {
1615 /*
1616 * The NOTREADY condition is being trapped; set the FAKE flag
1617 * so that we don't create more errors for this file. But _only_
1618 * set the FAKE flag if NOTREADY is trapped by method CALL.
1619 * Then raise the condition ...
1620 */
1621 if (!traps[SIGNAL_NOTREADY].invoked)
1622 ptr->flag |= FLAG_FAKE ;
1623
1624 condition_hook(TSD,SIGNAL_NOTREADY,rc+100,0,-1,Str_dupTSD(ptr->filename0),NULL);
1625 }
1626 }
1627 }
1628
1629
1630
1631 /*
1632 * This routine is supposed to be called when the condition is triggered
1633 * by method CALL. From the time the condition is raised until the CALL is
1634 * is triggered, I/O to the file is faked. But before the condition handler
1635 * is called, we try to tidy things up a bit.
1636 *
1637 * At least, we have to clear the FAKE flag. Other 'nice' things to do
1638 * is to clear error indicator in the file pointer, and to reset the
1639 * file in general. The ERROR state is not cleared, _unless_ the file
1640 * is one of the default streams.
1641 */
1642
fixup_file(tsd_t * TSD,const streng * filename)1643 void fixup_file( tsd_t *TSD, const streng *filename )
1644 {
1645 fileboxptr ptr=NULL ;
1646
1647 if ( filename )
1648 {
1649 /*
1650 * filename will be NULL when condition_hook() called with a NULL description
1651 * argument. This happens when NOTREADY occurs when pulling from an external
1652 * queue with a timeout and the timeout expires.
1653 */
1654 ptr = getfileptr( TSD, filename ) ;
1655 if (ptr)
1656 {
1657 /*
1658 * If the file is open, try to clear it, first clear the error
1659 * indicator, and then try to fseek() to a 'safe' point. If the
1660 * seeking didn't work out, don't bother, it was worth a try.
1661 */
1662 if (ptr->fileptr)
1663 {
1664 clearerr( ptr->fileptr ) ;
1665 if ( ptr->flag & FLAG_PERSIST )
1666 rx_fseek( ptr->fileptr, 0, SEEK_SET ) ;
1667 ptr->thispos = 0 ;
1668 ptr->oper = OPER_NONE ;
1669 }
1670
1671 if (ptr->flag & FLAG_SURVIVOR)
1672 {
1673 ptr->flag &= ~(FLAG_ERROR) ;
1674 /*
1675 * MHES Added following 4 flag resets - 30-11-2004
1676 */
1677 ptr->flag &= ~(FLAG_RDEOF) ;
1678 ptr->flag &= ~(FLAG_WREOF) ;
1679 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
1680 }
1681
1682 ptr->flag &= ~(FLAG_FAKE) ;
1683 }
1684 }
1685 }
1686
1687
1688
1689
1690 /*
1691 * This is stupid ... if the file exists, but is in error mode, we
1692 * shall not close it, but leave it open, so that the rest of the
1693 * operations on this file in this statement don't trip. Same happens
1694 * if we are not able to close it properly. Oh well ...
1695 *
1696 * On second thoughts ... Faking only applies for input and output.
1697 * So closing doesn't have to be faked. Remove the file, whatever
1698 * happens.
1699 */
closefile(tsd_t * TSD,const streng * name)1700 void closefile( tsd_t *TSD, const streng *name )
1701 {
1702 fileboxptr ptr=NULL ;
1703
1704 /* If it isn't open, don't try to close it ... */
1705 ptr = getfileptr( TSD, name ) ;
1706 if (ptr)
1707 {
1708 /*
1709 * If this is one of the default streams, don't let it be closed.
1710 * These file shall stay open, whatever happens.
1711 */
1712 if (ptr->flag & FLAG_SURVIVOR)
1713 return ;
1714
1715 /*
1716 * If the fileptr seems to point to something ... close it. We
1717 * really don't want to leak file table slots. Actually, we should
1718 * check that the close was ok, and not let the fileptr go unless
1719 * we know that it was really closed (and released for new use).
1720 * Previously, it only closed when file was not in error. I don't
1721 * know what is the correct action, but this seems to be the most
1722 * sensible ...
1723 */
1724 if (ptr->fileptr)
1725 fclose( ptr->fileptr ) ;
1726
1727 removefileptr( TSD, ptr ) ;
1728
1729 if (ptr->errmsg)
1730 Free_stringTSD( ptr->errmsg ) ;
1731
1732 Free_stringTSD( ptr->filename0 ) ;
1733 FreeTSD( ptr ) ;
1734 }
1735 }
1736
1737
1738
1739
1740 /*
1741 * This function is called when we need some kind of access to a file
1742 * but don't (yet) have it. It will only be called when we want to
1743 * open a file implicitly, e.g. it is open for reading, and it has then
1744 * been named in a output function.
1745 *
1746 * This is rather primitive ... but this function can only be called
1747 * when the file is open for read, and we want to open it for write;
1748 * or if the file i open for write, and we want to open it for read.
1749 * So I think this will suffice. It ignores the 'access' parameter
1750 * And just assumes that the file must be opened in both read and
1751 * write.
1752 *
1753 * To improve on this function, we ought to do a lot more checking,
1754 * e.g. that the 'access' wanted are required, and that the file is
1755 * already open in some kind of mode. If this don't hold, we probably
1756 * have an error condition.
1757 *
1758 * We should also check another thing, that the new file which is opened
1759 * is in fact the same file that we closed. Perferably, we should open
1760 * the new file, then check the device and inode of both the old and
1761 * new file to see whether they are the same (using stat()). If they
1762 * are not the same, the reopening should fail. As it is implemented
1763 * now, the Unix method for temporary files (open it, remove it,
1764 * use it, and then close it) will fail; and we loose access to the
1765 * original file too.
1766 */
reopen_file(tsd_t * TSD,fileboxptr ptr)1767 static void reopen_file( tsd_t *TSD, fileboxptr ptr )
1768 {
1769 if (!ptr)
1770 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
1771
1772 /*
1773 * We can not reopen the default streams, that makes no sence. If
1774 * tried, report an error.
1775 */
1776 if (ptr->flag & FLAG_SURVIVOR)
1777 {
1778 file_error( ptr, 0, "Invalid operation on default stream" ) ;
1779 return ;
1780 }
1781
1782 /*
1783 * Close the old file, and try to reopen the new file. There is the
1784 * same problem here as in closefile(); if closing didn't work (for
1785 * some mysterious reason), the system's file table should become
1786 * full. Better checking might be required.
1787 */
1788 errno = 0 ;
1789 fclose( ptr->fileptr ) ;
1790 #ifdef VMS
1791 ptr->fileptr = fopen( ptr->filename0->value, "r+" ) ;
1792 #else
1793 ptr->fileptr = fopen( ptr->filename0->value, "r+b" ) ;
1794 #endif
1795 if (ptr->fileptr==NULL)
1796 {
1797 file_error( ptr, errno, NULL ) ;
1798 return ;
1799 }
1800 ptr->oper = OPER_NONE ;
1801
1802 /*
1803 * We definitively want to set the close-on-exec flag. Suppose
1804 * an output file has not been flushed, and we execute a command.
1805 * This might perform an exec() and then a system(), which _will_
1806 * flush all files (close them). The result is that the file might
1807 * be flushed twice ... not good.
1808 *
1809 * This don't work on VMS ... but the file system on VMS is so
1810 * different anyway, so it will probably not create any problems.
1811 * Besides, we don't do exec() and system() on VMS.
1812 */
1813 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !(defined(WIN32) && defined(__IBMC__)) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__) && !defined(__LCC__) && !defined(SKYOS)
1814 if (ptr && ptr->fileptr)
1815 {
1816 int flags, fno ;
1817 fno = fileno( ptr->fileptr ) ;
1818 assert( fno >= -1) ;
1819 flags = fcntl( fno, F_GETFD ) ;
1820 flags |= FD_CLOEXEC ;
1821 if (fcntl( fno, F_SETFD, flags)== -1)
1822 exiterror( ERR_SYSTEM_FAILURE, 1, strerror(errno) ) ;
1823 }
1824 #endif
1825
1826 /*
1827 * If readposition is EOF (=illegal), then we "probably" needed to
1828 * open it in read mode. Set the current read position to the start
1829 * of the file.
1830 */
1831 if (ptr->readpos == (size_t) EOF)
1832 {
1833 ptr->readline = 1 ;
1834 ptr->linesleft = 0 ;
1835 ptr->readpos = 0 ;
1836 ptr->thispos = 0 ;
1837 if ( ptr->flag & FLAG_PERSIST )
1838 rx_fseek( ptr->fileptr, 0, SEEK_SET ) ;
1839 }
1840
1841 /*
1842 * Then do the same thing for write access. We always set this to the
1843 * end-of-file -- the default -- even though there are other write
1844 * modes available. If the file is implicitly open in write mode,
1845 * then the current write position should be set to the default
1846 * value.
1847 */
1848 if (ptr->writepos == (size_t) EOF)
1849 {
1850 ptr->writeline = 0 ;
1851 if ( ptr->flag & FLAG_PERSIST )
1852 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
1853 ptr->writepos = rx_ftell( ptr->fileptr ) ;
1854 ptr->thispos = ptr->writepos ;
1855 }
1856
1857 /*
1858 * Then, at last, do some simple bookkeeping, set both read and
1859 * write access, and clear any previous problem.
1860 */
1861 ptr->flag = FLAG_READ | FLAG_WRITE | FLAG_PERSIST ;
1862 ptr->error = 0 ;
1863 if (ptr->errmsg)
1864 Free_stringTSD(ptr->errmsg) ;
1865
1866 ptr->errmsg = NULL ;
1867 }
1868
1869
1870
1871 /*
1872 * This function explicitly opens a file. It will be called if the user
1873 * has called the built-in function STREAM() in order to open a file
1874 * in a particular mode. It will also be called if the file is not
1875 * previously open, and is used in a read or write operation.
1876 *
1877 * It takes two parameters, the name of the file to open, and the
1878 * mode in which it is to be opened. The mode has a value which is
1879 * matched by the ACCESS_ macros defined earlier.
1880 *
1881 * If the file is actually open in advance, then we close it before we
1882 * do any other operations. If the user is interested in the file in
1883 * one particular mode, he is probably not interested in any previous
1884 * modes.
1885 */
openfile(tsd_t * TSD,const streng * name,int faccess)1886 static fileboxptr openfile( tsd_t *TSD, const streng *name, int faccess )
1887 {
1888 fileboxptr ptr=NULL ;
1889 long lpos=0L ;
1890
1891 /*
1892 * First check wether this file is already open, and use that open
1893 * file if possible. However, that may not be possible, since we
1894 * may want to use the file for another operation now. So, if the
1895 * file _is_ open, check to see if access is right.
1896 */
1897 ptr = getfileptr( TSD, name ) ;
1898 if (ptr)
1899 {
1900 if (ptr->flag & FLAG_SURVIVOR)
1901 {
1902 file_error( ptr, 0, "Can't open a default stream" ) ;
1903 return ptr ;
1904 }
1905 closefile( TSD, name ) ;
1906 }
1907
1908 /*
1909 * Now, get a new file table entry, and fill in the various
1910 * field with appropriate (i.e. default) values.
1911 */
1912 ptr = (fileboxptr)MallocTSD( sizeof(filebox) ) ;
1913 ptr->filename0 = Str_dupstrTSD( name ) ;
1914 ptr->flag = 0 ;
1915 ptr->error = 0 ;
1916 ptr->errmsg = NULL ;
1917 ptr->readline = 0 ;
1918 ptr->linesleft = 0 ;
1919 ptr->writeline = 0 ;
1920 ptr->thispos = (size_t) EOF ;
1921 ptr->readpos = (size_t) EOF ;
1922 ptr->writepos = (size_t) EOF ;
1923 ptr->oper = OPER_NONE;
1924
1925 /*
1926 * suppose we tried to open, but didn't manage, well, stuff it into
1927 * the file table, we might want to retrieve information about it
1928 * later on. _And_ we need to know about the problem if the file
1929 * I/O is to be faked later on.
1930 */
1931 enterfileptr( TSD, ptr ) ;
1932 name = ptr->filename0 ;
1933 goto try_to_open ;
1934
1935 kill_one_file:
1936 swapout_file( TSD, ptr ) ;
1937
1938 try_to_open:
1939 /*
1940 * In most of these, we have to check that the file opened is really
1941 * a persistent file. We should not take that for granted.
1942 */
1943 errno = 0 ;
1944 if (faccess==ACCESS_READ)
1945 {
1946 #ifdef VMS
1947 if ((ptr->fileptr = fopen( name->value, "r" )) != NULL)
1948 #else
1949 if ((ptr->fileptr = fopen( name->value, "rb" )) != NULL)
1950 #endif
1951 {
1952 ptr->flag = FLAG_READ | FLAG_PERSIST ;
1953 ptr->readline = 1 ;
1954 ptr->linesleft = 0 ;
1955 ptr->thispos = ptr->readpos = 0 ;
1956 }
1957 else if (errno==EMFILE)
1958 goto kill_one_file ;
1959 else
1960 {
1961 file_error( ptr, errno, NULL ) ;
1962 }
1963 }
1964 else if (faccess==ACCESS_WRITE)
1965 {
1966 /*
1967 * This is really a problem. If opened in mode "w", it will
1968 * truncate the file if it did exist. If opened int mode "r+",
1969 * it will fail if the file did not exist. So we try to
1970 * combine the two.
1971 */
1972 ptr->flag = FLAG_READ ;
1973 #ifdef VMS
1974 ptr->fileptr = fopen( name->value, "r+" ) ;
1975 #else
1976 ptr->fileptr = fopen( name->value, "r+b" ) ;
1977 #endif
1978 errno = 0 ;
1979 if (!ptr->fileptr)
1980 #ifdef VMS
1981 ptr->fileptr = fopen( name->value, "w+" ) ;
1982 #else
1983 ptr->fileptr = fopen( name->value, "w+b" ) ;
1984 #endif
1985
1986 errno = 0 ;
1987 if (!ptr->fileptr)
1988 {
1989 #ifdef SGIkludges
1990 errno = EMFILE ;
1991 #else
1992 errno = 0 ;
1993 #endif
1994 #ifdef VMS
1995 ptr->fileptr = fopen( name->value, "w" ) ;
1996 #else
1997 ptr->fileptr = fopen( name->value, "wb" ) ;
1998 #endif
1999 ptr->flag &= 0 ;
2000 }
2001
2002 /*
2003 * Then set the current read and write positions to the start and
2004 * the end of the file, respectively. When we first open the file
2005 * we can quickly determine readpos, writepos and readline, but
2006 * writeline is expensive to determine, because we have to read
2007 * the whole file to determine this. So we don't do this when we
2008 * open the file, because we may never use it. Instead we set the
2009 * value of writeline to 0 to indicate that the actual position
2010 * is unknown. When we do want to use writeline, we have to
2011 * determine it then.
2012 */
2013 if (ptr->fileptr)
2014 {
2015 ptr->flag |= FLAG_WRITE | FLAG_PERSIST ;
2016 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
2017 lpos = rx_ftell( ptr->fileptr ) ;
2018 ptr->thispos = ptr->writepos = lpos ;
2019 ptr->writeline = 0 ;
2020 ptr->readpos = 0 ;
2021 ptr->readline = 1 ;
2022 ptr->linesleft = 0 ;
2023 }
2024 else if (errno==EMFILE)
2025 goto kill_one_file ;
2026 else
2027 file_error( ptr, errno, NULL ) ;
2028 }
2029 else if (faccess==ACCESS_APPEND)
2030 {
2031 /*
2032 * In append mode, the file is opened as a transient file, all
2033 * writing must be done at the end of the file. It is not
2034 * possible to perform reading on the file. Useful for files
2035 * to which you have write, but not read access (e.g. logfiles).
2036 */
2037 #ifdef VMS
2038 if ((ptr->fileptr = fopen( name->value, "a" )) != NULL)
2039 #else
2040 if ((ptr->fileptr = fopen( name->value, "ab" )) != NULL)
2041 #endif
2042 {
2043 ptr->flag = FLAG_WRITE | FLAG_WREOF ;
2044 }
2045 else if (errno==EMFILE)
2046 goto kill_one_file ;
2047 else
2048 file_error( ptr, errno, NULL ) ;
2049 }
2050 else if (faccess==ACCESS_STREAM_APPEND)
2051 {
2052 /*
2053 * In "stream" append mode, the file is opened as a persistent file, all
2054 * writing must be done at the end of the file. It is not
2055 * possible to perform reading on the file. Useful for files
2056 * to which you have write, but not read access (e.g. logfiles).
2057 */
2058 #ifdef VMS
2059 if ((ptr->fileptr = fopen( name->value, "a" )) != NULL)
2060 #else
2061 if ((ptr->fileptr = fopen( name->value, "ab" )) != NULL)
2062 #endif
2063 {
2064 ptr->flag = FLAG_WRITE | FLAG_WREOF | FLAG_PERSIST;
2065 if ( ptr->flag & FLAG_PERSIST )
2066 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
2067 lpos = rx_ftell( ptr->fileptr ) ;
2068 ptr->thispos = ptr->writepos = lpos ;
2069 ptr->writeline = 0 ; /* unknown position */
2070 ptr->readpos = 0 ;
2071 ptr->readline = 1 ;
2072 ptr->linesleft = 0 ;
2073 }
2074 else if (errno==EMFILE)
2075 goto kill_one_file ;
2076 else
2077 file_error( ptr, errno, NULL ) ;
2078 }
2079 else if (faccess==ACCESS_STREAM_REPLACE)
2080 {
2081 /*
2082 * The file is created if it didn't exist, and if it did exist
2083 * it is truncated and the file pointers set to the start of file.
2084 */
2085 #ifdef VMS
2086 if ((ptr->fileptr = fopen( name->value, "w+" )) != NULL)
2087 #else
2088 if ((ptr->fileptr = fopen( name->value, "w+b" )) != NULL)
2089 #endif
2090 {
2091 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_WREOF | FLAG_RDEOF |
2092 FLAG_PERSIST ;
2093 ptr->writeline = ptr->readline = 1 ;
2094 ptr->linesleft = 0 ;
2095 ptr->readpos = ptr->writepos = ptr->thispos = 0 ;
2096 }
2097 else if (errno==EMFILE)
2098 goto kill_one_file ;
2099 else
2100 file_error( ptr, errno, NULL ) ;
2101 }
2102 else if (faccess==ACCESS_UPDATE)
2103 {
2104 /*
2105 * Like read access, but it will not create the file if it didn't
2106 * already exist. Instead, an error is reported.
2107 */
2108 #ifdef VMS
2109 if ((ptr->fileptr = fopen( name->value, "r+" )) != NULL)
2110 #else
2111 if ((ptr->fileptr = fopen( name->value, "r+b" )) != NULL)
2112 #endif
2113 {
2114 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_PERSIST ;
2115 ptr->readline = 0 ;
2116 ptr->linesleft = 0 ;
2117 ptr->writeline = 0 ; /* unknown */
2118 }
2119 else if (errno==EMFILE)
2120 goto kill_one_file ;
2121 else
2122 file_error( ptr, errno, NULL ) ;
2123 }
2124 else if (faccess==ACCESS_CREATE)
2125 {
2126 /*
2127 * The file is created if it didn't exist, and if it did exist
2128 * it is truncated.
2129 */
2130 #ifdef VMS
2131 if ((ptr->fileptr = fopen( name->value, "w+" )) != NULL)
2132 #else
2133 if ((ptr->fileptr = fopen( name->value, "w+b" )) != NULL)
2134 #endif
2135 {
2136 ptr->flag = FLAG_WRITE | FLAG_READ | FLAG_WREOF | FLAG_RDEOF |
2137 FLAG_PERSIST ;
2138 ptr->writeline = ptr->readline = 1 ;
2139 ptr->linesleft = 0 ;
2140 ptr->readpos = ptr->writepos = ptr->thispos = 0 ;
2141 }
2142 else if (errno==EMFILE)
2143 goto kill_one_file ;
2144 else
2145 file_error( ptr, errno, NULL ) ;
2146 }
2147 else
2148 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
2149
2150 #if !defined(VMS) && !defined(MAC) && !defined(OS2) && !defined(DOS) && !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) && !defined(__EPOC32__) && !defined(__LCC__) && !defined(SKYOS)
2151 /*
2152 * Then we check to see if this is a transient or persistent file.
2153 * We can remove a 'persistent' setting, but add one, since we
2154 * sometimes want to access a persistent file as transient (append).
2155 */
2156 if (ptr->fileptr)
2157 {
2158 int fno, rc ;
2159 struct rx_stat_buf statbuf ;
2160 errno = 0 ;
2161 fno = fileno(ptr->fileptr) ;
2162 rc = rx_fstat( fno, &statbuf ) ;
2163 if (rc==0 && !S_ISREG(statbuf.st_mode))
2164 ptr->flag &= ~(FLAG_PERSIST) ;
2165 else if (rc!=0)
2166 file_error( ptr, errno, NULL ) ;
2167 }
2168
2169 /*
2170 * As with reopen_file(), we want to set the close-on-exec flag,
2171 * see reopen_file for more information.
2172 */
2173 if (ptr->fileptr)
2174 {
2175 int flags, fno ;
2176 fno = fileno(ptr->fileptr) ;
2177 assert( fno >= -1) ;
2178 flags = fcntl( fno, F_GETFD ) ;
2179 flags |= FD_CLOEXEC ;
2180 if (fcntl( fno, F_SETFD, flags)== -1)
2181 exiterror( ERR_SYSTEM_FAILURE, 1, strerror(errno) ) ;
2182 }
2183 #endif
2184
2185 return (ptr) ;
2186 }
2187
2188
2189
2190
2191 /* ------------------------------------------------------------------- */
2192 /* High level utility routines */
2193
2194
2195
2196
2197 /*
2198 * This function is really just an interface to the function getfileptr().
2199 * It takes a (possible) filename, and retrieves the corresponding
2200 * Rexx file table entry. If the file does not exist, it is opened in
2201 * the mode indicated by 'open_mode'. If it does exist, this routine
2202 * verifies that it it has been opened in a mode corresponding to
2203 * 'access' (OPER_READ or OPER_WRITE).
2204 *
2205 * If the file does not exist, it is opened in either normal read
2206 * or normal write. This correcspinds to an "implicit" file open
2207 * in Rexx.
2208 */
get_file_ptr(tsd_t * TSD,const streng * name,int faccess,int open_mode)2209 static fileboxptr get_file_ptr( tsd_t *TSD, const streng *name, int faccess, int open_mode )
2210 {
2211 fileboxptr ptr=NULL ;
2212
2213 ptr = getfileptr( TSD, name ) ;
2214 if (ptr==NULL)
2215 return openfile( TSD, name, open_mode ) ;
2216
2217 if (ptr->flag & FLAG_ERROR)
2218 return ptr ;
2219
2220 if (faccess==OPER_READ && (!(ptr->flag & FLAG_READ)))
2221 reopen_file( TSD, ptr ) ;
2222 else if (faccess==OPER_WRITE && (!(ptr->flag & FLAG_WRITE)))
2223 reopen_file( TSD, ptr ) ;
2224
2225 return ptr ;
2226 }
2227
2228
2229
2230 /*
2231 * This routine reads one complete line from the file indicated by
2232 * the file table entry 'ptr'. Or rather, it read from the current
2233 * read position, until and including the first EOL mark, and returns
2234 * that. If the EOL mark is implemented as certain characters, they are
2235 * not returned. It closely corresponds to the LINEIN() built-in
2236 * function.
2237 *
2238 * What is the upper limit for the size that we might read in? It's
2239 * best not to have any limit, so the method is the following: A
2240 * temporary area is used for storing the data read from the file.
2241 * We never know the size needed until the EOL mark is found. So
2242 * just read the data into the temporary area. If the EOL is found,
2243 * then we know the size, and we can transfer the data into a 'streng'
2244 * of suitable size. If the temporary area is too small, allocate
2245 * an area twice the size, and copy the data over. Afterwards, keep the
2246 * new area as the temporary area.
2247 *
2248 * This way, we normally use little memory, but we are still able to
2249 * read as large lines as the memory allows, if it is needed.
2250 *
2251 * No error condition is raised if noerrors is set. Instead, NULL is returned.
2252 */
readoneline(tsd_t * TSD,fileboxptr ptr)2253 static streng *readoneline( tsd_t *TSD, fileboxptr ptr )
2254 {
2255 int i=0, j=0, eolf=0, eolchars=1 ;
2256 /*#if !defined(UNIX)*/
2257 int k=0;
2258 /*#endif*/
2259 streng *ret=NULL ;
2260 fil_tsd_t *ft;
2261
2262 ft = (fil_tsd_t *)TSD->fil_tsd;
2263
2264 /*
2265 * First verify that we actually have a file that is not in an
2266 * ERROR state. If so, don't perform any operations.
2267 */
2268 if ( ptr->flag & FLAG_ERROR )
2269 {
2270 if (!(ptr->flag & FLAG_FAKE))
2271 file_error( ptr, 0, NULL ) ;
2272
2273 return nullstringptr() ;
2274 }
2275
2276 /*
2277 * If we have an EOF from the last linein() then cause the NOTREADY
2278 * condition now.
2279 */
2280 if ( ptr->flag & FLAG_RDEOF )
2281 {
2282 file_warning( ptr, 0, "EOF on line input" ) ;
2283 }
2284
2285 /*
2286 * If the string is not yet allocated, allocate it, and use an
2287 * initial size of 512 bytes. This can be increased during runtime.
2288 * Using higher initial sizes will waste allocation time on modern systems
2289 * with page sizes around 4KB. 512 fits most cases at its best.
2290
2291 * MH 4 Sep 02 We should read into the ft->rol_string 512 bytes at a
2292 * time and search through the buffer for the EOL. getc() for every
2293 * character is innefficient, however (at least on Linux) getc() is
2294 * implemented as a read( 4096 ), and then returns each character
2295 * from some internal buffer.
2296 */
2297 if (!ft->rol_string)
2298 {
2299 ft->rol_string = (char *)MallocTSD( (ft->rol_size=512) ) ;
2300 #ifdef TRACEMEM
2301 ft->rdarea = ft->rol_string ;
2302 #endif
2303 }
2304
2305 /*
2306 if (ptr->fileptr==stdin)
2307 fcntl( stdin, F_SETFL, O_NONBLOCK | fcntl(stdin,F_GETFL)) ;
2308 */
2309 errno = 0 ;
2310 SWITCH_OPER_READ(ptr);
2311 /*
2312 * Switch to the current readpos setting in case the current position
2313 * is based on the write position.
2314 */
2315 ptr->thispos = ptr->readpos;
2316 if ( ptr->flag & FLAG_PERSIST )
2317 rx_fseek(ptr->fileptr,ptr->thispos,SEEK_SET);
2318 for (i=0; ; i++)
2319 {
2320 j = getc(ptr->fileptr);
2321 if (j == REGINA_EOL)
2322 {
2323 eolf = REGINA_EOL;
2324 break;
2325 }
2326 /*#if !defined(UNIX) && !defined(MAC)*/
2327 /*
2328 * MH 25042003 - all platforms can read lines with
2329 * CRLF, LF, or CR - consistent with ObjectRexx
2330 */
2331 if (j == REGINA_CR)
2332 {
2333 k = getc(ptr->fileptr);
2334 if (k == REGINA_EOL)
2335 {
2336 eolf = REGINA_EOL;
2337 eolchars = 2;
2338 break;
2339 }
2340 else
2341 {
2342 ungetc(k,ptr->fileptr);
2343 eolf = REGINA_EOL;
2344 break;
2345 }
2346 }
2347 /*#endif*/
2348 /*
2349 * If we hit end-of-file, handle it carefully, and terminate the
2350 * reading. Note that this means that there we have read an
2351 * incomplete last line, so return what we've got, and report
2352 * an NOTREADY condition. (Partly, I disagree, but this is how
2353 * TRL defines it ... Case I: Programmer using Emacs forgets to
2354 * add a EOL after the last line; Rexx triggers NOTREADY when
2355 * reading that last, incomplete line.
2356
2357 * MH 4-Sep-2002 - change in semantics happened a while ago.
2358 * We treat incomplete line as a "normal" line, and NOTREADY
2359 * is NOT raised when reading an incomplete line.
2360 */
2361 if (j==EOF)
2362 {
2363 ptr->flag |= FLAG_RDEOF ;
2364 /* file_warning( ptr, 0, "EOF on line input" ) ; */
2365 break ;
2366 }
2367
2368 /*
2369 * We are trying to avoid any limits other than memory-imposed
2370 * limits. So if the buffer size that we currently have are too
2371 * small, double it, and hide the operation from the rest of the
2372 * interpreter.
2373 */
2374 if (i>=ft->rol_size)
2375 {
2376 char *tmpstring ;
2377
2378 assert( i == ft->rol_size ) ;
2379 tmpstring = (char *)MallocTSD( 2*ft->rol_size+10 ) ;
2380 memcpy( tmpstring, ft->rol_string, ft->rol_size ) ;
2381 FreeTSD( ft->rol_string ) ;
2382 ft->rol_string = tmpstring ;
2383 ft->rol_size *= 2 ;
2384 #ifdef TRACEMEM
2385 ft->rdarea = ft->rol_string ;
2386 #endif
2387 }
2388
2389 /*
2390 * Just an ordinary character ... append it to the buffer
2391 */
2392 ft->rol_string[i] = (char) j ;
2393 }
2394 /*
2395 * Attempt to set the read pointer and the current file
2396 * pointer based on the length of the line we just read.
2397 */
2398 #if 1 /* really MH */
2399 if ( ptr->thispos == ptr->readpos )
2400 {
2401 if ( ptr->thispos == (size_t) EOF )
2402 {
2403 errno = 0 ;
2404 ptr->thispos = ptr->readpos = rx_ftell( ptr->fileptr ) ;
2405 }
2406 else
2407 {
2408 ptr->thispos += (i - (j==EOF)) + eolchars ;
2409 ptr->readpos = ptr->thispos ;
2410 }
2411 }
2412 else
2413 {
2414 errno = 0 ;
2415 ptr->thispos = ptr->readpos = rx_ftell( ptr->fileptr ) ;
2416 }
2417 #else
2418 if (ptr->thispos != (size_t) EOF)
2419 ptr->thispos += (i - (j==EOF)) + eolchars ;
2420
2421 if (ptr->readpos != (size_t) EOF)
2422 ptr->readpos = ptr->thispos ;
2423 #endif
2424 /*
2425 * If we did read a complete line, we have to increment the line
2426 * count for the current read pointer of this file. This part of
2427 * the code is a bit Unix-ish. It will have to be reworked for
2428 * other types of operating systems.
2429 */
2430 if ((eolf==REGINA_EOL) && (ptr->readline > 0))
2431 {
2432 #if 1 /* really MH */
2433 ptr->readline += 1 ; /* only if we actually saw the "\n" !!! */
2434 #else
2435 ptr->readline += eolchars ; /* only if we actually saw the "\n" !!! */
2436 #endif
2437 if (ptr->linesleft)
2438 ptr->linesleft-- ;
2439 }
2440 /*
2441 * A bit of a hack here. Because countlines() determines if any lines
2442 * are left in the stream by using the feof() function, we have to
2443 * attempt to read the EOF after each line, and set the file's state
2444 * to EOF. If the character read is not EOF, then put it back on
2445 * the stream to be read later.
2446 * Only do this for persistent streams!!
2447 */
2448 if ( ptr->flag & FLAG_PERSIST
2449 && !feof( ptr->fileptr ) )
2450 {
2451 int ch0;
2452 ch0 = getc(ptr->fileptr);
2453 if (feof(ptr->fileptr))
2454 {
2455 ptr->flag |= FLAG_RDEOF ;
2456 /* file_warning( ptr, 0, "EOF on line input" ) ; */
2457 }
2458 else
2459 {
2460 ungetc(ch0,ptr->fileptr);
2461 }
2462 }
2463
2464 /*
2465 * Wrap up the data that was read, and return it as a 'streng'.
2466 *
2467 */
2468 /* if (i>1000) i = 1000 ; */
2469 ret = Str_makeTSD( i ) ;
2470 memcpy( ret->value, ft->rol_string, ret->len=i ) ;
2471 return ret ;
2472 }
2473
2474
positionfile_SEEK_SET(tsd_t * TSD,const char * bif,int argno,fileboxptr ptr,int oper,rx_64 lineno)2475 static rx_64 positionfile_SEEK_SET( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, rx_64 lineno )
2476 {
2477 int ch=0x00;
2478 rx_64 from_line=0, tmp=0 ;
2479 rx_64 from_char=0L ;
2480 rx_64 ret;
2481
2482 /*
2483 * We know the line number of at most three positions in the file:
2484 * the start of the file, the write position and the read position.
2485 * If the file is open only for reading or writing, we know at most
2486 * two positions. And in addition, the read and/or the write
2487 * position may be be invalid (i.e. previous operation was
2488 * character oriented). But at least, we know the line number of
2489 * one position, the start of the file, which is the first line.
2490 *
2491 * The best method seems to be: First start with the start of file
2492 * and then see if using the read or the write position instead is
2493 * a better deal. There is one drawback ... we assume that all lines
2494 * are equally long. That assumption is probably not too bad for text
2495 * files, but it may create unnecessary overhead for 'peculiar' files
2496 */
2497 from_line = 1 ;
2498 from_char = 0 ;
2499
2500 if ( oper & OPER_READ
2501 && ptr->flag & FLAG_PERSIST )
2502 {
2503 if ( rx_fseek( ptr->fileptr, ptr->readpos, SEEK_SET ) )
2504 {
2505 file_error( ptr, errno, NULL ) ;
2506 return 0;
2507 }
2508 ptr->thispos = ptr->readpos ;
2509 }
2510 if ( oper & OPER_WRITE
2511 && ptr->flag & FLAG_PERSIST )
2512 {
2513 if ( rx_fseek( ptr->fileptr, ptr->writepos, SEEK_SET ) )
2514 {
2515 file_error( ptr, errno, NULL ) ;
2516 return 0;
2517 }
2518 ptr->thispos = ptr->writepos ;
2519 }
2520
2521 /*
2522 * First, let's check to see if we gain anything from using the
2523 * read position instead. If the distance from the current read
2524 * position to the wanted line (counted in number of lines) is smaller
2525 * than the number of lines from the first line to the wanted line,
2526 * use the current read position in stead. But only if the current
2527 * read position is defined.
2528 */
2529 if ((ptr->flag & FLAG_READ) && (ptr->readline > 0))
2530 {
2531 assert( ptr->readpos != (size_t) EOF) ;
2532 tmp = ptr->readline - lineno ;
2533 if (tmp<0)
2534 tmp = (-tmp) ;
2535
2536 if (tmp < (lineno - from_line))
2537 {
2538 from_line = ptr->readline ;
2539 from_char = ptr->readpos ;
2540 }
2541 }
2542
2543 /*
2544 * Then, we check to see whether we can gain even more if we use
2545 * the current write position of the file instead.
2546 */
2547 if ((ptr->flag & FLAG_WRITE) && (ptr->writeline > 0))
2548 {
2549 assert( ptr->writepos != (size_t) EOF ) ;
2550 tmp = ptr->writeline - lineno ;
2551 if (tmp<0)
2552 tmp = (-tmp) ;
2553
2554 if (tmp < (lineno - from_line))
2555 {
2556 from_line = ptr->writeline ;
2557 from_char = ptr->writepos ;
2558 }
2559 }
2560
2561 /*
2562 * By now, the variables from_line, and from_char should contain
2563 * the optimal starting point from where a seek for the 'lineno'
2564 * line in the file can start, so first, move there. An in addition,
2565 * it should be the known position which is closest to the wanted
2566 * line.
2567 */
2568 if (from_char != (long) ptr->thispos)
2569 {
2570 errno = 0 ;
2571 if ( ptr->flag & FLAG_PERSIST
2572 && rx_fseek( ptr->fileptr, from_char, SEEK_SET ))
2573 {
2574 file_error( ptr, errno, NULL ) ;
2575 return 0;
2576 }
2577 ptr->oper = OPER_NONE;
2578 ptr->thispos = from_char ;
2579 }
2580 assert( from_char == rx_ftell(ptr->fileptr) ) ;
2581
2582 /*
2583 * Now we are positioned at the right spot, so seek forwards or
2584 * backwards until we reach the correct line. Actually, the method
2585 * we are going to use may seem a bit strange at first. First we
2586 * seek forward until we pass the line, and then we seek backwards
2587 * until we reach the line and at the end we back up to the first
2588 * preceding end-of-line marker. This may seem awkward, but it is
2589 * fairly simple. And in addition, it will always position us at
2590 * the _start_ of the wanted line.
2591 */
2592 once_more:
2593 while ((lineno>from_line)) /* seek forward */
2594 {
2595 SWITCH_OPER_READ(ptr);
2596 for (;((ch=getc(ptr->fileptr))!=EOF)&&(ch!=REGINA_EOL);from_char++) ;
2597 if (ch==REGINA_EOL)
2598 from_line++ ;
2599 else
2600 break ;
2601 }
2602
2603 /*
2604 * Then we seek backwards until we reach the line. The backwards
2605 * movement is _really_ awkward, so perhaps we should read in 512
2606 * bytes, and analyse the data in it instead? Indeed, another
2607 * algoritm should be chosen. Maybe later ...
2608 */
2609 while (lineno<=from_line && from_char>0)
2610 {
2611 errno = 0 ;
2612 if ( ptr->flag & FLAG_PERSIST
2613 && rx_fseek(ptr->fileptr, -1, SEEK_CUR))
2614 {
2615 /*
2616 * Should this happen? Only if someone overwrites EOF chars in
2617 * the file, but that _may_ happend ... Report error for
2618 * any errors from the fseek and ftell. If we hit the start of
2619 * the file, reset from_line check whether we are _below_ lineno
2620 * If so, jump back and seek from the start (then we *must*
2621 * start at line 1, since the data we've got are illegal).
2622 *
2623 * It will also happen if we are seeking backwards for the
2624 * first line.
2625 */
2626 errno = 0 ;
2627 if (rx_fseek(ptr->fileptr,0,SEEK_SET))
2628 {
2629 file_error( ptr, errno, NULL ) ;
2630 return 0;
2631 }
2632 ptr->oper = OPER_NONE;
2633
2634 from_line = 1 ;
2635 ptr->thispos = 0 ;
2636 if (from_line<lineno)
2637 {
2638 ptr->readline = (-1);
2639 ptr->writeline = 0; /* unknown */
2640 goto once_more ;
2641 }
2642
2643 break ; /* we were looking for the first line ... how lucky :-) */
2644 }
2645
2646 /*
2647 * After seeking one character backwards, we must read the character
2648 * that we just skipped over. Do that, and test whether it is
2649 * a end-of-line character.
2650 */
2651 SWITCH_OPER_READ(ptr);
2652 ch = getc(ptr->fileptr) ;
2653 if (ch==REGINA_EOL)
2654 {
2655 if (lineno==from_line)
2656 break ;
2657
2658 from_line-- ;
2659 }
2660
2661 /*
2662 * Then we move backwards once more, in order to compensate for
2663 * reading the character. Sigh, we are really doing a lot of
2664 * forward and backward reading, arn't we?
2665 */
2666 errno = 0 ;
2667 if ( ptr->flag & FLAG_PERSIST
2668 && rx_fseek(ptr->fileptr, -1, SEEK_CUR))
2669 {
2670 file_error( ptr, errno, NULL ) ;
2671 return 0;
2672 }
2673 ptr->oper = OPER_NONE;
2674 }
2675
2676 /*
2677 * Now we are almost finished. We just have to set the correct
2678 * information in the Rexx file table entry.
2679 */
2680 ptr->thispos = rx_ftell( ptr->fileptr ) ;
2681 if (oper & OPER_READ)
2682 {
2683 ptr->readline = from_line ; /* was lineno */
2684 ptr->readpos = ptr->thispos ;
2685 ptr->flag &= ~(FLAG_RDEOF) ;
2686 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
2687 }
2688 if (oper & OPER_WRITE)
2689 {
2690 ptr->writeline = from_line ; /* was lineno */
2691 ptr->writepos = ptr->thispos ;
2692 ptr->flag &= ~(FLAG_WREOF) ;
2693 }
2694
2695 if (oper & OPER_READ)
2696 ret = ptr->readline ;
2697 else
2698 ret = ptr->writeline ;
2699 return ret;
2700 }
2701
positionfile_SEEK_CUR(tsd_t * TSD,const char * bif,int argno,fileboxptr ptr,int oper,rx_64 lineno,rx_64 from_line,rx_64 from_char)2702 static rx_64 positionfile_SEEK_CUR( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, rx_64 lineno, rx_64 from_line, rx_64 from_char )
2703 {
2704 rx_64 tmp,ret;
2705
2706 /*
2707 * Do simple checks first.
2708 * If we are seeking back before the first line, then set:
2709 * READ:
2710 * ptr->readline = 1
2711 * ptr->readpos = 1
2712 * return 1
2713 * WRITE:
2714 * ptr->writeline = 1
2715 * ptr->writepos = 0
2716 * return 0
2717 */
2718 tmp = from_line + lineno;
2719
2720 if ( tmp < 1 )
2721 {
2722 /*
2723 * We have positioned to before the first line
2724 *
2725 */
2726 rx_fseek( ptr->fileptr, 0L, SEEK_SET );
2727 ptr->thispos = rx_ftell( ptr->fileptr );
2728 if ( oper == OPER_READ )
2729 {
2730 ptr->readline = 1;
2731 ptr->readpos = 1;
2732 ptr->oper = OPER_READ;
2733 return 1;
2734 }
2735 else
2736 {
2737 ptr->writeline = 1;
2738 ptr->writepos = 0;
2739 ptr->oper = OPER_WRITE;
2740 return 0;
2741 }
2742 }
2743 /*
2744 * We now have an absolute line number from the +ve relative line
2745 * number, so use the absolute positioning code.
2746 */
2747 ret = positionfile_SEEK_SET( TSD, bif, argno, ptr, oper, tmp );
2748 return ret;
2749 }
2750
positionfile_SEEK_END(tsd_t * TSD,const char * bif,int argno,fileboxptr ptr,int oper,rx_64 lineno)2751 static rx_64 positionfile_SEEK_END( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, rx_64 lineno )
2752 {
2753 /*
2754 * This function does file positioning on a line basis from the
2755 * end of the file.
2756 * There is not a lot of optimisation we can do reading backwards.
2757 * We first need to determine the last line; does it end in an EOL,
2758 * or is it incomplete; ie does not end in EOL. We treat an incomplete
2759 * last line as a line.
2760 * Our initial attempt at this will position the file at the
2761 * end, and read backwards; ie getc(ptr->fileptr), seek() back 2 chars,
2762 * until the start of file, or we have the specified number of lines.
2763 */
2764 rx_64 here, next, save_pos, i, ret, this_lineno, num_lines;
2765 size_t bret;
2766 char buf[512];
2767 int found = 0;
2768
2769 SWITCH_OPER_READ(ptr);
2770 /*
2771 * First, get the size of the file. We can only do positioning on
2772 * persistant files...
2773 */
2774 if ( ! (ptr->flag & FLAG_PERSIST) )
2775 {
2776 file_error( ptr, 0, "Cannot position on transient stream" );
2777 return 0;
2778 }
2779 if ( rx_fseek( ptr->fileptr, 0L, SEEK_END ) )
2780 {
2781 file_error( ptr, errno, NULL ) ;
2782 return 0;
2783 }
2784
2785 here = rx_ftell( ptr->fileptr );
2786 /*
2787 * Seek backwards one character and read the character. If the last
2788 * character is REGINA_EOL, then DON'T treat this as a new line.
2789 */
2790 if ( rx_fseek( ptr->fileptr, -1L, SEEK_CUR ) )
2791 {
2792 file_error( ptr, errno, NULL ) ;
2793 return 0;
2794 }
2795 buf[0] = (char)getc( ptr->fileptr );
2796 if ( buf[0] == REGINA_EOL )
2797 num_lines = 0;
2798 else
2799 num_lines = 1;
2800 /*
2801 * Move the file pointer back to after the last character
2802 */
2803 if ( rx_fseek( ptr->fileptr, 0L, SEEK_END ) )
2804 {
2805 file_error( ptr, errno, NULL ) ;
2806 return 0;
2807 }
2808
2809 /*
2810 * We have to read backwards in the file until we reach a known point.
2811 * The only point which we can guarantee is known is the start of the
2812 * file. We can't use ptr->(read/write)line as we really don't know
2813 * how many lines are in the file (we may have appended several earlier).
2814 * An incomplete last line of a file (ie no CRLF/LF) is treated as a complete
2815 * line.
2816 */
2817 for ( ; ; )
2818 {
2819 /*
2820 * Determine where we want to move the file pointer backwards
2821 * into the file, and then move the file pointer there ready for
2822 * reading a buffer.
2823 */
2824 next = min( 512, here );
2825 if ( rx_fseek( ptr->fileptr, -next, SEEK_CUR ) )
2826 {
2827 file_error( ptr, errno, NULL ) ;
2828 return 0;
2829 }
2830 /*
2831 * Save our current position; start of the buffer being read
2832 */
2833 save_pos = rx_ftell( ptr->fileptr );
2834 /*
2835 * Read a buffer, this moves the file pointer forward to the
2836 * end of the buffer
2837 */
2838
2839 bret = fread( buf, sizeof(char), next, ptr->fileptr );
2840 if ( bret != next
2841 && bret != EOF )
2842 {
2843 file_error( ptr, errno, NULL ) ;
2844 return 0;
2845 }
2846 /*
2847 * Count the number of lines from the end of the buffer
2848 */
2849 for ( i = next-1; i >= 0; i-- )
2850 {
2851 if ( buf[i] == REGINA_EOL )
2852 {
2853 num_lines++;
2854 if ( num_lines > lineno
2855 && !found )
2856 {
2857 /*
2858 * Calculate the actual char file position of the EOL
2859 */
2860 /*
2861 * the +1 is to point at the character AFTER the EOL; the
2862 * first character of the next line.
2863 */
2864 ptr->thispos = save_pos + i + 1;
2865 found = 1;
2866 }
2867 }
2868 }
2869 /*
2870 * move the file pointer back to the start of the buffer just
2871 * read, so the next fseek() backwards is from the START of the
2872 * buffer.
2873 */
2874 if ( rx_fseek( ptr->fileptr, save_pos, SEEK_SET ) )
2875 {
2876 file_error( ptr, errno, NULL ) ;
2877 return 0;
2878 }
2879 /*
2880 * Calculate our own file position
2881 */
2882 here -= next;
2883 if ( here == 0 )
2884 break;
2885 }
2886 /*
2887 * We are at the start of the file. If we haven't found our lineno
2888 * (because it was greater than the number of lines in the file),
2889 * that's where we stay.
2890 */
2891 if ( found )
2892 {
2893 /* ptr->thispos already set */
2894 this_lineno = 1 + (num_lines - lineno);
2895 }
2896 else
2897 {
2898 ptr->thispos = 0;
2899 this_lineno = 1;
2900 }
2901
2902 /*
2903 * Now we are almost finished. We just have to set the correct
2904 * information in the Rexx file table entry.
2905 */
2906 if ( rx_fseek( ptr->fileptr, ptr->thispos, SEEK_SET ) )
2907 {
2908 file_error( ptr, errno, NULL ) ;
2909 return 0;
2910 }
2911 if ( oper & OPER_READ )
2912 {
2913 ptr->readline = this_lineno;
2914 ptr->readpos = ptr->thispos;
2915 ptr->flag &= ~(FLAG_RDEOF);
2916 ptr->flag &= ~(FLAG_AFTER_RDEOF);
2917 }
2918 if ( oper & OPER_WRITE )
2919 {
2920 ptr->writeline = this_lineno;
2921 ptr->writepos = ptr->thispos ;
2922 ptr->flag &= ~(FLAG_WREOF) ;
2923 }
2924 /*
2925 * We just counted the number of lines between the end of the file and
2926 * our current position, so we know how many lines are left.
2927 * The number of lines counted is one more than actually left in the file.
2928 */
2929 ptr->linesleft = num_lines-1;
2930 if ( oper & OPER_READ )
2931 ret = ptr->readline ;
2932 else
2933 ret = ptr->writeline ;
2934 return ret;
2935 }
2936
2937 /*
2938 * This routine will position the current read or write position
2939 * of a file, to the start of a particular line. The file to be
2940 * operated on is 'ptr', the pointer to manipulate is indicated
2941 * by 'oper' (either OPER_READ or OPER_WRITE or both), and the linenumber
2942 * to position at is 'lineno'.
2943 * 'from' specifies if the positioning is done as an absolute position SEEK_SET,
2944 * a relative position from the current position SEEK_CUR, or relative to the
2945 * end of file: SEEK_END.
2946 *
2947 * There are (at least) two ways to do the backup of the current
2948 * position in the file. First to backup to the start of the file
2949 * and then to seek forward, or to seek backwards from the current
2950 * position of the file.
2951 *
2952 * Perhaps the first is best for the standard case, and the second
2953 * should be activated when the line-parameter is negative ... ?
2954 */
2955
positionfile(tsd_t * TSD,const char * bif,int argno,fileboxptr ptr,int oper,rx_64 lineno,int from)2956 static rx_64 positionfile( tsd_t *TSD, const char *bif, int argno, fileboxptr ptr, int oper, rx_64 lineno, int from )
2957 {
2958 rx_64 from_line=0;
2959 rx_64 from_char=0;
2960 rx_64 ret=0;
2961
2962 /*
2963 * If file is in ERROR state, don't touch it.
2964 */
2965 if (ptr->flag & FLAG_ERROR)
2966 {
2967 if (!(ptr->flag & FLAG_FAKE))
2968 file_error( ptr, 0, NULL ) ;
2969 return 0;
2970 }
2971
2972 /*
2973 * If this isn't a persistent file, then report an error. We can only
2974 * perform repositioning in persistent files.
2975 */
2976
2977 if (!(ptr->flag & FLAG_PERSIST ))
2978 exiterror( ERR_INCORRECT_CALL, 42, bif, tmpstr_of( TSD, ptr->filename0 ) ) ;
2979
2980 /*
2981 * If the operation is READ, but the file is not open for READ,
2982 * return an error.
2983 */
2984 if ((oper&OPER_READ) && !(ptr->flag & FLAG_READ))
2985 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "READ" ) ;
2986 /*
2987 * If the operation is WRITE, but the file is not open for WRITE,
2988 * return an error.
2989 */
2990 if ( (oper&OPER_WRITE) && !(ptr->flag & FLAG_WRITE) )
2991 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "WRITE" ) ;
2992
2993 /*
2994 * If we do any repositioning, then make the old estimate of lines
2995 * left to read invalid. This is not really needed in all cases, but
2996 * it is a good start. And you _may_ even want to recalculate the
2997 * number of lines left!
2998 */
2999 if ( ptr->linesleft > 0 )
3000 ptr->linesleft = 0 ;
3001
3002 if ( ptr->thispos == (size_t) EOF )
3003 {
3004 errno = 0 ;
3005 ptr->thispos = rx_ftell( ptr->fileptr ) ;
3006 }
3007
3008 /*
3009 * So, what we are going to do depends partly on whether we are moving
3010 * the read or the write position of the file. We may even be as
3011 * lucky as not to have to move anything ... :-) First we can clear
3012 * the EOF flag, if set. Repositioning will clean up any EOF state.
3013 */
3014 if (oper & OPER_READ)
3015 {
3016 ptr->flag &= ~(FLAG_RDEOF) ;
3017 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
3018 }
3019 if (oper & OPER_WRITE)
3020 ptr->flag &= ~(FLAG_WREOF) ;
3021
3022 /*
3023 * Positioning by line in a forwards direction is always going to be more efficient
3024 * starting at the current line position and reading forward.
3025 * Positioning by line in a backwards direction may be more efficient to start at the
3026 * beginning of the file and read forwards, rather than reading backwards; it
3027 * depends on how far back we are positioning.
3028 */
3029 switch( from )
3030 {
3031 case SEEK_CUR: /* position relative to current position */
3032 if ( oper & OPER_READ )
3033 {
3034 if ( ptr->readline > 0 )
3035 {
3036 from_line = ptr->readline ;
3037 from_char = ptr->readpos ;
3038 ret = positionfile_SEEK_CUR( TSD, bif, argno, ptr, OPER_READ, lineno, from_line, from_char );
3039 }
3040 else
3041 {
3042 /*
3043 * If the readpos is set, then we can (inefficiently) determine the line
3044 * position by starting at the beginning of the file.
3045 */
3046 if ( ptr->readpos != (size_t) -1 )
3047 {
3048 /*
3049 * FIXME: We need a mechanism to convert to readpos into a readline;
3050 * positionfile_SEEK_SET() doesn't do it for us.
3051 *
3052 */
3053 errno = 2;
3054 ret = -1;
3055 break;
3056 }
3057 else
3058 {
3059 /*
3060 * ERROR:2
3061 * Can't seek lines relatively if there is no current read char position
3062 */
3063 errno = 2;
3064 ret = -1;
3065 break;
3066 }
3067 }
3068 }
3069 if ( oper & OPER_WRITE )
3070 {
3071 if ( ptr->writeline > 0 )
3072 {
3073 from_line = ptr->writeline ;
3074 from_char = ptr->writepos ;
3075 ret = positionfile_SEEK_CUR( TSD, bif, argno, ptr, OPER_WRITE, lineno, from_line, from_char );
3076 }
3077 else
3078 {
3079 /*
3080 * If the writepos is set, then we can (inefficiently) determine the line
3081 * position by starting at the beginning of the file.
3082 */
3083 if ( ptr->writepos != (size_t) -1 )
3084 {
3085 /*
3086 * FIXME: We need a mechanism to convert to writepos into a writeline;
3087 * positionfile_SEEK_SET() doesn't do it for us.
3088 *
3089 */
3090 errno = 2;
3091 ret = -1;
3092 break;
3093 }
3094 else
3095 {
3096 /*
3097 * ERROR:2
3098 * Can't seek lines relatively if there is no current write char position
3099 */
3100 errno = 2;
3101 ret = -1;
3102 break;
3103 }
3104 }
3105 }
3106 /*
3107 * Now we are almost finished. We just have to set the correct
3108 * information in the Rexx file table entry.
3109 */
3110 if ( (oper & OPER_READ) && (oper & OPER_WRITE) )
3111 {
3112 ptr->oper = OPER_NONE;
3113 }
3114 if ( oper & OPER_READ )
3115 {
3116 ptr->flag &= ~(FLAG_RDEOF) ;
3117 ptr->flag &= ~(FLAG_AFTER_RDEOF) ;
3118 }
3119 if ( oper & OPER_WRITE )
3120 {
3121 ptr->flag &= ~(FLAG_WREOF) ;
3122 }
3123 break;
3124 case SEEK_END: /* position relative to end of file */
3125 ret = positionfile_SEEK_END( TSD, bif, argno, ptr, oper, lineno );
3126 break;
3127 case SEEK_SET: /* position absolute */
3128 ret = positionfile_SEEK_SET( TSD, bif, argno, ptr, oper, lineno );
3129 default: /* should not get here */
3130 break;
3131 }
3132 return ret;
3133 }
3134
3135
3136
3137 /*
3138 * I wish every function would be as easy as this! Basically, it
3139 * only contain simple error checking, and a direct positioning.
3140 * it is called by the built-in function CHARIN() and CHAROUT()
3141 * in order to position the current read or write position at the
3142 * correct place in the file.
3143 */
positioncharfile(tsd_t * TSD,const char * bif,int argno,fileboxptr fileptr,int oper,rx_64 where,int from)3144 static rx_64 positioncharfile( tsd_t *TSD, const char *bif, int argno, fileboxptr fileptr, int oper, rx_64 where, int from )
3145 {
3146 rx_64 where_read=0,where_write=0;
3147 /*
3148 * If the file is in state ERROR, don't touch it! Since we are not
3149 * to return any data, don't bother about the state of FAKE.
3150 */
3151 if (fileptr->flag & FLAG_ERROR)
3152 {
3153 if (!(fileptr->flag & FLAG_FAKE))
3154 file_error( fileptr, 0, NULL ) ;
3155 return 0;
3156 }
3157
3158 /*
3159 * If the file is not persistent, then positioning is not allowed.
3160 * Give the appropriate error for this.
3161 */
3162 if (!(fileptr->flag & FLAG_PERSIST))
3163 exiterror( ERR_INCORRECT_CALL, 42, bif, tmpstr_of( TSD, fileptr->filename0 ) ) ;
3164 /*
3165 * If the operation is READ, but the file is not open for READ,
3166 * return an error.
3167 */
3168 if ((oper&OPER_READ) && !(fileptr->flag & FLAG_READ))
3169 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "READ" ) ;
3170 /*
3171 * If the operation is WRITE, but the file is not open for WRITE,
3172 * return an error.
3173 */
3174 if ((oper&OPER_WRITE) && !(fileptr->flag & FLAG_WRITE))
3175 exiterror( ERR_INCORRECT_CALL, 921, bif, argno, "WRITE" ) ;
3176
3177 #ifdef TRUE_TRL_IO
3178 /*
3179 * TRL says that positioning the read position to after the last
3180 * character in the file, is an error. Unix allows it, and gives
3181 * an EOF at the next reading. So, we have to handle this as a
3182 * special case ... Check that the new position is valid.
3183 *
3184 * Should we give "Incorrect call to routine" when the character
3185 * position is greater than the size of the file? Perhaps we should
3186 * raise the NOTREADY condition instead?
3187 */
3188 {
3189 rx_64 oldp, endp ;
3190
3191 oldp = rx_ftell( fileptr->fileptr ) ;
3192 rx_fseek(fileptr->fileptr, 0, SEEK_END) ;
3193 endp = rx_ftell( fileptr->fileptr ) ;
3194 rx_fseek( fileptr->fileptr, oldp, SEEK_SET ) ;
3195 fileptr->oper = OPER_NONE;
3196
3197 /*
3198 * Determine the value of "where" depending on the starting
3199 * location determined by "from". "where" is passed in in an
3200 * external format; ie 1 based, internally it must be 0 based
3201 */
3202 switch(from)
3203 {
3204 case SEEK_CUR:
3205 if ( oper & OPER_READ )
3206 where_read = 1 + where + fileptr->readpos;
3207 if ( oper & OPER_WRITE )
3208 where_write = 1 + where + fileptr->writepos;
3209 break;
3210 case SEEK_END:
3211 if ( oper & OPER_READ )
3212 where_read = endp - where;
3213 #if SEEK_TO_EOF_FOR_WRITE_IS_AT_EOF
3214 if ( oper & OPER_WRITE )
3215 where_write = endp - where;
3216 #else
3217 if ( oper & OPER_WRITE )
3218 where_write = 1 + endp - where;
3219 #endif
3220 break;
3221 default: /* SEEK_SET */
3222 if ( oper & OPER_READ )
3223 where_read = where;
3224 if ( oper & OPER_WRITE )
3225 where_write = where;
3226 break;
3227 }
3228 if ( oper & OPER_READ )
3229 {
3230 if ( where_read < 1 )
3231 {
3232 file_error( fileptr, 0, "Repositioning before start of file" ) ;
3233 return 0;
3234 }
3235 if ( endp < where_read )
3236 {
3237 file_error( fileptr, 0, "Repositioning at or after EOF" ) ;
3238 return 0;
3239 }
3240 }
3241 if ( oper & OPER_WRITE )
3242 {
3243 if ( where_write < 1 )
3244 {
3245 file_error( fileptr, 0, "Repositioning before start of file" ) ;
3246 return 0;
3247 }
3248 if ( (endp+1) < where_write )
3249 {
3250 file_error( fileptr, 0, "Repositioning after EOF" ) ;
3251 return 0;
3252 }
3253 }
3254 }
3255 #endif
3256
3257 /*
3258 * Then do the actual positioning. Remember to clear errno first.
3259 * Previously, this code tested afterwards to see if ftell()
3260 * returned the same position that fseek() tried to set. Surely, that
3261 * must be unnecessary?
3262 * We need to reposition using both the read and write postions (if
3263 * required).
3264 */
3265 errno = 0 ;
3266 /*
3267 * Position the real file pointer to the write or read pointers
3268 * calculated. The "thispos" member is set to the last seek
3269 * executed. READ is done last as this is probably the most
3270 * likely use of character positioning, hence it may be slightly
3271 * more efficient.
3272 */
3273 if ( oper & OPER_WRITE )
3274 {
3275 if ( rx_fseek(fileptr->fileptr,(where_write-1),SEEK_SET ) )
3276 {
3277 file_error( fileptr, errno, NULL ) ;
3278 return 0;
3279 }
3280 fileptr->thispos = where_write ; /* this was where-1; is that correct ?*/
3281 }
3282 if ( oper & OPER_READ )
3283 {
3284 if ( rx_fseek(fileptr->fileptr,(where_read-1),SEEK_SET) )
3285 {
3286 file_error( fileptr, errno, NULL ) ;
3287 return 0;
3288 }
3289 fileptr->thispos = where_read ; /* this was where-1; is that correct ?*/
3290 }
3291 fileptr->oper = OPER_NONE;
3292
3293 /*
3294 * Then we have to update the file pointers in the entry in our
3295 * file table.
3296 *
3297 * Clear the end-of-file flag. Even if we *did* position to the
3298 * end of file, we don't want to discover that until we actually
3299 * _read_ data that is _off_ the end-of-file.
3300 */
3301
3302 if (oper & OPER_READ)
3303 {
3304 fileptr->readpos = where_read-1 ;
3305 fileptr->flag &= ~(FLAG_RDEOF) ;
3306 fileptr->flag &= ~(FLAG_AFTER_RDEOF) ;
3307 }
3308 if (oper & OPER_WRITE)
3309 {
3310 fileptr->writepos = where_write-1 ;
3311 fileptr->flag &= ~(FLAG_WREOF) ;
3312 }
3313 if (oper == OPER_NONE)
3314 file_error( fileptr, 0, NULL ) ;
3315
3316 /*
3317 * We have moved the file pointer by a number of characters which
3318 * may have spanned any number of lines. So we have no idea which
3319 * line we are in the file, so we need to invalidate the
3320 * read or write line position.
3321 */
3322 if (oper & OPER_READ)
3323 fileptr->readline = 0 ;
3324 if (oper & OPER_WRITE)
3325 fileptr->writeline = 0 ;
3326
3327 /*
3328 * Return the new position of the file pointer. If both file
3329 * pointers were set, then readpos and writepos are the same, so
3330 * the following test is valid.
3331 */
3332 if (oper & OPER_READ)
3333 return fileptr->readpos + 1; /* external representation */
3334 else
3335 return fileptr->writepos + 1; /* external representation */
3336 }
3337
3338
3339
3340 /*
3341 * This routine reads a string of data from a file indicated by
3342 * the Rexx file table entry 'ptr'. The read starts at the current
3343 * read position, and the length will be 'length' characters.
3344 *
3345 * Then, what if the data to be read are more than what is possible
3346 * to store in one string; let's say length=100,000, and the size of
3347 * length in a string is 16 bit. Well, That should return an error
3348 * in Str_makeTSD(), but maybe we should handle it more elegantly?
3349 *
3350 * No file_error() is thrown if noerrors is set.
3351 */
readbytes(tsd_t * TSD,fileboxptr fileptr,size_t length,int noerrors)3352 static streng *readbytes( tsd_t *TSD, fileboxptr fileptr, size_t length, int noerrors )
3353 {
3354 size_t didread=0 ;
3355 streng *retvalue=NULL ;
3356
3357 /*
3358 * If state is ERROR, then refuse to handle the file further.
3359 * If the state was 'only' EOF, then don't bother, the length of
3360 * the file might have increased since last attempt to read.
3361 */
3362 if (fileptr->flag & FLAG_ERROR)
3363 {
3364 if (!noerrors && !(fileptr->flag & FLAG_FAKE))
3365 file_error( fileptr, 0, NULL ) ;
3366 return nullstringptr() ;
3367 }
3368
3369 assert( fileptr->flag & FLAG_READ ) ;
3370
3371 /*
3372 * If we are not at the current read position, we have to
3373 * seek to the correct position, but first we have to the validity
3374 * of these positions.
3375 */
3376 if (fileptr->flag & FLAG_PERSIST)
3377 {
3378 if (fileptr->thispos != fileptr->readpos)
3379 {
3380 errno = 0 ;
3381 if ( fileptr->flag & FLAG_PERSIST
3382 && rx_fseek(fileptr->fileptr, fileptr->readpos, SEEK_SET ))
3383 {
3384 if (!noerrors)
3385 file_error( fileptr, errno, NULL ) ;
3386 return nullstringptr() ;
3387 }
3388 fileptr->thispos = fileptr->readpos ;
3389 fileptr->oper = OPER_NONE ;
3390 }
3391 }
3392
3393 /*
3394 * The joy of POSIX ... If a file is open for input and output, it
3395 * must be flushed when changing between the two. Therefore, check
3396 * the type of the last operation. Actually, this are not very likely
3397 * since that situation would in general have been handled above.
3398 */
3399 if (fileptr->oper==OPER_WRITE)
3400 {
3401 errno = 0 ;
3402 if ( fileptr->flag & FLAG_PERSIST
3403 && rx_fseek( fileptr->fileptr, 0L, SEEK_CUR ))
3404 {
3405 /* Hey, how could this have happened?!?! NFS down? */
3406 if (!noerrors)
3407 file_error( fileptr, errno, NULL ) ;
3408 return nullstringptr() ;
3409 }
3410 fileptr->oper = OPER_NONE ;
3411 }
3412
3413 /*
3414 * Lets get ready for the big event. First allocate enough space to
3415 * hold the data we are hoping to be able to read. Then read it
3416 * directly into the string.
3417 */
3418 retvalue = Str_makeTSD(length+1) ;
3419 errno = 0 ;
3420 didread = fread( retvalue->value, 1, length, fileptr->fileptr ) ;
3421 fileptr->oper = OPER_READ;
3422
3423 /*
3424 * Variable 'read' contains the number of items (=bytes) read, or
3425 * it contains EOF if an error occurred. Handle the error the
3426 * normal way; i.e. trigger file_error and return nothing.
3427 */
3428 if (didread==EOF)
3429 {
3430 if (!noerrors)
3431 file_error( fileptr, errno, NULL ) ;
3432 return nullstringptr() ;
3433 }
3434
3435 /*
3436 * What if we didn't manage to read all the data? Well, return what
3437 * we got, but still trigger an error, since EOF should be
3438 * considered a NOTREADY condition. However, we try to handle EOF
3439 * a bit more elegantly than other errors, since lots of programmers
3440 * are probably not bothering about EOF; an EOF condition should be
3441 * able to be reset using a file positioning.
3442 */
3443 assert( 0<=didread && didread<=length ) ; /* It'd better be! */
3444 retvalue->len = didread ;
3445 if (didread<length)
3446 {
3447 if (!noerrors)
3448 file_warning( fileptr, 0, "EOF on char input" ) ;
3449 fileptr->flag |= FLAG_RDEOF ;
3450 }
3451 else
3452 {
3453 fileptr->flag &= ~FLAG_RDEOF ;
3454 fileptr->flag &= ~FLAG_AFTER_RDEOF ;
3455 }
3456
3457 /*
3458 * Then, at the end, we have to set the pointers and counter to
3459 * the correct values
3460 */
3461 fileptr->thispos += didread ;
3462 fileptr->readpos += didread ;
3463 fileptr->readline = (-1) ;
3464 fileptr->linesleft = 0 ;
3465
3466 return retvalue ;
3467 }
3468
3469
3470
3471 /*
3472 * This routines write a string to a file pointed to by the Rexx file
3473 * table entry 'fileptr'. The string to be written is 'string', and the
3474 * length of the write is implicitly given as the length of 'string'
3475 *
3476 * This routine is called from the Rexx built-in function CHAROUT().
3477 * It is a fairly straight forward implementation.
3478 *
3479 * No file_error() is thrown if noerrors is set.
3480 */
writebytes(tsd_t * TSD,fileboxptr fileptr,const streng * string,int noerrors)3481 static size_t writebytes( tsd_t *TSD, fileboxptr fileptr, const streng *string, int noerrors )
3482 {
3483 #ifdef WIN32
3484 int rc=0;
3485 #endif
3486 long todo, done, written=0 ;
3487 const char *buf ;
3488
3489 /*
3490 * First, if this file is in state ERROR, don't touch it, what to
3491 * return depends on whether the file is in state FAKE.
3492 */
3493 if ( fileptr->flag & FLAG_ERROR )
3494 {
3495 if ( fileptr->flag & FLAG_FAKE )
3496 return string->len ;
3497 else
3498 {
3499 if (!noerrors)
3500 file_error( fileptr, 0, NULL ) ;
3501 if (fileptr->flag & FLAG_FAKE)
3502 return string->len ;
3503
3504 return 0 ;
3505 }
3506 }
3507 /*
3508 * If we are not at the current write position, we have to
3509 * seek to the correct position
3510 */
3511 if (fileptr->thispos != fileptr->writepos)
3512 {
3513 errno = 0 ;
3514 if ( fileptr->flag & FLAG_PERSIST
3515 && rx_fseek(fileptr->fileptr, fileptr->writepos, SEEK_SET ))
3516 {
3517 if (!noerrors)
3518 file_error( fileptr, errno, NULL ) ;
3519 return 0 ;
3520 }
3521 fileptr->thispos = fileptr->writepos ;
3522 fileptr->oper = OPER_NONE ;
3523 }
3524
3525 /*
3526 * If previous operation on this file was a read, we have to flush
3527 * the file before we can perform any write operations. This will
3528 * seldom happen, since it is in general handled above.
3529 */
3530 if (fileptr->oper == OPER_READ)
3531 {
3532 errno = 0 ;
3533 if ( fileptr->flag & FLAG_PERSIST
3534 && rx_fseek(fileptr->fileptr, 0, SEEK_CUR))
3535 {
3536 if (!noerrors)
3537 file_error( fileptr, errno, NULL ) ;
3538 return (fileptr->flag & FLAG_FAKE) ? string->len : 0 ;
3539 }
3540 fileptr->oper = OPER_NONE ;
3541 }
3542
3543 /*
3544 * Here comes the actual writing. This also works when the length
3545 * of string is zero.
3546 */
3547 errno = 0 ;
3548 buf = string->value ;
3549 todo = string->len ;
3550 fileptr->oper = OPER_WRITE ;
3551 do
3552 {
3553 #ifdef WIN32
3554 done = fwrite( buf, 1, todo, fileptr->fileptr ) ;
3555 #else
3556 done = fwrite( buf, 1, todo, fileptr->fileptr ) ;
3557 #endif
3558 /*
3559 * Win32 has a bug with fwrite and disk full. If the size of the
3560 * chunk to write is < 4096 and the disk fills up, then you don't get
3561 * an error indication. So flush the stream if the size of data is
3562 * < 4096 and test the result of fflush(). Bug 731664
3563 */
3564 #ifdef WIN32
3565 if (string->len < 4096 )
3566 rc = fflush( fileptr->fileptr );
3567 if (done < 0 || rc != 0 )
3568 #else
3569 if (done < 0)
3570 #endif
3571 {
3572 written = -1 ;
3573 break ;
3574 } else if (done == 0)
3575 break;
3576 else
3577 written += done ;
3578 buf += done ;
3579 todo -= done ;
3580 } while ( ( todo > 0 ) && noerrors ) ;
3581
3582 /*
3583 * Here comes the error checking. Note that this function will
3584 * return the number of elements written, it will never return
3585 * EOF as fread can, since the problems surrounding EOF can not
3586 * occur in this operation. Therefore, report a fullfleged error
3587 * whenever rc is less than the length of string.
3588 */
3589 assert( 0<=written && written<=string->len ) ;
3590 if (written < string->len )
3591 {
3592 if (!noerrors)
3593 file_error( fileptr, errno, NULL ) ;
3594 }
3595 else
3596 {
3597 /*
3598 * If the operation was successful, then we set misc status
3599 * information about the file, and the counters and pointers.
3600 */
3601 fileptr->writeline = 0 ;
3602 fileptr->flag &= ~FLAG_RDEOF ;
3603 fileptr->flag &= ~FLAG_AFTER_RDEOF ;
3604 fileptr->thispos += written ;
3605 fileptr->writepos += written ;
3606
3607 fflush( fileptr->fileptr ) ;
3608 fileptr->oper = OPER_NONE;
3609 }
3610
3611 return written ;
3612 }
3613
3614
3615 /*
3616 * This routine calculates the number of bytes remaining in the file,
3617 * i.e the number of bytes from the current read position until the
3618 * end-of-file. It is, of course, called from the Rexx built-in
3619 * function CHARS()
3620 */
3621
calc_chars_left(tsd_t * TSD,fileboxptr ptr)3622 static rx_64 calc_chars_left( tsd_t *TSD, fileboxptr ptr )
3623 {
3624 rx_64 left=0 ;
3625 rx_64 oldpoint=0L, newpoint=0L ;
3626
3627 if (!(ptr->flag & FLAG_READ))
3628 return 0 ;
3629
3630 /*
3631 * First, determine whether this file is in ERROR state. If so, we
3632 * don't want to touch it. Whether or not the file is in FAKE state
3633 * is fairly irrelevant in this situation
3634 */
3635 if ( ptr->flag & FLAG_ERROR )
3636 {
3637 if (!(ptr->flag & FLAG_FAKE))
3638 file_error( ptr, 0, NULL ) ;
3639 return 0 ;
3640 }
3641
3642 /*
3643 * If this is not a persistent file, then we have no means of finding
3644 * out how much of the file is available. Then, return 1 if we are not
3645 * at the end-of-file, and 0 otherwise.
3646 */
3647 if (!(ptr->flag & FLAG_PERSIST))
3648 {
3649 #if 0
3650 left = ( !(ptr->flag & FLAG_RDEOF)) ;
3651 #else
3652 struct rx_stat_buf finfo;
3653 int fno;
3654
3655 fno = fileno( ptr->fileptr ) ;
3656 rx_fstat( fno, &finfo );
3657 left = finfo.st_size;
3658 #endif
3659 }
3660 else
3661 {
3662 /*
3663 * This is a persistent file, which is not in error state. OK, then
3664 * we must record the current point, fseek to the end-of-file,
3665 * ftell to get that position, and fseek back to where we started.
3666 * And we have to check for errors everywhere ... sigh.
3667 *
3668 * First, record the current position in the file.
3669 */
3670 errno = 0 ;
3671 oldpoint = rx_ftell( ptr->fileptr ) ;
3672 if (oldpoint==EOF)
3673 {
3674 file_error( ptr, errno, NULL ) ;
3675 return 0 ;
3676 }
3677
3678 /*
3679 * Then, move the current position to the end-of-file
3680 */
3681 errno = 0 ;
3682 if (rx_fseek(ptr->fileptr, 0L, SEEK_END))
3683 {
3684 file_error( ptr, errno, NULL ) ;
3685 return 0 ;
3686 }
3687 ptr->oper = OPER_NONE;
3688
3689 /*
3690 * And record the position of the end-of-file
3691 */
3692 errno = 0 ;
3693 newpoint = rx_ftell( ptr->fileptr ) ;
3694 if (newpoint==EOF)
3695 {
3696 file_error( ptr, errno, NULL ) ;
3697 return 0 ;
3698 }
3699
3700 /*
3701 * And, at last, position back to the place where we started.
3702 * Actually, this may not be necessary, since we _can_ leave the
3703 * current position at the end-of-file. After all, the next read
3704 * or write _will_ position back correctly. However, let's be
3705 * nice ...
3706 */
3707 errno = 0 ;
3708 if (rx_fseek(ptr->fileptr, oldpoint, SEEK_SET))
3709 {
3710 file_error( ptr, errno, NULL ) ;
3711 return 0 ;
3712 }
3713
3714 /*
3715 * Then we have some accounting to do; calculate the size of the
3716 * last part of the file. And also set oper to NONE, we _have_
3717 * done a repositioning ... actually, several :-)
3718 */
3719 left = newpoint - ptr->readpos ;
3720 /* left = newpoint - oldpoint ; */ /* YURI - wrong */
3721 ptr->oper = OPER_NONE ;
3722 }
3723
3724 return left ;
3725 }
3726
3727
3728 /*
3729 * This routine counts the complete lines remaining in the file
3730 * pointed to by the Rexx file table entry 'ptr'. The count starts
3731 * at the current read or write position, and the current line will be counted
3732 * even if the current read position points to the middle of a line.
3733 * The last line will only be counted if it was actually terminated
3734 * by a EOL marker. If the current line is the last line, but it was
3735 * not explicitly terminated by a EOL marker, zero is returned.
3736 */
countlines(tsd_t * TSD,fileboxptr ptr,int actual,int oper)3737 static rx_64 countlines( tsd_t *TSD, fileboxptr ptr, int actual, int oper )
3738 {
3739 long oldpoint=0L ;
3740 int left=0, ch=0;
3741 int prevch=-1 ;
3742
3743 /*
3744 * If this file is in ERROR state, we really don't want to try to
3745 * operate on it. Just report an error, and return 0.
3746 */
3747 if ( ptr->flag & FLAG_ERROR )
3748 {
3749 if (!(ptr->flag & FLAG_FAKE))
3750 file_error( ptr, 0, NULL ) ;
3751 return 0 ;
3752 }
3753
3754 /*
3755 * Counting lines requires us to reposition in the file. However,
3756 * we can not reposition in transient files. If this is not a
3757 * persistent file, don't do any repositioning, just return one
3758 * for any situation where we are not sure whether there are more
3759 * data or not (i.e. unless we are sure that there are no more data,
3760 * return "1"
3761 */
3762 if (!(ptr->flag & FLAG_PERSIST))
3763 return (!feof(ptr->fileptr)) ;
3764 else if (!actual)
3765 {
3766 /*
3767 * We just want to know of there are any lines left for a persistent
3768 * file. First check if we have reached EOF. Return 0 if so.
3769 * Next check if the file size is 0; if so then return 0, otherwise
3770 * there is at least 1 line left; return 1
3771 */
3772 if ( feof(ptr->fileptr ) )
3773 return 0;
3774 else
3775 {
3776 struct rx_stat_buf buffer ;
3777 int fno;
3778 memset( &buffer, 0, sizeof(buffer) );
3779 fno = fileno( ptr->fileptr ) ;
3780 rx_fstat( fno, &buffer ) ;
3781 return ((buffer.st_size==0) ? 0 : 1);
3782 }
3783 }
3784 else
3785 {
3786 /*
3787 * Take advantage of the cached value of the lines left in the
3788 * file
3789 */
3790 if (ptr->linesleft)
3791 return ptr->linesleft ;
3792
3793 /*
3794 * If, however, this is a persistent file, we have to read from
3795 * the current read position to the end-of-file, and count all
3796 * the lines. First, make sure that we position at the current
3797 * read position.
3798 */
3799 errno = 0 ;
3800 oldpoint = rx_ftell( ptr->fileptr ) ;
3801 if (oldpoint==EOF)
3802 {
3803 file_error( ptr, errno, NULL ) ;
3804 return 0 ;
3805 }
3806
3807 /*
3808 * Then read the rest of the file, and keep a count of all the files
3809 * read in the process.
3810 */
3811 SWITCH_OPER_READ(ptr);
3812 /*
3813 * Switch to the current read or write pos setting
3814 */
3815 if ( oper == OPER_READ )
3816 ptr->thispos = ptr->readpos;
3817 else
3818 ptr->thispos = ptr->writepos;
3819 rx_fseek(ptr->fileptr,ptr->thispos,SEEK_SET);
3820 #if defined(UNIX) || defined(MAC)
3821 for(left=0;((ch=getc(ptr->fileptr))!=EOF);)
3822 {
3823 if (ch==REGINA_EOL)
3824 left++ ;
3825 prevch = ch;
3826 }
3827 if (prevch != REGINA_EOL
3828 && prevch != -1)
3829 left++;
3830 #else
3831 for(left=0;;)
3832 {
3833 ch = getc(ptr->fileptr);
3834 if (ch == EOF)
3835 break;
3836 if ( ch == REGINA_CR)
3837 left++ ;
3838 else
3839 {
3840 if ( ch == REGINA_EOL && prevch != REGINA_CR)
3841 left++ ;
3842 }
3843 prevch = ch;
3844 }
3845 if (prevch != REGINA_EOL
3846 && prevch != REGINA_CR
3847 && prevch != -1)
3848 left++;
3849 #endif
3850
3851 /*
3852 * At the end, try to reposition back to the old current read
3853 * position, and report an error if that attempt failed.
3854 */
3855 errno = 0 ;
3856 if ( ptr->flag & FLAG_PERSIST
3857 && rx_fseek(ptr->fileptr, oldpoint, SEEK_SET))
3858 {
3859 file_error( ptr, errno, NULL ) ;
3860 return 0 ;
3861 }
3862 ptr->oper = OPER_NONE;
3863 ptr->linesleft = left ;
3864 }
3865 return left ;
3866 }
3867
3868
3869
3870 /*
3871 * This routine writes a line to the file indicated by 'ptr'. The line
3872 * to be written is 'data', and it will be terminated by an extra
3873 * EOL marker after the charactrers in 'data'.
3874 */
writeoneline(tsd_t * TSD,fileboxptr ptr,const streng * data)3875 static int writeoneline( tsd_t *TSD, fileboxptr ptr, const streng *data )
3876 {
3877 const char *i=NULL ;
3878 int num_eol_chars=0;
3879
3880 /*
3881 * First, make sure that the file is not in ERROR state. If it is
3882 * report an error, and return a result depending on whether this
3883 * file is to be faked.
3884 */
3885 if (ptr->flag & FLAG_ERROR)
3886 {
3887 if (ptr->flag & FLAG_FAKE)
3888 return 0 ;
3889 else
3890 {
3891 file_error( ptr, 0, NULL ) ;
3892 if (ptr->flag & FLAG_FAKE)
3893 return 0 ;
3894 return 1 ;
3895 }
3896 }
3897
3898 /*
3899 * If we are to write a new line, we ought to truncate the file after
3900 * that line. Or rather, we truncate the file at the start of the
3901 * new line, before we write it out. But only if we have the non-POSIX
3902 * function ftruncate() available. And not if we are already there.
3903 */
3904 #if defined(HAVE_FTRUNCATE)
3905 if ( get_options_flag( TSD->currlevel, EXT_LINEOUTTRUNC ) )
3906 {
3907 if (ptr->oper != OPER_WRITE && !(ptr->flag & (FLAG_WREOF)) &&
3908 (ptr->flag & FLAG_PERSIST))
3909 {
3910 int fno ;
3911 errno = 0 ;
3912 SWITCH_OPER_WRITE(ptr); /* Maybe, ftruncate is a write operation in
3913 * the meaning of POSIX. This shouldn't do
3914 * any harm in other systems.
3915 */
3916
3917 fno = fileno( ptr->fileptr ) ;
3918 if (ftruncate( fno, ptr->writepos) == -1)
3919 {
3920 file_error( ptr, errno, NULL ) ;
3921 return !(ptr->flag & FLAG_FAKE) ;
3922 }
3923 if ( ptr->flag & FLAG_PERSIST )
3924 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
3925 ptr->oper = OPER_NONE;
3926 ptr->thispos = ptr->writepos = rx_ftell( ptr->fileptr ) ;
3927 if (ptr->readpos>ptr->thispos && ptr->readpos!= (size_t) EOF)
3928 {
3929 ptr->readpos = ptr->thispos ;
3930 ptr->readline = 0 ;
3931 ptr->linesleft = 0 ;
3932 }
3933 }
3934 }
3935 #endif
3936
3937 /*
3938 * Then, output the characters in 'data', and sense any problem.
3939 * If there is a problem, report an error
3940 */
3941 errno = 0 ;
3942 SWITCH_OPER_WRITE(ptr);
3943 for (i=data->value; i<Str_end(data); i++)
3944 {
3945 if (putc( *i, ptr->fileptr)==EOF)
3946 {
3947 file_error( ptr, errno, NULL ) ;
3948 return 1 ;
3949 }
3950 }
3951
3952 /*
3953 * After all the data has been written out, we have to explicitly
3954 * terminate the file with an end-of-line marker. Under Unix this
3955 * is the single character EOL. Under Macintosh this is the single
3956 * character CR, and all others it is CR and EOL.
3957 */
3958 #if !defined(UNIX)
3959 SWITCH_OPER_WRITE(ptr);
3960 if (putc( REGINA_CR, ptr->fileptr)==EOF)
3961 {
3962 file_error( ptr, errno, NULL ) ;
3963 return 1 ;
3964 }
3965 num_eol_chars++;
3966 #endif
3967 #if !defined(MAC)
3968 SWITCH_OPER_WRITE(ptr);
3969 if (putc( REGINA_EOL, ptr->fileptr)==EOF)
3970 {
3971 file_error( ptr, errno, NULL ) ;
3972 return 1 ;
3973 }
3974 num_eol_chars++;
3975 #endif
3976
3977 /*
3978 * Then we have to update the counters and pointers in the Rexx
3979 * file table entry. We must do that in order to be able to keep
3980 * track of where we are.
3981 */
3982 ptr->thispos += data->len + num_eol_chars ; /* fix 736578 */
3983 ptr->writepos = ptr->thispos ;
3984 ptr->oper = OPER_WRITE ;
3985
3986 /*
3987 * FIXME - under what circumstances will writeline be 0 ?
3988 * If it hasn't been determined by what calls this function, the
3989 * a) the calling function(s) should determine the line number, or
3990 * b) the current line number should be determined here.
3991 */
3992 if (ptr->writeline)
3993 ptr->writeline++ ;
3994
3995 ptr->flag |= FLAG_WREOF ;
3996
3997 /*
3998 * At the end, we flush the data. We do this in order to avoid
3999 * surprises later. Maybe we shouldn't do that, since it may force
4000 * a systemcall, which might give away the timeslice and decrease
4001 * system time. So you might want to remove this call ... at your
4002 * own risk :-)
4003 */
4004 errno = 0 ;
4005 if (fflush( ptr->fileptr ))
4006 {
4007 file_error( ptr, errno, NULL ) ;
4008 return 1 ;
4009 }
4010
4011 return 0 ;
4012 }
4013
4014 /*
4015 * This routine is a way of retrieving the information returned by the
4016 * standard Unix call stat(). It takes the name of a file as parameter,
4017 * and return information about that file. This is not standard Rexx,
4018 * but quite useful. It is accessed through the built-in function
4019 * STREAM(), command 'FSTAT'
4020 * This is now also used for the "standard" STREAM() options.
4021 * *Persistent will be set to 1 if the stream's type is a file. The setting
4022 * happens on success and if Persistent isn't NULL.
4023 */
getstatus(tsd_t * TSD,const streng * filename,int subcommand)4024 static streng *getstatus( tsd_t *TSD, const streng *filename , int subcommand )
4025 {
4026 fileboxptr ptr=NULL ;
4027 int rc=0 ;
4028 int fno=0 ;
4029 rx_64 pos_read = -2L, pos_write = -2L, line_read = -2L, line_write = -2;
4030 int streamtype = STREAMTYPE_UNKNOWN;
4031 streng *result=NULL ;
4032 struct rx_stat_buf buffer ;
4033 struct tm tmdata, *tmptr ;
4034 char *fn=NULL;
4035 #if 0
4036 static const char *fmt = "%02d-%02d-%02d %02d:%02d:%02d" ;
4037 static const char *iso = "%04d-%02d-%02d %02d:%02d:%02d" ;
4038 #endif
4039 static const char *streamdesc[] = { "UNKNOWN", "PERSISTENT", "TRANSIENT" };
4040 char tmppwd[50];
4041 char tmpgrp[50];
4042 char *ptmppwd=tmppwd,*ptmpgrp=tmpgrp;
4043 #if !(defined(VMS) || defined(MAC) || defined(OS2) || defined(DOS) || (defined (__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__LCC__))
4044 struct passwd *ppwd;
4045 struct group *pgrp;
4046 #endif
4047
4048 memset( &buffer, 0, sizeof(buffer) );
4049 /*
4050 * Nul terminate the input filename string, as stat() will barf if
4051 * it isn't and other functions stuff up!
4052 */
4053 fn = str_ofTSD(filename);
4054 /*
4055 * First get the Rexx file table entry associated with the file,
4056 * and then call stat() for that file. If the file is already open,
4057 * then call fstat, since that will in general be a 'safer' way
4058 * to be sure that it is _really_ the file that is open in Rexx.
4059 */
4060 ptr = getfileptr( TSD, filename ) ;
4061 if (ptr && ptr->fileptr)
4062 {
4063 fno = fileno( ptr->fileptr ) ;
4064 rc = rx_fstat( fno, &buffer ) ;
4065 if (ptr->flag & FLAG_PERSIST)
4066 streamtype = STREAMTYPE_PERSISTENT;
4067 else
4068 streamtype = STREAMTYPE_TRANSIENT;
4069 pos_read = ptr->readpos;
4070 pos_write = ptr->writepos;
4071 line_read = ptr->readline;
4072 line_write = ptr->writeline;
4073 }
4074 else
4075 {
4076 /*
4077 * To be consistent with other functions when determining persistence,
4078 * we need to check for a "regular" file. Everything other than a
4079 * "regular" file in transient.
4080 * If we don't have S_ISREG macro, then revert to a simple check; if the
4081 * stream is a directory it is transent; ugly!
4082 */
4083 rc = rx_stat( fn, &buffer ) ;
4084 if ( rc != 0 )
4085 streamtype = STREAMTYPE_UNKNOWN;
4086 else
4087 {
4088 /*
4089 * Resolves 802114.
4090 */
4091 streamtype = stream_types[determine_stream_type( buffer.st_mode )].streamtype;
4092 }
4093 }
4094
4095 /*
4096 * If we were able to retrieve any useful information, store it
4097 * in a string of suitable length, and return that string.
4098 * If the filename does not exist, always return an empty string.
4099 */
4100 if ( rc == -1 )
4101 {
4102 if ( fn )
4103 FreeTSD( fn );
4104 checkProperStreamName( TSD, NULL, (const char *) tmpstr_of( TSD, filename ), errno );
4105 return nullstringptr();
4106 }
4107 switch ( subcommand )
4108 {
4109 case COMMAND_FSTAT:
4110 #ifdef HAVE_LSTAT
4111 /*
4112 * If we have lstat(), use it to gather details, this is the only
4113 * way to determine if the file is a symlink.
4114 */
4115 lstat(fn, &buffer) ;
4116 #endif
4117 #if defined(VMS) || defined(MAC) || defined(OS2) || defined(DOS) || (defined (__WATCOMC__) && !defined(__QNX__)) || defined(_MSC_VER) || (defined(WIN32) && defined(__IBMC__)) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__EPOC32__) || defined(__LCC__)
4118 ptmppwd = "USER";
4119 ptmpgrp = "GROUP";
4120 #else
4121 ppwd = getpwuid( buffer.st_uid );
4122 if ( ppwd )
4123 ptmppwd = ppwd->pw_name;
4124 else
4125 sprintf( tmppwd, "%d", buffer.st_uid );
4126
4127 pgrp = getgrgid( buffer.st_gid );
4128 if ( pgrp )
4129 ptmpgrp = pgrp->gr_name;
4130 else
4131 sprintf( tmpgrp, "%d", buffer.st_gid );
4132 #endif
4133 result = Str_makeTSD( 128 ) ;
4134 #if defined(HAVE_I64U)
4135 sprintf( result->value,
4136 "%ld %ld %03o %d %s %s %I64u",
4137 (long)(buffer.st_dev), (long)(buffer.st_ino),
4138 buffer.st_mode & ACCESSPERMS, buffer.st_nlink,
4139 ptmppwd, ptmpgrp,
4140 buffer.st_size ) ;
4141 #else
4142 if ( sizeof(rx_64) > 4 )
4143 sprintf( result->value,
4144 "%ld %ld %03o %lu %s %s %ld",
4145 (long)(buffer.st_dev), (long)(buffer.st_ino),
4146 buffer.st_mode & ACCESSPERMS, buffer.st_nlink,
4147 ptmppwd, ptmpgrp,
4148 (rx_64)(buffer.st_size) ) ;
4149 else
4150 sprintf( result->value,
4151 "%ld %ld %03o %lu %s %s %ld",
4152 (long)(buffer.st_dev), (long)(buffer.st_ino),
4153 buffer.st_mode & ACCESSPERMS, buffer.st_nlink,
4154 ptmppwd, ptmpgrp,
4155 (long)(buffer.st_size) ) ;
4156 #endif
4157 /*
4158 * Append the stream type name...
4159 */
4160 strcat( result->value, stream_types[determine_stream_type( buffer.st_mode )].streamname );
4161 break;
4162 case COMMAND_QUERY_EXISTS:
4163 if ( streamtype == STREAMTYPE_TRANSIENT )
4164 {
4165 result = nullstringptr();
4166 }
4167 else
4168 {
4169 result = Str_makeTSD( REXX_PATH_MAX );
4170 my_fullpath( TSD, result->value, fn );
4171 result->len = strlen( result->value );
4172 }
4173 break;
4174 case COMMAND_QUERY_SIZE:
4175 if ( streamtype == STREAMTYPE_TRANSIENT )
4176 {
4177 result = nullstringptr() ;
4178 }
4179 else
4180 {
4181 result = Str_makeTSD( 50 ) ;
4182 #if defined(HAVE_I64U)
4183 sprintf( result->value, "%I64u", buffer.st_size ) ;
4184 #else
4185 if ( sizeof(rx_64) > 4 )
4186 sprintf( result->value, "%ld", (rx_64)(buffer.st_size) ) ;
4187 else
4188 sprintf( result->value, "%ld", (long)(buffer.st_size) ) ;
4189 #endif
4190 }
4191 break;
4192 case COMMAND_QUERY_HANDLE:
4193 if (fno)
4194 {
4195 result = Str_makeTSD( 10 ) ;
4196 sprintf( result->value, "%d", fno ) ;
4197 }
4198 else
4199 result = nullstringptr() ;
4200 break;
4201 case COMMAND_QUERY_STREAMTYPE:
4202 result = Str_makeTSD( 12 ) ;
4203 sprintf( result->value, "%s", streamdesc[streamtype] ) ;
4204 break;
4205 case COMMAND_QUERY_DATETIME:
4206 if ( streamtype == STREAMTYPE_TRANSIENT )
4207 {
4208 result = nullstringptr() ;
4209 }
4210 else
4211 {
4212 time_t num64;
4213 num64 = buffer.st_mtime;
4214 if ( ( tmptr = localtime( &num64 ) ) != NULL )
4215 tmdata = *tmptr;
4216 else
4217 memset(&tmdata,0,sizeof(tmdata)); /* what shall we do in this case? */
4218 result = Str_makeTSD( 20 ) ;
4219 #if 0
4220 sprintf( result->value, fmt, tmdata.tm_mon+1, tmdata.tm_mday,
4221 (tmdata.tm_year % 100), tmdata.tm_hour, tmdata.tm_min,
4222 tmdata.tm_sec ) ;
4223 #else
4224 strftime( result->value, 20, "%m-%d-%y %H:%M:%S", &tmdata );
4225 #endif
4226 }
4227 break;
4228 case COMMAND_QUERY_TIMESTAMP:
4229 if ( streamtype == STREAMTYPE_TRANSIENT )
4230 {
4231 result = nullstringptr() ;
4232 }
4233 else
4234 {
4235 time_t num64;
4236 num64 = buffer.st_mtime;
4237 if ( ( tmptr = localtime( &num64 ) ) != NULL )
4238 tmdata = *tmptr;
4239 else
4240 memset( &tmdata, 0, sizeof(tmdata) ); /* what shall we do in this case? */
4241 result = Str_makeTSD( 20 ) ;
4242 #if 0
4243 sprintf( result->value, iso, tmdata.tm_year+1900, tmdata.tm_mon+1,
4244 tmdata.tm_mday,
4245 tmdata.tm_hour, tmdata.tm_min,
4246 tmdata.tm_sec ) ;
4247 #else
4248 strftime( result->value, 20, "%Y-%m-%d %H:%M:%S", &tmdata );
4249 #endif
4250 }
4251 break;
4252 case COMMAND_QUERY_CREATETIME:
4253 if ( streamtype == STREAMTYPE_TRANSIENT )
4254 {
4255 result = nullstringptr() ;
4256 }
4257 else
4258 {
4259 result = Str_makeTSD( 20 ) ;
4260 #if defined(HAVE_I64U)
4261 sprintf( result->value, "%I64u", buffer.st_ctime );
4262 #else
4263 if ( sizeof(off_t) > 4 )
4264 sprintf( result->value, "%lld", (long long) buffer.st_ctime );
4265 else
4266 sprintf( result->value, "%ld", (long) buffer.st_ctime);
4267 #endif
4268 }
4269 break;
4270 case COMMAND_QUERY_MODIFYTIME:
4271 if ( streamtype == STREAMTYPE_TRANSIENT )
4272 {
4273 result = nullstringptr() ;
4274 }
4275 else
4276 {
4277 result = Str_makeTSD( 20 ) ;
4278 #if defined(HAVE_I64U)
4279 sprintf( result->value, "%I64u", buffer.st_mtime );
4280 #else
4281 if ( sizeof(off_t) > 4 )
4282 sprintf( result->value, "%lld", (long long) buffer.st_mtime );
4283 else
4284 sprintf( result->value, "%ld", (long) buffer.st_mtime);
4285 #endif
4286 }
4287 break;
4288 case COMMAND_QUERY_ACCESSTIME:
4289 if ( streamtype == STREAMTYPE_TRANSIENT )
4290 {
4291 result = nullstringptr() ;
4292 }
4293 else
4294 {
4295 result = Str_makeTSD( 20 ) ;
4296 #if defined(HAVE_I64U)
4297 sprintf( result->value, "%I64u", buffer.st_atime );
4298 #else
4299 if ( sizeof(off_t) > 4 )
4300 sprintf( result->value, "%lld", (long long) buffer.st_atime );
4301 else
4302 sprintf( result->value, "%ld", (long) buffer.st_atime);
4303 #endif
4304 }
4305 break;
4306 case COMMAND_QUERY_POSITION_READ_CHAR:
4307 case COMMAND_QUERY_POSITION_SYS:
4308 if (pos_read != (-2))
4309 {
4310 result = Str_makeTSD( 50 ) ;
4311 #if defined(HAVE_I64U)
4312 sprintf( result->value, "%I64u", pos_read + 1 );
4313 #else
4314 if ( sizeof(off_t) > 4 )
4315 sprintf( result->value, "%ld", pos_read + 1 );
4316 else
4317 sprintf( result->value, "%ld", (long)(pos_read + 1) );
4318 #endif
4319 }
4320 else
4321 result = nullstringptr() ;
4322 break;
4323 case COMMAND_QUERY_POSITION_WRITE_CHAR:
4324 if (pos_write != (-2))
4325 {
4326 result = Str_makeTSD( 50 ) ;
4327 #if defined(HAVE_I64U)
4328 sprintf( result->value, "%I64u", pos_write + 1 );
4329 #else
4330 if ( sizeof(off_t) > 4 )
4331 sprintf( result->value, "%ld", pos_write + 1 );
4332 else
4333 sprintf( result->value, "%ld", (long)(pos_write + 1) );
4334 #endif
4335 }
4336 else
4337 result = nullstringptr() ;
4338 break;
4339 case COMMAND_QUERY_POSITION_READ_LINE:
4340 if (line_read != (-2))
4341 {
4342 result = Str_makeTSD( 50 ) ;
4343 #if defined(HAVE_I64U)
4344 sprintf( result->value, "%I64u", line_read );
4345 #else
4346 if ( sizeof(off_t) > 4 )
4347 sprintf( result->value, "%ld", line_read );
4348 else
4349 sprintf( result->value, "%ld", (long)line_read );
4350 #endif
4351 }
4352 else
4353 result = nullstringptr() ;
4354 break;
4355 case COMMAND_QUERY_POSITION_WRITE_LINE:
4356 if ( line_write == 0 )
4357 {
4358 rx_64 here;
4359 rx_64 char_count;
4360 int ch;
4361 /*
4362 * When a file is first opened for both read and
4363 * write (default for implicit open), it is inexpensive
4364 * to determine pos_read, pos_write and line_read, but
4365 * is very expensive to determine line_write, so we
4366 * don't do it. It is set to 0 indicating that we don't
4367 * know the current write position. So to reduce the
4368 * cost when we may never use it, we have to pay the
4369 * price the first time we need the value; this is
4370 * one of the times we pay the price!
4371 */
4372 result = Str_makeTSD( 50 ) ;
4373 /*
4374 * We can't use countlines(), so do our our counting
4375 * of lines form the beginning of the file to the current
4376 * write pos...
4377 */
4378 here = rx_ftell( ptr->fileptr );
4379 rx_fseek( ptr->fileptr, 0L, SEEK_SET );
4380 SWITCH_OPER_READ(ptr);
4381 for( char_count = 0, line_write = 0; char_count < (long) ptr->writepos; char_count++ )
4382 {
4383 ch = getc( ptr->fileptr );
4384 if ( ch == EOF )
4385 break;
4386 if ( ch == REGINA_EOL )
4387 line_write++;
4388 }
4389 #if defined(HAVE_I64U)
4390 sprintf( result->value, "%I64u", line_write + 1 );
4391 #else
4392 if ( sizeof(off_t) > 4 )
4393 sprintf( result->value, "%ld", line_write + 1 ) ;
4394 else
4395 sprintf( result->value, "%ld", (long)(line_write + 1) ) ;
4396 #endif
4397 rx_fseek( ptr->fileptr, here, SEEK_SET );
4398 }
4399 else if (line_write != (-2))
4400 {
4401 result = Str_makeTSD( 50 ) ;
4402 #if defined(HAVE_I64U)
4403 sprintf( result->value, "%I64u", line_write );
4404 #else
4405 if ( sizeof(off_t) > 4 )
4406 sprintf( result->value, "%ld", line_write ) ;
4407 else
4408 sprintf( result->value, "%ld", (long)line_write ) ;
4409 #endif
4410 }
4411 else
4412 result = nullstringptr() ;
4413 break;
4414 }
4415 result->len = strlen( result->value ) ;
4416
4417 if ( fn )
4418 FreeTSD( fn );
4419 return result ;
4420 }
4421
4422
4423 /*
4424 * This little sweet routine returns information stored in the Rexx
4425 * file table entry about the named file 'filename'. It is perhaps more
4426 * of a debugging function than a Rexx function. It is accessed by the
4427 * Rexx built-in function STREAM(), command 'STATUS'. One of the nice
4428 * pieces of information this function returns is whether a file is
4429 * transient or persistent.
4430 *
4431 * This is really a simple function, just retrieve the Rexx file
4432 * table entry, and store the information in that entry into a string
4433 * and return that string.
4434 *
4435 * The difference between getrexxstatus() and getstatus() is that
4436 * that former returns information stored in Rexx's datastructures,
4437 * while the latter return information about the file stored in and
4438 * managed by the operating system
4439 */
getrexxstatus(const tsd_t * TSD,cfileboxptr ptr)4440 static streng *getrexxstatus( const tsd_t *TSD, cfileboxptr ptr )
4441 {
4442 streng *result=NULL ;
4443
4444 if (ptr==NULL)
4445 return nullstringptr() ;
4446
4447 result = Str_makeTSD(64) ; /* Ought to be enough */
4448 result->value[0] = 0x00 ;
4449
4450 if ((ptr->flag & FLAG_READ) && (ptr->flag & FLAG_WRITE))
4451 strcat( result->value, "READ/WRITE" ) ;
4452 else if (ptr->flag & FLAG_READ)
4453 strcat( result->value, "READ" ) ;
4454 else if (ptr->flag & FLAG_WRITE)
4455 strcat( result->value, "WRITE" ) ;
4456 else
4457 strcat( result->value, "NONE" ) ;
4458
4459 #if defined(HAVE_I64U)
4460 sprintf( result->value + strlen(result->value),
4461 " READ: char=%I64u line=%I64u WRITE: char=%I64u line=%I64u %s",
4462 (ptr->readpos+1), ptr->readline,
4463 (ptr->writepos+1), ptr->writeline,
4464 (ptr->flag & FLAG_PERSIST) ? "PERSISTENT" : "TRANSIENT" ) ;
4465 #else
4466 if ( sizeof(rx_64) > 4 )
4467 sprintf( result->value + strlen(result->value),
4468 " READ: char=%ld line=%ld WRITE: char=%ld line=%ld %s",
4469 (ptr->readpos+1), ptr->readline,
4470 (ptr->writepos+1), ptr->writeline,
4471 (ptr->flag & FLAG_PERSIST) ? "PERSISTENT" : "TRANSIENT" ) ;
4472 else
4473 sprintf( result->value + strlen(result->value),
4474 " READ: char=%ld line=%ld WRITE: char=%ld line=%ld %s",
4475 (long)(ptr->readpos+1), (long)ptr->readline,
4476 (long)(ptr->writepos+1), (long)ptr->writeline,
4477 (ptr->flag & FLAG_PERSIST) ? "PERSISTENT" : "TRANSIENT" ) ;
4478 #endif
4479
4480 result->len = strlen(result->value) ;
4481 return result ;
4482 }
4483
4484
4485 /*
4486 * This routine parses the remainder of the parameters passed to the
4487 * Stream(,'C','QUERY...') function.
4488 */
getquery(tsd_t * TSD,const streng * filename,const streng * subcommand)4489 static streng *getquery( tsd_t *TSD, const streng *filename , const streng *subcommand)
4490 {
4491 streng *result=NULL, *psub=NULL, *psubsub=NULL ;
4492 char oper = 0;
4493 char seek_oper = 0;
4494
4495 /*
4496 * Get the subcommand to QUERY
4497 */
4498 oper = get_querycommand( subcommand );
4499 switch ( oper )
4500 {
4501 case COMMAND_QUERY_DATETIME :
4502 case COMMAND_QUERY_TIMESTAMP :
4503 case COMMAND_QUERY_CREATETIME :
4504 case COMMAND_QUERY_MODIFYTIME :
4505 case COMMAND_QUERY_ACCESSTIME :
4506 case COMMAND_QUERY_EXISTS :
4507 case COMMAND_QUERY_HANDLE :
4508 case COMMAND_QUERY_SIZE :
4509 case COMMAND_QUERY_STREAMTYPE :
4510 result = getstatus( TSD, filename, oper );
4511 break;
4512 case COMMAND_QUERY_SEEK :
4513 case COMMAND_QUERY_POSITION :
4514 if ( oper == COMMAND_QUERY_SEEK )
4515 {
4516 psub = Str_nodupTSD( subcommand, 4, subcommand->len - 4 );
4517 seek_oper = 1;
4518 }
4519 else
4520 {
4521 psub = Str_nodupTSD( subcommand, 8, subcommand->len - 8 );
4522 seek_oper = 0;
4523 }
4524 psub = Str_strp( psub, ' ', STRIP_LEADING);
4525 oper = get_querypositioncommand( psub );
4526 switch ( oper )
4527 {
4528 case COMMAND_QUERY_POSITION_SYS :
4529 result = getstatus(TSD, filename, oper );
4530 break;
4531 case COMMAND_QUERY_POSITION_READ :
4532 psubsub = Str_nodupTSD( psub, 4, psub->len - 4 );
4533 psubsub = Str_strp( psubsub, ' ', STRIP_LEADING);
4534 oper = get_querypositionreadcommand( psubsub );
4535 switch( oper )
4536 {
4537 case COMMAND_QUERY_POSITION_READ_CHAR:
4538 case COMMAND_QUERY_POSITION_READ_LINE:
4539 result = getstatus( TSD, filename, oper );
4540 break;
4541 default:
4542 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK READ":"QUERY POSITION READ", "CHAR LINE ''", tmpstr_of( TSD, psubsub ) ) ;
4543 break;
4544 }
4545 break;
4546 case COMMAND_QUERY_POSITION_WRITE :
4547 psubsub = Str_nodupTSD( psub, 5, psub->len - 5 );
4548 psubsub = Str_strp( psubsub, ' ', STRIP_LEADING);
4549 oper = get_querypositionwritecommand( psubsub );
4550 switch( oper )
4551 {
4552 case COMMAND_QUERY_POSITION_WRITE_CHAR:
4553 case COMMAND_QUERY_POSITION_WRITE_LINE:
4554 result = getstatus( TSD, filename, oper );
4555 break;
4556 default:
4557 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK WRITE":"QUERY POSITION WRITE", "CHAR LINE ''", tmpstr_of( TSD, psubsub ) ) ;
4558 break;
4559 }
4560 break;
4561 default:
4562 exiterror( ERR_STREAM_COMMAND, 1, (seek_oper)?"QUERY SEEK":"QUERY POSITION", "READ WRITE SYS", tmpstr_of( TSD, psub ) ) ;
4563 break;
4564 }
4565 Free_stringTSD(psub);
4566 break;
4567 default:
4568 exiterror( ERR_STREAM_COMMAND, 1, "QUERY", "DATETIME TIMESTAMP CREATETIME MODIFYTIME ACCESSTIME EXISTS HANDLE SIZE STREAMTYPE SEEK POSITION", tmpstr_of( TSD, subcommand ) ) ;
4569 break;
4570 }
4571
4572 return result ;
4573 }
4574
4575 /*
4576 * This routine parses the remainder of the parameters passed to the
4577 * Stream(,'C','OPEN...') function.
4578 */
getopen(tsd_t * TSD,const streng * filename,const streng * subcommand)4579 static streng *getopen( tsd_t *TSD, const streng *filename , const streng *subcommand)
4580 {
4581 fileboxptr ptr=NULL ;
4582 streng *result=NULL, *psub=NULL ;
4583 char oper = 0;
4584 char buf[20];
4585
4586 /*
4587 * Get the subcommand to OPEN
4588 */
4589 oper = get_opencommand( subcommand );
4590 switch ( oper )
4591 {
4592 case COMMAND_OPEN_BOTH :
4593 if ( subcommand->len >= 4
4594 && memcmp(subcommand->value, "BOTH", 4) == 0 )
4595 psub = Str_nodupTSD( subcommand, 4, subcommand->len - 4 );
4596 else
4597 psub = Str_dupTSD( subcommand );
4598 psub = Str_strp( psub, ' ', STRIP_LEADING);
4599 oper = get_opencommandboth( psub );
4600 if ( TSD->restricted )
4601 exiterror( ERR_RESTRICTED, 4 ) ;
4602 switch ( oper )
4603 {
4604 case COMMAND_OPEN_BOTH :
4605 closefile( TSD, filename ) ;
4606 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
4607 break;
4608 case COMMAND_OPEN_BOTH_APPEND :
4609 closefile( TSD, filename ) ;
4610 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND ) ;
4611 break;
4612 case COMMAND_OPEN_BOTH_REPLACE :
4613 closefile( TSD, filename ) ;
4614 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE ) ;
4615 break;
4616 default:
4617 exiterror( ERR_STREAM_COMMAND, 1, "OPEN BOTH", "APPEND REPLACE ''", tmpstr_of( TSD, psub ) ) ;
4618 break;
4619 }
4620 Free_stringTSD(psub);
4621 if (ptr->fileptr)
4622 result = Str_creTSD( "READY:" ) ;
4623 else
4624 {
4625 sprintf(buf,"ERROR:%d",errno);
4626 result = Str_creTSD( buf ) ;
4627 }
4628 break;
4629 case COMMAND_OPEN_READ :
4630 closefile( TSD, filename ) ;
4631 ptr = openfile( TSD, filename, ACCESS_READ ) ;
4632 if (ptr->fileptr)
4633 result = Str_creTSD( "READY:" ) ;
4634 else
4635 {
4636 sprintf(buf,"ERROR:%d",errno);
4637 result = Str_creTSD( buf ) ;
4638 }
4639 break;
4640 case COMMAND_OPEN_WRITE :
4641 if ( TSD->restricted )
4642 exiterror( ERR_RESTRICTED, 4 ) ;
4643 psub = Str_nodupTSD( subcommand, 5, subcommand->len - 5 );
4644 psub = Str_strp( psub, ' ', STRIP_LEADING);
4645 oper = get_opencommandwrite( psub );
4646
4647 switch ( oper )
4648 {
4649 case COMMAND_OPEN_WRITE :
4650 closefile( TSD, filename ) ;
4651 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
4652 break;
4653 case COMMAND_OPEN_WRITE_APPEND :
4654 closefile( TSD, filename ) ;
4655 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND ) ;
4656 break;
4657 case COMMAND_OPEN_WRITE_REPLACE :
4658 closefile( TSD, filename ) ;
4659 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE ) ;
4660 break;
4661 default:
4662 exiterror( ERR_STREAM_COMMAND, 1, "OPEN WRITE", "APPEND REPLACE ''", tmpstr_of( TSD, psub ) ) ;
4663 break;
4664 }
4665 Free_stringTSD(psub);
4666 if (ptr->fileptr)
4667 result = Str_creTSD( "READY:" ) ;
4668 else
4669 {
4670 sprintf(buf,"ERROR:%d",errno);
4671 result = Str_creTSD( buf ) ;
4672 }
4673 break;
4674 default:
4675 exiterror( ERR_STREAM_COMMAND, 1, "OPEN", "BOTH READ WRITE ''", tmpstr_of( TSD, subcommand ) ) ;
4676 break;
4677 }
4678
4679 return result ;
4680 }
4681
4682
getseek(tsd_t * TSD,const streng * filename,const streng * cmd)4683 static streng *getseek( tsd_t *TSD, const streng *filename, const streng *cmd )
4684 {
4685 #define STATE_START 0
4686 #define STATE_WORD 1
4687 #define STATE_DELIM 2
4688 char *word[5] = {NULL,NULL,NULL,NULL};
4689 char *str;
4690 char *offset=NULL;
4691 int i,j=0;
4692 int state=STATE_START;
4693 int seek_by_line=0;
4694 int seek_type=0;
4695 int seek_sign=0;
4696 rx_64 seek_offset=0,pos=0;
4697 int pos_type=OPER_NONE,num_params=0;
4698 int str_start=0,str_end=(-1),words;
4699 fileboxptr ptr;
4700 streng *result=NULL;
4701 char buf[20];
4702
4703 str = str_ofTSD(cmd);
4704 words = 4;
4705 for (i=0;i<Str_len(cmd);i++)
4706 {
4707 switch(state)
4708 {
4709 case STATE_START:
4710 if (*(str+i) == ' ')
4711 {
4712 state = STATE_DELIM;
4713 break;
4714 }
4715 if ( j < 3 )
4716 word[j] = str+str_start;
4717 j++;
4718 if (str_end != (-1))
4719 {
4720 *(str+str_end) = '\0';
4721 }
4722 state = STATE_WORD;
4723 break;
4724 case STATE_WORD:
4725 if (*(str+i) == ' ')
4726 {
4727 state = STATE_DELIM;
4728 str_end = i;
4729 str_start = str_end + 1;
4730 break;
4731 }
4732 break;
4733 case STATE_DELIM:
4734 state = STATE_WORD;
4735 if (*(str+i) == ' ')
4736 {
4737 state = STATE_DELIM;
4738 }
4739 if (state == STATE_WORD)
4740 {
4741 if ( j < 3 )
4742 word[j] = str+str_start;
4743 j++;
4744 if (str_end != (-1))
4745 {
4746 *(str+str_end) = '\0';
4747 }
4748 }
4749 break;
4750 }
4751 }
4752 num_params = j;
4753 if (num_params < 1)
4754 exiterror( ERR_INCORRECT_CALL, 922, "STREAM", 3, 2, num_params+1 );
4755 if (num_params > 3)
4756 exiterror( ERR_INCORRECT_CALL, 923, "STREAM", 3, 4, num_params+1 );
4757
4758 switch( num_params )
4759 {
4760 case 3:
4761 if (strcmp(word[2],"CHAR") == 0)
4762 seek_by_line = 0;
4763 else
4764 {
4765 if (strcmp(word[2],"LINE") == 0)
4766 seek_by_line = 1;
4767 else
4768 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "CHAR LINE", word[2] );
4769 }
4770 /* meant to fall through */
4771 case 2:
4772 /*
4773 * 2 params(to SEEK), last one (word[1]) could be READ/WRITE or CHAR/LINE
4774 */
4775 if (strcmp(word[1],"READ") == 0)
4776 pos_type = OPER_READ;
4777 else if (strcmp(word[1],"WRITE") == 0)
4778 pos_type = OPER_WRITE;
4779 else if (strcmp(word[1],"CHAR") == 0)
4780 seek_by_line = 0;
4781 else if (strcmp(word[1],"LINE") == 0)
4782 seek_by_line = 1;
4783 else
4784 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "READ WRITE CHAR LINE", word[1] );
4785 }
4786 /*
4787 * Determine the position type if not supplied prior
4788 */
4789 if ( pos_type == OPER_NONE )
4790 {
4791 ptr = getfileptr( TSD, filename ) ;
4792 if ( ptr != NULL )
4793 {
4794 if ( ptr->flag & FLAG_READ )
4795 pos_type |= OPER_READ;
4796 if ( ptr->flag & FLAG_WRITE )
4797 pos_type |= OPER_WRITE;
4798 }
4799 }
4800 offset = word[0];
4801 switch(*offset)
4802 {
4803 case '=':
4804 seek_type = SEEK_SET;
4805 offset++;
4806 break;
4807 case '-':
4808 seek_type = SEEK_CUR;
4809 seek_sign = 1;
4810 offset++;
4811 break;
4812 case '+':
4813 seek_type = SEEK_CUR;
4814 seek_sign = 0;
4815 offset++;
4816 break;
4817 case '<':
4818 seek_type = SEEK_END;
4819 offset++;
4820 break;
4821 default:
4822 seek_type = SEEK_SET;
4823 break;
4824 }
4825 for (i=0;i<(int)strlen(offset);i++)
4826 {
4827 if (!rx_isdigit(*(offset+i)))
4828 exiterror( ERR_INCORRECT_CALL, 924, "STREAM", 3, "n, +n, -n, =n or <n", word[0] );
4829 }
4830 #if defined(HAVE_ATOLL)
4831 seek_offset = atoll(offset);
4832 #elif defined(HAVE__ATOI64)
4833 seek_offset = _atoi64(offset);
4834 #else
4835 seek_offset = atol(offset);
4836 #endif
4837 if (seek_sign) /* negative */
4838 seek_offset *= -1;
4839 ptr = get_file_ptr( TSD, filename, pos_type, (pos_type&OPER_WRITE) ? ACCESS_WRITE : ACCESS_READ ) ;
4840 if (!ptr)
4841 {
4842 sprintf(buf,"ERROR:%d",errno);
4843 result = Str_creTSD( buf ) ;
4844 }
4845 if (seek_by_line) /* position by line */
4846 pos = positionfile( TSD, "STREAM", 3, ptr, pos_type, seek_offset, seek_type ) ;
4847 else
4848 pos = positioncharfile( TSD, "STREAM", 3, ptr, pos_type, seek_offset, seek_type ) ;
4849 if ( pos >= 0 )
4850 {
4851 result = Str_makeTSD( 20 ) ; /* should be enough digits */
4852 #if defined(HAVE_I64U)
4853 sprintf(result->value, "%I64u", pos );
4854 #else
4855 if ( sizeof(rx_64) > 4 )
4856 sprintf(result->value, "%ld", pos );
4857 else
4858 sprintf(result->value, "%ld", (long)pos );
4859 #endif
4860 Str_len( result ) = strlen( result->value );
4861 }
4862 else
4863 {
4864 sprintf(buf,"ERROR:%d",errno);
4865 result = Str_creTSD( buf ) ;
4866 }
4867 FreeTSD(str);
4868 return result ;
4869 }
4870
4871
4872
4873 /* ------------------------------------------------------------------- */
4874 /* Rexx builtin functions (level 3) */
4875 /*
4876 * This part consists of one function for each of the Rexx builtin
4877 * functions that operates on filesystem I/O
4878 */
4879
4880
4881 /*
4882 * This routine implements the Rexx built-in function CHARS(). It is
4883 * really quite simple, little more than a wrap-around to the
4884 * function calc_chars_left.
4885 */
std_chars(tsd_t * TSD,cparamboxptr parms)4886 streng *std_chars( tsd_t *TSD, cparamboxptr parms )
4887 {
4888 char opt = 'N';
4889 streng *string=NULL ;
4890 fileboxptr ptr=NULL ;
4891 int was_closed=0;
4892 rx_64 result=0 ;
4893 fil_tsd_t *ft;
4894
4895 ft = (fil_tsd_t *)TSD->fil_tsd;
4896
4897 /* Syntax: chars([filename]) */
4898 checkparam( parms, 0, 2 , "CHARS" ) ;
4899
4900 if (parms&&parms->next&&parms->next->value)
4901 opt = getoptionchar( TSD, parms->next->value, "CHARS", 2, "CN", "" ) ;
4902
4903 string = (parms->value && parms->value->len) ? parms->value : ft->stdio_ptr[0]->filename0 ;
4904 /*
4905 * Get a pointer to the Rexx file table entry of the file, and
4906 * calculate the number of characters left.
4907 */
4908 ptr = getfileptr( TSD, string ) ;
4909 was_closed = (ptr==NULL) ;
4910 if (!ptr)
4911 ptr = get_file_ptr( TSD, string, OPER_READ, ACCESS_READ ) ;
4912
4913 result = calc_chars_left( TSD, ptr ) ;
4914 if (was_closed)
4915 closefile( TSD, string ) ;
4916
4917 return rx64_to_streng( TSD, result ) ;
4918 }
4919
4920
4921
4922 /*
4923 * Implements the Rexx builtin function charin(). This function takes
4924 * three parameters, and they are treated pretty straight forward
4925 * according to TRL. If called with no start position, and a length of
4926 * zero, it may be used to do some fancy work (flushing I/O?), although
4927 * that is probably more needed for output :-) Note that the file in
4928 * entered into the file table in this case, so it might be used to
4929 * explicitly open a file for reading. However, consider using stream()
4930 * to do this, it's a much cleaner approach!
4931 */
std_charin(tsd_t * TSD,cparamboxptr parms)4932 streng *std_charin( tsd_t *TSD, cparamboxptr parms )
4933 {
4934 streng *filename=NULL, *result=NULL ;
4935 fileboxptr ptr=NULL ;
4936 size_t length=0 ;
4937 rx_64 start=0 ;
4938 fil_tsd_t *ft;
4939
4940 ft = (fil_tsd_t *)TSD->fil_tsd;
4941
4942 /* Syntax: charin([filename][,[start][,length]]) */
4943 checkparam( parms, 0, 3 , "CHARIN" ) ;
4944
4945 /*
4946 * First, let's get the information about the file from the
4947 * file table, and open it in the correct mode if is not already
4948 * availble as such.
4949 */
4950 filename = (parms->value && parms->value->len) ? (parms->value) : ft->stdio_ptr[0]->filename0 ;
4951 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
4952
4953 /*
4954 * Then, get the starting point, or set it to zero.
4955 */
4956 parms = parms->next ;
4957 if ((parms)&&(parms->value))
4958 start = atoposrx64( TSD, parms->value, "CHARIN", 2 ) ;
4959 else
4960 start = 0 ;
4961
4962 /*
4963 * At last, get the length, or use the default value one.
4964 */
4965 if (parms)
4966 parms = parms->next ;
4967
4968 if ((parms)&&(parms->value))
4969 length = atozpos( TSD, parms->value, "CHARIN", 3 ) ;
4970 else
4971 length = 1 ;
4972
4973 /*
4974 * Position current position in file if necessary
4975 */
4976 if (start)
4977 positioncharfile( TSD, "CHARIN", 2, ptr, OPER_READ, start, SEEK_SET ) ;
4978
4979 if (length)
4980 result = readbytes( TSD, ptr, length, 0 ) ;
4981 else
4982 {
4983 if (!start)
4984 flush_input( ptr ) ; /* Whatever happens ... */
4985 result = nullstringptr() ;
4986 }
4987
4988 return result ;
4989 }
4990
4991
4992
4993 /*
4994 * This function implements the Rexx built-in function CHAROUT(). It
4995 * is basically a wrap-around for the two functions that perform
4996 * character repositioning in a file; and writes out characters.
4997 */
4998
std_charout(tsd_t * TSD,cparamboxptr parms)4999 streng *std_charout( tsd_t *TSD, cparamboxptr parms )
5000 {
5001 streng *filename=NULL, *string=NULL ;
5002 int length=0 ;
5003 long pos=0 ;
5004 fileboxptr ptr=NULL ;
5005 fil_tsd_t *ft;
5006
5007 ft = (fil_tsd_t *)TSD->fil_tsd;
5008
5009 if ( TSD->restricted )
5010 exiterror( ERR_RESTRICTED, 1, "CHAROUT" ) ;
5011
5012 /* Syntax: charout([filename][,[string][,start]]) */
5013 checkparam( parms, 0, 3 , "CHAROUT" ) ;
5014
5015 filename = (parms->value && parms->value->len) ? (parms->value) : ft->stdio_ptr[1]->filename0 ;
5016
5017 /* Read the data to be written, if any */
5018 parms = parms->next ;
5019 if (parms && parms->value )
5020 string = parms->value ;
5021 else
5022 string = NULL ;
5023
5024 /* Read the position to start writing, is any */
5025 if (parms)
5026 parms = parms->next ;
5027
5028 if ( parms && parms->value )
5029 pos = atopos( TSD, parms->value, "CHAROUT", 3 ) ;
5030 else
5031 pos = 0 ;
5032
5033 ptr = get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE ) ;
5034
5035 /*
5036 * If we are to position the write position somewhere, do that first.
5037 */
5038 if (pos)
5039 positioncharfile( TSD, "CHAROUT", 3, ptr, OPER_WRITE, pos, SEEK_SET ) ;
5040
5041 /*
5042 * Then, write the actual data, or flush output if neither data nor
5043 * position was given.
5044 */
5045 if (string)
5046 length = string->len - writebytes( TSD, ptr, string, 0 ) ;
5047 else
5048 {
5049 length = 0;
5050 if ( !pos )
5051 {
5052 /*
5053 * flush_output() will swap out the file and close it, but leave ALL positions
5054 * intact
5055 * We need to set the write positions to end of file (NOT EOF)
5056 * See ANSI 9.7.2, 9.7.5, A.5.8.9
5057 * For efficiency sake, we will have to set writeline = 0 :-(
5058 * We do this BEFORE flush_output() otherwise we won't have a ptr->fileptr!
5059 */
5060 if ( ptr->flag & FLAG_PERSIST )
5061 {
5062 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
5063 ptr->writepos = rx_ftell( ptr->fileptr ) ;
5064 }
5065 else
5066 ptr->writepos = 0;
5067 ptr->writeline = 0;
5068
5069 if ( flush_output( TSD, ptr ) == -1 )
5070 length = 1; /* simulate (at least) 1 byte not written */
5071 }
5072 }
5073
5074 return int_to_streng( TSD, length ) ;
5075 }
5076
5077
5078
5079 /*
5080 * Simple routine that implements the Rexx built-in function LINES().
5081 * Really just a wrap-around to the countlines() routine.
5082 */
5083
std_lines(tsd_t * TSD,cparamboxptr parms)5084 streng *std_lines( tsd_t *TSD, cparamboxptr parms )
5085 {
5086 char opt = 'N';
5087 fileboxptr ptr=NULL ;
5088 streng *filename=NULL ;
5089 int was_closed=0;
5090 rx_64 result=0 ;
5091 int actual;
5092 fil_tsd_t *ft;
5093
5094 ft = (fil_tsd_t *)TSD->fil_tsd;
5095
5096 /* Syntax: lines([filename][,C|N]) */
5097 checkparam( parms, 0, 2 , "LINES" ) ;
5098
5099 if (parms&&parms->next&&parms->next->value)
5100 opt = getoptionchar( TSD, parms->next->value, "LINES", 2, "CN", "" ) ;
5101
5102 /*
5103 * Get the name of the file (use defaults if necessary), and get
5104 * a pointer to the entry of that file from the file table
5105 */
5106 if (parms->value
5107 && parms->value->len)
5108 filename = parms->value ;
5109 else
5110 filename = ft->stdio_ptr[0]->filename0 ;
5111
5112 /*
5113 * Try to get the Rexx file table entry, if it doesn't work, then
5114 * try again ... and a bit harder
5115 */
5116 ptr = getfileptr( TSD, filename ) ;
5117 was_closed = (ptr==NULL) ;
5118 if (!ptr)
5119 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
5120
5121 /*
5122 * If 'C' is passed, we ALWAYS get the actual number of lines
5123 * If 'N' is passed (or unset), we get the actual number of lines
5124 * unless FAST_LINES_BIF is set (default is set)
5125 * Bug: 1000227
5126 */
5127 if ( opt == 'C' )
5128 actual = 1;
5129 else
5130 {
5131 if ( get_options_flag( TSD->currlevel, EXT_FAST_LINES_BIF_DEFAULT ) )
5132 actual = 0;
5133 else
5134 actual = 1;
5135 }
5136 result = countlines( TSD, ptr, actual, OPER_READ ) ;
5137
5138 if (was_closed)
5139 closefile( TSD, filename ) ;
5140
5141
5142 return rx64_to_streng( TSD, result ) ;
5143 }
5144
5145
5146
5147 /*
5148 * The Rexx built-in function LINEIN() reads a line from a file.
5149 * The actual reading is performed in 'readoneline', while this routine
5150 * takes care of range checking of parameters, and decides which
5151 * lower level routines to call.
5152 */
5153
std_linein(tsd_t * TSD,cparamboxptr parms)5154 streng *std_linein( tsd_t *TSD, cparamboxptr parms )
5155 {
5156 streng *filename=NULL, *res=NULL ;
5157 fileboxptr ptr=NULL ;
5158 int count=0, line=0 ;
5159 fil_tsd_t *ft;
5160
5161 ft = (fil_tsd_t *)TSD->fil_tsd;
5162
5163 /* Syntax: linein([filename][,[line][,count]]) */
5164 checkparam( parms, 0, 3 , "LINEIN" ) ;
5165
5166 /*
5167 * First get the name of the file, or use the appropriate default
5168 */
5169 if (parms->value
5170 && parms->value->len)
5171 filename = parms->value ;
5172 else
5173 filename = ft->stdio_ptr[0]->filename0 ;
5174
5175 /*
5176 * Then get the line number at which the read it to start, or set
5177 * set it to zero if none was specified.
5178 */
5179 if (parms)
5180 parms = parms->next ;
5181
5182 if (parms && parms->value)
5183 line = atopos( TSD, parms->value, "LINEIN", 2 ) ;
5184 else
5185 line = 0 ; /* Illegal value */
5186
5187 /*
5188 * And at last, read the count, which can be only 0 or 1, and which
5189 * is the number of lines to read.
5190 */
5191 if (parms)
5192 parms = parms->next ;
5193
5194 if (parms && parms->value)
5195 {
5196 count = atozpos( TSD, parms->value, "LINEIN", 3 ) ;
5197 if (count!=0 && count!=1)
5198 exiterror( ERR_INCORRECT_CALL, 39, "LINEIN", tmpstr_of( TSD, parms->value ) ) ;
5199 }
5200 else
5201 count = 1 ; /* The default */
5202
5203 /*
5204 * Now, get the pointer to the entry in the file table that contains
5205 * information about this file, or make it automatically create
5206 * an entry if one didn't exist.
5207 */
5208 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ ) ;
5209
5210 /*
5211 * If line was specified, we must reposition the current read
5212 * position of the file.
5213 */
5214 if (line)
5215 positionfile( TSD, "LINEIN", 2, ptr, OPER_READ, line, SEEK_SET ) ;
5216
5217 /*
5218 * As the last thing, read in the data. If no data was wanted, skip it
5219 * but call flushing if line wasn't specified either.
5220 */
5221 if (count)
5222 res = readoneline( TSD, ptr ) ;
5223 else
5224 {
5225 if (!line)
5226 flush_input( ptr ) ;
5227 res = nullstringptr() ;
5228 }
5229
5230 return res ;
5231 }
5232
5233
5234
5235
5236 /*
5237 * This function is a wrap-around for the Rexx built-in function
5238 * LINEOUT(). It performs parameter checking and decides which lower
5239 * level routines to call.
5240 */
5241
std_lineout(tsd_t * TSD,cparamboxptr parms)5242 streng *std_lineout( tsd_t *TSD, cparamboxptr parms )
5243 {
5244 streng *string=NULL, *file=NULL ;
5245 int lineno=0, result=0 ;
5246 fileboxptr ptr=NULL ;
5247 fil_tsd_t *ft;
5248
5249 ft = (fil_tsd_t *)TSD->fil_tsd;
5250
5251 if ( TSD->restricted )
5252 exiterror( ERR_RESTRICTED, 1, "LINEOUT" ) ;
5253
5254 /* Syntax: lineout([filename][,[string][,line]]) */
5255 checkparam( parms, 0, 3 , "LINEOUT" ) ;
5256
5257 /*
5258 * First get the pointer for the file to operate on. If omitted,
5259 * use the standard output stream
5260 */
5261 if (parms->value
5262 && parms->value->len)
5263 file = parms->value ;
5264 else
5265 file = ft->stdio_ptr[1]->filename0 ;
5266 /*
5267 * The file pointer is needed in ALL circumstances!
5268 */
5269 ptr = get_file_ptr( TSD, file, OPER_WRITE, ACCESS_WRITE ) ;
5270
5271 /*
5272 * Then, get the data to be written, if any.
5273 */
5274 if (parms)
5275 parms = parms->next ;
5276
5277 if (parms && parms->value)
5278 string = parms->value ;
5279 else
5280 string = NULL ;
5281
5282 /*
5283 * At last, we must find the line number of the file to write. We
5284 * must position the file at this line before the write.
5285 */
5286 if (parms)
5287 parms = parms->next ;
5288
5289 if (parms && parms->value)
5290 lineno = atopos( TSD, parms->value, "LINEOUT", 3 ) ;
5291 else
5292 lineno = 0 ; /* illegal value */
5293
5294 /*
5295 * First, let's reposition the file if necessary.
5296 */
5297 if (lineno)
5298 positionfile( TSD, "LINEOUT", 2, ptr, OPER_WRITE, lineno, SEEK_SET ) ;
5299
5300 /*
5301 * And then, we write out the data. If there are not data, it may have
5302 * been just positioning. However, if there are neither data nor
5303 * a linenumber, something magic may happen.
5304 */
5305 if (string)
5306 result = writeoneline( TSD, ptr, string ) ;
5307 else
5308 {
5309 if ( !lineno )
5310 {
5311 /*
5312 * flush_output() will swap out the file and close it, but leave ALL positions
5313 * intact
5314 * We need to set the write positions to end of file (NOT EOF)
5315 * See ANSI 9.7.2, 9.7.5, A.5.8.9
5316 * For efficiency sake, we will have to set writeline = 0 :-(
5317 * We do this BEFORE flush_output() otherwise we won't have a ptr->fileptr!
5318 */
5319 if ( ptr->flag & FLAG_PERSIST )
5320 {
5321 /*
5322 * When using LINEOUT() to close a file and that file is NOT writeable, ptr->fileptr
5323 * gets set to NULL, so guard against this
5324 */
5325 if ( ptr->fileptr )
5326 {
5327 rx_fseek( ptr->fileptr, 0, SEEK_END ) ;
5328 ptr->writepos = rx_ftell( ptr->fileptr ) ;
5329 }
5330 }
5331 else
5332 ptr->writepos = 0;
5333 ptr->writeline = 0;
5334 /*
5335 * ANSI states that a file is not necessarily closed in this case.
5336 * Position of file pointers is explicitly stated in ANSI and if the
5337 * file is NOT closed they cause breakage.
5338 * Therefore implement ANSI in STRICT_ANSI mode and normal behaviour
5339 * (that does not cause breakage) in "regina" mode.
5340 * MH 22/06/2004 - after non-conclusive discussions on ANSI mailing list
5341 */
5342 if ( get_options_flag( TSD->currlevel, EXT_STRICT_ANSI ) )
5343 {
5344 flush_output( TSD, ptr );
5345 }
5346 else
5347 {
5348 closefile( TSD, file ) ;
5349 }
5350 }
5351 result = 0;
5352 }
5353
5354 return int_to_streng( TSD, result ) ;
5355 }
5356
5357
5358
5359
5360 /*
5361 * This function checks whether a particular file is accessable by
5362 * the user in a certain mode, which may be read, write or execute.
5363 * Unfortunately, this function differs a bit from the functionality
5364 * of several others. It explicitly checks a file, so that if the
5365 * file didn't exist in advance, it is _not_ opened. And even _if_
5366 * the file existed, the file in the file system is checked, not the
5367 * file opened by Regina. The two may differ slightly under certain
5368 * circumstances.
5369 */
5370
is_accessable(const tsd_t * TSD,const streng * filename,int mode)5371 static int is_accessable( const tsd_t *TSD, const streng *filename, int mode )
5372 {
5373 int res=0 ;
5374 char *fn ;
5375
5376 fn = str_ofTSD( filename ) ;
5377 /*
5378 * First, call access() with the 'correct' parameters, and store
5379 * the result in 'res'. If 'mode' had an "impossible" value, give
5380 * an error.
5381 */
5382 #if defined(WIN32) && ( defined(__IBMC__) || defined(__LCC__) )
5383 {
5384 DWORD Attrib;
5385 res=-1;
5386 Attrib=GetFileAttributes(fn);
5387 if (Attrib==(DWORD)-1)
5388 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5389 if ((Attrib&FILE_ATTRIBUTE_DIRECTORY)!=FILE_ATTRIBUTE_DIRECTORY)
5390 {
5391 if ((mode == COMMAND_READABLE) && ((Attrib&FILE_ATTRIBUTE_READONLY)==FILE_ATTRIBUTE_READONLY))
5392 res = 0 ;
5393 else if ((mode == COMMAND_WRITEABLE) || (mode == COMMAND_EXECUTABLE))
5394 res = 0 ;
5395 }
5396 }
5397 #else
5398 if (mode == COMMAND_READABLE)
5399 res = access( fn, R_OK ) ;
5400 else if (mode == COMMAND_WRITEABLE)
5401 res = access( fn, W_OK ) ;
5402 else if (mode == COMMAND_EXECUTABLE)
5403 res = access( fn, X_OK ) ;
5404 else
5405 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5406 #endif
5407
5408 /*
5409 * Perhaps we should analyze the output a bit before returning?
5410 * If res==EACCES, that is not really an error, while other errno
5411 * code _do_ signify an error. However ... since the return code
5412 * a boolean variable, just return it.
5413 */
5414 FreeTSD(fn) ;
5415 return (res==0) ;
5416 }
5417
5418
5419
5420 /*
5421 * This little function implements the RESET command of the Rexx
5422 * built-in function STREAM(). Basically, most of the job is done in
5423 * the function 'fixup_file()'. Except from removing the ERROR flag.
5424 * The 'fixup_file()' function is intended for fixing the file at the
5425 * start of a condition handler for the NOTREADY condition.
5426 *
5427 * The value returned from this function is either "READY" or "UNKNOWN",
5428 * and reflects the philosophy that the file _is_ fixed, unless it
5429 * is impossible to open it. Of course, that may be a false READY,
5430 * since the actual _problem_ might not have been fixed, but at least
5431 * you have another try at the problem.
5432 */
5433
reset_file(tsd_t * TSD,fileboxptr fileptr)5434 static streng *reset_file( tsd_t *TSD, fileboxptr fileptr )
5435 {
5436 if (!fileptr)
5437 return nullstringptr() ;
5438
5439 fixup_file( TSD, fileptr->filename0 ) ;
5440 fileptr->flag &= ~(FLAG_ERROR | FLAG_FAKE) ;
5441
5442 if (fileptr->fileptr)
5443 return Str_creTSD( "READY" ) ; /* Per definition */
5444 else
5445 return Str_creTSD( "UNKNOWN" ) ;
5446 }
5447
5448
5449
5450 /*
5451 * The built-in function STREAM() is new in TRL2. It is supposed to be
5452 * a sort of all-round function for just about anything having to do with
5453 * files. The details of its specification in TRL2 leaves a lot of room
5454 * for the implementors. Two of the options to this command -- the Status
5455 * and Description options are treated as defined by TRL, the Command
5456 * option takes several command, defined by the COMMAND_ macros.
5457 */
std_stream(tsd_t * TSD,cparamboxptr parms)5458 streng* std_stream( tsd_t *TSD, cparamboxptr parms )
5459 {
5460 char oper=' ' ;
5461 streng *command=NULL, *result=NULL, *filename=NULL, *psub=NULL ;
5462 fileboxptr ptr=NULL ;
5463
5464 /* Syntax: stream(filename[,[oper][,command ...]]) */
5465 if ((!parms)||(!parms->value))
5466 exiterror( ERR_INCORRECT_CALL, 5, "STREAM", 1 ) ;
5467 checkparam( parms, 1, 3 , "STREAM" ) ;
5468
5469 /*
5470 * Get the filepointer to Rexx's file table, but make sure that the
5471 * file is not in any way created if it didn't exist.
5472 */
5473 filename = Str_dupstrTSD( parms->value );
5474 ptr = getfileptr( TSD, filename ) ;
5475 /*
5476 * Read the 'operation'. This is really just an 'option'. The
5477 * default option is 'S'.
5478 */
5479 parms = parms->next ;
5480 if (parms && parms->value)
5481 oper = getoptionchar( TSD, parms->value, "STREAM", 2, "CSD", "" ) ;
5482 else
5483 oper = 'S' ;
5484
5485 /*
5486 * If the operation was 'C', we _must_ have a third parameter, on the
5487 * other hand, if it was not 'C', we must never have a third parameter.
5488 * Make sure that these rules are followed.
5489 */
5490 command = NULL ;
5491 if (oper=='C')
5492 {
5493 parms = parms->next ;
5494 if (parms && parms->value)
5495 command = parms->value ;
5496 else
5497 exiterror( ERR_INCORRECT_CALL, 3, "STREAM", 3 ) ;
5498 }
5499 else
5500 if (parms && parms->next && parms->next->value)
5501 exiterror( ERR_INCORRECT_CALL, 4, "STREAM", 2 ) ;
5502
5503 /*
5504 * Here comes the main loop.
5505 */
5506 result = NULL ;
5507 switch ( oper )
5508 {
5509 case 'C':
5510 /*
5511 * Read the command, and 'translate' it into an integer which
5512 * describes it, see the implementation of get_command(), and
5513 * the COMMAND_ macros. The first of these are rather simple,
5514 * in fact, they could probably be compressed to save some
5515 * space.
5516 */
5517 command = Str_strp( command, ' ', STRIP_BOTH );
5518 oper = get_command( command ) ;
5519 switch(oper)
5520 {
5521 case COMMAND_READ:
5522 closefile( TSD, filename ) ;
5523 ptr = openfile( TSD, filename, ACCESS_READ ) ;
5524 break;
5525 case COMMAND_WRITE:
5526 closefile( TSD, filename ) ;
5527 ptr = openfile( TSD, filename, ACCESS_WRITE ) ;
5528 break;
5529 case COMMAND_APPEND:
5530 closefile( TSD, filename ) ;
5531 ptr = openfile( TSD, filename, ACCESS_APPEND ) ;
5532 break;
5533 case COMMAND_UPDATE:
5534 closefile( TSD, filename ) ;
5535 ptr = openfile( TSD, filename, ACCESS_UPDATE ) ;
5536 break;
5537 case COMMAND_CREATE:
5538 closefile( TSD, filename ) ;
5539 ptr = openfile( TSD, filename, ACCESS_CREATE ) ;
5540 break;
5541 case COMMAND_CLOSE:
5542 /*
5543 * The file is always unknown after is has been closed. Does
5544 * that sound convincing, or does it sound like I didn't feel
5545 * to implement the rest of this ... ?
5546 */
5547 closefile( TSD, filename ) ;
5548 result = Str_creTSD( "UNKNOWN" ) ;
5549 break ;
5550 case COMMAND_FLUSH:
5551 /*
5552 * Flush the file. Actually, this might not be needed, since
5553 * the functions that write out data may contain explicit
5554 * calls to fflush()
5555 */
5556 ptr = getfileptr( TSD, filename ) ;
5557 if (ptr && ptr->fileptr)
5558 {
5559 errno = 0 ;
5560 if (fflush( ptr->fileptr))
5561 {
5562 file_error( ptr, errno, NULL ) ;
5563 result = Str_creTSD( "ERROR" ) ;
5564 }
5565 else
5566 result = Str_creTSD( "READY" ) ;
5567 }
5568 else if (ptr)
5569 result = Str_creTSD( "ERROR" ) ;
5570 else
5571 result = Str_creTSD( "UNKNOWN" ) ;
5572 break ;
5573 case COMMAND_STATUS:
5574 ptr = getfileptr( TSD, filename ) ;
5575 result = getrexxstatus( TSD, ptr ) ;
5576 break;
5577 case COMMAND_FSTAT:
5578 result = getstatus( TSD, filename, COMMAND_FSTAT );
5579 break;
5580 case COMMAND_RESET:
5581 ptr = getfileptr( TSD, filename ) ;
5582 result = reset_file( TSD, ptr ) ;
5583 break;
5584 case COMMAND_READABLE:
5585 case COMMAND_WRITEABLE:
5586 case COMMAND_EXECUTABLE:
5587 result = int_to_streng( TSD, is_accessable( TSD, filename, oper )) ;
5588 break;
5589 case COMMAND_QUERY:
5590 /*
5591 * We have to further parse the remainder of the command
5592 * to determine what sub-command has been passed.
5593 */
5594 psub = Str_nodupTSD( command , 5, command->len - 5);
5595 psub = Str_strp( psub, ' ', STRIP_LEADING);
5596 result = getquery( TSD, filename, psub ) ;
5597 Free_stringTSD(psub);
5598 break;
5599 case COMMAND_OPEN:
5600 /*
5601 * We have to further parse the remainder of the command
5602 * to determine what sub-command has been passed.
5603 */
5604 psub = Str_nodupTSD( command , 4, command->len - 4);
5605 psub = Str_strp( psub, ' ', STRIP_LEADING);
5606 result = getopen( TSD, filename, psub ) ;
5607 Free_stringTSD(psub);
5608 break;
5609 case COMMAND_SEEK:
5610 psub = Str_nodupTSD( command , 4, command->len - 4);
5611 psub = Str_strp( psub, ' ', STRIP_LEADING);
5612 result = getseek( TSD, filename, psub ) ;
5613 Free_stringTSD(psub);
5614 break;
5615 case COMMAND_POSITION:
5616 psub = Str_nodupTSD( command , 8, command->len - 8);
5617 psub = Str_strp( psub, ' ', STRIP_LEADING);
5618 result = getseek( TSD, filename, psub ) ;
5619 Free_stringTSD(psub);
5620 break;
5621 default:
5622 exiterror( ERR_STREAM_COMMAND, 3, "CLOSE FLUSH OPEN POSITION QUERY SEEK", tmpstr_of( TSD, command ) ) ;
5623 break;
5624 }
5625 break ;
5626
5627 case 'D':
5628 /*
5629 * Get a description of the most recent error for this file
5630 */
5631 if (ptr)
5632 {
5633 if (ptr->errmsg)
5634 result = Str_dupTSD(ptr->errmsg) ;
5635 else if (ptr->error)
5636 result = Str_creTSD( strerror(ptr->error) ) ;
5637 }
5638 break ;
5639
5640 case 'S':
5641 /*
5642 * Get a simple status for the file in question. If the file
5643 * doesn't exist in Rexx's tables, UNKNOWN is returned. If the
5644 * file is in error state, return ERROR, else return READY,
5645 * unless current read position is at EOF, in which case
5646 * NOTREADY is return. Note that ERROR and NOTREADY are the
5647 * two states that will raise the NOTREADY condition.
5648 */
5649 if (ptr)
5650 {
5651 if (ptr->flag & FLAG_ERROR)
5652 {
5653 result = Str_creTSD( "ERROR" ) ;
5654 }
5655 #if 1 /* really MH */
5656 else if (ptr->flag & FLAG_AFTER_RDEOF)
5657 {
5658 result = Str_creTSD( "NOTREADY" ) ;
5659 }
5660 #else
5661 else if (ptr->flag & FLAG_RDEOF)
5662 {
5663 result = Str_creTSD( "NOTREADY" ) ;
5664 }
5665 #endif
5666 else
5667 {
5668 result = Str_creTSD( "READY" ) ;
5669 }
5670 }
5671 else
5672 result = Str_creTSD( "UNKNOWN" ) ;
5673
5674 break ;
5675
5676 default:
5677 exiterror( ERR_INTERPRETER_FAILURE, 1, __FILE__, __LINE__, "" ) ;
5678 }
5679
5680 if (result==NULL)
5681 result = nullstringptr() ;
5682
5683 Free_stringTSD(filename);
5684 return result ;
5685 }
5686
5687
5688
5689 /*
5690 * This routine will traverse the list of open files, and dump relevant
5691 * information about each of them. Really a debugging routine. It is
5692 * not available when Regina is compiled with optimalization.
5693 */
5694 #ifndef NDEBUG
dbg_dumpfiles(tsd_t * TSD,cparamboxptr parms)5695 streng *dbg_dumpfiles( tsd_t *TSD, cparamboxptr parms )
5696 {
5697 fileboxptr ptr ;
5698 int i=0, fno ;
5699 char string[11] ;
5700 fil_tsd_t *ft;
5701 char opt='N' ;
5702 int slot;
5703
5704 ft = (fil_tsd_t *)TSD->fil_tsd;
5705
5706 checkparam( parms, 0, 2 , "DUMPFILES" ) ;
5707
5708 if ( parms && parms->value )
5709 opt = getoptionchar( TSD, parms->value, "DUMPFILES", 1, "LN", "" ) ;
5710
5711 if ( parms )
5712 parms = parms->next ;
5713
5714 if ( parms && parms->value )
5715 slot = atopos( TSD, parms->value, "DUMPFILES", 2 ) ;
5716 else
5717 slot = -1 ; /* dump all slots */
5718
5719 if (TSD->stddump == NULL)
5720 return nullstringptr() ;
5721
5722 if ( opt == 'N' )
5723 {
5724 fprintf(TSD->stddump,
5725 " Read Write\n" ) ;
5726 fprintf(TSD->stddump,
5727 "File Filename Flags line char line char\n");
5728
5729 for ( ptr = ft->mrufile; ptr ; ptr = ptr->older )
5730 {
5731 fno = fileno( ptr->fileptr ) ;
5732 fprintf( TSD->stddump,"%4d %-30s", fno, ptr->filename0->value);
5733 i = 0 ;
5734
5735 string[0] = (char) (( ptr->flag & FLAG_READ ) ? 'r' : ' ') ;
5736 string[1] = (char) (( ptr->flag & FLAG_WRITE ) ? 'w' : ' ') ;
5737 string[2] = (char) (( ptr->flag & FLAG_PERSIST ) ? 'p' : 't') ;
5738 string[3] = (char) (( ptr->flag & FLAG_RDEOF ) ? 'R' : ' ') ;
5739 string[4] = (char) (( ptr->flag & FLAG_AFTER_RDEOF ) ? 'A' : ' ') ;
5740 string[5] = (char) (( ptr->flag & FLAG_WREOF ) ? 'W' : ' ') ;
5741 string[6] = (char) (( ptr->flag & FLAG_SURVIVOR ) ? 'S' : ' ') ;
5742 string[7] = (char) (( ptr->flag & FLAG_ERROR ) ? 'E' : ' ') ;
5743 string[8] = (char) (((ptr->flag & FLAG_FAKE) && (ptr->flag & FLAG_ERROR) ) ? 'F' : ' ') ;
5744 string[9] = 0x00 ;
5745
5746 #if defined(HAVE_I64U)
5747 fprintf( TSD->stddump, " %8s %4I64u %4I64u %4I64u %4I64u\n", string,
5748 ptr->readline, ptr->readpos,
5749 ptr->writeline,ptr->writepos ) ;
5750 #else
5751 if ( sizeof(rx_64) > 4 )
5752 fprintf( TSD->stddump, " %8s %4lld %4lld %4lld %4lld\n", string,
5753 ptr->readline, ptr->readpos,
5754 ptr->writeline,ptr->writepos ) ;
5755 else
5756 fprintf( TSD->stddump, " %8s %4ld %4ld %4ld %4ld\n", string,
5757 (long)(ptr->readline), (long)(ptr->readpos),
5758 (long)(ptr->writeline),(long)(ptr->writepos) ) ;
5759 #endif
5760 if (ptr->flag & FLAG_ERROR)
5761 {
5762 if (ptr->errmsg)
5763 fprintf(TSD->stddump, " ==> %s\n", ptr->errmsg->value ) ;
5764 else if (ptr->error)
5765 fprintf(TSD->stddump, " ==> %s\n", strerror( ptr->error )) ;
5766 }
5767 }
5768 fprintf( TSD->stddump," r=read, w=write, p=persistent, t=transient, e=eof\n");
5769 fprintf( TSD->stddump," R=read-eof, W=write-eof, S=special, E=error, F=fake\n");
5770 }
5771 else
5772 {
5773 int start, end;
5774 if ( slot == -1 )
5775 {
5776 start = 0;
5777 end = 131;
5778 }
5779 else
5780 {
5781 start = slot;
5782 end = start + 1;
5783 }
5784 for ( i = start; i < end; i++ )
5785 {
5786 ptr = ft->filehash[i];
5787 if ( ptr )
5788 {
5789 fno = 0;
5790 if ( ptr->fileptr )
5791 fno = fileno( ptr->fileptr ) ;
5792 fprintf( TSD->stddump,"Slot: %3d: %4d %8lx prev: %8lx next: %8lx %-30s\n",
5793 i, fno, (long)ptr, (long)ptr->prev, (long)ptr->next, ptr->filename0->value);
5794 for ( ptr = ptr->next; ptr; ptr = ptr->next )
5795 {
5796 fno = 0;
5797 if ( ptr->fileptr )
5798 fno = fileno( ptr->fileptr ) ;
5799 fprintf( TSD->stddump," %4d %8lx prev: %8lx next: %8lx %-30s\n",
5800 fno, (long)ptr, (long)ptr->prev, (long)ptr->next, ptr->filename0->value );
5801 }
5802 }
5803 else
5804 {
5805 fprintf( TSD->stddump,"Slot: %3d is empty\n", i );
5806 }
5807 }
5808 }
5809
5810 return nullstringptr() ;
5811 }
5812 #endif
5813
5814
5815
5816
5817 /*
5818 * Read from stdin using readoneline()
5819 */
readkbdline(tsd_t * TSD)5820 streng *readkbdline( tsd_t *TSD )
5821 {
5822 fil_tsd_t *ft;
5823
5824 ft = (fil_tsd_t *)TSD->fil_tsd;
5825 return readoneline( TSD, ft->stdio_ptr[DEFAULT_STDIN_INDEX] );
5826 }
5827
addr_reopen_file(tsd_t * TSD,const streng * filename,char code,int iserror)5828 void *addr_reopen_file( tsd_t *TSD, const streng *filename, char code,
5829 int iserror )
5830 /* This is the open routine for the ADDRESS WITH-redirection. filename is
5831 * the name of the file. code is either 'r' for "READ",
5832 * 'A' for "WRITE APPEND", 'R' for "WRITE REPLACE". In case of READ
5833 * already opened files will be reused. In case of APPEND or REPLACE the
5834 * files are (re-)opened. An internal structure for files is returned and
5835 * should be used for calls to addr_io_file.
5836 * An already opened file for write can't be used. See J18PUB.pdf, 5.5.1.
5837 * The return value may be NULL in case of an error. A NOTREADY condition
5838 * may have been raised in this case.
5839 * filename may be NULL for a default file.
5840 * iserror can be set or not. If set, stderr instead of stdout should be used.
5841 */
5842 {
5843 fileboxptr ptr;
5844 fil_tsd_t *ft;
5845
5846 ft = (fil_tsd_t *)TSD->fil_tsd;
5847
5848 iserror = ( iserror ) ? 1 : 0;
5849 switch ( code )
5850 {
5851 case 'r':
5852 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5853 return ft->stdio_ptr[DEFAULT_STDIN_INDEX];
5854 ptr = get_file_ptr( TSD, filename, OPER_READ, ACCESS_READ );
5855 if ( ptr != NULL )
5856 ptr->readpos = 0;
5857 break;
5858
5859 case 'A':
5860 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5861 return ft->stdio_ptr[DEFAULT_STDOUT_INDEX + iserror];
5862 if ( ( ptr = getfileptr( TSD, filename ) ) != NULL )
5863 {
5864 if ( ptr->flag & FLAG_SURVIVOR )
5865 return get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE );
5866 }
5867 closefile( TSD, filename );
5868 ptr = openfile( TSD, filename, ACCESS_STREAM_APPEND );
5869 break;
5870
5871 case 'R':
5872 if ( ( filename == NULL ) || ( Str_len( filename ) == 0 ) )
5873 return ft->stdio_ptr[DEFAULT_STDOUT_INDEX + iserror];
5874 if ( ( ptr = getfileptr( TSD, filename ) ) != NULL )
5875 {
5876 if ( ptr->flag & FLAG_SURVIVOR )
5877 return get_file_ptr( TSD, filename, OPER_WRITE, ACCESS_WRITE );
5878 }
5879 closefile( TSD, filename );
5880 ptr = openfile( TSD, filename, ACCESS_STREAM_REPLACE );
5881 break;
5882
5883 default:
5884 ptr = NULL;
5885 break;
5886 }
5887
5888 if ( ( ptr != NULL ) && ( ptr->fileptr == NULL ) )
5889 ptr = NULL;
5890
5891 return ptr;
5892 }
5893
addr_io_file(tsd_t * TSD,void * fileptr,const streng * buffer)5894 streng *addr_io_file( tsd_t *TSD, void *fileptr, const streng *buffer )
5895 /* This is the working routine for the ADDRESS WITH-redirection. fileptr is
5896 * the return value of addr_reopen_file. buffer must be NULL for a read
5897 * operation or a filled buffer.
5898 * The return value is NULL in case of a write operation or in case of EOF
5899 * while reading.
5900 * All IO is done by charin/charout.
5901 * A NOTREADY condition won't be raised.
5902 */
5903 {
5904 streng *retval = NULL ;
5905
5906 if ( fileptr == NULL ) /* fixes bug 806948 */
5907 return retval;
5908
5909 if ( buffer == NULL )
5910 retval = readbytes( TSD, (fileboxptr)fileptr, 0x1000, 1 ) ;
5911 else
5912 writebytes( TSD, (fileboxptr)fileptr, buffer, 1 ) ;
5913
5914 return( retval ) ;
5915 }
5916
addr_reset_file(tsd_t * TSD,void * fileptr)5917 void addr_reset_file( tsd_t *TSD, void *fileptr )
5918 /* This is the "close" routine for the ADDRESS WITH-redirection. We don't close
5919 * the stream, we simply perform a reset, that's enough to restart reading
5920 * or writing.
5921 */
5922 {
5923 fileboxptr ptr = (fileboxptr)fileptr;
5924
5925 if ( fileptr == NULL ) /* fixes bug 806948 */
5926 return;
5927
5928 if ( ptr->fileptr )
5929 {
5930 clearerr( ptr->fileptr );
5931 if ( ptr->flag & FLAG_PERSIST )
5932 rx_fseek( ptr->fileptr, 0, SEEK_SET );
5933 ptr->thispos = 0;
5934 ptr->oper = OPER_NONE;
5935 }
5936
5937 if ( ptr->flag & FLAG_SURVIVOR )
5938 ptr->flag &= ~FLAG_ERROR;
5939
5940 ptr->flag &= ~FLAG_FAKE;
5941 }
5942
5943
addr_file_info(tsd_t * TSD,const streng * source,int defchannel)5944 streng *addr_file_info( tsd_t *TSD, const streng *source, int defchannel )
5945 /*
5946 * addr_file_info is a helper for the ADDRESS WITH-redirection. source is the
5947 * name of the file and may be NULL for a default channel. The channel's number
5948 * is used then, 0 = stdin, 1 = stdout, 2 = stderr.
5949 * The return name is a fresh copy of the qualified name of the file.
5950 */
5951 {
5952 fil_tsd_t *ft;
5953 streng *result;
5954 fileboxptr p;
5955
5956 ft = (fil_tsd_t *)TSD->fil_tsd;
5957
5958 /*
5959 * We don't know anything about the default channels. So just return our
5960 * internal name.
5961 */
5962 if ( source == NULL )
5963 return Str_dupstrTSD( ft->stdio_ptr[ defchannel ]->filename0 );
5964
5965 /*
5966 * Check for well known devices, the SURVIVORs. Return a comparable name
5967 * which makes sure "<stdin>" is equal to "stdin".
5968 */
5969 if ( ( p = getfileptr( TSD, source ) ) != NULL )
5970 {
5971 if ( p->flag & FLAG_SURVIVOR )
5972 {
5973 if ( p->fileptr == stdin )
5974 defchannel = 0;
5975 else if ( p->fileptr == stdout )
5976 defchannel = 1;
5977 else
5978 defchannel = 2;
5979
5980 return Str_dupstrTSD( ft->stdio_ptr[ defchannel ]->filename0 );
5981 }
5982 }
5983
5984 /*
5985 * Even in case of character devices a name resolution makes sense, e.g.
5986 * "tty" may expand to "/dev/tty" or the fifo "local" may expand to
5987 * "/somethere/local".
5988 */
5989 result = Str_makeTSD( REXX_PATH_MAX );
5990 my_fullpathstreng( TSD, result->value, source );
5991 result->len = strlen( result->value );
5992
5993 return result;
5994 }
5995
5996
5997 /*
5998 * This routine is not really interesting. You should use the STREAM()
5999 * built-in function for greater portability and functionality. It is
6000 * left in the code for portability reasons.
6001 */
unx_open(tsd_t * TSD,cparamboxptr parms)6002 streng *unx_open( tsd_t *TSD, cparamboxptr parms )
6003 {
6004 fileboxptr ptr=NULL ;
6005 char ch=' ' ;
6006 int iaccess=ACCESS_NONE ;
6007
6008 checkparam( parms, 1, 2 , "OPEN" ) ;
6009
6010 if ((parms->next)&&(parms->next->value))
6011 {
6012 ch = getoptionchar( TSD, parms->next->value, "OPEN", 2, "RW", "" ) ;
6013 if ( ch == 'R' ) /* bja */
6014 iaccess = ACCESS_READ ;
6015 else if ( ch == 'W' ) /* bja */
6016 iaccess = ACCESS_WRITE ;
6017 else
6018 assert( 0 ) ;
6019 }
6020 else
6021 iaccess = ACCESS_READ ;
6022
6023 ptr = openfile( TSD, parms->value, iaccess ) ;
6024
6025 return int_to_streng( TSD,( ptr && ptr->fileptr )) ;
6026 }
6027
6028
6029 /*
6030 * This routine is not really interesting. You should use the CLOSE
6031 * command of the STREAM() built-in function for greater portability
6032 * and compatibility. It is left in the code only for compatibility
6033 * reasons.
6034 */
unx_close(tsd_t * TSD,cparamboxptr parms)6035 streng *unx_close( tsd_t *TSD, cparamboxptr parms )
6036 {
6037 fileboxptr ptr=NULL ;
6038
6039 checkparam( parms, 1, 1 , "CLOSE" ) ;
6040 ptr = getfileptr( TSD, parms->value ) ;
6041 closefile( TSD, parms->value ) ;
6042
6043 return int_to_streng( TSD, ptr!=NULL ) ;
6044 }
6045
6046
6047 /*
6048 * a function called exists that checks if a file with a certain name
6049 * exists. This function was taken from the ARexx API.
6050 */
arexx_exists(tsd_t * TSD,cparamboxptr parms)6051 streng *arexx_exists( tsd_t *TSD, cparamboxptr parms )
6052 {
6053 char *name;
6054 streng *retval;
6055 struct rx_stat_buf st;
6056
6057 checkparam( parms, 1, 1, "EXISTS" ) ;
6058
6059 name = str_of( TSD, parms->value ) ;
6060 retval = int_to_streng( TSD, rx_stat( name, &st ) != -1 ) ;
6061 Free_TSD( TSD, name ) ;
6062
6063 return retval;
6064 }
6065
6066 /*
6067 * get_external_routine_file opens a file in binary mode and returns the
6068 * fully qualified path name on success. NULL is returned otherwise.
6069 * The opened file pointer is returned in *fp.
6070 */
get_external_routine_file(const tsd_t * TSD,const char * inname,FILE ** fp)6071 static streng *get_external_routine_file( const tsd_t *TSD,
6072 const char *inname, FILE **fp )
6073 {
6074 char buf[3 * REXX_PATH_MAX + 1];
6075
6076 #ifdef VMS
6077 *fp = fopen( inname, "r" );
6078 #else
6079 *fp = fopen( inname, "rb" );
6080 #endif
6081 if ( *fp == NULL )
6082 return NULL;
6083
6084 my_fullpath( (tsd_t *) TSD, buf, inname );
6085
6086 return Str_crestrTSD( buf );
6087 }
6088
6089 /*
6090 * See get_external_routine for comments. This function processes one path
6091 * element which is passed in path.
6092 * suffixes is either NULL or the list of extra suffixes which should be
6093 * tested. *fp must be NULL on entry.
6094 * path may be NULL if no further directory processing shall happen.
6095 */
get_external_routine_path(const tsd_t * TSD,const char * inname,FILE ** fp,const char * path,const char * suffixes,int emptySuffixAllowed)6096 static streng *get_external_routine_path( const tsd_t *TSD,
6097 const char *inname, FILE **fp,
6098 const char *path,
6099 const char *suffixes,
6100 int emptySuffixAllowed )
6101 {
6102 char outname[REXX_PATH_MAX+1];
6103 int i,ilen,hlen;
6104 streng *retval;
6105 static const char *default_suffixes = "rexx,rex,cmd,rx";
6106 const char *suffixlist[2];
6107 const char *suffix;
6108 int suffixlen;
6109
6110 ilen = strlen( inname );
6111 if ( !path )
6112 hlen = 0;
6113 else
6114 hlen = strlen( path );
6115 if ( !hlen )
6116 {
6117 if ( ilen > REXX_PATH_MAX )
6118 return NULL;
6119 strcpy( outname, inname );
6120 }
6121 else
6122 {
6123 if ( ( strchr( FILE_SEPARATORS, inname[0] ) == NULL ) &&
6124 ( strchr( FILE_SEPARATORS, path[hlen - 1] ) == NULL ) )
6125 {
6126 if ( ilen + hlen + 1 > REXX_PATH_MAX )
6127 return NULL;
6128 strcpy( outname, path );
6129 strcat( outname, FILE_SEPARATOR_STR );
6130 strcat( outname, inname );
6131 }
6132 else
6133 {
6134 if ( ilen + hlen > REXX_PATH_MAX )
6135 return NULL;
6136 strcpy( outname, path );
6137 strcat( outname, inname );
6138 }
6139 }
6140
6141 /*
6142 * The filename is constructed. Try without fiddling with suffixes first.
6143 */
6144 if ( emptySuffixAllowed )
6145 {
6146 if ( ( retval = get_external_routine_file( TSD, outname, fp ) ) != NULL )
6147 return retval;
6148 }
6149
6150 /*
6151 * Next try the supplied suffix list, then try the default list.
6152 * First check if a known extension exists, after every check do the
6153 * application.
6154 */
6155
6156 suffixlist[0] = suffixes;
6157 suffixlist[1] = default_suffixes;
6158 ilen = strlen( outname );
6159 #define IsDelim(c) ( ( (c) == ',' ) || ( (c) == '.' ) || \
6160 ( (c) == PATH_SEPARATOR ) || rx_isspace(c) )
6161 for ( i = 0; i < 2; i++ )
6162 {
6163 suffixes = suffixlist[i];
6164
6165 while ( suffixes )
6166 {
6167 while ( IsDelim(*suffixes) )
6168 suffixes++;
6169 if ( *suffixes == '\0' )
6170 break;
6171
6172 for ( suffixlen = 1; !IsDelim(suffixes[suffixlen]); suffixlen++ )
6173 if ( suffixes[suffixlen] == '\0' )
6174 break;
6175
6176 suffix = suffixes;
6177 suffixes += suffixlen;
6178
6179 if ( suffixlen + 1 > ilen )
6180 continue;
6181 if ( outname[ ilen - suffixlen - 1 ] != '.' )
6182 continue;
6183 #ifdef CASE_SENSITIVE_FILENAMES
6184 if ( memcmp( suffix, outname + ilen - suffixlen, suffixlen - 1 ) )
6185 #else
6186 if ( mem_cmpic( suffix, outname + ilen - suffixlen, suffixlen - 1 ) )
6187 #endif
6188 continue;
6189
6190 /*
6191 * A matching suffix forces us to terminate every further seeking a
6192 * proper file.
6193 */
6194 if ( !emptySuffixAllowed )
6195 return get_external_routine_file( TSD, outname, fp );
6196 return NULL;
6197 }
6198 }
6199
6200 /*
6201 * Try the extensions.
6202 */
6203 for ( i = 0; i < 2; i++ )
6204 {
6205 suffixes = suffixlist[i];
6206
6207 while ( suffixes )
6208 {
6209 while ( IsDelim(*suffixes) )
6210 suffixes++;
6211 if ( *suffixes == '\0' )
6212 break;
6213
6214 for ( suffixlen = 1; !IsDelim(suffixes[suffixlen]); suffixlen++ )
6215 if ( suffixes[suffixlen] == '\0' )
6216 break;
6217
6218 suffix = suffixes;
6219 suffixes += suffixlen;
6220
6221 if ( suffixlen + 1 + ilen > REXX_PATH_MAX )
6222 continue;
6223 outname[ ilen ] = '.';
6224 memcpy( outname + ilen + 1, suffix, suffixlen );
6225 outname[ilen + 1 + suffixlen] = '\0';
6226 if ( ( retval = get_external_routine_file( TSD, outname, fp ) ) !=
6227 NULL )
6228 return retval;
6229 }
6230 }
6231
6232 #undef Delim
6233 return NULL;
6234 }
6235
6236 /*
6237 * See get_external_routine for comments. This function processes a list of
6238 * path elements delimited by the path separator which is passed in paths.
6239 * suffixes is either NULL or the list of extra suffixes which should be
6240 * tested. *fp must be NULL on entry.
6241 * paths will be destroyed.
6242 */
get_external_routine_paths(const tsd_t * TSD,const char * inname,FILE ** fp,char * paths,const char * suffixes,int emptySuffixAllowed)6243 static streng *get_external_routine_paths( const tsd_t *TSD,
6244 const char *inname, FILE **fp,
6245 char *paths, const char *suffixes,
6246 int emptySuffixAllowed )
6247 {
6248 char *path;
6249 streng *retval;
6250
6251 if ( *paths == '\0' )
6252 return NULL;
6253
6254 while ( paths )
6255 {
6256 path = paths;
6257 paths = strchr( paths, PATH_SEPARATOR );
6258 if ( paths != NULL )
6259 *paths++ = '\0';
6260
6261 if ( *path == '\0')
6262 {
6263 /*
6264 * An empty string is counted as "." in unix systems and ignored in
6265 * all other systems.
6266 */
6267 #ifdef UNIX
6268 path = ".";
6269 #else
6270 continue;
6271 #endif
6272 }
6273
6274 retval = get_external_routine_path( TSD, inname, fp, path, suffixes,
6275 emptySuffixAllowed );
6276
6277 if ( retval )
6278 return retval;
6279 }
6280
6281 return NULL;
6282 }
6283
6284 /*
6285 * get_external_routine searches for a script called inname. Some paths are
6286 * search if the file is not found and an extension may be added if no file is
6287 * found.
6288 *
6289 * On success *fp is set to the opened (binary) file and the return value is
6290 * the fully qualified file name. If no file was found the return value is
6291 * NULL and *fp will be NULL, too.
6292 * The returned file name is extended by a terminating '\0' without counting
6293 * is in the string's length.
6294 *
6295 * This is the search algorithm:
6296 *
6297 * First of all we process the environment variable REGINA_MACROS. If no file
6298 * is found we proceed with the current directory and then with the environment
6299 * variable PATH. The semantics of the use of REGINA_MACROS and PATH are the
6300 * same, and the search in the current directory is omitted for the superuser
6301 * in unix systems for security reasons. The current directory must be
6302 * specified explicitely by the superuser.
6303 * When processing an environment variable the content is split into the
6304 * different paths and each path is processed separately.
6305 * Note that the search algorithm to this point is ignored if the script name
6306 * contains a file path specification. eg. If "CALL .\MYPROG" is called, then
6307 * no searching of REGINA_MACROS or PATH is done; only the concatenation of
6308 * suffixes is carried out.
6309 *
6310 * For each file name and path element a concatenated file name is created. If
6311 * a known file extension is part of the file name only this file is searched,
6312 * otherwise the file name is extended by the extensions "<empty>", ".rexx",
6313 * ".rex", ".cmd", ".rx" in this order. The file name case is ignored on
6314 * systems that ignore the character case for normal file operations like DOS,
6315 * Windows, OS/2.
6316 *
6317 * The first matching file terminates the whole algorithm and the found file
6318 * is returned.
6319 *
6320 * The environment variable REGINA_SUFFIXES extends the list of known suffixes
6321 * as specified above, and is inserted after the "<empty"> extension in the
6322 * process. REGINA_SUFFIXES has to contain a space or comma separated list of
6323 * extensions, a dot in front of each entry is allowed, e.g.
6324 * ".macro,.mac,regina" or "macro mac regina"
6325 *
6326 * Note that it is planned to extend the list of known suffixes by ".rxc" in
6327 * version 3.4 to allow for seemless integration of precompiled macros.
6328 */
get_external_routine(const tsd_t * TSD,const char * inname,FILE ** fp)6329 streng *get_external_routine( const tsd_t *TSD, const char *inname, FILE **fp )
6330 {
6331 streng *retval=NULL;
6332 char *paths;
6333 char *suffixes;
6334
6335 *fp = NULL;
6336
6337 suffixes = mygetenv( TSD, "REGINA_SUFFIXES", NULL, 0 );
6338
6339 if ( strpbrk( inname, FILE_SEPARATORS ) != NULL )
6340 {
6341 retval = get_external_routine_path( TSD, inname, fp, NULL, suffixes, 1 );
6342 if ( retval )
6343 {
6344 if ( suffixes )
6345 FreeTSD( suffixes );
6346 return retval;
6347 }
6348 return NULL;
6349 }
6350
6351 if ( ( paths = mygetenv( TSD, "REGINA_MACROS", NULL, 0 ) ) != NULL )
6352 {
6353 retval = get_external_routine_paths( TSD, inname, fp, paths, suffixes, 1 );
6354 FreeTSD( paths );
6355 if ( retval )
6356 {
6357 if ( suffixes )
6358 FreeTSD( suffixes );
6359 return retval;
6360 }
6361 }
6362
6363 paths = ".";
6364 #ifdef UNIX
6365 if ( geteuid() == 0 )
6366 paths = NULL;
6367 #endif
6368 if ( paths )
6369 {
6370 retval = get_external_routine_path( TSD, inname, fp, paths, suffixes, 1 );
6371 if ( retval )
6372 {
6373 if ( suffixes )
6374 FreeTSD( suffixes );
6375 return retval;
6376 }
6377 }
6378
6379 if ( ( paths = mygetenv( TSD, "PATH", NULL, 0 ) ) != NULL )
6380 {
6381 retval = get_external_routine_paths( TSD, inname, fp, paths, suffixes, 0 );
6382 FreeTSD( paths );
6383 }
6384
6385 if ( suffixes )
6386 FreeTSD( suffixes );
6387 return retval;
6388 }
6389
6390 /*
6391 * find_shared_library is used for HP/UX purpose only.
6392 * It looks for the file inname in the content of the environment variable
6393 * inenv and puts the result into retname. retname has to have a size of
6394 * at least REXX_PATH_MAX+1.
6395 * retname becomes inname if no other file is found.
6396 */
find_shared_library(const tsd_t * TSD,const char * inname,const char * inenv,char * retname)6397 void find_shared_library(const tsd_t *TSD, const char *inname, const char *inenv, char *retname)
6398 {
6399 char *paths;
6400 char outname[REXX_PATH_MAX+1];
6401 char *env_path;
6402
6403 strcpy( retname, inname );
6404 env_path = mygetenv( TSD, inenv, NULL, 0 ); /* fixes bug 595293 */
6405 if ( !env_path )
6406 return;
6407 paths = env_path;
6408 while ( paths && *paths )
6409 {
6410 int pathlen;
6411 char *sep;
6412
6413 sep = strchr( paths, PATH_SEPARATOR );
6414 pathlen = sep ? sep-paths : strlen( paths );
6415 strncpy( outname, paths, pathlen );
6416 outname[pathlen] = 0;
6417
6418 if ( ( pathlen > 0 ) && ( outname[pathlen-1] != FILE_SEPARATOR ) )
6419 strcat( outname, FILE_SEPARATOR_STR );
6420 strcat( outname, inname );
6421 paths = sep ? sep+1 : 0; /* set up for next pass */
6422 if ( access( outname,F_OK ) == 0)
6423 {
6424 strcpy( retname,outname );
6425 break;
6426 }
6427 }
6428 FreeTSD( env_path );
6429 return;
6430 }
6431
6432 /* CloseOpenFiles closes all scripting input files and it closes all opened
6433 * STREAM files without destroying the associated informations.
6434 * Bug 982062: Added FilePtrDisposition to allow for purging fileptr
6435 * table if requested.
6436 */
CloseOpenFiles(const tsd_t * TSD,FilePtrDisposition fpd)6437 void CloseOpenFiles( const tsd_t *TSD, FilePtrDisposition fpd )
6438 {
6439 sysinfobox *ptr;
6440
6441 if ( fpd == fpdRETAIN )
6442 {
6443 ptr = TSD->systeminfo;
6444 while (ptr)
6445 {
6446 if (ptr->input_fp)
6447 {
6448 fclose(ptr->input_fp);
6449 ptr->input_fp = NULL;
6450 }
6451 ptr = ptr->previous;
6452 }
6453 /*
6454 * Cheat about the const-state.
6455 */
6456 swapout_all( ( tsd_t *) TSD );
6457 }
6458 else
6459 {
6460 purge_filetable( ( tsd_t *) TSD );
6461 }
6462 return;
6463 }
6464
ConfigStreamQualified(tsd_t * TSD,const streng * name)6465 streng *ConfigStreamQualified( tsd_t *TSD, const streng *name )
6466 {
6467 char *fn=NULL;
6468 streng *result=NULL ;
6469
6470 /*
6471 * Nul terminate the input filename string, as stat() will barf if
6472 * it isn't and other functions stuff up!
6473 */
6474 fn = str_ofTSD(name);
6475
6476 result = Str_makeTSD( REXX_PATH_MAX );
6477 if ( my_fullpath( TSD, result->value, fn ) == -1 )
6478 {
6479 /*
6480 * my_fullpath failed, so split the supplied file into filename
6481 * and directory. Then look for directory and append the filename
6482 * TODO -
6483 */
6484 }
6485 result->len = strlen( result->value );
6486 return( result );
6487 }
6488
6489 #if defined(HAVE__FULLPATH) || defined(__EMX__)
6490 /*
6491 * my_fullpath tries to get the fully qualified name of a file or directory
6492 * even if it doesn't exist. It tries to return a reasonable value even if
6493 * a path element is missing.
6494 * The return value is 0 on success, -1 in case of a severe error.
6495 */
my_fullpath(tsd_t * TSD,char * dst,const char * src)6496 int my_fullpath( tsd_t *TSD, char *dst, const char *src )
6497 {
6498 char *copy=NULL;
6499 int rc = 0, len;
6500 # if defined(__EMX__)
6501 int i;
6502
6503 if ( _fullpath( dst, src, REXX_PATH_MAX ) == -1)
6504 strcpy( dst, src );
6505 /*
6506 * Convert / back to \.
6507 */
6508 len = strlen( dst );
6509 for ( i = 0; i < len; i++ )
6510 {
6511 if ( dst[i] == '/' )
6512 dst[i] = '\\';
6513 }
6514 # else
6515 /* attempt to get the full path with the supplied source */
6516 if ( _fullpath( dst, src, REXX_PATH_MAX ) == NULL )
6517 {
6518 # if defined(WIN32)
6519 /* now try by removing trailing slash */
6520 /* this code may not actually be needed */
6521 len = strlen( src ) - 1;
6522 if ( src[len] == '\\' || src[len] == '/')
6523 {
6524 copy = (char *)MallocTSD( len+2 ) ;
6525 strcpy( copy, src );
6526 copy[len] = '\0';
6527 if ( _fullpath( dst, copy, REXX_PATH_MAX ) == NULL )
6528 {
6529 strcpy( dst, src );
6530 rc = -1;
6531 }
6532 }
6533 else
6534 {
6535 strcpy( dst, src );
6536 rc = -1;
6537 }
6538 if ( copy )
6539 FreeTSD( copy );
6540 # else
6541 strcpy( dst, src );
6542 rc = -1;
6543 # endif
6544 }
6545 # endif
6546 return rc;
6547 }
6548 #elif defined(HAVE__TRUENAME)
6549
my_fullpath(tsd_t * TSD,char * dst,const char * src)6550 int my_fullpath( tsd_t *TSD, char *dst, const char *src )
6551 {
6552 _truename( src, dst );
6553
6554 return 0;
6555 }
6556 #elif defined(HAVE_REALPATH)
6557
my_fullpath(tsd_t * TSD,char * dst,const char * src)6558 int my_fullpath( tsd_t *TSD, char *dst, const char *src )
6559 {
6560 /* hack for leading ~/ */
6561 int len = strlen( src );
6562 char *source=NULL;
6563 char *copy=NULL;
6564 if ( ( len > 1 && strncmp( src, "~/", 2 ) == 0 ) || ( len == 1 && strncmp( src, "~", 1 ) == 0) )
6565 {
6566 char *env = getenv( "HOME" );
6567 if ( env != NULL )
6568 {
6569 int len2 = strlen( env );
6570 copy = (char *)MallocTSD( len+len2+2 ) ;
6571 strcpy( copy, env );
6572 strcat( copy, src+1 );
6573 source = copy;
6574 }
6575 else
6576 {
6577 source = src;
6578 }
6579 }
6580 else
6581 {
6582 source = src;
6583 }
6584 realpath( source, dst );
6585 if ( copy )
6586 FreeTSD( copy );
6587
6588 return 0;
6589 }
6590 #elif defined(VMS)
6591 # include <ssdef.h>
6592 # include <rmsdef.h>
6593 # include <descrip.h>
6594
my_fullpath(tsd_t * TSD,char * dst,const char * src)6595 int my_fullpath( tsd_t *TSD, char *dst, const char *src )
6596 {
6597 char *s;
6598 int status, context = 0;
6599 struct dsc$descriptor_d result_dx = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0};
6600 struct dsc$descriptor_d finddesc_dx = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0};
6601
6602 finddesc_dx.dsc$a_pointer = (char *)src; /* You may need to cast this */
6603 finddesc_dx.dsc$w_length = strlen(src);
6604 status = lib$find_file( &finddesc_dx, &result_dx, &context, 0, 0, 0, 0 );
6605 if ( status == RMS$_NORMAL )
6606 {
6607 memcpy(dst,result_dx.dsc$a_pointer,result_dx.dsc$w_length);
6608 *(dst+result_dx.dsc$w_length) = '\0';
6609 }
6610 else
6611 strcpy(dst,src);
6612 lib$find_file_end(&context);
6613 str$free1_dx(&result_dx);
6614 return(0);
6615 }
6616 #else /* neither _FULLPATH, _TRUENAME, REALNAME, VMS */
6617
my_fullpath(tsd_t * TSD,char * dst,const char * src)6618 int my_fullpath( tsd_t *TSD, char *dst, const char *src )
6619 {
6620 char tmp[REXX_PATH_MAX+1];
6621 char curr_path[REXX_PATH_MAX+1];
6622 char path[REXX_PATH_MAX+1];
6623 char fname[REXX_PATH_MAX+1];
6624 int i = 0, len = -1, retval;
6625 struct rx_stat_buf stat_buf;
6626
6627 getcwd(curr_path,REXX_PATH_MAX);
6628 strcpy(tmp,src);
6629 /*
6630 * First determine if the supplied filename is a directory.
6631 */
6632 # if defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
6633 for ( i = 0; i < strlen( tmp ); i++ )
6634 if ( tmp[ i ] == '\\' )
6635 tmp[ i ] = '/';
6636 # endif
6637 if ((rx_stat(tmp,&stat_buf) == 0)
6638 && (stat_buf.st_mode & S_IFMT) == S_IFDIR)
6639 {
6640 strcpy(path,tmp);
6641 strcpy(fname,"");
6642 }
6643 else /* here if the file doesn't exist or is not a directory */
6644 {
6645 for (i=strlen(tmp),len=-1;i>-1;i--)
6646 {
6647 if (tmp[i] == '/')
6648 {
6649 len = i;
6650 break;
6651 }
6652 }
6653 switch(len)
6654 {
6655 case (-1):
6656 getcwd(path,REXX_PATH_MAX);
6657 strcpy(fname,tmp);
6658 break;
6659 case 0:
6660 strcpy(path,tmp);
6661 path[1] = '\0';
6662 strcpy(fname,tmp+1+len);
6663 break;
6664 default:
6665 strcpy(path,tmp);
6666 path[len] = '\0';
6667 strcpy(fname,tmp+1+len);
6668 break;
6669 }
6670 }
6671 /*
6672 * Change directory to the supplied path, if possible and store the
6673 * expanded path.
6674 * If an error, restore the current path.
6675 */
6676 if (chdir(path) != 0)
6677 {
6678 retval = -1;
6679 }
6680 else
6681 {
6682 getcwd(path,REXX_PATH_MAX);
6683 retval = 0;
6684 }
6685 chdir(curr_path);
6686 /*
6687 * Append the OS directory character to the path if it doesn't already
6688 * end in the character.
6689 */
6690 len = strlen(path);
6691 if (len > 0)
6692 {
6693 # if defined(__WINS__) || defined(__EPOC32__)
6694 if ( path[ len - 1 ] != '\\'
6695 # else
6696 if ( path[ len - 1 ] != '/'
6697 # endif
6698 && strlen( fname ) != 0 )
6699 {
6700 strcat(path,"/");
6701 len++;
6702 }
6703 # if defined(DJGPP) || defined(__WINS__) || defined(__EPOC32__)
6704 for ( i = 0; i < len; i++ )
6705 if ( path[ i ] == '/' )
6706 path[ i ] = '\\';
6707 # endif
6708 }
6709 strcpy(dst,path);
6710 strcat(dst,fname);
6711
6712 return retval;
6713 }
6714 #endif
6715
my_fullpathstreng(const tsd_t * TSD,char * dst,const streng * src)6716 int my_fullpathstreng( const tsd_t *TSD, char *dst, const streng *src )
6717 {
6718 char *copy;
6719 int retval;
6720
6721 copy = str_ofTSD( src );
6722 retval = my_fullpath( (tsd_t *) TSD, dst, copy );
6723 FreeTSD( copy );
6724
6725 return retval;
6726 }
6727
6728 #if !defined(HAVE__SPLITPATH2) && !defined(HAVE__SPLITPATH) && !defined(__EMX__) && !defined(DJGPP)
my_splitpath2(const char * in,char * out,char ** drive,char ** dir,char ** name,char ** ext)6729 int my_splitpath2( const char *in, char *out, char **drive, char **dir, char **name, char **ext )
6730 {
6731 int inlen = strlen(in);
6732 int last_slash_pos=-1,last_dot_pos=-1,last_pos=0,i=0;
6733
6734 for (i=0;i<inlen;i++)
6735 {
6736 if ( *(in+i) == '/' || *(in+i) == '\\' )
6737 last_slash_pos = i;
6738 else if ( *(in+i) == '.' )
6739 last_dot_pos = i;
6740 }
6741 /*
6742 * drive is always empty !
6743 */
6744 out[0] = '\0';
6745 *drive = out;
6746
6747 *ext = out+1;
6748 if (last_dot_pos > last_slash_pos)
6749 {
6750 strcpy(*ext,in+last_dot_pos);
6751 last_pos = 2 + (inlen - last_dot_pos);
6752 inlen = last_dot_pos;
6753 }
6754 else
6755 {
6756 **ext = '\0';
6757 last_pos = 2;
6758 }
6759 *dir = out+last_pos;
6760 /*
6761 * If there is a path component (last_slash_pos not -1), then copy
6762 * from the start of the in string to the last_slash_pos to out[1]
6763 */
6764 if (last_slash_pos != -1)
6765 {
6766 memcpy(*dir, in, last_slash_pos + 1);
6767 last_pos += last_slash_pos + 1;
6768 out[last_pos++] = '\0';
6769 *name = out+last_pos;
6770 memcpy(*name, in+last_slash_pos+1,(inlen - last_slash_pos - 1) );
6771 out[last_pos + (inlen - last_slash_pos - 1)] = '\0';
6772 }
6773 else
6774 {
6775 **dir = '\0';
6776 last_pos++;
6777 *name = out+last_pos;
6778 memcpy(*name, in, inlen);
6779 *(*name+inlen) = '\0';
6780 }
6781 return(0);
6782 }
6783 #endif
6784