OpenVMS Programming FAQ

This document will some day hopefully evolve into an OpenVMS Programming FAQ. It is maintained by Martin Vorländer. At the moment, it's merely a collection of the VMS programming experience I and others have gathered. The original version of this document is situated at http://vms.pdv-systeme.de/users/martinv/VMS_Programming_FAQ.html.

If you see a section containing the placeholder {TBS} (To Be Supplied) and feel an urge to fill that gap, please do so. Also, if you see any incorrect information, drop me a mail.


Disclaimer: I take no responsibility for the accuracy of any information in this document.

Though Digital Equipment Corporation (DEC) is no more, this name is used throughout this document. Some products may by now go under a name that starts with "HP" or "Compaq" instead of "DEC" or "Digital".

DEC, Digital, Compaq, HP and/or other HP products referenced herein are either trademarks or registered trademarks of Hewlett-Packard. Other product and company names mentioned herein may be the trademarks of their respective owners.

This document was last compiled from underlying sources on Fri Jun 5 08:29:15 2009.


Table of contents

1. General Information
1.1. I can't afford to buy licenses
1.2. What languages are available? Which are free?
1.3. What programming tools are there?
1.4. Run Time Libraries
1.5. The system API
1.6. The Calling Standard
1.7. Condition Values and System Messages
1.8. Symbolic Names Nomenclature
1.9. What is a descriptor?
1.10. What is an IO status block?
1.11. What is an item list?
1.12. What is an event flag?

2. Language Specifics
2.1. Programming in C
2.1.1. Where are the system header files for C?
2.1.2. The Calling Standard in C
2.1.3. VAX C and DEC C, and other C Programming Considerations
2.1.4. How to get prototypes for system services
2.1.5. How to use the VMS v7 C RTL under VMS v6
2.1.6. Traps and Pitfalls in C
2.2. Programming in Pascal
2.2.1. Where are the system header files for Pascal?
2.2.2. The Calling Standard in Pascal
2.3. Programming in Fortran
2.3.1. Where are the system header files for Fortran?
2.3.2. The Calling Standard in Fortran
2.4. Programming in BLISS
2.4.1. Where are the system header files for BLISS?
2.4.2. The Calling Standard in BLISS
2.4.3. Compiler can't find SYS$LIBRARY:STARLET.L32
2.5. Programming in BASIC
2.5.1. Where are the system header files for BASIC?
2.5.2. The Calling Standard in BASIC
2.6. Programming in MACRO
2.6.1. Where are the system header files for MACRO?
2.6.2. The Calling Standard in MACRO

3. How Do I ...?
3.1. ...create a program source file?
3.2. ...compile a program source file?
3.3. ...link a program?
3.4. ...link against a shareable image?
3.5. ...build a shareable image?
3.6. ...debug an application?
3.7. ...get F$EDIT functionality from a program?
3.8. ...validate a user's password?
3.9. ...debug an application that uses the terminal, or a detached application?
3.10. ...get info from the command line? Do I need to parse it myself?
3.11. ...get the DCLTABLE verb a program was called by?
3.12. ...get info about the DECwindows display?
3.13. ...program a deterministic automaton?
3.14. ...get the value of an unknown symbolic condition code?

4. Porting Software from Unix
4.1. Converting a Unix Makefile to a DESCRIP.MMS
4.2. Libraries to Aid Porting
4.3. Links to Other Porting Resources

5. DCL Tips & Tricks
5.1. Getting the date from a binary quadword value
5.2. Producing a random number
5.3. Computing the julian day number
5.4. Toggling an image's debug bit
5.5. Computing the first and the last day of a month
5.6. Tricks with F$VERIFY()
5.7. Finding the name of the calling procedure
5.8. Generating a password
5.9. Getting the elapsed time
5.10. Sorting DIRECTORY by modification date
5.11. Getting the /LOG value from within a batch job
5.12. Getting the next available UIC
5.13. Validating a date of format dd-mm-yyyy
5.14. Determining whether a process with a given PID exists
5.15. Testing for a leap year
5.16. Opening a mailbox (e.g. for IPC)

6. Resources
6.1. OpenVMS Programming Documentation
6.2. OpenVMS FAQ
6.3. DJE VMS mentor pages
6.4. Arne Vajhoej's pages
6.5. OpenVMS Freeware CD
6.6. How to Write a Native VMS Command
6.7. DCL resources

7. Credits


1. General Information

This section deals with some fundamental topics.

1.1. I can't afford to buy licenses

There exists an OpenVMS hobbyist program by which one can get licenses for OpenVMS and a lot of layered products for non-commercial use, see http://www.openvmshobbyist.org/.

1.2. What languages are available? Which are free?



1.3. What programming tools are there?

See "I can't afford to buy licenses" for the commercial products mentioned. Also see the section "How do I ...create a program source file?" for available editors.

1.4. Run Time Libraries

Run-time libraries (at least for DEC languages) come with the operating system! Exceptions to this rule are

1.5. The system API



1.6. The Calling Standard

Calling functions that are written in one language from another languages is easy, as long as caller and callee adhere to a concept called the "Calling Standard". This is a set of conventions on how to pass parameters.

See the "Access" and "Mechanism" points in the section "The system API" for common parameter types and calling mechanisms.

For further information on how the Calling Standard is implemented in various languages, see "The Calling Standard in BASIC", "The Calling Standard in BLISS", "The Calling Standard in C", "The Calling Standard in Fortran", "The Calling Standard in MACRO", and "The Calling Standard in Pascal".


1.7. Condition Values and System Messages

A condition value is a longword returned by many system services and system library routines. It is coded as follows: (bit 0 is the LSB, bit 31 the MSB). This means that all odd condition values signal success, while even ones mean trouble of varying degree.

Named constants for condition values of the various facilities are in *DEF.xxx system header/include/PEN files (e.g. for C, SSDEF.H for system services, LIBDEF.H for the LIB$ routines, SHRDEF.H for codes shared by various facilities, etc.). See Symbolic Names Nomenclature.

As the severity and/or facility could vary, a returned condition code value should not be compared for equality to some constant, but checked against a message number using the LIB$MATCH_COND routine.

All system messages, complete with cause and action to take, are described in the "Error Codes And Recovery Procedures" manual; the $HELP/MESSAGE error DCL command provides an online interface to these (VMS 6 and later); under VMS 5.5, the information can be accessed by HELP @SYSMSGHELP error.


