REXX for VAX OpenVMS 7.3

REXX for VAX OpenVMS 7.3

My VAXstations now have REXX: Regina 3.3, the free implementation of IBM’s Restructured Extended Executor, as a kit for OpenVMS VAX 7.3, with the DCL lexical functions built in as REXX functions, DCL commands as ADDRESS environments, and DCL symbols and logical names reachable with VALUE(). The kit is a BACKUP saveset with a DCL installer and a DCL uninstaller; it needs nothing on the target but the tools every VMS has.

Claude Code assisted, which is to say, did much of the hard work. (This seems to be my tagline these days.)

One correction first, because I made the mistake myself: my notes said Regina was “available” in the Digiater freeware collection with a REGINA command. It is not. The only copies on my media are two source tarballs on DECUS tapes (a 1994 original VMS port and Regina 3.3 from 2004), and the 3.3 sources did not compile on VMS: two of the files begin with a block of instructions to the programmer that is not even a comment.

Downloads, with SHA-256 sums in SHA256SUMS.TXT:

Licence

Regina is free software under the GNU Library General Public License, version 2 (or any later version). The kit therefore does what that licence asks: every original file keeps its header and its copyright notice, every file I changed carries a dated “MODIFIED 12-OCT-2026” notice and SOURCES.TXT lists each change. The saveset itself carries the complete source of everything linked into REXX.EXE and the procedure to rebuild it, and so does the source kit above. The complete source, with the changes and the build procedures as well, are in the download. Nothing restricts running Regina or using it for your scripts. The installer, uninstaller, setup procedure and examples are mine; the three host tools are in the public domain.

What REXX is

REXX was designed by Mike Cowlishaw at IBM in the late 1970s as a scripting language for people, not for compilers: no declarations, every value a string, arithmetic in decimal with as many digits as you ask for, and PARSE to pull strings apart by template. It was the command language of VM/CMS, then of OS/2 and the Amiga (ARexx), and became an ANSI standard in 1996. Regina, by Anders Christensen of Norway and later Mark Hessling and Florian Grosse-Coosmann, is the portable free one; there was a VMS port of its early versions in 1994, which I did not use. This is what a REXX program looks like (the picture at the top):

/* HERO.REX -- Regina REXX on a VAX: DCL lexical functions built in */
parse version name .
say name 'on' strip(F$GETSYI('HW_NAME')) 'under OpenVMS',
    strip(F$GETSYI('VERSION'))
disk = 'SYS$SYSDEVICE'
say 'system disk' F$GETDVI(disk, 'VOLNAM'),
    'has' F$GETDVI(disk, 'MAXBLOCK') 'blocks'
numeric digits 40
say '2**128 =' 2**128
address system 'DIRECTORY/NOHEADING/NOTRAILING *.REX' with output stem f.
say f.0 'REXX programs in' F$DIRECTORY()
say 'REXX asks DCL:' F$MESSAGE(98962)
$ REXX HERO
REXX-Regina_3.3 on VAXserver 3900 Series under OpenVMS V7.3
system disk V96SYS has 8388608 blocks
2**128 = 340282366920938463463374607431768211456
14 REXX programs in [SYSLIB.REXX.EXAMPLES]
REXX asks DCL: %RMS-E-FNF, file not found

(The “VAXserver 3900 Series” and the volume name V96SYS are what my simulator reports: it runs a clone of my VAXstation 4000/96’s system disk.)

What was built

Regina 3.3 builds natively on the VAX with Compaq C V6.4: 43 modules, a 69-second batch job on the simulator, an 883-block image; no MACRO, MMS, MMK, lex or yacc. The work was in the VMS-specific files, which had never really worked with this compiler:

  • The lexical functions: F$GETJPI, F$GETDVI, F$GETSYI, F$TRNLNM, F$FILE_ATTRIBUTES, F$SEARCH, F$PARSE, F$CVTIME, F$FAO and about 25 more, as REXX functions of the same names. About a dozen of them had bugs – found by running every one next to the real DCL lexical function and comparing 128 results (below). Two more were added, F$EDIT and F$DEVICE.
  • Commands: ADDRESS SYSTEM 'any DCL command' runs it in a subprocess; WITH OUTPUT STEM d. / FIFO / INPUT STEM capture and feed it. The original code used Unix fork and pipes, which do not exist here: commands now go through temporary files and LIB$SPAWN.
  • Symbols and logical names: VALUE('NAME',,'SYMBOL'), 'GLOBAL', 'LOGICAL'. A symbol set from REXX is still there when the script ends.
  • Exit status: EXIT 0 is $STATUS 1; EXIT n is an error status with the message display suppressed, so IF .NOT. $STATUS and ON ERROR work and DCL prints nothing.

Issues

  • WRITE SYS$OUTPUT in a subprocess fails with %DCL-W-UNDFIL if the parent has a user-mode SYS$OUTPUT logical name (what DEFINE/USER SYS$OUTPUT file makes): the child inherits it and it hides the output stream DCL opened for it. The fix is to take the definition away while the child runs.
  • An image can only make logical names in user mode: what a script defines with VALUE(...,'LOGICAL') is gone when it ends, as with DEFINE/USER.
  • 'b='b in REXX is a binary-string literal ('...'B): the interpreter says “Invalid hexadecimal or binary string”. Put a blank after the quote.
  • A DCL command line given to LIB$SPAWN does no apostrophe substitution.

