When talking about a "file" in the REXX language, the more ambiguous terminology "stream" is substituted for "file".

As you've seen, functions such as LINEIN, LINEOUT, CHAROUT, CHARIN, etc, can read/write data to a file upon a permanent media such as a hard drive. We call such a file a persistent stream.

But these same functions may also read/write data to devices that don't permanently store their data such as the console (window) or serial port or some pipes. We call such a device a transient stream.

In other words, you can use CHAROUT to write characters to both a console window as well as a file on disk. With the former, the characters are simply displayed, but otherwise not stored. With the latter, the operating system actually stores the characters to a permanent file. The net result is that you can go back and reread the data you wrote out in the latter case, but not the former case. This distinction is important to us because sometimes we have to disallow certain operations for transient streams, such as "seeking" to various "stream positions". Also, for transient streams, we may have to wait for incoming data (that we read with LINEIN, CHARIN, or VALUEIN), whereas with persistent streams, we don't.

So there are some differences with a transient stream (versus persistent streams), in regards to not being able to set the current read/write position, and also a call to LINEIN, CHARIN, or VALUEIN perhaps suspending a script until such time as the requested number of characters become available.

But otherwise, you can treat a transient stream like a permanent stream.

Under different operating systems, there may be different "names" for the serial port, parallel port, and other ports and devices that LINEIN/LINEOUT/CHARIN/CHAROUT may access. For example, under Windows, the first serial port's name is COM1. Under another operating system, this name may be different. You'll have to consult the documentation with your operating system to see if various ports/devices can be written to or read from like a normal file.

In addition to using an operating specific name, some ports/devices may need to be opened with specific access modes. What that means is that you may need to explicitly open the port/device with an appropriate STREAM OPEN command that gives you the particular access you need. For example, maybe a particular port supports only reading data (ie, not writing) in which case you'd have to use STREAM's "OPEN READ" command.

/* Send a Hayes (Modem) reset command to a COM port (with
 * a modem presumably attached), and wait for the OK response.
 */

/* Change this to whatever COM port your modem is attached to */
name = 'COM2'

/* Open the COM port */
IF STREAM(name, 'C', 'OPEN BOTH') == 'READY:' THEN DO

   /* Send the Hayes command string 'ATZ' (followed by a line feed,
    * so we use LINEOUT to automatically send that terminating line
    * feed) to the modem
    */
   SAY '/* ====== Send the modem reset string ===== */'
   err = LINEOUT(name, 'ATZ')
   IF err == 0 THEN DO

      /* Read the "OK" line that we expect back from the modem. The modem
       * would send this with a line feed character at the end, so we can use
       * LINEIN
       */
      SAY '/* ====== Wait for "OK" response from the modem ===== */'
      DO UNTIL err = 'OK'
         err = LINEIN(name)
         SAY err  /* Print out the line that we read from the modem */
      END

   END
   ELSE SAY 'Error sending ATZ'

   /* Close the COM port. */
   STREAM(name, 'C', 'CLOSE')

END

/* COM port can't be opened. SAY why */
ELSE SAY STREAM(name, 'D')
Some useful device names under Windows are PRN (default printer), LPTx (parallel port where x is the port number, for example LPT1 for parallel port 1), and COMx (serial port).

For more exacting control over a COM port, you may wish to instead use the RXCOMM Add-on DLL.