1.8. Symbolic Names Nomenclature

[after posts to comp.os.vms by JF Mezei (jfmezei.spamnot@teksavvy.com) and others on 21-FEB-2005]

VMS has a very standard nomemclature to name field and constants in compiler/language modules:

fac$x_yyyyyyy
where fac denotes the facility, yyyyyy denotes the name, and the x denotes the type of data (and implicit size of value):

1.9. What is a descriptor?

[taken from section 10.12 (PROG13) of the OpenVMS FAQ]

A descriptor is a data structure that describes a string or an array. Each descriptor contains information that describes the type of the data being referenced, the size of the data, and the address of the data. It also includes a description of the storage used for the data, typically static or dynamic. Descriptors are passed by reference.

The following are examples of creating and using descriptors in C:

    #include <descrip.h>
    #include <lib$routines.h>
    #include <stsdef.h>
    int RetStat;
    char TxtBuf[TXTSIZ];
    struct dsc$descriptor StaticDsc =
      { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
    struct dsc$descriptor DynDsc =
      { 0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL };
    int DynDscLen = 255;
    $DESCRIPTOR( ConstDsc, "This is a string" );

    /* finish setting up a static descriptor */
    StaticDsc.dsc$w_length      = TXTSIZ;
    StaticDsc.dsc$a_pointer     = (void *) TxtBuf;

    /* finish setting up a dynamic descriptor */
    RetStat = lib$sget1_dd( &DynDscLen, &DynDsc );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
      return RetStat;

    /* release the dynamic storage */
    RetStat = lib$sfree1_dd( &DynDsc );
    if (!$VMS_STATUS_SUCCESS( RetStat ))
      return RetStat;

Static descriptors reference storage entirely under application program control, and the contents of the descriptor data structure can be modified as required (by the application). OpenVMS routines do not modify the contents of a static descriptor, nor do they alter the address or length values stored in the static descriptor. (The term "static" refers to the descriptor data structure, and not necessarily to the storage referenced by the descriptor.)

Dynamic descriptors reference storage under the control of the run-time library, and the contents of a dynamic descriptor data structure - once initialized - can only be modified under control of run-time library routines. The dynamic storage referenced by the dynamic descriptor is allocated and maintained by the run-time library routines. Various OpenVMS routines do alter the contents of the descriptor data structure, changing the value for the amount and the address of the storage associated with the dynamic descriptor, as required. Routines can obviously access and alter the contents of the storage referenced by the descriptor.

OpenVMS languages that include support for strings or arrays are expected to use descriptors for the particular structure. Most OpenVMS languages, such as Fortran and BASIC, use descriptors entirely transparently. Some, like DEC C, require the programmer to explicitly create and maintain the descriptor.

For further information on string descriptors, see the OpenVMS Programming Concepts manual, part of the OpenVMS documentation set.

Fortran defaults to passing integers by reference and characters by descriptor. The following sites discuss mixing Fortran and C source code in the same application:


Also see the section about "The Calling Standard".


1.10. What is an IO status block?

System routines involving I/O operations often refer to a structure called an "I/O status block". It is mainly used in asynchronous operations, or to pass additional status information.

The IOSB consists of a 16bit word holding the number of bytes transfered, a 16bit word holding the condition value describing the I/O operation's completion status, and additional information (dependant on the operation and the device).

It should always be used with asynchronous operations, because the condition value returned from the system routine only refers to the queuing of the operation, while the actual result of the operation is passed in the IOSB.


1.11. What is an item list?

Itemlists are structures that are used to pass a varying number of arguments to a system routine. There are 32- and (on Alpha) 64-bit itemlist. The general structure is: An itemlist is an array of entries, and is terminated by an entry containing 0 in the first 64bit.

1.12. What is an event flag?

{TBS}

2. Language Specifics

This section discusses language specific issues.

2.1. Programming in C

2.1.1. Where are the system header files for C?

For VAX C, they are in SYS$LIBRARY:.

DEC C uses, for performance reasons, the library SYS$LIBRARY:DECC$RTLDEF.TLB. At installation time, there's an option to install reference copies in SYS$SYSROOT:[DECC$LIB.REFERENCE...] (though those are not used in compilation!).

The header file for system services is called STARLET.H, library routines are declared in various *$ROUTINES.H headers. Other definitions reside in *DEF.H files.


2.1.2. The Calling Standard in C



2.1.3. VAX C and DEC C, and other C Programming Considerations

[taken from section 10.23 (SOFT5) of the OpenVMS FAQ]

VAX C V3.2 was released for OpenVMS VAX systems in 1991. DEC C V4.0 replaced VAX C V3.2 in 1993 as the HP C compiler for OpenVMS VAX systems. HP C is the ANSI C compiler for OpenVMS Alpha systems. VAX C predates the ANSI C standards, and has various areas that are not compliant with ANSI C requirements. HP C is an ANSI C compiler, and can also compile most VAX C code when /STANDARD=VAXC is specified. Versions of this compiler between V3.2 and V6.5 (exclusive) were known as DEC C, DIGITAL C, and Compaq C.

Both compilers can be installed at the same time on the same OpenVMS VAX system, allowing a migration from VAX C to DEC C, and allowing the same DEC C code to be used on OpenVMS VAX and OpenVMS Alpha.

The system manager can choose the system default C compiler when HP C is installed on a system with VAX C, and a C programmer can explicitly select the required compiler for a any particular compilation.

A current "C" license PAK allows access to both VAX C and HP C on the same OpenVMS VAX system.

Various HP C versions can be installed on OpenVMS VAX V5.5-2 and later. OpenVMS VAX releases such as V5.5-2 and V6.0 will require the installation of a HP C RTL kit, a kit that is included with the HP C compiler. OpenVMS VAX versions V6.1 and later do not require a seperate RTL kit, but HP C RTL ECO kits are available to resolve problems found with the C RTL on various OpenVMS releases.

With HP C, for automatic resolution of the standard C library routines by the LINKER utility, use the /PREFIX qualifier, such as /PREFIX=ALL_ENTRIES. If a particular application program replaces an existing C library routine, use /PREFIX=(ALL_ENTRIES,EXCEPT=(...)). (VAX C required explicit specification of an RTL shareable image or C object library during the link.)

When the /PREFIX is requested, the compiler generates a decc$ prefix on the specified symbols. This prefix allows the LINKER to resolve the external symbols against the symbols present in the DECC$SHR library. The DECC$SHR library is included in the IMAGELIB.OLB shareable image library, and IMAGELIB is searched by default when any program (written in any language) is LINKed. Because the standard C library routine names are very likely to match application routines written in other languages, a prefix decc$ is added to the C symbol names to assure their uniqueness; to prevent symbol naming conflicts. C programs, however, can sometimes have private libraries for various purposes, and the external routines share the same names as the library routines. (This is not recommended, but there are applications around that use this technique.) Thus the need to explicity specify whether or not the decc$ prefix should be prepended to the external symbol names by the compiler.

The qualifiers, and most (all?) with associated pragmas, that may be of interest when migrating VAX C code to HP C include:

/PREFIX=ALL_ENTRIES
As mentioned above. Failure to specificy this qualifier can cause the compiler to not add the prefixes for the names of the C library routines into the references placed in the object module, which can in turn cause problems resolving the external symbols in the library when the object code is linked.
/ASSUME=WRITABLE_STRING_LITERALS
Some VAX C programs erroneously write to the string literals. By default, HP C does not allow the constants to change.
/SHARE_GLOBALS
Enables sharing ("shr") of globals and of extern variables. HP C sets externs as non-shareable ("noshr"), VAX C as "shr".
/EXTERN_MODE=COMMON_BLOCK
VAX C assumes common block model for external linkages.
/[NO]MEMBER_ALIGNMENT
Refers to the padding placed between member elements within a struct. Disabling member alignment packs the data more tightly into memory, but this packaging has performance implications, both on OpenVMS VAX and particularly on OpenVMS Alpha systems.

Permit structure members to be naturally aligned whenever possible, and avoid using /NOMEMBER_ALIGNMENT. If you need to disable member alignment, use the equivilent #pragma to designate the specific structures. The alignment of structure members normally only comes into play with specific unaligned data structures - such as the sys$creprc quota itemlist - and with data structures that are using data that was organized by a system using byte or other non-member alignment.

Versions of HP C such as V6.0 include the capability to extract the contents of the standard header libraries into directories such as SYS$SYSROOT:[DECC$LIB...], and provide various logical names that can be defined to control library searches. With HP C versions such as V6.0, the default operations of the compiler match the expectations of most OpenVMS programmers, without requiring any definitions of site-specific library-related logical names. (And logical names left from older DEC C versions can sometimes cause the compiler troubles locating header files.)

HP C V5.6 and later include a backport library, a mechanism by which HP C running on older OpenVMS releases can gain access to newer RTL routines added to the RTL in later OpenVMS releases - the language RTLs ship with OpenVMS itself, and not with the compilers.

Example C code is available in SYS$EXAMPLES:, in DECW$EXAMPLES: (when the DECwindows examples are installed), in TCPIP$EXAMPLES: (or on older releases, UCX$EXAMPLES:) when HP TCP/IP Services is installed), on the OpenVMS Freeware CD-ROMs, and at web sites such as



