So that is why our previous examples of using LINEOUT and CHAROUT to write to a file, and LINEIN and CHARIN to read from a file, never explicitly called any function to first "open" that file nor set any access mode to it. We just dove right into our calls to LINEOUT, CHAROUT, LINEIN, and CHARIN.
But sometimes, you may wish to explicitly open a file, or set a particular access mode to it. For example, perhaps you wish to allow some other software to simultaneously read from the same file that you're writing to. (REXX does not normally open a file with this sort of "sharing" access, and therefore either your script or the other program would have an error trying to operate upon the same file). Or maybe you would like to check if a file exists before trying to read/write to it, but don't want to trap NOTREADY in order to determine this. In this case, the STREAM Function has several OPEN commands that you can use. STREAM will return the string READY: if the file opens successfully, or an error string if not.
Note:
Some interpreters return the string ERROR for an error opening the file. Reginald returns ERROR: xxx where xxx is an operating system error number. An appropriate error message can be retrieved with STREAM's "D" option.Another advantage of using STREAM to explicitly open a file is that it clears any "error state" that may have been on that file. Such an "error state" would be due to a previously failed LINEIN, LINEOUT, CHAROUT, CHARIN, etc, that resulted in a NOTREADY that your script did not trap.
Reading an existing file
If you would like to open a file for only reading (ie, no writing), then you can use STREAM's "OPEN READ" command. This will allow other programs to also read from (but not write to) the file. If the file doesn't exist, then an error is returned, so "OPEN READ" is an effective way to make sure that you don't try to LINEIN or CHARIN a file that doesn't exist.
Below, we try to open the file named "mytext.txt":
/* Open "mytext.txt" for reading, but only if it exists */ IF STREAM("mytext.txt", 'C', 'OPEN READ') == 'READY:' THEN DO SAY 'mytext.txt exists' /* Here you can use CHARIN and LINEIN to read from the file */ END /* File can't be opened. SAY why */ ELSE SAY STREAM("mytext.txt", 'D')Note: You can actually also LINEOUT or CHAROUT to this file, but REXX will transparently reopen the file as if you had used STREAM's "OPEN BOTH". This can then prevent sharing.
Reading/writing to a file
If you would like to open a file for both reading and writing, then you can use STREAM's "OPEN BOTH" command. Other programs can neither read from nor write to the file. If the file doesn't exist, then REXX will create a new, empty file. If the file does already exist, REXX will open it and retain its data (and any CHAROUT and LINEOUT you do will append to that existing data unless you specify a different position to write at).
Note: This is how REXX implicitly opens a file when you call LINEIN, CHARIN, LINEOUT, or CHAROUT without first calling STREAM to open that file yourself. But you may wish to nevertheless use STREAM's "OPEN BOTH" command because it is then possible to determine whether there is a genuine error when opening/creating the file without needing to resort to trapping NOTREADY.
Below, we try to open/create the file named "mytext.txt":
/* Open "mytext.txt" for reading/writing. Retain existing data * if it exists. Otherwise, create a new, empty file. */ IF STREAM("mytext.txt", 'C', 'OPEN BOTH') == 'READY:' THEN DO SAY 'mytext.txt ready' /* Here you can use CHAROUT and LINEOUT to write to the file, * and/or CHARIN and LINEIN to read from it. */ END /* File can't be opened. SAY why */ ELSE SAY STREAM("mytext.txt", 'D')
Overwriting a file
Sometimes, you may want to write a file, but if the file already exists, then you want its original contents deleted. Previously, you saw that you could do this by passing a position of 1 to your first LINEOUT or CHAROUT call. But STREAM's "OPEN BOTH REPLACE" can open the file and delete any previous contents (so you need not pass any position args to LINEOUT or CHAROUT).
/* Open "mytext.txt" for reading/writing. Erase existing data * if it exists. Otherwise, create a new, empty file. */ IF STREAM("mytext.txt", 'C', 'OPEN BOTH REPLACE') == 'READY:' THEN DO SAY 'mytext.txt created as an empty file' /* Here you can use CHAROUT and LINEOUT to write to the file */ END /* File can't be opened. SAY why */ ELSE SAY STREAM("mytext.txt", 'D')
Sharing a file
As seen above, you can use "OPEN READ" if you want to open a file for reading only, and also allow another program to do likewise simultaneously. This level of sharing is always safe to do.
But there may be a situation where your script, or/and the other program, wishes to write data to a file at the same time that the other has the same file open. This is not so safe. You need to make sure that you coordinate access to the file. You don't want to be reading data from the file at the very same moment that the other program is overwriting that data, and vice versa. But assuming that you have worked out some sort of "communication" between your script and the other program so that you ensure such "race conditions" don't exist, then you can use STREAM's "OPEN BOTH SHARED". It works just like "OPEN BOTH", except that another program can simultaneously read from and write to the same file.
Note: "OPEN BOTH SHARED" is available only with Reginald.