Installing

You need OpenVMS VAX 7.3, SYSTEM (or SYSPRV), and about 21,000 free blocks on the system disk while installing. No compiler.

  1. Put the saveset and the two .COM files into one directory on the VAX. Transfer modes matter: the .BCK in binary, the .COM files in ASCII.

  2. A binary transfer loses the saveset’s record attributes; the installer puts them back itself:

     $ SET FILE/ATTRIBUTE=(RFM:FIX,LRL:32256,MRS:32256,RAT:NONE) -
           REXX_VAX_20261012B.BCK
    
  3. As SYSTEM:

     $ @REXX_INSTALL
    

    (about 9 to 10 seconds on the simulator and 33 seconds on the VAXstation 4000/96 (first kit), for 268 files, most of them the sources.)

  4. Each user, at the prompt or in LOGIN.COM:

     $ @SYS$COMMON:[SYSLIB.REXX]REXX_SETUP
     $ REXX QUEENS                 ! found in the examples directory, type left out
     $ REXX MYSCRIPT.REX "some arguments"
    

@REXX_UNINSTALL CHECK says what an uninstall would remove, and @REXX_UNINSTALL does it: only files the manifest lists and that are still as installed.

Examples (all with expected output and a test)

RUN_TESTS.COM runs 11 programs and three more checks, and compares each with an expected output made by an independent Python implementation (or by DCL itself):

ProgramWhat it shows
QUEENSrecursion with a procedure and stems: 2, 10, 4, 40, 92 solutions for N = 4..8
PRIMESa sieve; exact 40! and 2**200, fib(200), square root and e to 30 digits
WORDFREQPARSE, TRANSLATE, stems as tables
ROMANRoman numerals both ways
DATESDATE('B') and week days against Python’s datetime
DIRTABLEF$SEARCH, F$PARSE, F$FILE_ATTRIBUTES; blocks checked against DIRECTORY/SIZE
LOGLISTa search-list logical name walked with F$TRNLNM
SYMBOLSlocal and global symbols and a logical name, set from REXX and read by DCL
CAPTUREcommand output into a stem and onto the stack, a command fed from a stem, a failing command’s condition value
DCLCALLREXX running a command procedure and using its status; and DCL running REXX and decoding $STATUS
SYSREPORTa system and process report compared with the same report in DCL

and RUN_ORACLE.COM, the comparison of every F$ function with DCL (128 lines).

Results

Measured on a simulated MicroVAX 3900 (SIMH, OpenVMS VAX 7.3), except where the real machine is named:

  • Build, link included: 69.7 s for 43 modules; the whole recipe, from the original sources to a tested kit and its lifecycle test, 2 min 21 s in one batch job. Simulated-VAX times say little about a real VAX; they are ratios.
  • The DCL comparison: 128 of 128 tag=value lines of the REXX functions equal DCL’s, as an ordinary user with the installed kit.
  • The examples: 14 of 14 as a fresh unprivileged user with the installed kit, about 10 s.
  • The kit: 5,031,936 bytes (9,828 blocks); 268 files once installed (8,491 blocks, most of them the sources).
  • The lifecycle test (install, refuse a second install, uninstall CHECK, uninstall that keeps a changed file and a user file, REPLACE, reinstall): 21 of 21 checks.
  • On my VAXstation 4000/96, the first kit (the same REXX.EXE; its test procedures left three temporary files in the home directory, which this kit’s do not) was installed system-wide on 12-OCT-2026: RUN_TESTS passed 14 of 14 (38 s) and RUN_ORACLE 128 of 128 (3 s) as an ordinary user; the install took 33 s. The kit named here, with the corrected test procedures, was tested on the simulator only: 14 of 14, 128 of 128, lifecycle 21 of 21, and the user’s home directory unchanged (746 files before and after).

What I did not check

  • Real hardware, mostly: only the VAXstation 4000/96 (above). Not the 4000/60 or any other of my VAXen.
  • The SAA API and the shared library: the kit is the stand-alone interpreter only.
  • F$CONTEXT, F$CSID, F$ENVIRONMENT, F$NODE, F$VERIFY: not implemented.
  • Regina 3.9.x (the current version): not on my media, and VMS support unverified. Alpha and Itanium: nothing.
  • Speed: not measured against Perl or DCL on a real VAX.

Rebuilding

REBUILD.md in the source kit has every command, in order, from the DECUS tarball (hash given) to a tested kit with the time of each step. The VAX has no tar, so the tree goes over as one ASCII bundle made by a small Python 3 script and unpacked by a small C program; the rebuild is then one batch job.

How this was done

As with Emacs, Perl and Poplog: Claude sessions with written briefs, on a VAXstation and a simulator, with the rule that nothing counts until a test shows it, and the transcripts kept. Every number above comes from one of those transcripts.

Credits: Mike Cowlishaw for REXX; Anders Christensen, Mark Hessling and Florian Grosse-Coosmann for Regina; the DECUS library, which kept the tarballs; and Claude Code (Anthropic’s Claude models) for much of the building, debugging, testing and writing.