2.1.4. How to get prototypes for system services

With DEC C v5.6 and later, defining the macro __NEW_STARLET to 1 before including any system header files results in prototyped functions. There are, however, problems with certain types in the supplied prototypes (most notably quadwords).

2.1.5. How to use the VMS v7 C RTL under VMS v6

The VMS v7 C RTL offers a lot more Unix functionality than previous versions. This can make porting Unix applications easier, if not possible at all. Under VMS v6, when using DEC C v5.6 or later, one can link against a static version of the VMS v7 C RTL by defining
  $ define decc$crtlmap sys$library:decc$crtl.exe
  $ define lnk$library  sys$library:decc$crtl.olb
It becomes necessary to use this functionality when the declaration of functions in the header files is conditionalized by
  #if __CRTL_VER >= 70000000
    ...
  #endif
However, using this of course bars the application from taking advantage of bugfixes in newer versions of the RTL.

Beware that the Unix time functions that use timezone information (e.g. tzset()) and I18N features are not supported pre-VMS v7.
The documentation for this feature is in SYS$LIBRARY:DECC$CRTL.README.


2.1.6. Traps and Pitfalls in C



2.2. Programming in Pascal

2.2.1. Where are the system header files for Pascal?

{TBS}

2.2.2. The Calling Standard in Pascal

{TBS}

2.3. Programming in Fortran

2.3.1. Where are the system header files for Fortran?

{TBS}

2.3.2. The Calling Standard in Fortran

{TBS}

2.4. Programming in BLISS

2.4.1. Where are the system header files for BLISS?

{TBS}

2.4.2. The Calling Standard in BLISS

{TBS}

2.4.3. Compiler can't find SYS$LIBRARY:STARLET.L32

Problem: After installation of BLISS on an Alpha system, compiling BLISS programs fails with
LIBRARY 'SYS$LIBRARY:STARLET';
........^
%BLS32-E-M_OPENIN, error opening SYS$LIBRARY:STARLET as input
-RMS-E-FNF, file not found

Solution: Build STARLET.L32 and LIB.L32:

  $ SET DEFAULT SYS$COMMON:[SYSLIB]
  $ BLISS /LIBRARY STARLET.REQ
  $ BLISS /LIBRARY=LIB.L32 STARLET.REQ+LIB.REQ

(This is done during BLISS installation on a VAX system.)


2.5. Programming in BASIC

2.5.1. Where are the system header files for BASIC?

{TBS}

2.5.2. The Calling Standard in BASIC

{TBS}

2.6. Programming in MACRO

2.6.1. Where are the system header files for MACRO?

{TBS}

2.6.2. The Calling Standard in MACRO

{TBS}

3. How Do I ...?

3.1. ...create a program source file?

Editors available:

3.2. ...compile a program source file?

{TBS}

3.3. ...link a program?

{TBS}

3.4. ...link against a shareable image?

{TBS}

3.5. ...build a shareable image?

See The Shareable Image Cookbook.

3.6. ...debug an application?

{TBS}
See also "How do I ...debug an application that uses the terminal, or a detached application?".

3.7. ...get F$EDIT functionality from a program?

[posted to comp.os.vms by Lennart Börjeson (lennartb@front.se) on 14-MAR-1996]

I've always used BAS$EDIT for that purpose. On Alpha/VMS, BASRTL is replaced by DBASIC$RTL. The following C program shows how to call EDIT on both platforms (tested with DECC 5.0 on Vax/VMS 6.1, Alpha/VMS 6.1).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <descrip.h>
#include <stsdef.h>

#pragma nostandard

