GNU EMACS FOR OPENVMS VAX -- 19.28 AND 21.2, BINARY AND SOURCE KIT ================================================================== Douglas P. Fields, Jr. 23 September 2026 (first published 20 September 2026; this kit is built FROM THE REPOSITORY -- vax/19.28 and vax/21.2, commit ids in section 6 -- not from the original hand-built tree) Two terminal-only (no DECwindows) builds of GNU Emacs for OpenVMS VAX, built and tested on OpenVMS VAX V7.3 with Compaq C V6.4 on a VAXstation 4000/96, and installed from this kit on a VAXstation 4000/60 and on a simulated MicroVAX 3900 (SIMH). GNU Emacs 19.28 Richard Levitte's VMS port ("VMS patches created on Wednesday, December 25 1996"). Small and quick: about 815 files, 24 MB. The long-proven one. GNU Emacs 21.2 Thien-Thi Nguyen's VMS port (emacs212_3). As far as we could find it had only run on Alpha, Itanium and x86-64; this is a VAX build. Text-mode menu bar, MULE, font-lock, 1,894 files, 76 MB. The 21.2 tree used here came by way of Eric Brown's OpenVMS x86-64 work on that port (configure.com for current VMS, vms7-3.h, TRADCPP); our VAX changes sit on top of his. Thanks to him, to Thien-Thi Nguyen and to Richard Levitte. They install side by side, share no file, and can each be running as a "kept editor" at the same time. Emacs is free software under the GNU General Public License (see ETC]COPYING in either tree); the matching source, and every change made to it, is in the source kit. There is NO WARRANTY. 1. REQUIREMENTS --------------- * OpenVMS VAX V7.3. Older versions are UNTESTED. (Not Alpha, not Itanium: these are VAX images.) * TCP/IP Services for OpenVMS. Both images are linked against UCX$IPC_SHR; without it they presumably will not activate (untested). * About 400,000 free blocks (200 MB) on the system disk while installing both versions: 196,000 for the savesets, which you can delete afterwards, and about 190,000 for the two installed trees. * SYSTEM (or SYSPRV) to install. No compiler is needed. * The install location is FIXED. The directory is compiled into the images, so they run from SYS$SYSDEVICE:[GNU...] and nowhere else: 19.28 SYS$SYSDEVICE:[GNU.BIN], [GNU.LIB...], [GNU.INFO], ... 21.2 SYS$SYSDEVICE:[GNU.EMACS21...] To use another location, rebuild from the source kit with a different --prefix (section 6). 2. WHAT IS IN THE BINARY KIT ---------------------------- EMACS1928_VAX.BCK BACKUP saveset, 47,376 blocks (24,256,512 bytes) EMACS212_VAX.BCK BACKUP saveset, 148,491 blocks (76,027,392 bytes) EMACS_KIT_INSTALL.COM the installer GNU_EMACS_LOGIN.COM defines EMACS, EMACS19, EMACS21 at login KEPTEMACS.COM the kept-editor procedure EMACSTEST.COM, .EL regression test EMACSTEST-HELPER.COM makes the two record-format files EMACSTEST.EL cannot make itself -- REQUIRED alongside them GNU_STARTUP.COM 19.28's own startup procedure (boot-time logicals) MAKE_EMACS_KIT.COM how the savesets were made SHA256SUMS.TXT checksums of all of the above Either saveset may be left out; the installer installs what it finds. 3. INSTALLING ------------- Get all the files into ONE directory on the VAX. Create the directory at a terminal first. TRANSFER MODES MATTER: the two .BCK savesets BINARY (FTP "binary") every .COM, .EL and .TXT file TEXT (FTP "ascii") A procedure sent in binary arrives as 512-byte fixed-length records, and DCL answers the first command you type with %RMS-W-RTB, 512 byte record too large for user's buffer The kit is not corrupt: send the text files again in ascii mode. (UnZip for VMS gets both kinds right by itself.) The savesets arrive with the wrong record attributes however they travel, and BACKUP would say "not a BACKUP save set"; the installer fixes that itself, with SET FILE/ATTRIBUTES=(RFM:FIX,LRL:32256,MRS:32256,RAT:NONE) $ SET DEFAULT dev:[the.kit.directory] $ @EMACS_KIT_INSTALL CHECK ! says what it would do; changes nothing $ @EMACS_KIT_INSTALL It announces each step: 1. sets the savesets' record attributes 2. restores them to SYS$SYSDEVICE:[GNU...] with BACKUP/NEW_VERSION (every restored file becomes the new highest version), then PURGEs SYS$SYSDEVICE:[GNU...] down to one version per file -- so installing twice, or over an older copy of this kit, is safe and leaves exactly one copy of everything. (An earlier version of this installer used BACKUP/REPLACE, which only overwrites a file already on disk at the SAME version number the saveset carries; once a target's own versions had drifted -- any earlier reinstall bumps them -- the next kit landed BESIDE the old files instead of replacing them, roughly doubling the tree. Fixed R09, MEASURED: see reviews/R09-request.md.) A file you edited IN PLACE under [GNU...] is still overwritten -- keep site changes in the SITE-LISP directory. 3. makes everything world-readable 4. copies the four shared procedures to SYS$SYSDEVICE:[GNU] 5. copies GNU_STARTUP.COM to SYS$COMMON:[SYS$STARTUP] if none is there; if one is, leaves it alone and says whether it matches the kit's 6. shows the two lines that SYLOGIN.COM needs and ASKS before adding them (the old file remains as the previous version); on a 19.28 install, ASKS SEPARATELY before adding the ETAGS/B2M/EMACSCLIENT line too (see below), inserted ABOVE the first. It never touches SYSTARTUP_VMS.COM; it prints the one line to add yourself: $ @SYS$STARTUP:GNU_STARTUP * "TABLE=/SYSTEM,QUIET" (19.28 only: the ESHELL logical name and the GNU help library.) 7. runs the regression test; the procedure's exit status is the test's. The SYLOGIN lines define EMACS, EMACS19, EMACS21 and the RUNEMACS commands. 19.28's other commands -- ETAGS, B2M, EMACSCLIENT -- come from GNU_STARTUP itself; the installer now offers to add this line for you, ABOVE the lines it added for EMACS/EMACS19/EMACS21 -- answer its second question, or add it yourself: $ @SYS$STARTUP:GNU_STARTUP * NOLOGICALS,QUIET Either SYLOGIN edit goes before a final "$ EXIT" (any case, with or without a status) if the file ends with one, otherwise at the end. THE INSTALLER DOES NOT DETECT an EXIT inside an IF block, or a SYLOGIN.COM that branches around its own end -- it will append after such an EXIT instead of before it. Read the result (the procedure prints the new file's version number) if your SYLOGIN.COM does either. It stops, having changed nothing further, if BACKUP cannot restore. To remove: delete SYS$SYSDEVICE:[GNU.EMACS21...] for 21.2, or all of [GNU...] for both; take the hook lines out of SYLOGIN.COM and SYSTARTUP_VMS.COM; delete SYS$COMMON:[SYS$STARTUP]GNU_STARTUP.COM. 4. USING IT ----------- After the next login (or @SYS$SYSDEVICE:[GNU]GNU_EMACS_LOGIN ): $ EMACS19 [file] GNU Emacs 19.28 $ EMACS21 [file] 21.2 $ EMACS [file] the default version $ RUNEMACS21 -batch -l file.el run directly, no kept process EMACS / EMACS19 / EMACS21 start a KEPT EDITOR: C-z returns you to DCL and leaves Emacs alive in a subprocess; the same command re-attaches to it with your buffers intact. C-x C-c really exits. The default is 19.28 if installed. To change it, define the logical name GNU_EMACS_DEFAULT as 19 or 21 -- for everyone: $ DEFINE/SYSTEM/EXECUTIVE GNU_EMACS_DEFAULT 21 (and at boot) or for yourself, in LOGIN.COM, followed by @SYS$SYSDEVICE:[GNU]GNU_EMACS_LOGIN Keyboards. On a terminal, "M-x" means press ESC, then x. DEC LK201 / LK401 / LK411 keyboards have no Esc key: use F11 (both builds map it), or Ctrl-[ anywhere. If Ctrl-S freezes the screen instead of searching, a terminal, terminal server or emulator is using it for flow control: either turn XON/XOFF off there, or M-x enable-flow-control and then use C-\ for C-s and C-^ for C-q. Terminal types VT100 through VT500-series are recognised as VMS reports them (SET TERMINAL/INQUIRE). For anything else: $ DEFINE EMACS_TERM "vt200" 5. TESTING ---------- $ @SYS$SYSDEVICE:[GNU]EMACSTEST [19|21|ALL] [mutate-check-name] 254 checks per version (255 with the one KNOWN_BUG line), in 20 named GROUPs: the Lisp system, regexps, strings and lists, time and timers, VMS file version numbers, VMS file names and search lists, record formats (Stream, Variable, VFC), backup and auto-save, file attributes, file-name completion (the class the 21.2 completion crash slipped through) and dired, access checks compared with the privileges the process actually holds, synchronous and asynchronous DCL subprocesses, garbage collection under load, byte compilation, editing primitives, and system information. It prints "GROUP name" once per group, "PASS name" / "FAIL name: detail" per check, "KNOWN_BUG name" for a check that asserts a currently-open bug's behaviour on purpose, and a final "EMACSTEST: n passed, m failed, k known bugs" line; it exits with a VMS error status if anything FAILED (a KNOWN_BUG does not count as a failure). RUN IT AS AN ORDINARY USER TOO: several of the bugs below exist only for unprivileged users or only in -batch. P2, if given, is a check NAME exactly as printed: that one check's outcome is deliberately negated (EMACSTEST_MUTATE), so its FAIL line and this procedure's own bad exit status can be demonstrated on demand -- the fail-first proof every check in this suite has been shown to pass, either this way or against a real known-bad image (the completion group against the pre-patch-10 build; several subprocess checks against a pre-patch-8 build). To run against a different image for one version (such as a known-bad build), define RUNEMACS19 or RUNEMACS21 locally in the session before calling this procedure -- it reads whatever symbol is in effect. EMACSTEST-HELPER.COM (it creates the Variable-length and VFC files the record-formats group reads) travels next to EMACSTEST.COM and EMACSTEST.EL -- in the installed SYS$SYSDEVICE:[GNU] for the command above, or wherever else you copy all three files together. EMACSTEST.COM defines the EMACSTEST_HOME logical to that directory before loading EMACSTEST.EL, which finds the helper there; EMACSTEST_HOME falls back to SYS$LOGIN: only if EMACSTEST.EL is loaded some other way (e.g. "RUNEMACSnn -batch -l EMACSTEST.EL" by hand, from a directory holding both files, without going through EMACSTEST.COM). Nothing needs to be copied into SYS$LOGIN: first: the command above works for any account, from a fresh login, with nothing uploaded. Verified 20/21-SEP-2026 with the original 33-check suite: 25/25, then 33/33, for both versions, as SYSTEM and as an unprivileged user, on the build machine and on machines installed from this kit. Grown to 245 checks in 20 groups 22-SEP-2026 (R06): every check shown to fail on demand (mutation) or, for the patch-10 (completion group) and patch-8 (subprocess checks) guards, against the preserved pre-patch image; 244-245/245 for both versions as SYSTEM, USER1 and EMTEST (NOPRIV) on the build machine, one open bug each on 19.28 (a timer, KNOWN_BUG) and 21.2 (a directory writability check, KNOWN_BUG, unprivileged only); full run under 2 minutes per version. Also by hand, earlier: both kept editors at once, M-!, and M-x shell, as the unprivileged user. Grown to 254 checks 23-SEP-2026 (R09), against a kit built and installed FROM THE REPOSITORY -- fresh installs on the build machine (VAX96), a true clean-room simulator install (nothing pre-existing, every account and file created by the kit itself), and a reinstall of the hardware install machine (VAX60): 254 passed + 1 known bug, BOTH versions, on EVERY target, as SYSTEM/PRIV, an unprivileged account, and a NOPRIV-only account. The simulator's is its first real baseline ever (it had only ever carried a pre-kit 19.28 before). Re-verified after the EMACSTEST_HOME fix (this section, and section 7's "FIXES SINCE" list) the same way, plus once more each as a genuinely fresh, un-staged account running the exact command above with nothing uploaded first: 254 passed + 1 known bug, both versions, on every target, every account. See reviews/R09-request.md for every count and transcript. 6. THE SOURCE KIT, AND WHAT WAS CHANGED --------------------------------------- As of R09 (23-SEP-2026) the source kit is built straight from this project's own git repository, not from a hand-assembled changes/ directory: emacs-19_28-19961225.zip Richard Levitte's 19.28 VMS kit, UNMODIFIED (the true upstream) emacs-21.2-vms-x86-64.zip Eric Brown's 21.2 VMS x86-64 kit (emacs212_3), UNMODIFIED emacs-19.28-vax-source-8ef4c9a.zip `git archive` of this repository's vax/19.28 branch, commit 8ef4c9ae6e8f1d7509920252b23bb667e 54f3e98 -- the COMPLETE tree as built (every file, not a diff) emacs-21.2-vax-source-07e550d.zip `git archive` of vax/21.2, commit 07e550d3068fb3b4209b357b2679774950 c9bd0a -- likewise complete SOURCE_KIT_README.TXT, SHA256SUMS.TXT The two vax/* archives ARE the ten changes below (and every build-system repair), already applied, as ordinary source files -- "diff" them against the two unmodified upstream archives yourself, or read the changes as commits in the repository's own `emacs/` submodule history (`git log vax/19.28`, `git log vax/21.2`). Older kits (through 21-SEP-2026) carried a hand-built emacs-NN-vms-vax-changes/ directory of whole pristine/patched file pairs instead; that format is retired. To build 21.2 from the vax/21.2 archive: unzip it in a directory under your own account, then $ SUBMIT/NOPRINT/LOG_FILE=SYS$LOGIN:E21.LOG/PARAMETERS=(CONFIGURE) - dev:[...]BUILD_EMACS21.COM (18 minutes on a 4000/96), and the same with /PARAMETERS=(INSTALL) as SYSTEM. P2 is the prefix if you want another one. Needs Compaq C and MMS. 19.28 has no BUILD_EMACS21.COM equivalent yet; build it the way docs/EMACS_ON_VAX_INSTALLATION.md section 7 describes (configure.com, then the generated makefiles repaired -- legacy-changes/19.28/ fix_generated.py -- then MMS). Emacs 21.2 -- ten changes. 1-7 and 10 are VAX-only and leave Alpha/x86 alone: 1 m/vax.h compile switch /OPTIMIZE=(LEVEL=5,TUNE=HOST) is Alpha's 2 emacs.c does not exist on VAX (and is not used) 3 sysdep.c 64-bit integers: the VAX compiler has none. Privilege mask held as two longwords 4 vmsproc.c "long long" delta time -> LIB$EMUL; and XPROCESS() applied to what is already a struct pointer 5 vmsmap.c the VAX linker puts the BSS markers beside DATA, so the BSS range duplicated DATA and the dump aborted 6 vmsfns.c a symbol name from VSI C on x86-64, typed into the source; on VAX the function is plain sys_access 7 descrip.mms_in_in make-docfile commands too long for DCL when the build directory is deep; that batch of docstrings was silently left out of DOC. Re-batched 3 files per command 8 vmsproc.c ALL PLATFORMS. The last record a subprocess wrote before exiting was dropped (M-! SHOW TIME printed nothing) 9 sysdep.c ALL PLATFORMS. Under -batch, file access was checked as the account that BUILT Emacs, because the UIC is cached in a static that the dump file preserves 10 dired.c Compaq C for VAX inlines a function that calls the alloca builtin, then keeps using SP-relative slots after SP has moved: file-name completion (C-x C-f TAB, RET in dired) jumped into data and killed Emacs. That buffer now comes from the heap. 19.28 is immune: it uses Emacs's own alloca.c + etc/termcap.src VMS terminal names (vt200-80 ...) and VT400/VT500 + lisp/term/vt500.el new A traditional C preprocessor (TRADCPP, in the 21.2 kit) is used in place of Compaq C's, which puts a space between every token and so wrecks the generated MMS files. The 19.28 build predates our finding that, and repairs the generated files with a script instead (fix_generated.py). Emacs 19.28 -- build-system repairs, plus: vmsproc.c the same dropped-record bug as 8 above callproc.c close() was called on an index into Emacs's own mailbox table; in "emacs -batch -l file.el" that closed the file being loaded, and Emacs died at the next read of it etc/termcap.dat VT400/VT500; lisp/term/vt500.el new 7. FIXES SINCE THE 21-SEP-2026 KIT ----------------------------------- By symptom, one line each (full detail: PLAN.md's Status table and TODO register; commit ids in section 6 above are where they landed): * 21.2 M-x shell was slow -- DIRECTORY SYS$SYSTEM: 10.97 -> 1.81 s on VAX96 (per-record CPU cost in the pty read path, drained per read). * A one-shot asynchronous subprocess (both versions) never exited -- DCL kept reading for another command, so no sentinel ever ran. * A one-shot subprocess's last output record could be dropped at exit. * A -batch pty subprocess delivered every output record to its filter truncated to its first 10 characters. * delete-process of a genuinely idle interactive DCL child hung. * 19.28: an interactive shell's exit sentinel ran only at the next keystroke, not when the child actually exited. * 19.28, -batch: sit-for never returned after an interactive child process logged out. * 21.2: a failing subprocess's exit status was shown as a Unix signal instead of the real VMS completion code. * 19.28: dired's RET said "Buffer is read-only" instead of visiting the file (now bound the same as f). * start-process with a program but no argument (a bare interactive subshell) crashed the whole image, both versions. * The kit installer doubled the installed tree on a reinstall -- BACKUP/REPLACE only overwrites a file already at the SAME version the saveset carries; a target whose versions had drifted got the new files ALONGSIDE the old ones. Fixed with BACKUP/NEW_VERSION + PURGE (MEASURED, R09: fail-first reproduced the doubling with a minimal saveset, then shown fixed). * EMACSTEST-HELPER.COM (required by EMACSTEST.EL for the record-formats checks) was missing from the kit entirely -- not packaged, not installed, not staged for the installer's own self-test. Every install's regression test, and every future EMACSTEST run, would have been missing the two checks that need it. Now packaged, installed to SYS$SYSDEVICE:[GNU], and staged to SYS$LOGIN: before the self-test. * That fix above was not enough: EMACSTEST.EL still loaded the helper by the literal name SYS$LOGIN:EMACSTEST-HELPER.COM, so a genuinely fresh account with nothing staged (i.e. every real outside user following section 5's own documented command) still failed the record-formats group -- caught by an orchestrator review that ran that exact command from a fresh login with nothing uploaded. Fixed: EMACSTEST.COM now defines EMACSTEST_HOME to its own directory before loading EMACSTEST.EL, which finds the helper there, falling back to SYS$LOGIN: only when run some other way (section 5). Verified fail-first (the old behavior reproduced) then passing, as an unprivileged and a NOPRIV-only account, on both hardware machines, from fresh logins with nothing uploaded. * tests/emacs/run_all.py gained an --installed Tier 1 mode that runs the exact command section 5 documents against whatever is already installed, with no upload or staging seam -- the check that would have caught the bug above; it is this project's release gate from now on. * The installer now also offers the ETAGS/B2M/EMACSCLIENT SYLOGIN line (19.28 only), previously only printed as an instruction to add by hand (section 3 above). * tools/vaxsync.py's `unpack` refuses a non-empty target unless --purge is given (a repeated UNZIP into one doubled its file count too, the same class of bug as the installer's). 8. KNOWN LIMITATIONS -------------------- * 21.2 images built before 20-SEP-2026 20:30 (none was published) die in file-name completion: change 10 above. To tell, complete a file name: a fixed Emacs prints a list, an unfixed one dies with a traceback naming file_name_completion. EMACSTEST checks it. * 21.2: a completion list shows a name once per VMS file version, so the same name can appear several times; 19.28 shows it once. * No X/DECwindows support was built. * Output captured from a subprocess ends with one extra blank line. * 19.28, M-x shell: the mode line keeps saying "run" after the subshell has logged out. * 19.28 is configured with the VMS 5.5 system description, the newest that kit has; it works on 7.3. * The link reports %LINK-W-MULDEF for DECC$MALLOC, DECC$FREE ... and the directory routines: Emacs's own versions replacing the C library's, by design, in both ports. * GNU_STARTUP.COM carries the build machine's node name in comment lines. They are data for the kit's own installer and are never executed. * Neither version notices a terminal window resize; redraw at the new width needs an explicit M-x set-screen-width or C-l. * 21.2, unprivileged accounts only: file-writable-p on the account's OWN home directory (not a file) says nil, contradicted by a real write there succeeding right afterward. KNOWN_BUG in EMACSTEST. * 19.28: run-at-time never fires a scheduled timer in -batch mode (it shells out to a `timer` helper program this port never builds). KNOWN_BUG in EMACSTEST. 21.2 is unaffected.