DCL File Access

Creating, reading, and writing files are common programming tasks, particularly with DCL and other scripting languages. While syntax and semantics for file operations vary between various languages, few of these differences are consequential; most are merely questions of syntax. Discerning which differences are significant from the insignificant differences in syntax often has subtleties. In the case of OpenVMS DCL, the precise syntax for the OPEN command can give rise to misleading presumptions. These syntactic presumptions sometimes produce confusion, particularly when viewed from the perspective of programming in other environments. This need not be the case. DCL's file handling is as powerful as most any third generation programming language, once the syntax and its implications are understood.

One significant difference is how a file is identified. Conventional programming languages on OpenVMS either use RMS channels, or data structures which in turn point to RMS channels (e.g. FORTRAN unit numbers, C uses file handles and file pointers). By contrast, DCL I/O statements access files that have been first associated with logical names in the process' process logical name table. This implementation provides the same features as those of other programming languages however some mechanical details can appear to differ. When combined with DCL's symbol substitution and interpretive nature, confusion sometimes result. However, when understood in their correct context, they present implementation opportunities for the well informed. Consider the somewhat trivial example:

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ WRITE OUTPUT_FILE "Written from main procedure"
$ CLOSE OUTPUT_FILE

The example writes a single line to a file, a simple enough task. The task can be slightly complicated if we extend the example to include a subroutine, for example:

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ CALL ASUBROUTINE "Written for main procedure by ASUBROUTINE"
$ CLOSE OUTPUT_FILE
$ ASUBROUTINE: SUBROUTINE
$      STRING = P1
$      WRITE OUTPUT_FILE 'STRING'
$      EXIT
$      ENDSUBROUTINE

This second example does not present complications. However, if we expand the sample slightly, complexities begin to appear: For example,

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ OPEN/WRITE LISTING_FILE X.TMP
$ CALL ASUBROUTINE "Written for main procedure by ASUBROUTINE"
$ CALL BSUBROUTINE "Written for main procedure by BSUBROUTINE"
$ CLOSE OUTPUT_FILE
$ ASUBROUTINE: SUBROUTINE
$      STRING = P1
$      WRITE OUTPUT_FILE 'STRING'
$      EXIT
$      ENDSUBROUTINE
$ BSUBROUTINE: SUBROUTINE
$      STRING = P1
$      WRITE LISTING_FILE 'STRING'
$      EXIT
$      ENDSUBROUTINE

ASUBROUTINE and BSUBROUTINE are virtually identical. They differ solely in which file is written. In a large command procedure, a particular subroutine may need to work with different files. A similar case arises when a single subroutine is packaged and invoked as an external file (e.g., $ @LIBRARY:ASUBROUTINE). How does one deal with the fact that the desired output file may (or may not) be different names depending on what procedure is invoking the library subroutine?

There are multiple ways of dealing with this requirement. One involves using logical name indirection, the other involves symbol substitution. (A set of sample command procedures demonstrating both techniques is available for download via my firm’s www site at http://www.rlgsc.com/demonstrations/openvms-dcl-fileaccess-logicals.html ).

The logical name used in a READ or WRITE statement is inherently translated iteratively. One can make use of this iteration to create a level of indirection: a logical name that points to the logical name to which the file is attached. As an example:

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ OPEN/WRITE LISTING_FILE X.TMP
$ ASSIGN OUTPUT_FILE KUMQUAT_OUTPUT_FILE
$ CALL KUMQUAT "Written for main procedure by KUMQUAT"
$ ASSIGN LISTING_FILE KUMQUAT_OUTPUT_FILE
$ CALL KUMQUAT "Written for main procedure by KUMQUAT"
$ CLOSE OUTPUT_FILE
$ KUMQUAT: SUBROUTINE
$      STRING = P1
$      WRITE KUMQUAT_OUTPUT_FILE 'STRING'
$      EXIT
$      ENDSUBROUTINE

The method shown in the preceding example has several advantages. If the logical name is not defined upon entry to KUMQUAT, a default file can be automatically assigned. If the logical name KUMQUAT_OUTPUT_FILE is defined on entry, then the file pointed to by that logical name will be used. This aids building and debugging complex command procedures often composed of several files.

An alternative approach to using logical names is to employ DCL symbols to pass the required information to sub-procedures, and then use symbol substitution in the READ and WRITE statements. The symbols may be defined in an outer scope, or may be passed as formal parameters to the sub-procedure, as in:

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ KIWI_OUTPUT_FILE = "OUTPUT_FILE"
$ CALL KIWI "Written for main procedure by KIWI"
$ CLOSE OUTPUT_FILE
$ KIWI: SUBROUTINE
$      STRING = P1
$      WRITE 'KIWI_OUTPUT_FILE' 'STRING'
$      EXIT
$      ENDSUBROUTINE

There are other ways of employing symbol substitution, but with greater degrees of flexibility and somewhat better structure. One such technique is to include the name of the file as part of the parameter list to the sub-procedure, as in:

$ OPEN/WRITE OUTPUT_FILE X.TMP
$ CALL KIWI "OUTPUT_FILE" "Written for main procedure by KIWI"
$ CLOSE OUTPUT_FILE
$ KIWI: SUBROUTINE
$      OUTPUT_FILE = P1
$      STRING = P2
$      WRITE 'OUTPUT_FILE' 'STRING'
$      EXIT
$      ENDSUBROUTINE

These examples illustrate several different ways that the logical name of a file opened by an outer DCL procedure can be communicated to an inner or peer DCL procedure. Each of the techniques has its strengths and weaknesses, but the general principle of passing the identity of an already opened file has many advantages.

URLs for referencing this entry

Picture of Robert Gezelter, CDP
RSS Feed Icon RSS Feed Icon
Add to Technorati Favorites
Follow us on Twitter
Bringing Details into Focus, Focused Innovation, Focused Solutions
Robert Gezelter Software Consultant Logo
http://www.rlgsc.com
+1 (718) 463 1079