#define MAX_STR_LENGTH 250

typedef struct dsc$descriptor_vs vs_descr;

#define VS_STRING(name,len) struct { \
  short length;                      \
  char  body[len];                   \
  vs_descr descr;                    \
} name = {0,"",{len,DSC$K_DTYPE_VT,DSC$K_CLASS_VS,(char*)&name.length}}

#define STRING(name) VS_STRING(name,MAX_STR_LENGTH)

#ifdef __alpha
#define EDIT DBASIC$EDIT
#else
#define EDIT BAS$EDIT
#endif

extern int EDIT (vs_descr *tgt, vs_descr *src, int flags);

#define EDIT_DISCARD_PARITY  1 /* Discards bit 7 */
#define EDIT_COLLAPSE        2 /* Discards spaces and tabs */
#define EDIT_DISCARD_FORMS   4 /* Discards cr, lf, ff, del, esc and nul */
#define EDIT_TRIM            8 /* Discards leading and trailing spaces and tabs */
#define EDIT_COMPRESS       16 /* Compresses multiple spaces and tabs to single spaces */
#define EDIT_UPCASE         32 /* Converts lowercase to uppercase */
#define EDIT_UNBRACKET      64 /* Converts [ and ] to ( and ) */
#define EDIT_TRAIL         128 /* Discards trailing spaces and tabs */
#define EDIT_QUOTE         256 /* Preserves characters within quotes */

main() {
  int sts;
  STRING(st1);
  STRING(st2);

  (void) printf("Str1: ");
  (void) gets(st1.body);
  st1.length = strlen(st1.body);

  sts = EDIT(&st2.descr, &st1.descr,
             EDIT_COMPRESS | EDIT_TRIM | EDIT_UPCASE | EDIT_QUOTE);

  if (!$VMS_STATUS_SUCCESS(sts)) {
    lib$signal(sts);
    exit(EXIT_FAILURE);
  }

  st2.length = strlen(st2.body);
  (void) printf("st2= '%s'\n", st2.body);
}
#pragma standard


3.8. ...validate a user's password?

[adapted from a post to comp.os.vms by Carl J Lydick on 9-MAY-1996]

/*
** int validate_account( const char *username, const char *password )
**
** if username == NULL or empty, gets the current username
**
** Condition values returned:
**   SS$_NORMAL   - account validated
**   SHR$_VALERR  - account validation failed
**      (BTW: anyone know a method of finding a decent condition code
**            given a condition it should match?)
**   any condition codes indicating an error returned by
**     STR$UPCASE, LIB$GETJPI, $GETUAI, $HASH_PASSWORD
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define __NEW_STARLET 1
#include ssdef
#include shrdef
#include stsdef
#include jpidef
#include uaidef
#include descrip
#include starlet
#include lib$routines
#include str$routines

#define USERNAME_MAX  12
#define PASSWORD_MAX  32  /* == CIA$K_PASSWORD_MAX_LENGTH on recent VMS versions */

typedef struct {
	unsigned short  buflen;
	unsigned short  itemcode;
	void           *bufadr;
	unsigned short *lenadr;
} item_list_3;

typedef struct { int lo, hi; } quadword;

#ifdef __DECC
#	pragma message disable (ADDRCONSTEXT,NOTCONSTQUAL)
#endif

#define CHKCOND(st) {                      \
  int chkcond_stat = (st);                 \
  if (!$VMS_STATUS_SUCCESS(chkcond_stat))  \
    return chkcond_stat;                   \
}

static int mystr2dsc( struct dsc$descriptor_s *dsc, const char *src ) {
	size_t l = strlen( src );

	if (l < dsc->dsc$w_length) dsc->dsc$w_length = l;
	strncpy( dsc->dsc$a_pointer, src, l );

	return STR$UPCASE(dsc, dsc);
}

int validate_account( const char *username, const char *password ) {
	char usr[USERNAME_MAX];
	char pwd[PASSWORD_MAX];
	$DESCRIPTOR(usr_dsc, usr);
	$DESCRIPTOR(pwd_dsc, pwd);
	unsigned char  alg;
	unsigned short salt;
	quadword hash, uai_hash;
	item_list_3 uai_itmlst[] = {
		{ sizeof(alg)     , UAI$_ENCRYPT, &alg     , NULL },
		{ sizeof(salt)    , UAI$_SALT   , &salt    , NULL },
		{ sizeof(uai_hash), UAI$_PWD    , &uai_hash, NULL },
		{ 0, 0, NULL, NULL }
	};

	if (username == NULL || *username == '\0') {
		int item_code = JPI$_USERNAME;
		char *p;
		unsigned short l;

		CHKCOND( LIB$GETJPI(&item_code, NULL,NULL,NULL, &usr_dsc, NULL) );

		/* The returned username is blank-padded; find length */
		for (p = usr, l = 0;
		     (isalnum(*p) || *p == '$' || *p == '_') && l < USERNAME_MAX;
		    ++p, ++l)
			;
		usr_dsc.dsc$w_length = l;
	} else {
		CHKCOND( mystr2dsc(&usr_dsc, username) );
	}
	CHKCOND( mystr2dsc(&pwd_dsc, password) );

	CHKCOND( SYS$GETUAI(0L,NULL, &usr_dsc, uai_itmlst, 0L,0L,0L) );
	CHKCOND( SYS$HASH_PASSWORD(&pwd_dsc, alg, salt, &usr_dsc, &hash) );

	return (memcmp(&hash, &uai_hash, sizeof(quadword)) == 0)
	        ? SS$_NORMAL : SHR$_VALERR;
}

#ifdef TEST
#include <stdio.h>
int main( int argc, char *argv[] ) {
	char username[USERNAME_MAX+1],
	     password[PASSWORD_MAX+1];
	int result;

	printf("Username: "); gets(username);
	printf("Password: "); gets(password);

	result = validate_account(username, password);
	if ($VMS_STATUS_SUCCESS(result)) {
		printf("Account validated\n");
	} else {
		char msg[1024];
		$DESCRIPTOR(msg_dsc, msg);
		unsigned short msg_len = 0;

		SYS$GETMSG( result, &msg_len, &msg_dsc, 0x0F, NULL );
		msg[msg_len] = 0;
		printf( "Account validation failed:\n%s\n", msg );
	}
}
#endif /* TEST */


3.9. ...debug an application that uses the terminal, or a detached application?



3.10. ...get info from the command line? Do I need to parse it myself?

