FMS$ - Forms Management System
Overview
VX/FMS$ is a native C reimplementation of OpenVMS FMS (Forms Management System) for x86 and ARM Linux. It exports the OpenVMS FDV$ Forms Driver callable interface, so applications that call FDV$LOAD, FDV$DISPW, FDV$GET and the rest of the 66-routine set link and run unchanged. Forms authored in the FMS form language compile to the same binary form format and load from the same form libraries.
The product ships as a static library (libfms.a) plus command-line utilities and a terminal form editor. Compiled forms render to VT, ANSI, and xterm terminals through an escape-sequence driver rather than a VMS terminal driver, and every FDV$ routine returns the same VMS-style condition codes, so forms-based applications keep their existing screen behaviour and error handling on Linux.
<!-- Webflow embed (min). Source: fms-api-page-technical/fms-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … -->
<div class="s7-tech-embed" data-vxfmsd-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">Architecture and dispatch</h2><p>
VX/FMS$ is a linked library, not a daemon. An application links against <code>libfms.a</code>
(or <code>libfmsx.a</code> 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 <code>__VMS</code> 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.
</p><h6>Two-layer call dispatch</h6><p>
Every FDV$ call passes through two layers. This is the single most important structural
fact about the runtime.
</p><div class="table-wrapper"><table><thead><tr><th>Layer</th><th>Symbol shape</th><th>Role</th></tr></thead><tbody><tr><td>Public entry (VMS ABI)</td><td><code>FDV_D_XXX</code>, aliased to <code>FDV$XXX</code> / <code>fdv$xxx</code></td><td>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.</td></tr><tr><td>Implementation</td><td><code>FCC_XXX</code></td><td>The actual work: validates arguments, checks the current terminal control area and cancel state, mutates the workspace, drives the terminal, and returns an <code>FDV__*</code> condition code.</td></tr></tbody></table></div><p>
The dollar-sign name is bound to a dollar-free real symbol through an alias table, because
<code>$</code> is legal in OpenVMS identifiers but not in a portable C symbol. The public
thunk and its implementation look like this:
</p>
<pre><span class="cmt">/* public thunk: normalises the VMS ABI */</span>
<span class="kw">U32</span> FDV_D_ADLVA(<span class="kw">S32</span> *video)
{
begin_FDV();
<span class="cmt">#ifdef __VMS</span>
<span class="kw">int</span> prmcnt; va_count(prmcnt);
<span class="kw">if</span>( prmcnt < 1 ) video = <span class="kw">NULL</span>; <span class="cmt">/* default an omitted arg */</span>
<span class="cmt">#endif</span>
call_FDV( FCC_ADLVA( video ) );
end_FDV();
}
__AliasF(FDV_D_ADLVA, FDV$ADLVA, fdv$adlva); <span class="cmt">/* export the VMS-visible name */</span>
<span class="cmt">/* implementation: the real work + status ladder */</span>
<span class="kw">U32</span> FCC_ADLVA(<span class="kw">S32</span> *video)
{
enter_FDV
<span class="kw">if</span>( ! video ) return_FDV( FDV__ARG ) <span class="cmt">/* bad/omitted arg */</span>
<span class="kw">if</span>( ! FMS_curr_tca ) return_FDV( FDV__TCA ) <span class="cmt">/* no terminal control area */</span>
<span class="kw">if</span>( FMS_t_cancel(FMS_curr_tca) ) return_FDV( FDV__CAN )
<span class="cmt">/* ... do the work ... */</span>
return_FDV( FDV__SUC ) <span class="cmt">/* 2719889 = success */</span>
}</pre>
<div class="callout"><strong>Consistent error ladder.</strong> Nearly every routine follows the same guard
order: <code>FDV__ARG</code> (bad or omitted argument), then <code>FDV__TCA</code> (no
terminal control area), then <code>FDV__CAN</code> (call cancelled), then the routine's
own work, then <code>FDV__SUC</code>. The <code>enter_FDV</code> / <code>return_FDV</code>
wrappers keep the last status current so <code>FDV$STAT</code> can report it afterwards.
</div><h6>Core runtime state</h6><p>The runtime is organised around a small set of structures:</p><div class="table-wrapper"><table><thead><tr><th>Structure</th><th>Role</th></tr></thead><tbody><tr><td>Terminal Control Area (TCA)</td><td>Per-terminal context in a linked list, holding display state, the cancel flag, and the current workspace root. <code>FMS_curr_tca</code> is the active one, selected by <code>FDV$STERM</code>.</td></tr><tr><td>Workspace</td><td>Where a loaded form lives; attached with <code>FDV$AWKSP</code> and selected with <code>FDV$SWKSP</code>.</td></tr><tr><td>Form library</td><td>An open library file on a channel, holding compiled forms; opened with <code>FDV$LOPEN</code>.</td></tr><tr><td>Loaded form</td><td>An in-memory copy of a compiled form, brought in from the library by <code>FDV$LOAD</code>.</td></tr></tbody></table></div><h6>Call lifecycle</h6><style>.s7-tech-embed .fmsapi-flow {margin: 1.5rem 0;padding: 16px;border: 1px solid var(--s7t-border);border-radius: 10px;background: rgba(255, 255, 255, 0.01);}.s7-tech-embed .fmsapi-flow-stages {display: grid;grid-template-columns: repeat(4, minmax(0, 1fr));gap: 10px;}.s7-tech-embed .fmsapi-flow-card {min-width: 0;padding: 15px;border-top: 3px solid var(--s7t-accent);border-radius: 6px;background: var(--s7t-surface2);color: var(--s7t-text);}.s7-tech-embed .fmsapi-flow-card strong,.s7-tech-embed .fmsapi-flow-card span:last-child {display: block;}.s7-tech-embed .fmsapi-flow-card strong {margin-bottom: 4px;}.s7-tech-embed .fmsapi-flow-card span:last-child {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed .fmsapi-flow-label {display: block;margin-bottom: 10px;font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-transform: uppercase;}.s7-tech-embed .fmsapi-flow-output {border: 1px solid var(--s7t-note-bd);border-top: 3px solid var(--s7t-accent);background: var(--s7t-note-bg);}@media (max-width: 960px) {.s7-tech-embed .fmsapi-flow-stages {grid-template-columns: repeat(2, minmax(0, 1fr));}}@media (max-width: 600px) {.s7-tech-embed .fmsapi-flow-stages {grid-template-columns: 1fr;}}</style><div class="fmsapi-flow" aria-label="VX/FMS API session lifecycle"><div class="fmsapi-flow-stages"><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">01</span><strong>Attach and select terminal</strong><span><code>FDV$ATERM</code> attaches the terminal and creates its TCA; <code>FDV$STERM</code> selects that TCA as current.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">02</span><strong>Open form library</strong><span><code>FDV$LOPEN(file)</code> opens the library on a channel.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">03</span><strong>Attach and select workspace</strong><span><code>FDV$AWKSP</code> attaches the workspace; <code>FDV$SWKSP</code> selects it as current.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">04</span><strong>Load compiled form</strong><span><code>FDV$LOAD(name)</code> reads <code>form.fbn</code> into the workspace.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">05</span><strong>Display and wait</strong><span><code>FDV$DISPW</code> paints the form to the terminal and waits until it is ready.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">06</span><strong>Interact with fields</strong><span><code>FDV$PUT*</code> seeds values, then <code>FDV$GET*</code> or <code>FDV$PFT</code> reads them, then <code>FDV$RET*</code> harvests the result.</span></div><div class="fmsapi-flow-card"><span class="fmsapi-flow-label">07</span><strong>Close library</strong><span><code>FDV$LCLOS</code> releases the form library channel.</span></div><div class="fmsapi-flow-card fmsapi-flow-output"><span class="fmsapi-flow-label">08</span><strong>Detach terminal</strong><span><code>FDV$DTERM</code> ends the terminal session.</span></div></div></div><h2 id="fdv-routines">FDV$ callable routines</h2><p>
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 <span class="ac-live">live</span>.
</p><div class="callout"><strong>FDV$, not FDL$.</strong> These are the <em>Forms Driver</em> routines of OpenVMS
FMS. They are not the RMS File Definition Language routines (FDL$), which are a separate
product. This page documents FDV$ only.
</div><h6>Terminal and workspace setup</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$ATERM</code></td><td>Attach a terminal and create a terminal control area on a channel.</td></tr><tr><td><code>FDV$STERM</code></td><td>Select a terminal control area, making it current.</td></tr><tr><td><code>FDV$DTERM</code></td><td>Detach a terminal and tear down its terminal control area.</td></tr><tr><td><code>FDV$AWKSP</code></td><td>Attach (register) a workspace of a given size.</td></tr><tr><td><code>FDV$SWKSP</code></td><td>Select a workspace as the current one.</td></tr><tr><td><code>FDV$DWKSP</code></td><td>Detach (deregister) a workspace.</td></tr><tr><td><code>FDV$FIX_SCREEN</code></td><td>Repair and repaint the physical screen.</td></tr></tbody></table></div><h6>Form libraries and loading</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$LOPEN</code></td><td>Open a form library file on a channel.</td></tr><tr><td><code>FDV$LCLOS</code></td><td>Close the current form library.</td></tr><tr><td><code>FDV$LOAD</code></td><td>Load a form by name from the library into the current workspace.</td></tr><tr><td><code>FDV$READ</code></td><td>Read a binary form image into caller-supplied memory.</td></tr><tr><td><code>FDV$DEL</code></td><td>Delete (undisplay) a named form from the workspace.</td></tr><tr><td><code>FDV$FCHAN</code></td><td>Return the form-library channel of the current terminal control area.</td></tr><tr><td><code>FDV$LCHAN</code></td><td>Return the current library channel.</td></tr><tr><td><code>FDV$TCHAN</code></td><td>Return the terminal channel of the current terminal control area.</td></tr></tbody></table></div><h6>Display and refresh</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$DISP</code></td><td>Display a form without waiting.</td></tr><tr><td><code>FDV$DISPW</code></td><td>Display the current form and wait for the terminal to be ready.</td></tr><tr><td><code>FDV$NDISP</code></td><td>Display without clearing (overlay a new display).</td></tr><tr><td><code>FDV$CDISP</code></td><td>Continue or append the display of a form at an offset.</td></tr><tr><td><code>FDV$RFRSH</code></td><td>Refresh (repaint) the screen from the workspace.</td></tr><tr><td><code>FDV$USER_REFRESH</code></td><td>Register a user refresh callback.</td></tr><tr><td><code>FDV$CLEAR</code></td><td>Clear a count of screen lines from a starting line.</td></tr><tr><td><code>FDV$PUTL</code></td><td>Put a text string on a screen line.</td></tr><tr><td><code>FDV$PRINT_SCREEN</code></td><td>Print or dump the current screen to a file.</td></tr></tbody></table></div><h6>Field input and output</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$GET</code></td><td>Read one field (value plus terminator key).</td></tr><tr><td><code>FDV$GETAF</code></td><td>Get all fields starting at a named field.</td></tr><tr><td><code>FDV$GETAL</code></td><td>Get all field values into a single buffer.</td></tr><tr><td><code>FDV$GETDL</code></td><td>Get a data line with a prompt on a screen line.</td></tr><tr><td><code>FDV$GETSC</code></td><td>Get a field from a scrolled area.</td></tr><tr><td><code>FDV$PFT</code></td><td>Put field and transmit; scroll or advance a scrolled area.</td></tr><tr><td><code>FDV$WAIT</code></td><td>Wait for a terminator key without reading a field.</td></tr><tr><td><code>FDV$PUT</code></td><td>Put a value into a field.</td></tr><tr><td><code>FDV$PUTAL</code></td><td>Put all field values from one buffer.</td></tr><tr><td><code>FDV$PUTD</code></td><td>Put the default value into a field.</td></tr><tr><td><code>FDV$PUTDA</code></td><td>Put defaults into all fields.</td></tr><tr><td><code>FDV$PUTSC</code></td><td>Put a value into a scrolled-area field.</td></tr></tbody></table></div><h6>Retrieving form context and status</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$RET</code></td><td>Return (retrieve) a field's current value.</td></tr><tr><td><code>FDV$RETAL</code></td><td>Return all field values.</td></tr><tr><td><code>FDV$RETCX</code></td><td>Return full context: terminal control area, workspace, form, cursor position, terminator, insert/overstrike, and help state.</td></tr><tr><td><code>FDV$RETDI</code></td><td>Return named data by index.</td></tr><tr><td><code>FDV$RETDN</code></td><td>Return named data by name.</td></tr><tr><td><code>FDV$RETFL</code></td><td>Return a form line image.</td></tr><tr><td><code>FDV$RETFN</code></td><td>Return the current field's name.</td></tr><tr><td><code>FDV$RETFO</code></td><td>Return a field name by ordinal number.</td></tr><tr><td><code>FDV$RETLE</code></td><td>Return a field's length.</td></tr><tr><td><code>FDV$STAT</code></td><td>Return the last FMS status and I/O status.</td></tr><tr><td><code>FDV$SSRV</code></td><td>Set service: return the status and I/O status pair.</td></tr></tbody></table></div><h6>Attributes, keyboard, and control</h6><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FDV$ADLVA</code></td><td>Set the down-line-loadable video attribute on the current terminal control area.</td></tr><tr><td><code>FDV$AFCX</code></td><td>Access field context (insert/overstrike, cursor position, field).</td></tr><tr><td><code>FDV$AFVA</code></td><td>Alter field video attributes.</td></tr><tr><td><code>FDV$CLEAR_VA</code></td><td>Clear video-attribute state.</td></tr><tr><td><code>FDV$DPCOM</code></td><td>Set the display or data-path communication mode.</td></tr><tr><td><code>FDV$SPADA</code></td><td>Set the pad or scrolled-area display mode.</td></tr><tr><td><code>FDV$SPON</code></td><td>Turn supervisor mode on.</td></tr><tr><td><code>FDV$SPOFF</code></td><td>Turn supervisor mode off.</td></tr><tr><td><code>FDV$DFKBD</code></td><td>Define the keyboard (key to function mapping).</td></tr><tr><td><code>FDV$BELL</code></td><td>Sound the terminal bell and reset video and LEDs.</td></tr><tr><td><code>FDV$LEDON</code></td><td>Turn a keyboard LED on.</td></tr><tr><td><code>FDV$LEDOF</code></td><td>Turn a keyboard LED off.</td></tr><tr><td><code>FDV$ILTRM</code></td><td>Set the illegal-terminator handling mode.</td></tr><tr><td><code>FDV$SSIGQ</code></td><td>Set the signal-queue mode.</td></tr><tr><td><code>FDV$SIGOP</code></td><td>Signal the operator and drive the message line.</td></tr><tr><td><code>FDV$STIME</code></td><td>Set the GET or WAIT timeout period.</td></tr><tr><td><code>FDV$CANCL</code></td><td>Cancel the pending GET or WAIT on the current terminal control area.</td></tr><tr><td><code>FDV$SCR_LENGTH</code></td><td>Return the screen length in rows.</td></tr><tr><td><code>FDV$SCR_WIDTH</code></td><td>Return the screen width in columns.</td></tr></tbody></table></div><p>
One further name, <code>FDV$RESETTERMINAL</code>, appears in the public header but is not
part of the exported 66-routine set; treat it as internal-only.
</p><h2 id="status-codes">Status and terminator codes</h2><p>
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 <code>FDV__*</code> codes in total,
numbered contiguously from <code>2719889</code> (<code>FDV__SUC</code>, normal completion).
The table lists the load-bearing codes an application is most likely to test or log.
</p><div class="table-wrapper"><table><thead><tr><th>Code</th><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>FDV__SUC</code></td><td>2719889</td><td>Normal completion (success).</td></tr><tr><td><code>FDV__INC</code></td><td>2719897</td><td>Form incomplete after a PFT call.</td></tr><tr><td><code>FDV__MOD</code></td><td>2719905</td><td>Input successful; a field may have changed.</td></tr><tr><td><code>FDV__IMP</code></td><td>2719922</td><td>Workspace too small.</td></tr><tr><td><code>FDV__FSP</code></td><td>2719930</td><td>Illegal file spec in a LOPEN call.</td></tr><tr><td><code>FDV__IOL</code></td><td>2719938</td><td>Error opening the form library.</td></tr><tr><td><code>FDV__FLB</code></td><td>2719946</td><td>Specified file is not a form library.</td></tr><tr><td><code>FDV__ICH</code></td><td>2719954</td><td>Invalid channel number specified.</td></tr><tr><td><code>FDV__FRM</code></td><td>2719970</td><td>Invalid binary form.</td></tr><tr><td><code>FDV__FNM</code></td><td>2719978</td><td>Specified form does not exist.</td></tr><tr><td><code>FDV__FLD</code></td><td>2719994</td><td>Invalid field specification.</td></tr><tr><td><code>FDV__DSP</code></td><td>2720010</td><td>GET-type call is illegal for a display-only field.</td></tr><tr><td><code>FDV__UTR</code></td><td>2720042</td><td>Undefined field terminator.</td></tr><tr><td><code>FDV__ARG</code></td><td>2720066</td><td>Wrong number of arguments for the call.</td></tr><tr><td><code>FDV__INI</code></td><td>2720074</td><td>Workspace not attached.</td></tr><tr><td><code>FDV__IVM</code></td><td>2720090</td><td>Insufficient virtual memory.</td></tr><tr><td><code>FDV__ITT</code></td><td>2720106</td><td>Invalid terminal type.</td></tr><tr><td><code>FDV__TCA</code></td><td>2720114</td><td>Terminal Control Area invalid or undefined.</td></tr><tr><td><code>FDV__WID</code></td><td>2720130</td><td>Form too wide for the terminal or context.</td></tr><tr><td><code>FDV__NFL</code></td><td>2720138</td><td>No form loaded into the specified workspace.</td></tr><tr><td><code>FDV__NDS</code></td><td>2720154</td><td>GET-type call to an undisplayed form.</td></tr><tr><td><code>FDV__CAN</code></td><td>2720194</td><td>Call was cancelled.</td></tr><tr><td><code>FDV__TMO</code></td><td>2720234</td><td>Timeout exceeded on a GET-type call or WAIT.</td></tr><tr><td><code>FDV__VAL</code></td><td>2720250</td><td>Value of a parameter out of range.</td></tr><tr><td><code>FDV__SYS</code></td><td>2720266</td><td>FDV encountered a system error response.</td></tr></tbody></table></div><p>
A further block of field-attribute codes (for example <code>FDV__ALPHA</code>,
<code>FDV__NUMERC</code>, <code>FDV__MUSTFILL</code>, <code>FDV__NOHELP</code>) describes
field validation classes and is what a User Action Routine inspects.
</p><h6>Field terminator codes</h6><p>
The terminator out-parameter of <code>FDV$GET</code>, <code>FDV$GETSC</code>,
<code>FDV$PFT</code> and <code>FDV$WAIT</code> carries the key that ended input. There are
17 terminator values; the common ones are:
</p><div class="table-wrapper"><table><thead><tr><th>Constant</th><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>FDV_K_FT_NTR</code></td><td>0</td><td>Enter (end of GETs).</td></tr><tr><td><code>FDV_K_FT_NXT</code></td><td>1</td><td>Next field.</td></tr><tr><td><code>FDV_K_FT_PRV</code></td><td>2</td><td>Previous field.</td></tr><tr><td><code>FDV_K_FT_ATB</code></td><td>3</td><td>Auto-tab to the next field.</td></tr><tr><td><code>FDV_K_FT_XBK</code></td><td>4</td><td>Exit scrolled area backward.</td></tr><tr><td><code>FDV_K_FT_XFW</code></td><td>5</td><td>Exit scrolled area forward.</td></tr><tr><td><code>FDV_K_FT_SNX</code></td><td>6</td><td>Scroll forward to the next field.</td></tr><tr><td><code>FDV_K_FT_SPR</code></td><td>7</td><td>Scroll backward to the previous field.</td></tr><tr><td><code>FDV_K_FT_SFW</code></td><td>8</td><td>Scroll forward.</td></tr><tr><td><code>FDV_K_FT_SBK</code></td><td>9</td><td>Scroll backward.</td></tr></tbody></table></div><h2 id="form-language">The FMS form language and binary forms</h2><p>
A form is authored as a text <code>.flg</code> file, compiled to a binary <code>.fbn</code>
form, and grouped with other forms into a form library. At run time <code>FDV$LOPEN</code>
opens the library on a channel and <code>FDV$LOAD</code> locates a form by appending
<code>.fbn</code> to its name. The form language and binary format match the OpenVMS
originals, so existing form sources compile unchanged.
</p><h6>Statement types</h6><p>The top-level statements in a <code>.flg</code> form description:</p><div class="table-wrapper"><table><thead><tr><th>Statement</th><th>Purpose</th></tr></thead><tbody><tr><td><code>FORM</code></td><td>Begin a form; must be the first statement.</td></tr><tr><td><code>ATTRIBUTE_DEFAULTS</code></td><td>Set default attributes for the form.</td></tr><tr><td><code>TEXT</code></td><td>Literal screen text.</td></tr><tr><td><code>FIELD</code></td><td>Define an input or output field.</td></tr><tr><td><code>DRAW</code></td><td>Line-drawing block.</td></tr><tr><td><code>VIDEO</code></td><td>Video-attribute block.</td></tr><tr><td><code>SCROLL</code></td><td>Define a scrolled area.</td></tr><tr><td><code>ORDER</code></td><td>Field ordering.</td></tr><tr><td><code>NAMED_DATA</code></td><td>Attach named constant data to the form.</td></tr><tr><td><code>END_OF_FORM</code></td><td>Terminate a form.</td></tr></tbody></table></div><h6>Item keywords</h6><p>
Clauses within a statement select field behaviour, validation, and video attributes. The
full keyword set includes:
</p><div class="kw-grid"><div>ACTION_ROUTINE</div><div>AREA_TO_CLEAR</div><div>BACKGROUND</div><div>CHARACTER_SET</div><div>CLEAR_CHARACTER</div><div>DATA</div><div>DATE_FIELD</div><div>DEFAULT</div><div>HELP</div><div>HELP_FORM</div><div>HIGHLIGHT</div><div>INDEX</div><div>INDEXED</div><div>NAME</div><div>PICTURE</div><div>TIME_FIELD</div><div>WIDTH</div><div>AUTOTAB</div><div>BLANK_FILL</div><div>BLINKING</div><div>BOLD</div><div>DISPLAY_ONLY</div><div>ECHO</div><div>FIXED_DECIMAL</div><div>LEFT_JUSTIFIED</div><div>MUST_FILL</div><div>RESPONSE_REQUIRED</div><div>REVERSE</div><div>RIGHT_JUSTIFIED</div><div>SUPERVISOR_ONLY</div><div>SUPPRESS</div><div>UNDERLINE</div><div>UPPERCASE</div><div>ZERO_FILL</div></div><p>
Date pictures (<code>MDY</code>, <code>DDMMMYY</code>, <code>MMDDYY</code>,
<code>DDMMYY</code>) and time pictures (<code>HHMMSS</code>, <code>HHMMAMPM</code>) are
built in, and the compiler enforces that <code>FORM</code> is the first statement before
writing the binary form.
</p><h6>Example form source</h6>
<pre><span class="kw">FORM</span> NAME=<span class="str">'ACCOUNT_INQUIRY'</span>
AREA_TO_CLEAR=1:23
WIDTH=80
BACKGROUND=CURRENT
HIGHLIGHT=BOLD:UNDERLINE
FUNCTION_KEY_ACTION_ROUTINE=<span class="str">'FILTER_UAR'</span>
;
<span class="kw">TEXT</span> (5,10) <span class="str">'Account Number :'</span>
;
<span class="kw">FIELD</span> ACCT_NO (5,27)
PICTURE=<span class="str">'NNNNNNNNNN'</span>
RESPONSE_REQUIRED
UPPERCASE
;</pre>
<p>
Positions are given as <code>(row,col)</code>, an ampersand continues a string across
lines, and each statement ends with a semicolon.
</p><h6>Linux form editor</h6><p>
VX/FMS$ ships <code>fmsedit</code>, a Linux reimplementation of the VMS form editor that
reads and writes the same <code>.flg</code> format and renders to an ANSI or VT terminal.
It follows the VMS Gold-key idiom (PF1 as Gold) and also builds a headless render tool and
a round-trip regression tool for validating form files in a build pipeline.
</p><h2 id="terminal-rendering">Terminal rendering</h2><p>
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:
</p>
<pre><span class="cmt">/* video attribute sequences */</span>
BST_SEQ_VID_BEG <span class="str">"\033["</span> <span class="cmt">/* CSI */</span>
BST_SEQ_BOLD <span class="str">"1"</span>
BST_SEQ_UNDERLINE <span class="str">"4"</span>
BST_SEQ_REVERSE <span class="str">"7"</span>
BST_SEQ_VID_END <span class="str">"m"</span></pre>
<p>
Terminal type is auto-detected from the <code>$TERM</code> environment variable at startup.
An X-window terminal resolves to <code>XTERM</code>; otherwise a prefix match selects
<code>ANSI</code>, <code>HFT</code>, or <code>VT</code>, and anything unmatched falls back to
<code>VT</code>. 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.
</p><div class="callout"><strong>Same forms, portable output.</strong> Because rendering is driven entirely by
<code>$TERM</code>, a form built for a VT terminal displays correctly under xterm and
other ANSI-compatible terminals without any change to the form source.
</div><h2 id="call-path">End-to-end call path</h2><p>
A migrated program links against <code>libfms.a</code> 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 (<code>status & 1</code>). The example below loads and displays a form,
then reads fields from a scrolled area, checking status after each call.
</p>
<pre><span class="cmt">/* fail fast: report the FMS message and exit on any error */</span>
<span class="cmt">#define CHECK if( !(fmsstat & 1) ) { \</span>
<span class="cmt"> fprintf(stderr, "FMS error %d [%s] at line %d\r\n", \</span>
<span class="cmt"> fmsstat, FMS_disp_deb_mess_1(fmsstat), __LINE__); exit(0); }</span>
<span class="cmt">/* simple form: select workspace, load, display, wait */</span>
fmsstat = fdv$swksp( &acct_wksp ); CHECK
fmsstat = fdv$load ( _DESCR(<span class="str">"ACCOUNT_INQUIRY"</span>) ); CHECK
fmsstat = fdv$dispw( 0 ); CHECK
fmsstat = fdv$wait ( 0 ); CHECK
<span class="cmt">/* scrolled area: read fields until the terminator says stop */</span>
fmsstat = fdv$swksp( &txn_wksp ); CHECK
fmsstat = fdv$load ( _DESCR(<span class="str">"TXN_HISTORY"</span>) ); CHECK
fmsstat = fdv$dispw( 0 ); CHECK
fmsstat = fdv$getsc( _DESCR(<span class="str">"TXN_LINE"</span>), &val, &trm ); CHECK
<span class="kw">while</span>( 1 ) {
<span class="kw">switch</span>( trm ) { <span class="cmt">/* trm is an FDV_K_FT_* code */</span>
<span class="kw">case</span> FDV_K_FT_NXT: <span class="kw">case</span> FDV_K_FT_ATB:
<span class="kw">case</span> FDV_K_FT_SFW: <span class="kw">case</span> FDV_K_FT_SNX:
fmsstat = fdv$pft( &trm, _DESCR(<span class="str">"TXN_LINE"</span>),
_DESCR(line_buf), 0, 0 ); CHECK
<span class="kw">break</span>;
<span class="cmt">/* ... handle other terminators ... */</span>
}
}</pre>
<p>
Note the trailing <code>0, 0</code> passed to <code>fdv$pft</code>: 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.
</p><p>
The complete ordered sequence, including terminal and workspace selection, library and
form loading, interaction, and teardown, is shown in
<a href="#architecture">Architecture and dispatch: Call lifecycle</a>.
</p><p><code>FDV$STAT</code> can report the last condition value at any point in this sequence.
</p><h2 id="compatibility">Compatibility and divergences</h2><p>
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.
</p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th></tr></thead><tbody><tr><td>66 FDV$ Forms Driver routines (each with an implementation body)</td><td><span class="ac-live">live</span></td></tr><tr><td>FDV$ condition codes and message text</td><td><span class="ac-live">live</span></td></tr><tr><td>Field terminator and validation-class codes</td><td><span class="ac-live">live</span></td></tr><tr><td>FMS form language and statement / keyword set</td><td><span class="ac-live">live</span></td></tr><tr><td>Binary form load (<code>FDV$LOAD</code> / <code>FDV$READ</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>Form libraries (<code>FDV$LOPEN</code> / <code>FDV$LCLOS</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>VT, ANSI, xterm, and HFT terminal rendering</td><td><span class="ac-live">live</span></td></tr><tr><td>Scrolled areas (<code>FDV$GETSC</code> / <code>FDV$PFT</code> / <code>FDV$SPADA</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>User Action Routines and the UAR generator utility</td><td><span class="ac-live">live</span></td></tr><tr><td><code>fmsedit</code> Linux form editor (read, write, render <code>.flg</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td><code>FDV$RESETTERMINAL</code> public export</td><td><span class="ac-no">not exposed</span></td></tr></tbody></table></div><h6>Divergences from OpenVMS FMS</h6><div class="table-wrapper"><table><thead><tr><th>Divergence</th><th>Reason</th></tr></thead><tbody><tr><td>Each routine is exported twice: a dollar-free real symbol (<code>FDV_D_XXX</code>) plus a <code>FDV$XXX</code> alias.</td><td>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.</td></tr><tr><td>Optional trailing arguments must be passed explicitly as NULL on Linux.</td><td>The VMS argument-count probe is not available on Linux, so callers pass omitted arguments as NULL or zero.</td></tr><tr><td>Rendering uses ANSI and VT escape strings, driven by <code>$TERM</code>.</td><td>There is no VMS terminal driver or real VT hardware on Linux.</td></tr><tr><td>A form binary is located by concatenating the form name with <code>.fbn</code> in a library directory.</td><td>Maps OpenVMS form-library semantics onto a Linux directory and file layout.</td></tr><tr><td>A standalone build (<code>libfmsx.a</code>) is available alongside the default <code>libfms.a</code>.</td><td>The standalone build decouples FMS from the rest of the VX/RT runtime.</td></tr></tbody></table></div><h2 id="quick-reference">Quick reference</h2><h6>Linking</h6><p>
Link with <code>-lfms</code> (or <code>-lfmsx</code> for the standalone build) from
<code>libfms.a</code>; include the FDV$ routine prototypes and pull in the FMS standard
header.
</p><h6>Minimal call flow</h6><p>
Use the ordered card flow in
<a href="#architecture">Architecture and dispatch: Call lifecycle</a> 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.
</p><h6>Common status codes</h6><div class="kw-grid"><div>FDV__SUC = 2719889</div><div>FDV__ARG (bad args)</div><div>FDV__TCA (no terminal)</div><div>FDV__CAN (cancelled)</div><div>FDV__FNM (form not found)</div><div>FDV__FRM (bad binary form)</div><div>FDV__NFL (no form loaded)</div><div>FDV__TMO (timeout)</div></div><p>Success test: <code>status & 1</code> true means success.</p><h6>File extensions</h6><div class="kw-grid"><div>.flg (form source)</div><div>.fbn (binary form)</div><div>form library on a channel</div></div><h6>Common terminators</h6><div class="kw-grid"><div>FDV_K_FT_NTR = 0 (Enter)</div><div>FDV_K_FT_NXT = 1</div><div>FDV_K_FT_PRV = 2</div><div>FDV_K_FT_ATB = 3</div></div><h6>Environment</h6><p><code>$TERM</code> selects XTERM, ANSI, HFT, or VT rendering at startup.</p></div>