Architecture and dispatch
VX/FMS$ is a linked library, not a daemon. An application links against libfms.a
(or libfmsx.a in standalone mode) and calls the FDV$ Forms Driver routines
directly. The whole product is a single portable source tree: the same files build for
OpenVMS and for Linux, guarded by __VMS conditionals, so behaviour on Linux
tracks the VMS original rather than forking from it. Three pieces make up the product: the
FDV$ Forms Driver runtime, the FMS form language and binary form format, and a terminal
driver that paints forms with escape sequences.
Two-layer call dispatch
Every FDV$ call passes through two layers. This is the single most important structural
fact about the runtime.
| Layer | Symbol shape | Role |
|---|
| Public entry (VMS ABI) | FDV_D_XXX, aliased to FDV$XXX / fdv$xxx | The ABI-facing thunk. On VMS it probes the caller's argument count and NULLs out omitted trailing arguments, then calls the implementation and normalises the return value. |
| Implementation | FCC_XXX | The actual work: validates arguments, checks the current terminal control area and cancel state, mutates the workspace, drives the terminal, and returns an FDV__* condition code. |
The dollar-sign name is bound to a dollar-free real symbol through an alias table, because
$ is legal in OpenVMS identifiers but not in a portable C symbol. The public
thunk and its implementation look like this:
/* public thunk: normalises the VMS ABI */
U32 FDV_D_ADLVA(S32 *video)
{
begin_FDV();
#ifdef __VMS
int prmcnt; va_count(prmcnt);
if( prmcnt < 1 ) video = NULL; /* default an omitted arg */
#endif
call_FDV( FCC_ADLVA( video ) );
end_FDV();
}
__AliasF(FDV_D_ADLVA, FDV$ADLVA, fdv$adlva); /* export the VMS-visible name */
/* implementation: the real work + status ladder */
U32 FCC_ADLVA(S32 *video)
{
enter_FDV
if( ! video ) return_FDV( FDV__ARG ) /* bad/omitted arg */
if( ! FMS_curr_tca ) return_FDV( FDV__TCA ) /* no terminal control area */
if( FMS_t_cancel(FMS_curr_tca) ) return_FDV( FDV__CAN )
/* ... do the work ... */
return_FDV( FDV__SUC ) /* 2719889 = success */
}Consistent error ladder. Nearly every routine follows the same guard
order: FDV__ARG (bad or omitted argument), then FDV__TCA (no
terminal control area), then FDV__CAN (call cancelled), then the routine's
own work, then FDV__SUC. The enter_FDV / return_FDV
wrappers keep the last status current so FDV$STAT can report it afterwards.
Core runtime state
The runtime is organised around a small set of structures:
| Structure | Role |
|---|
| Terminal Control Area (TCA) | Per-terminal context in a linked list, holding display state, the cancel flag, and the current workspace root. FMS_curr_tca is the active one, selected by FDV$STERM. |
| Workspace | Where a loaded form lives; attached with FDV$AWKSP and selected with FDV$SWKSP. |
| Form library | An open library file on a channel, holding compiled forms; opened with FDV$LOPEN. |
| Loaded form | An in-memory copy of a compiled form, brought in from the library by FDV$LOAD. |
Call lifecycle
01Attach and select terminalFDV$ATERM attaches the terminal and creates its TCA; FDV$STERM selects that TCA as current.
02Open form libraryFDV$LOPEN(file) opens the library on a channel.
03Attach and select workspaceFDV$AWKSP attaches the workspace; FDV$SWKSP selects it as current.
04Load compiled formFDV$LOAD(name) reads form.fbn into the workspace.
05Display and waitFDV$DISPW paints the form to the terminal and waits until it is ready.
06Interact with fieldsFDV$PUT* seeds values, then FDV$GET* or FDV$PFT reads them, then FDV$RET* harvests the result.
07Close libraryFDV$LCLOS releases the form library channel.
08Detach terminalFDV$DTERM ends the terminal session.
FDV$ callable routines
VX/FMS$ exports the full OpenVMS FDV$ Forms Driver set: 66 callable routines, each backed
by a real implementation body. On the VMS ABI every argument is passed by reference and
trailing arguments are optional, defaulted to NULL by the public thunk. The routines are
grouped below by function; every routine listed is live.
FDV$, not FDL$. These are the
Forms Driver routines of OpenVMS
FMS. They are not the RMS File Definition Language routines (FDL$), which are a separate
product. This page documents FDV$ only.
Terminal and workspace setup
| Routine | Purpose |
|---|
FDV$ATERM | Attach a terminal and create a terminal control area on a channel. |
FDV$STERM | Select a terminal control area, making it current. |
FDV$DTERM | Detach a terminal and tear down its terminal control area. |
FDV$AWKSP | Attach (register) a workspace of a given size. |
FDV$SWKSP | Select a workspace as the current one. |
FDV$DWKSP | Detach (deregister) a workspace. |
FDV$FIX_SCREEN | Repair and repaint the physical screen. |
Display and refresh
| Routine | Purpose |
|---|
FDV$DISP | Display a form without waiting. |
FDV$DISPW | Display the current form and wait for the terminal to be ready. |
FDV$NDISP | Display without clearing (overlay a new display). |
FDV$CDISP | Continue or append the display of a form at an offset. |
FDV$RFRSH | Refresh (repaint) the screen from the workspace. |
FDV$USER_REFRESH | Register a user refresh callback. |
FDV$CLEAR | Clear a count of screen lines from a starting line. |
FDV$PUTL | Put a text string on a screen line. |
FDV$PRINT_SCREEN | Print or dump the current screen to a file. |
Retrieving form context and status
| Routine | Purpose |
|---|
FDV$RET | Return (retrieve) a field's current value. |
FDV$RETAL | Return all field values. |
FDV$RETCX | Return full context: terminal control area, workspace, form, cursor position, terminator, insert/overstrike, and help state. |
FDV$RETDI | Return named data by index. |
FDV$RETDN | Return named data by name. |
FDV$RETFL | Return a form line image. |
FDV$RETFN | Return the current field's name. |
FDV$RETFO | Return a field name by ordinal number. |
FDV$RETLE | Return a field's length. |
FDV$STAT | Return the last FMS status and I/O status. |
FDV$SSRV | Set service: return the status and I/O status pair. |
Attributes, keyboard, and control
| Routine | Purpose |
|---|
FDV$ADLVA | Set the down-line-loadable video attribute on the current terminal control area. |
FDV$AFCX | Access field context (insert/overstrike, cursor position, field). |
FDV$AFVA | Alter field video attributes. |
FDV$CLEAR_VA | Clear video-attribute state. |
FDV$DPCOM | Set the display or data-path communication mode. |
FDV$SPADA | Set the pad or scrolled-area display mode. |
FDV$SPON | Turn supervisor mode on. |
FDV$SPOFF | Turn supervisor mode off. |
FDV$DFKBD | Define the keyboard (key to function mapping). |
FDV$BELL | Sound the terminal bell and reset video and LEDs. |
FDV$LEDON | Turn a keyboard LED on. |
FDV$LEDOF | Turn a keyboard LED off. |
FDV$ILTRM | Set the illegal-terminator handling mode. |
FDV$SSIGQ | Set the signal-queue mode. |
FDV$SIGOP | Signal the operator and drive the message line. |
FDV$STIME | Set the GET or WAIT timeout period. |
FDV$CANCL | Cancel the pending GET or WAIT on the current terminal control area. |
FDV$SCR_LENGTH | Return the screen length in rows. |
FDV$SCR_WIDTH | Return the screen width in columns. |
One further name, FDV$RESETTERMINAL, appears in the public header but is not
part of the exported 66-routine set; treat it as internal-only.
Status and terminator codes
FDV$ routines return VMS-style condition codes: an odd value is success, an even value is
failure, and callers test the low bit. There are 73 FDV__* codes in total,
numbered contiguously from 2719889 (FDV__SUC, normal completion).
The table lists the load-bearing codes an application is most likely to test or log.
| Code | Value | Meaning |
|---|
FDV__SUC | 2719889 | Normal completion (success). |
FDV__INC | 2719897 | Form incomplete after a PFT call. |
FDV__MOD | 2719905 | Input successful; a field may have changed. |
FDV__IMP | 2719922 | Workspace too small. |
FDV__FSP | 2719930 | Illegal file spec in a LOPEN call. |
FDV__IOL | 2719938 | Error opening the form library. |
FDV__FLB | 2719946 | Specified file is not a form library. |
FDV__ICH | 2719954 | Invalid channel number specified. |
FDV__FRM | 2719970 | Invalid binary form. |
FDV__FNM | 2719978 | Specified form does not exist. |
FDV__FLD | 2719994 | Invalid field specification. |
FDV__DSP | 2720010 | GET-type call is illegal for a display-only field. |
FDV__UTR | 2720042 | Undefined field terminator. |
FDV__ARG | 2720066 | Wrong number of arguments for the call. |
FDV__INI | 2720074 | Workspace not attached. |
FDV__IVM | 2720090 | Insufficient virtual memory. |
FDV__ITT | 2720106 | Invalid terminal type. |
FDV__TCA | 2720114 | Terminal Control Area invalid or undefined. |
FDV__WID | 2720130 | Form too wide for the terminal or context. |
FDV__NFL | 2720138 | No form loaded into the specified workspace. |
FDV__NDS | 2720154 | GET-type call to an undisplayed form. |
FDV__CAN | 2720194 | Call was cancelled. |
FDV__TMO | 2720234 | Timeout exceeded on a GET-type call or WAIT. |
FDV__VAL | 2720250 | Value of a parameter out of range. |
FDV__SYS | 2720266 | FDV encountered a system error response. |
A further block of field-attribute codes (for example FDV__ALPHA,
FDV__NUMERC, FDV__MUSTFILL, FDV__NOHELP) describes
field validation classes and is what a User Action Routine inspects.
Field terminator codes
The terminator out-parameter of FDV$GET, FDV$GETSC,
FDV$PFT and FDV$WAIT carries the key that ended input. There are
17 terminator values; the common ones are:
| Constant | Value | Meaning |
|---|
FDV_K_FT_NTR | 0 | Enter (end of GETs). |
FDV_K_FT_NXT | 1 | Next field. |
FDV_K_FT_PRV | 2 | Previous field. |
FDV_K_FT_ATB | 3 | Auto-tab to the next field. |
FDV_K_FT_XBK | 4 | Exit scrolled area backward. |
FDV_K_FT_XFW | 5 | Exit scrolled area forward. |
FDV_K_FT_SNX | 6 | Scroll forward to the next field. |
FDV_K_FT_SPR | 7 | Scroll backward to the previous field. |
FDV_K_FT_SFW | 8 | Scroll forward. |
FDV_K_FT_SBK | 9 | Scroll backward. |
Terminal rendering
Forms are painted by emitting escape sequences directly, not through curses. A dedicated
terminal layer handles cursor addressing, line erasing, video attributes, and output
flushing. Video attributes map to standard CSI sequences:
/* video attribute sequences */
BST_SEQ_VID_BEG "\033[" /* CSI */
BST_SEQ_BOLD "1"
BST_SEQ_UNDERLINE "4"
BST_SEQ_REVERSE "7"
BST_SEQ_VID_END "m"
Terminal type is auto-detected from the $TERM environment variable at startup.
An X-window terminal resolves to XTERM; otherwise a prefix match selects
ANSI, HFT, or VT, and anything unmatched falls back to
VT. This is the main Linux divergence point: the OpenVMS original drove real
VT-series hardware through the VMS terminal driver, while VX/FMS$ renders the same forms to
a Linux pseudo-terminal using ANSI and VT escape sequences.
Same forms, portable output. Because rendering is driven entirely by
$TERM, a form built for a VT terminal displays correctly under xterm and
other ANSI-compatible terminals without any change to the form source.
End-to-end call path
A migrated program links against libfms.a and calls the FDV$ routines exactly
as it did on OpenVMS. The success test is the same: the low bit of the returned status is
set on success (status & 1). The example below loads and displays a form,
then reads fields from a scrolled area, checking status after each call.
/* fail fast: report the FMS message and exit on any error */
#define CHECK if( !(fmsstat & 1) ) { \
fprintf(stderr, "FMS error %d [%s] at line %d\r\n", \
fmsstat, FMS_disp_deb_mess_1(fmsstat), __LINE__); exit(0); }
/* simple form: select workspace, load, display, wait */
fmsstat = fdv$swksp( &acct_wksp ); CHECK
fmsstat = fdv$load ( _DESCR("ACCOUNT_INQUIRY") ); CHECK
fmsstat = fdv$dispw( 0 ); CHECK
fmsstat = fdv$wait ( 0 ); CHECK
/* scrolled area: read fields until the terminator says stop */
fmsstat = fdv$swksp( &txn_wksp ); CHECK
fmsstat = fdv$load ( _DESCR("TXN_HISTORY") ); CHECK
fmsstat = fdv$dispw( 0 ); CHECK
fmsstat = fdv$getsc( _DESCR("TXN_LINE"), &val, &trm ); CHECK
while( 1 ) {
switch( trm ) { /* trm is an FDV_K_FT_* code */
case FDV_K_FT_NXT: case FDV_K_FT_ATB:
case FDV_K_FT_SFW: case FDV_K_FT_SNX:
fmsstat = fdv$pft( &trm, _DESCR("TXN_LINE"),
_DESCR(line_buf), 0, 0 ); CHECK
break;
/* ... handle other terminators ... */
}
}
Note the trailing 0, 0 passed to fdv$pft: on Linux the optional
arguments a VMS caller could omit are passed explicitly as NULL, because the argument-count
probe used on VMS is not available.
The complete ordered sequence, including terminal and workspace selection, library and
form loading, interaction, and teardown, is shown in
Architecture and dispatch: Call lifecycle.
FDV$STAT can report the last condition value at any point in this sequence.
Compatibility and divergences
The table summarises what is implemented. The core forms driver, the form language, the
binary form format, terminal rendering, scrolled areas, and User Action Routines are all
present and exercised.
| Capability | Status |
|---|
| 66 FDV$ Forms Driver routines (each with an implementation body) | live |
| FDV$ condition codes and message text | live |
| Field terminator and validation-class codes | live |
| FMS form language and statement / keyword set | live |
Binary form load (FDV$LOAD / FDV$READ) | live |
Form libraries (FDV$LOPEN / FDV$LCLOS) | live |
| VT, ANSI, xterm, and HFT terminal rendering | live |
Scrolled areas (FDV$GETSC / FDV$PFT / FDV$SPADA) | live |
| User Action Routines and the UAR generator utility | live |
fmsedit Linux form editor (read, write, render .flg) | live |
FDV$RESETTERMINAL public export | not exposed |
Divergences from OpenVMS FMS
| Divergence | Reason |
|---|
Each routine is exported twice: a dollar-free real symbol (FDV_D_XXX) plus a FDV$XXX alias. | A dollar sign is legal in OpenVMS identifiers but not in a portable C symbol; the alias reproduces the VMS-visible name so existing object references resolve. |
| Optional trailing arguments must be passed explicitly as NULL on Linux. | The VMS argument-count probe is not available on Linux, so callers pass omitted arguments as NULL or zero. |
Rendering uses ANSI and VT escape strings, driven by $TERM. | There is no VMS terminal driver or real VT hardware on Linux. |
A form binary is located by concatenating the form name with .fbn in a library directory. | Maps OpenVMS form-library semantics onto a Linux directory and file layout. |
A standalone build (libfmsx.a) is available alongside the default libfms.a. | The standalone build decouples FMS from the rest of the VX/RT runtime. |
Quick reference
Linking
Link with -lfms (or -lfmsx for the standalone build) from
libfms.a; include the FDV$ routine prototypes and pull in the FMS standard
header.
Minimal call flow
Use the ordered card flow in
Architecture and dispatch: Call lifecycle for the complete
session sequence. The minimal path selects the terminal, opens the library, selects the
workspace, loads and displays the form, reads and returns field values, then closes the
library.
Common status codes
FDV__SUC = 2719889
FDV__ARG (bad args)
FDV__TCA (no terminal)
FDV__CAN (cancelled)
FDV__FNM (form not found)
FDV__FRM (bad binary form)
FDV__NFL (no form loaded)
FDV__TMO (timeout)
Success test: status & 1 true means success.
File extensions
.flg (form source)
.fbn (binary form)
form library on a channel
Common terminators
FDV_K_FT_NTR = 0 (Enter)
FDV_K_FT_NXT = 1
FDV_K_FT_PRV = 2
FDV_K_FT_ATB = 3
Environment
$TERM selects XTERM, ANSI, HFT, or VT rendering at startup.