See http://home.earthlink.net/~djesys/vms/freevms/mentor/dcl_cmd.html.

3.11. ...get the DCLTABLE verb a program was called by?

[posted to comp.os.vms by Joshua Cope (cope@star.enet.dec.com) on 5-NOV-1998]

Feeding the special label $VERB to the CLI$GET_VALUE routine gives you the verb.


3.12. ...get info about the DECwindows display?

[posted to comp.os.vms by Stephen Hoffman (hoffman@xdelta.enet.dec.com) on 23-JUN-1995]

There are undocumented and unsupported itemcodes that one can `aim' at the DECW$DEVICE (WSAn:) device. The core C code looks like this:

    #define IO$M_WS_DISPLAY         64
    #define DECW$C_WS_DSP_NODE       1
    #define DECW$C_WS_DSP_TRANSPORT  2
    #define DECW$C_WS_DSP_SERVER     3
    #define DECW$C_WS_DSP_SCREEN     4

    RetStat = sys$qiow( Ef, Chan, IO$_SENSEMODE|IO$M_WS_DISPLAY, IosbPtr,
      0, 0, NodeBufferPtr, NodeBufferSize, DECW$C_WS_DSP_NODE, 0, 0, 0 );
The Digital Customer Support Centers (CSCs) should have one or more full example programs available online.

Standard disclaimers apply: this interface is undocumented, unsupported and subject to change without notice, etc.


3.13. ...program a deterministic automaton?

The system library has a routine to easily implement a table-driven finite state parser. It's LIB$TPARSE for VAX, and LIB$TABLE_PARSE for Alpha. Implementing the state table is easily done only from MACRO or BLISS.

3.14. ...get the value of an unknown symbolic condition code?

[from a post to comp.os.vms by mckinneyj@cpva.saic.com on 05-JUN-2002]

Some condition codes don't show up in the *DEF.H files. If the shareable image that houses them is known, you can get to them by SDA, e.g. to find the numeric value of LBR$_ILLFMT in SYS$LIBRARY:LBRSHR.EXE:

$ ANALYZE /SYSTEM
SDA> READ /IMAGE SYS$LIBRARY:LBRSHR.EXE
%SDA-I-READSYM, 78 symbols read from SYS$COMMON:[SYSLIB]LBRSHR.EXE;3
SDA> SET OUTPUT LBRMSGDEF.LIS
SDA> SHOW SYMBOL /ALL LBR$_
SDA> EXIT
$ TYPE LBRMSGDEF.LIS
...
LBR$_ILLIDXNUM                  = 00000000.00269022 : 4D414E47.4D495F54
LBR$_ILLFMT                     = 00000000.0026902A : 00000000.0000435F
LBR$_ILLFUNC                    = 00000000.00269032 : 00000000.00000000
...
$ EXIT %X0026902A
%LBR-E-ILLFMT, illegal library format


4. Porting Software from Unix

4.1. Converting a Unix Makefile to a DESCRIP.MMS

Of course, these can only be rules-of-thumb. A Unix Makefile can (mostly due to excessive use of Unixisms, e.g. shell scripting) be partly or completely un-convertable.



4.2. Libraries to Aid Porting

Out of the porting of the Apache webserver software came the OpenVMS Porting Library.
For GUIs, Compaq has the GTK+ for OpenVMS Alpha package.
The OpenVMS Freeware CD has a number of libraries.
In the course of porting Samba to VMS, the FRONTPORT Porting Library. came to life.

4.3. Links to Other Porting Resources

David Mathog's website has "Unix to OpenVMS Porting Notes" and "X11/Motif portability concerns" .
There is a project on SourceForge called "GNU's not VMS" with the goal to build a Unix environment on VMS.

5. DCL Tips & Tricks

In this section, we'll discuss a few tricks the DIGITAL Command Language (DCL) has to offer.

5.1. Getting the date from a binary quadword value



5.2. Producing a random number

[posted to comp.os.vms by brivan@spire.com (Brian VandeMerwe) on 17-AUG-1995]

$   now_time = F$time()
$   hrs = F$cvtime(now_time,, "hour")
$   min = F$cvtime(now_time,, "minute")
$   sec = F$cvtime(now_time,, "second")
$   hun = F$cvtime(now_time,, "hundredth")
$
$   !
$   ! Get a good seed to start the madness.
$   !
$   seed = 360000 * hrs + 6000 * min + 100 * sec + hun
$   seed = seed .and. %XFFFF
$
$   highval = 100	    ! Get a number from 0 to 100.
$   gosub _RANDOM	    ! Get the random
$   write sys$output random
$
$   exit
$
$_RANDOM:
$   seed = (seed * 69069 + 1) .and. %XFFFFFFFF
$   rand = (seed / %X100) .and. %XFFFF
$   t = rand / highval
$   k = %XFFFF / highval
$   if rand .gt. (k * highval) then goto _RANDOM
$   random = rand - t * highval + 1
$
$ Return


5.3. Computing the julian day number

[posted to comp.os.vms by Pierre.Bru@spotimage.fr on 20-AUG-1998]

here is a DCL version of the R.G. Tantzen algorithm [algorithm 199 of the Association for Computiong Machinery (ACM)] that convert gregorian date to julian day number and back. all is integer arithmetic and 32-bit integers. this algorithm is year-2000-proof, leap-year-proof, century-leap-year-proof and you can compute julian day with this algorithm back to the begining of the gregorian calendar, october 15, 1582.

To compute the julian day number from d/m/y:

$ if m .gt. 2
$ then
$    m = m - 3
$ else
$    m = m + 9
$    y = y - 1
$ endif
$
$ c = y / 100
$ y = y - 100*c
$
$ j = c*146097/4 + y*1461/4 + (m*153 + 2)/5 + d + 1721119
To compute d/m/y from the julian day number:
$ j = j - 1721119
$
$ y = (4*j - 1)/146097
$ j = (4*j - 1)-146097*y
$ d = j/4
$
$ j = (4*d + 3)/1461
$ d = (4*d + 3)-1461*j
$ d = (d+4)/4
$
$ m = (5*d - 3)/153
$ d = (5*d - 3)-153*m
$ d = (d+5)/5
$
$ y = y*100 + j
$
$ if m .lt. 10
$ then
$    m = m + 3
$ else
$    m = m - 9
$    y = y + 1
$ endif


5.4. Toggling an image's debug bit

Sometimes it comes handy to switch off the debugger being called, even if the image has been linked with the /DEBUG qualifier. Judging from posts to comp.os.vms, this will not work on Itanium-based systems.

[posted to comp.os.vms by Hein RMS van den Heuvel (vandenheuvel@eps.enet.dec.com) on 21-NOV-1995]

$ if p1.eqs."" then $inquire p1 "image file name"
$ open/read/write file 'f$parse(p1,".exe")
$ offset = 80 !Alpha. We'll assume exe matches current system.
$ if f$getsyi("arch_name").eqs."VAX" then offset = 32
$ read file header
$ old_state = "OFF"
$ new_state = "OFF"
$ old_bit = f$cvsi(offset*8,1,header)
$ new_bit = 0
$ if old_bit then old_state = "ON"
$ if p2.eqs."" then p2 = .not.old_bit
$ if p2 then new_bit = 1
$ if p2 then new_state = "ON"
$ header[offset*8,1] = new_bit
$ write/update/symbol file header
$ close file
$ write sys$output "Debug bit was ", old_state, ". Now ", new_state, "."


5.5. Computing the first and the last day of a month



5.6. Tricks with F$VERIFY()

[posted to comp.os.vms by Dave Cantor (DCantor@shore.net) on 4-NOV-1999]

  1. Change $ SET NOVERIFY to
         $ SAVE_VERIFY = 'F$VERIFY(0)'
    
    The use of apostrophes causes F$VERIFY(0) to be executed before this command is interpreted (and be shown, should Verify have been on).
  2. Change the exit statement(s) (or insert at the end of the file) to
         $  EXIT $STATUS + (0 * F$VERIFY(SAVE_VERIFY))
    
If you want to get fancy, define, say in SYS$MANAGER:SYLOGICALS.COM, this:
     $ DEFINE/SYSTEM   SYLOGIN_VERIFY  0
and replace the 'F$VERIFY(0)' with
'F$VERIFY(F$INTEGER(F$TRNLNM("SYLOGIN_VERIFY")))'  [2]
Then you can turn initial verification on and off by defining the logical name. (You can even turn on initial verification for a particular UIC group with something like $ DEFINE/TABLE=LNM$GROUP_000345 SYLOGIN_VERIFY 1.)

[2] Another neat trick circumvents the possibility of the logical name not being defined:

	'F$VERIFY(F$INTEGER("0"+F$TRNLNM("SYLOGIN_VERIFY")))'


5.7. Finding the name of the calling procedure

[posted to comp.os.vms by Brian Schenkenberger (VAXman@tmesis.com) on 27-JAN-2000]

The following will return the previous procedure file spec in a DCL symbol called PREVIOUS_PROCEDURE.

;++
; Copyright  1993 - 1998  by Brian Schenkenberger and TMESIS.
; ALL RIGHTS RESERVED.
;                            Notice of Disclaimer
;                         -------------------------
; 
; This Software is provided "AS IS" and is supplied for informational purpose
; only.  No warranty is expressed or implied and no liability can be accepted
; for any direct, indirect or consequential damages or for any damage whatso-
; ever resulting in the loss of systems, data or profit from the use of this
; software or from any of the information contained herein.  The author makes
; no claims as to the suitablility or fitness of this Software or information
; contain herein for any particular purpose.  
;
;                         -------------------------
; NO TITLE TO AND/OR OWNERSHIP OF THIS SOFTWARE IS HEREBY TRANSFERRED.  ANY
; MODIFICATION WITHOUT THE PRIOR WRITTEN CONSENT OF THE COPYRIGHT HOLDER IS
; PROHIBITED.  ANY USE, IN WHOLE OR PART, OF THIS SOFTWARE FOR A COMMERCIAL
; PRODUCT WITHOUT THE PRIOR WRITTEN CONSENT OF THE COPYRIGHT HOLDER IS ALSO
; PROHIBITED.  THE TECHNIQUES EMPLOYED IN THE SOFTWARE ARE THE INTELLECTUAL
; PROPERTY OF THE COPYRIGHT HOLDER. 
;--

;++
.SBTTL	Determine the target architecture
;--
	.NTYPE	...ON_ALPHA...,R31
	.IIF EQ,<...ON_ALPHA...@-4&^XF>-5, ALPHA=0
	.IIF DF,ALPHA, .DISABLE	FLAGGING

;++
.SBTTL	Primitive program datum definitions
;--
	ZERO = 0		; |_ 
	BYTE = 1@0		; |_|_ 
	WORD = 1@1		; |___|___ 
	LONG = 1@2		; |_______|_______
	QUAD = 1@3		; |_______________|_______________
	OCTA = 1@4		; |_______________|_______________|
	PAGE = 1@9		; VAX page ; Alpha Pagelet
	BLOCK= 1@9		; Standard disk block size

;++
; The following macro is used to check the return status of system calls.
; It can take up to three arguments:
;   STS ...... register or memory containing the status value
;   ERROR .... instruction to execute if an error is detected
;   NORMAL ... change status to a success before taking ERROR
;--
	.MACRO	CHKSTS,STS=,NORMAL=,ERROR=,?LBL
	.IIF DIF,,,	MOVZWL	STS,R0
	.IIF IDN,,,	BLBS	R0,LBL
	.IIF DIF,,,	BBSS	#0,R0,LBL
	.IRP	ERR,
	.SHOW	MEB
	ERR
	.NOSHOW	MEB
	.ENDR
LBL:	.ENDM	CHKSTS

	.LIBRARY	"SYS$LIBRARY:STARLET.MLB"	; look here for:

	$DSCDEF
	$SSDEF

	.PSECT	DATA,WRT,NOEXE,5
PREV_PROC:	.ASCID	/PREVIOUS_PROCEDURE/
		.ALIGN	QUAD
FILENAME_DSC:	.LONG	>!-
                        >!0
		.ADDRESS	FILENAME_STR
FILENAME_STR:	.BYTE	^a" "[255]

	.PSECT	CODE,NOWRT,EXE,5
	.ENTRY	GO,0
	$CMEXEC_S	-
			routin	= GET_IDF_FILENAME
	CHKSTS

	PUSHAL	#LIB$K_CLI_LOCAL_SYM
	PUSHAQ	FILENAME_DSC
	PUSHAQ	PREV_PROC	
	CALLS	#3,G^LIB$SET_SYMBOL
	CHKSTS
	RET

	.ENTRY	GET_IDF_FILENAME,^m
	MOVAB	G^EXE$SIGTORET,(FP)		; return signalled errors
	MOVAB	@#CTL$AG_CLIDATA,R1		; get address of the PPD
	MOVL	PPD$L_PRC(R1),R1		; adr of CLI own storage
	MOVL	PRC_L_IDFLNK(R1),R1		; get adr of IDF listhead
	MOVL	IDF_L_LNK(R1),R0		; get next IDF in queue
	BEQL	10$				; there is none!  at top!
	MOVL	R0,R1				; update IDF block pointer
10$:	MOVL	IDF_L_FILENAME(R1),R1		; get the filename (ASCIC)
	MOVZBL	(R1)+,R0			; get filename's length
	MOVW	R0,FILENAME_DSC			; store it in descriptor
	MOVC3	R0,(R1),FILENAME_STR		; copy name to descriptor
	MOVL	#SS$_NORMAL,R0			; say things went normal
	RET
	.END	GO
To build it: $ MACRO PREV_PROC
Link on VAX: $ LINK PREV_PROC,SYS$SYSTEM:SYS.STB,DCLDEF.STB
Link on Alpha: $ LINK PREV_PROC/SYSEXE,SYS$LOADABLE_IMAGES:DCLDEF.STB


5.8. Generating a password

[idea taken from DSN$STARTUP.COM, © Digital Equipment Corporation]
The following DCL snippet will generate a password:
$ numchar = 20    ! length of password to be generated
$ password == ""  ! holds the resulting password
$
$ pstr = "$_abcdefghijklmnopqrstuvwxyz0123456789zyxwvutsrqponmlkjihgfedcba"
$ time = f$time() - "-" - "-" - " " - ":" - ":" - "."
$ tstr =  f$extract(10,4,time) + -
          f$extract( 0,1,time) + -
          f$extract( 0,2,f$string(f$getjpi("","cputim"))) + -
          f$extract(15,2,time)
$ seed = ""
$ seed[0,32] = f$integer(tstr)
$ seed = seed+seed+seed+seed
$ startbit = 0
$ numbits = 5
$ pass_loop:
$   currentchar = f$cvui(startbit,numbits+1,seed)
$   password == password + f$extract(currentchar,1,pstr)
$   startbit = startbit + numbits
$   numchar = numchar - 1
$   if numchar .gt. 0 then goto pass_loop


5.9. Getting the elapsed time

[posted to comp.os.vms by Rob Brown (brown@gmcl.com) on 27-SEP-2003]

To get the elapsed time from a lexical function:

I don't know where this trick came from, but I learned it from Jack Harvey.

$ start_time = f$cvtime ("",, "time")
$ write sys$output "This is the start ... ''start_time'"
$ wait 00:00:10
$ write sys$output "This is the elapsed time : " -
  + f$cvtime ("-''start_time'",, "time")


5.10. Sorting DIRECTORY by modification date

[posted to comp.os.vms by Peter Weaver (WeaverConsultingServices@sympatico.ca) on 29-APR-2004]

$ ASSIGN LIB$DATE_FORMAT_037,-
   LIB$TIME_FORMAT_001 -
   LIB$DT_FORMAT/USER_MODE
$ directx -  ! Ignore any DIR symbols
   /date=modified-
   /width=(file:80,display:132)-
   /out=out.txt
$ sort out.txt -
    /key=(pos:83,siz:22) tt:
should do it as long as someone with privileges did the @SYS$STARTUP:LIB$DT_STARTUP first.

5.11. Getting the /LOG value from within a batch job

[adopted from a post to comp.os.vms by Barry on 25-FEB-2005]

To get the /LOG value from within a batch job, use

$ junk = f$getqui("cancel_operation")
$ job_log_null = f$getqui("display_job","job_log_null",,"this_job")
$ job_name = f$getqui("display_job","job_name",,"this_job")
$ log_specification = f$getqui("display_job","log_specification",,"this_job")
$ mylog = ""
$ if .not. job_log_null then -
  mylog = f$parse(log_specification,job_name,"sys$login:.log")
Notice, however, that job_name doesn't include the version number, If there are multiple jobs with the same name, you can't get the information which version of the log file is the correct one.

5.12. Getting the next available UIC

[from a post to comp.os.vms by Alan E. Feldman on 12-SEP-2007]

To get the next available UIC, given the group number:

$!+ OPS_NEXT_UIC.COM
$!
$!! PURPOSE: To find the next available UIC.
$!
$   _GROUP_NUMBER = P1
$   N = -1
$_LOOP:
$   N = N + 1
$   _MEMBER_NUMBER = F$FAO("!OW",N)
$   UIC_INT = (%O'_MEMBER_NUMBER' + (%X10000 * %O'_GROUP_NUMBER'))
$   UIC_NAME = F$IDENTIFIER(UIC_INT,"NUMBER_TO_NAME")
$   IF (UIC_NAME .NES. "") THEN $   GOTO _LOOP
$   UIC = F$FAO("!%U",UIC_INT)
$   WRITE SYS$OUTPUT "The next available UIC is ''UIC'."
$   EXIT
This works if every UIC has a rights identifier coupled with it.

5.13. Validating a date of format dd-mm-yyyy

[from a post on ITRC by John Gillings on 30-OCT-2007]

To validate a date of format dd-mm-yyyy (e.g. 30-10-2007), assuming your date string is in symbol p1:

$ PIPE date=F$CVTIME(F$ELEMENT(0,"-",p1)+"-"+-
F$ELEMENT(F$INTEGER(F$ELEMENT(1,"-",p1)),"/",-
"/JAN/FEB/MAR/APR/MAY/JUN/JUL/AUG/SEP/OCT/NOV/DEC/")+"-"+-
F$ELEMENT(2,"-",p1),"ABSOLUTE") >nl: 2>nl:
$ WRITE SYS$OUTPUT $STATUS
$ SHOW SYMBOL date
A valid date will be converted into VMS absolute format as symbol "date" and $STATUS will be successful (odd).
An invalid date will set $STATUS to %DCL-W-IVATIME and symbol date will be undefined (or unchanged).
The >nl: 2>nl: redirects SYS$OUTPUT and SYS$ERROR to NL: in order to suppress the error message. Since it's only -W- by default DCL will continue processing without needing SET NOON.

5.14. Determining whether a process with a given PID exists

[posted to ITRC by John Gillings on 26-FEB-2008]

$ pid = P1 ! PID for which process existance is to be tested
$ PIPE newpid = F$GETJPI(F$FAO("!8XL",F$INTEGER("%X''pid'")),"PID") >NL: 2>NL:
$ ProcessExists = $STATUS .AND. F$INTEGER("%X''newpid'").EQ.F$INTEGER("%X''pid'")


5.15. Testing for a leap year

[posted to comp.os.vms by Hein RMS van den Heuvel on 29-FEB-2008]

Here is an DCL aided leap-year test:

IF F$CVTIME("1-MAR-''year' -1-",,"DAY") ...  ! Leap year?
Remember Odd is True! 28 is not.

5.16. Opening a mailbox (e.g. for IPC)

[provided by Chuck Newman (HP) via email on 21-MAY-2009]

Using pure DCL, a mailbox can be opened (e.g. for inter-process communication). Though later versions of OpenVMS have the command CREATE/MAILBOX, this will work in older versions also (as far back as 1993er versions). This uses the fact that a termination mailbox is set up for a subprocess with a reproducable logical name (that isn't documented, AFAIK). When the subprocess exits the mailbox still has a connection so it doesn't get closed.

$ IF p1 .eqs. "SUB"
$  THEN
$   DEFINE/JOB/NOLOG sub_pid_'p2' 'F$GETJPI(0,"PID")'
$   ATTACH/IDENT='p2'
$   EXIT
$ ELSE
$   my_pid = F$GETJPI(0,"PID")
$   SPAWN/NOLOG/NOLOGICAL/NOSYMBOL @'F$PARSE(F$ENVIRONMENT("PROCEDURE"),,,,"NO_CONCEAL")' "SUB" 'my_pid'
$   mailbox_name = "sub_pid_''my_pid'"
$   OPEN/READ/WRITE/SHARE mailbox DCL$ATTACH_'F$TRNLNM(mailbox_name)'
$   ATTACH/IDENT='F$TRNLNM(mailbox_name)'
$   DEASIGN/JOB DCL$ATTACH_'F$TRNLNM(mailbox_name)'
$   DEASIGN/JOB 'mailbox_name'
$ ENDIF
After executing this snippet, there's an open filehandle named MAILBOX.

6. Resources

6.1. OpenVMS Programming Documentation

Much of what is OpenVMS Programming is laid down in a subset of the OpenVMS documentation, consisting of (as of OpenVMS/Alpha 7.2-1):

DEC C Run-Time Library Utilities Reference Manual
DEC Text Processing Utility Reference Manual
Digital Portable Mathematics Library
Extensible Versatile Editor Reference Manual
Guide to I/O User's Reference
Guide to Creating OpenVMS Modular Procedures
Guide to DECthreads
Guide to OpenVMS File Applications
OpenVMS Alpha Guide to 64-Bit Addressing and VLM Features
OpenVMS Alpha Guide to Upgrading Privileged-Code Applications
OpenVMS Alpha System Analysis Tool Manual
OpenVMS Calling Standard
OpenVMS Connectivity Developer Guide
OpenVMS Command Definition, Librarian, and Message Utilities Manual
OpenVMS Delta/XDelta Debugger Manual
OpenVMS Debugger Manual
OpenVMS Guide to Extended File Specifications
OpenVMS Linker Utility Manual
OpenVMS Programming Interfaces: Calling a System Routine
OpenVMS Programming Concepts
OpenVMS Record Management Services
OpenVMS Record Management Utilities Reference Manual
OpenVMS RTL General Purpose (OTS$) Manual
OpenVMS RTL Library (LIB$) Manual
OpenVMS RTL String Manipulation (STR$) Manual
OpenVMS RTL Screen Management (SMG$) Manual
OpenVMS System Services Reference Manual
OpenVMS Utility Routines Manual
OpenVMS VAX System Dump Analyzer Utility Manual
OpenVMS VAX RTL Mathematics (MTH$) Manual
POLYCENTER Software Installation Utility Developer's Guide
Porting VAX MACRO Code to OpenVMS Alpha
VAX MACRO and Instruction Set Reference Manual

Documentation for the current OpenVMS version (V7.3 at the time of writing) as well as a few versions back is available online at http://www.openvms.compaq.com/doc/.


6.2. OpenVMS FAQ

The OpenVMS FAQ has a section about Programming. The following topics are discussed there (taken from the June 2002 issue):

Can I have an example of calling...?
How do I get the arguments from the command line?
How do I get a formatted error message in a variable?
How do I link against SYS$SYSTEM:SYS.STB on an Alpha system?
How do I do a SET DEFAULT from inside a program?
How do I turn my Fortran COMMON into a shareable image on Alpha?
How do I convert between IEEE and VAX floating data?
How do I get the argument count in a Fortran routine?
How do I get a unique system ID for licensing purposes?
What is an executable, shareable, system or UWSS image?
How do I do a file copy from a program?
What is a descriptor?
How many bytes are in a disk block?
How many bytes are in a memory page?
How do I create a process under another username?
Why do lib$spawn, lib$set_symbol fail in detached processes?
Where can I obtain Bliss, and the libraries and supporting files?
How can I open a file for shared access?
How can I have common sources for messages, constants?
How do I activate the OpenVMS Debugger from an application?
Dealing with Endian-ness?
How to resolve LINK-I-DATMISCH errors?
VAX C and DEC C, and other OpenVMS C Programming Considerations?
Status of Programming Tools on OpenVMS VAX?

The OpenVMS FAQ is archived in (at least) the following locations:



6.3. DJE VMS mentor pages

See DJE VMS mentor pages

6.4. Arne Vajhoej's pages

Arne Vajhøj has a lot of VMS programming examples at ftp://ftp.hhs.dk/pub/vms/.

6.5. OpenVMS Freeware CD

Not exactly a resource for programming examples, the OpenVMS Freeware CD contains lots of packages (most of them in source) that can be helpful in the everyday use of VMS.

The CD is packaged with the OpenVMS distribution; its contents is available online at http://www.digiater.nl/openvms/freeware/.


6.6. How to Write a Native VMS Command

A good example on which system facilities a programmer wanting to implement a native-looking VMS command can and should use: http://members.fortunecity.com/twirlip/vms_command.html . This includes

6.7. DCL resources

Kevin G. Barkes' DCL Dialogue column from DEC Professional/Digital Age

7. Credits

My warmest Thank You to

Martin Vorländer - on the web - mv@pdv-systeme.de - martin@radiogaga.harz.de