VSI COBOL User Manual

Document Number: DO-DCOBUG-01A
Publication Date: April 2024
Software Version:
VSI COBOL Version 3.1-7 for OpenVMS
Operating System and Version:
VSI OpenVMS IA-64 Version 8.4-1H1 or higher
VSI OpenVMS Alpha Version 8.4-2L1 or higher

Preface

1. About VSI

VMS Software, Inc. (VSI) is an independent software company licensed by Hewlett Packard Enterprise to develop and support the OpenVMS operating system.

2. Intended Audience

This manual is intended for experienced applications programmers who have a thorough understanding of the COBOL language. Some familiarity with your operating system is also recommended. This is not a tutorial manual.

If you are a new COBOL user, you may need to read introductory COBOL textbooks or take COBOL courses. Additional prerequisites are described at the beginning of each chapter or appendix, if appropriate.

3. Related Documents

The following documents contain additional information directly related to various topics covered in this manual:

  • Release Notes

    Consult the VSI COBOL release notes for your installed version for late corrections and new features.

    On the OpenVMS Alpha, I64 operating system, the release notes are in:
    • SYS$HELP:COBOLnnn.RELEASE_NOTES (ASCII text)
    • SYS$HELP:COBOLnnn_RELEASE_NOTES.PS

    Where nnn is the version and release number.

    On the UNIX, the release notes are in:
    • /usr/lib/cmplrs/cobol/relnotes
  • VSI COBOL Reference Manual

    Describes the concepts and rules of the VSI COBOL programming language under the supported operating systems.

  • VSI COBOL DBMS Database Programming Manual

    This manual provides information on using VSI COBOL for database programming with Oracle CODASYL DBMS on the OpenVMS Alpha, the OpenVMS I64, or OpenVMS VAX operating systems.

  • The OpenVMS Documentation Set

    This set contains information about using the features of the OpenVMS I64 and OpenVMS Alpha operating systems and their tools.

  • The UNIX Documentation Set

    This set contains introductory and detailed information about using the features of the UNIX operating system and its tools.

4. OpenVMS Documentation

The full VSI OpenVMS documentation set can be found on the VMS Software Documentation webpage at https://docs.vmssoftware.com.

5. VSI Encourages Your Comments

You may send comments or suggestions regarding this manual or any VSI document by sending electronic mail to the following Internet address: . Users who have VSI OpenVMS support contracts through VSI can contact for help with this product.

6. Conventions

The following conventions are used in this manual:

ConventionMeaning
Ctrl/xA sequence such as Ctrl/x indicates that you must hold down the key labeled Ctrl while you press another key or a pointing device button.
PF1 xA sequence such as PF1 x indicates that you must first press and release the key labeled PF1 and then press and release another key (x) or a pointing device button.
EnterIn examples, a key name in bold indicates that you press that key.
...
A horizontal ellipsis in examples indicates one of the following possibilities:- Additional optional arguments in a statement have been omitted.- The preceding item or items can be repeated one or more times.- Additional parameters, values, or other information can be entered.
.
.
.
A vertical ellipsis indicates the omission of items from a code example or command format; the items are omitted because they are not important to the topic being discussed.
( )

In command format descriptions, parentheses indicate that you must enclose choices in parentheses if you specify more than one. In installation or upgrade examples, parentheses indicate the possible answers to a prompt, such as:

Is this correct? (Y/N) [Y]

[ ]

In command format descriptions, brackets indicate optional choices. You can choose one or more items or no items. Do not type the brackets on the command line. However, you must include the brackets in the syntax for directory specifications and for a substring specification in an assignment statement. In installation or upgrade examples, brackets indicate the default answer to a prompt if you press Enter without entering a value, as in:

Is this correct? (Y/N) [Y]

|In command format descriptions, vertical bars separate choices within brackets or braces. Within brackets, the choices are optional; within braces, at least one choice is required. Do not type the vertical bars on the command line.
{ }In command format descriptions, braces indicate required choices; you must choose at least one of the items listed. Do not type the braces on the command line.
bold typeBold type represents the name of an argument, an attribute, or a reason. In command and script examples, bold indicates user input. Bold type also represents the introduction of a new term.
italic typeItalic type indicates important information, complete titles of manuals, or variables. Variables include information that varies in system output (Internal error number), in command lines (/PRODUCER=name), and in command parameters in text (where dd represents the predefined code for the device type).
UPPERCASE TYPEUppercase type indicates a command, the name of a routine, the name of a file, or the abbreviation for a system privilege.
Example

This typeface indicates code examples, command examples, and interactive screen displays. In text, this type also identifies website addresses, UNIX command and pathnames, PC-based commands and folders, and certain elements of the C programming language.

-
A hyphen at the end of a command format description, command line, or code line indicates that the command or statement continues on the following line.
numbersAll numbers in text are assumed to be decimal unless otherwise noted. Nondecimal radixes—binary, octal, or hexadecimal—are explicitly indicated.

Chapter 1. Developing VSI COBOL Programs

VSI COBOL is a family of powerful COBOL compilers produced by VSI OpenVMS. VSI COBOL operates comfortably in the VSI common language environment; on Alpha and I64, it is based on GEM, which is the highly advanced code generator and optimizer that VSI uses in its Alpha and I64 families of languages, which includes COBOL, C, C++, FORTRAN, BASIC, Ada, and PASCAL. In addition to standard COBOL features, VSI COBOL includes extensions that make new application development efficient and effective, with features helpful in porting legacy COBOL programs to OpenVMS Alpha, OpenVMS I64, and UNIX systems.

Developing software applications with VSI COBOL will be a familiar process. You set up your development environment, create your source, compile, link, and run. A few of the specific tasks are:
  • Choosing a reference format: terminal or ANSI

  • Carefully considering Alpha and Itanium architecture system resources; for example, you might invest more system resources at compile time to get faster execution at run time

  • Using various system-independent features for program development

1.1. Developing Programs on UNIX

This section briefly describes the UNIX commands (commands used at the operating system prompt) that you use to create, compile, link, and run VSI COBOL programs on UNIX systems.

1.1.1. Creating an VSI COBOL Program on UNIX

Use a text editor, such as vi or emacs, to create and revise your source files. For instance, to edit the file prog1.cob using the vi editor, type:

% vi prog1.cob

Figure 1.1 shows the basic steps in VSI COBOL program development on UNIX systems.

Figure 1.1. Commands for Developing VSI COBOL Programs on UNIX
Commands for Developing VSI COBOL Programs on UNIX

When naming a source file, choose one of the four file name extensions that the cobol compiler recognizes as COBOL file suffixes. These suffixes are:

  • .cob

  • .COB

  • .cbl

  • .CBL

Table 1.1 shows other file name suffixes.

Table 1.1. Other File Name Suffixes

Suffix

Description

.c

Identifies C language files passed to the C compiler driver cc, which performs additional command line parsing before invoking the C language compiler.

.s

Identifies assembler files passed to cc. VSI COBOL does not generate .s files.

.o

Identifies object files passed to cc, which are in turn passed to ld.

.a

Identifies archive object libraries passed to cc, which are in turn passed to ld. All routines in the specified object library will be searched during linking to resolve external references. This is one method of specifying special libraries for which the cobol command does not automatically search.

.so

Identifies shared object libraries passed to cc, which are in turn passed to ld. All routines in the specified object library will be searched during linking to resolve external references. This is one method of specifying special libraries for which the cobol command does not automatically search.

The following cobol command compiles the program named prog1.cob and automatically uses the linker ld to link the main program into an executable program file named a.out (the name used if you do not specify a name):
% cobol prog1.cob

The cobol command automatically passes a standard default list of UNIX and VSI COBOL libraries to the ld linker. If all external routines used by a program reside in these standard libraries, additional libraries or object files are not specified on the cobol command line.

If your path definition includes the directory containing a.out, you can run the program by simply typing its name:
% a.out

If the executable image is not in your current directory path, specify the directory path in addition to the file name.

The COPY Statement and Libraries

As you write a program, you can use the COPY statement in your source program to include text from another file. With the COPY statement, separate programs can share common source text kept in libraries, reducing development and testing time as well as storage. The VSI COBOL Reference Manual explains how to use the COPY statement.

Special Considerations for Routines Named main

If you have a program or routine named main, declared either in a VSI COBOL or other module, your application may not work correctly. The VSI COBOL library contains a routine named main, which initializes the run-time environment for the CALL by data name statements, extended ACCEPT and DISPLAY statements, and some error handling. When your application also declares a main, your routine preempts the VSI COBOL routine, and the run-time initialization is not performed.

VSI recommends that you not name a VSI COBOL program main.

If you have a C routine named main, you can work around this problem by having the main routine directly call the VSI COBOL initialization routine, cob_init. The cob_init routine interface (in C) is as follows:
      void cob_init (       /* init the RTL */
        int argc,           /* argument count */
        char **argv,        /* arguments */
        char **envp         /* environment variable pointers */ )

1.1.2. Compiling a VSI COBOL Program on UNIX

Compilation does the following for you:
  • Detects errors in your program syntax

  • Displays compiler messages on your terminal screen

  • Generates machine language instructions from valid source statements

  • Groups the instructions into an object module for the linker ld

To compile your program, use the cobol command.

The COBOL Command Driver

The cobol command invokes a compiler driver that is the actual user interface to the VSI COBOL compiler. It accepts a list of command flags and file names and causes one or more processors (compiler, assembler, or linker) to process each file.

After the VSI COBOL compiler processes the appropriate files to create one or more object files, the compiler driver passes a list of files, certain flags, and other information to the cc compiler. After the cc compiler (the default C compiler on your system) processes relevant files and information, it passes certain information (such as .o object files) to the ld linker. The cobol command executes each processor; if any processor returns other than normal status, further processing is discontinued and the VSI COBOL compiler displays a message identifying the processor (and its returned status, in hexadecimal) before terminating its own execution.

1.1.2.1. Format of the COBOL Command on UNIX

The cobol command has the following format:
cobol [-flags [options]]... filename[.suffix][filename[.suffix]]... [-flags [options]]...
flags [options]

Indicates either special actions to be performed by the compiler or linker, or special properties of input or output files. For details about command-line flags, see Section 1.1.2.2. If you specify the -l string flag (which indicates libraries to be searched by the linker) or an object library file name, place it after the file names and after other flags.

filename.suffix

Specifies the source files containing the program units to be compiled, where the file name has a suffix that indicates the type of file used. The recognized COBOL suffix characters are .cob, .COB, .cbl, and .CBL.

Note that the compiler driver checks for a valid suffix on filename. If you omit suffix, or if it is not one of the types recognized by the cobol command, the file is assumed to be an object file and is passed directly to the linker.

An example cobol command line would be:
% cobol -v test.cob pas.o
This command specifies the following:
  • The -v flag displays the compilation and link passes with their arguments and files, including the libraries passed to ld.

  • The file test.cob is passed to the VSI COBOL compiler for compilation. The resulting object file is then linked.

  • The object file pas.o is passed directly to the linker.

As an additional example, you might find that your compiler command lines are getting rather long, as shown in the following example:
% cobol -rsv foreign_extensions -flagger high_fips -warn information zeroes.cob
To work around this, you may truncate compiler flag options (arguments) to their shortest unambiguous form, as follows:
% cobol -rsv for -flagger high -warn info zeroes.cob

1.1.2.2. COBOL Command Flags

Flags to thecobol command affect how the compiler processes a file. The simplest form of the cobol command is often sufficient.

If you compile parts of your program (compilation units) using multiple cobol commands, flags that affect the execution of the program should be used consistently for all compilations, especially if data will be shared or passed between procedures.

For a complete list of VSI COBOL flags, see Table 1.2. For more information about the VSI COBOL flags, access the reference (man) page for COBOL at the UNIX system prompt. For example:
% man cobol

Table 1.2.  VSI COBOL Command Flags on UNIX

Flag

Default

-align [padding]

off

-ansi

off

-arch
-arch generic
-arithmetic native
-arithmetic native
-arithmetic standard
-arithmetic native
-C

off

-c

on

-call_shared

on

-check all

off

-check [no]bounds
-check nobounds
-check [no]decimal
-check nodecimal
-check [no]perform
-check noperform
-check none

on

-conditionals [selector]

off

-convert [no]leading_blanks
-convert noleading_blanks
-copy

off

-copy_list

off

-cord

off

-cross_reference

off

-cross_reference alphabetical

off

-cross_reference declared

off

-D num 

off

-display_formatted

off

-feedback file 

off

-fips 74

off

-flagger [option]

off

-granularity byte
-granularity long
-granularity quad
-granularity quad
-g0

off

-g1

on

-g2
or
-g

off

-g3

off

-include

off

-K

off

-L

off

-Ldir

off

-list

off

-lstring

off

-mach
or
-machine_code

off

-map

off

-map alphabetical

off

-map declared

off

-math_intermediate cit3
-math_intermediate cit4
-math_intermediate float
-math_intermediate float
-names as_is
-names lower
-names lowercase
-names upper
-names uppercase
-names lowercase
-nationality japan
-nationality us
-nationality us
-nolocking

off

-noobject

off

-non_shared
-call_shared
-nowarn

off

-O0

off

-O1

off

-O2

off

-O3

off

-O4
or
-O

on

-o output

a.out

-p0

on

-p1
or
-p

off

-relax_key_checking
or
-rkc

off

-rsv [no]200x
-rsv no200x
-rsv [no]foreign_extensions
-rsv noforeign_extensions
-rsv [no]xopen
-rsv xopen
-seq
or
-sequence_check

off

-shared
-call_shared
-show code

off

-show copy

off

-show xref

off

-std
or
-std 85

on

-std [no]mia
-std nomia
-std [no]syntax
-std nosyntax
-std [no]v3
-std nov3
-std [no]xopen
-std xopen
-T num

off

-taso

off

-tps

off

-trunc

off

-tune
-tune generic
Technical Notes:
  1. If your program compile generates Errors (E-level diagnostics on OpenVMS), the link phase of the two steps taken by the compiler driver will be aborted and the object file(s) deleted. You can override this deletion by specifying the -c flag:
    % cobol -c test.cob % cobol test.o

    The VSI COBOL compiler driver (see Section 1.1.2) controls a sequence of operations (as required): compiling, assembling, linking. The -c flag signals the compiler driver to break the sequence.

    (For additional information, see the section called “The COBOL Command Driver” description (earlier in this chapter), Section 1.1.2.12, and the -c description under man cobol.)

  2. The -tps flag causes the VSI COBOL compiler to use an external file handler (produced by a third party), providing increased flexibility in cross platform, transaction processing application development. See Section 1.1.2.3 for more information.

  3. Specifying the -xref_stdout option directs the compiler to output the data file to standard output.

  4. Any copy file that contains a PROGRAM-ID or END PROGRAM statement for a program must contain that entire program.

1.1.2.3.  External File Handler Support

The -tps flag allows VSI COBOL applications to make use of ACMSxp, the Application Control and Management System/Cross-Platform Edition.

-tps specifies that files are part of a transaction processing system, and enables Encina Structured File System (SFS) record storage for applicable files. It is intended to be used in conjunction with the Transarc Encina external file handler and ACMSxp, allowing access to data in a wide variety of databases, without the need to write code in the language of the databases. This approach provides access to transaction processing technology, and incorporates industry standards for data communications and distributed computing. ACMSxp conforms to the the Multivendor Integration Architecture (MIA).

COBOL is one of the languages approved by MIA for transaction processing (TP) client programs, customer-written presentation procedures, and processing procedures. For database access, Structured Query Language (SQL) is the MIA-required access language. The SQL is embedded in COBOL and C.

Refer to the ACMSxp documentation for full details. Additional information can also be found in published Distributed Computing Environment (DCE) documentation.

1.1.2.4. Specifying Multiple Files and Flags

The cobol command can specify multiple file names and multiple flags. Multiple file names are delimited by spaces. If appropriate, each file name can have a different suffix. The file name suffix could result in the following actions:
  • Calling another language compiler, such as the C compiler

  • Passing object files directly to the linker, which the linker combines with other object files

  • Passing an object library to the linker, which the linker uses to search for unresolved global references

When a file is not in your current working directory, specify the directory path before the file name.

1.1.2.5.  Compiling Multiple Files

An entire set of source files can be compiled and linked together using a single cobol command:
% cobol -o calc mainprog.cob array_calc.cob calc_aver.cob
This cobol command:
  • Uses the -o flag to specify the name of the executable program as calc

  • Compiles the file array_calc.cob

  • Compiles the file calc_aver.cob

  • Compiles the file mainprog.cob, which contains the main program

  • Uses ld to link both the main program and object files into an executable program file named calc

The files can also be compiled separately, as follows:
% cobol -c array_calc.cob
% cobol -c calc_aver.cob
% cobol -o calc mainprog.cob array_calc.o calc_aver.o

In this case, the -c option prevents linking and retains the .o files. The first command creates the file array_calc.o. The second command creates the file calc_aver.o. The last command compiles the main program and links the object files into the executable program named calc.

If your path definition includes the directory containing calc, you can run the program by simply typing its name:
% calc
You can compile multiple source files by concatenating them:
% cat proga1.cob proga2.cob proga3.cob > com1.cob
% cat progb1.cob progb2.cob > com2.cob
% cobol -c com1.cob com2.cob
The resulting file names are com1.o and com2.o. The OpenVMS Alpha and I64 equivalent to this is:
$ COBOL proga1+proga2+proga3,progb1+progb2

1.1.2.6. Debugging a Program

To debug a program using the Ladebug Debugger, compile the source files with the -g flag to request additional symbol table information for source line debugging in the object and executable program files. The following cobol command also uses the -o flag to name the executable program file calc_debug:
% cobol -g -o calc_debug  mainprog.cob array_calc.cob calc_aver.cob
To debug an executable program named calc_debug, type the following command:
% ladebug calc_debug

For more information on running the program within the debugger, refer to the Ladebug Debugger Manual.

Pay attention to compiler messages. Informational and warning messages (as well as error-level messages) do not prevent the production of an object file, which you can link and execute. However, the messages sometimes point out otherwise undetected logic errors, and the structure of the program might not be what you intended.

1.1.2.7. Output Files: Object, Executable, Listing, and Temporary Files

The output produced by the cobol command includes:
  • An object file, if you specify the -c flag on the command line

  • An executable file, if you omit the -c flag

  • A listing file, if you specify the -V flag

If the environment variable TMPDIR is set, the value is used as the directory for temporary files.

You control the production of these files by specifying the appropriate flags on the cobol command line. Unless you specify the -c flag, the compiler generates a single temporary object file, whether you specify one source file or multiple source files separated by blanks. The ld linker is then invoked to link the object file into one executable image file.

The object file is in UNIX extended coff format. The object file provides the following information:
  • The name of the entry point. It takes this name from the program name in the first PROGRAM-ID paragraph in the source program.

  • A list of variables that are declared in the module. The linker uses this information when it binds two or more modules together and must resolve references to the same names in the modules.

  • A symbol table and a source line correlation table (if you request them with the -g flag, for debugging). A symbol table is a list of the names of all external and internal variables within a module, with definitions of their locations. The source line correlation table associates lines in your source file with lines in your program. These tables are of use in debugging.

If severe errors are encountered during compilation or if you specify certain flags such as -c, linking does not occur.

1.1.2.8. Naming Output Files

To specify a file name (other than a.out) for the executable image file, use the -o output flag, where output specifies the file name. You can also use the mv command to rename the file. The following command requests a file name of prog1.out for the source file test1.cob:
% cobol -o prog1.out test1.cob

Besides specifying the name of the executable image file, you can use the -o output flag to rename the object file if you specified the -c flag. If you specify the -c flag and omit the -o output flag, the name of the first specified file is used with a .o suffix substituted for the source file suffix.

1.1.2.9.  Temporary Files

Temporary files created by the compiler or a preprocessor reside in the /tmp directory and are deleted (unless the -K flag is specified). You can set the environment variable TMPDIR to specify a directory to contain temporary files if /tmp is not acceptable.

To view the file name and directory where each temporary file is created, use the -v flag. To create object files in your current working directory, use the -c flag. Any object files (.o files) that you specify on the cobol command line are retained.

1.1.2.10. Examples of the COBOL Command

The following examples show the use of the cobol command. Each command is followed by a description of the output files that it produces.

  1. % cobol -V aaa.cob bbb.cob ccc.cob

    The VSI COBOL source files aaa.cob, bbb.cob, and ccc.cob are compiled into temporary object files. The temporary object files are passed to the ld linker. The ld linker produces the executable file a.out. The -V flag causes the compiler to create the listing files aaa.lis, bbb.lis, and ccc.lis.

  2. % cobol -V *.cob

    VSI COBOL source files with file names that end with .cob are compiled into temporary object files, which are then passed to the ld linker. The ld linker produces the a.out file.

When the compilation completes, the cobol driver returns one of the following status values:
  • 0 – SUCCESS
  • 1 – FAILURE
  • 2 – SUBPROCESS_FAILURE (cobol or cc)
  • 3 – SIGNAL

1.1.2.11. Other Compilers

You can compile and link multilanguage programs using a single cobol command.

The cobol command recognizes C or Assembler program files by their file suffix characters and passes them to the cc compiler for compilation. Before compilation, cc applies the cpp preprocessor to files that it recognizes, such as any file with a .c suffix.

Certain flags passed to cc are passed to the ld linker.

1.1.2.12.  Interpreting Messages from the Compiler

The VSI COBOL compiler identifies syntax errors and violations of language rules in the program. If the compiler finds any errors, it writes messages to the stderr output file and any listing file. If you enter the cobol command interactively, the messages are displayed on your terminal.

Compiler messages have the following format:
cobol: severity: filename, line n, message-text
[text-in-error]
--------^
The pointer ( –^) indicates the exact place on the source line where the error was found. For example, the following error message shows the format and message text in a listing file when an END DO statement was omitted:
cobol: Severe: disp.cob, line 7: Missing period is assumed
        05 VAR-1 PIC X.
--------^
The severity level is one of the following:

Severe

The compiler does not produce an object module. You must correct the error before you can compile the program to produce an object module.

Error

The compiler makes an assumption about what you intended and continues. However, the compiler's assumption may not relate to your intention. Correct the error.

Warning

The compiler attempts to correct the error in the statement, but you should verify that the compiler's action is acceptable. Otherwise, your program may produce unexpected results.

Informational

This message usually appears with other messages to inform you of specific actions taken by the compiler. No action is necessary on your part.

Any messages issued during the compilation are inserted in the listing file. A listing file is useful for debugging the source code. Use the -V or -list flag to produce a listing; you may also use -cross_reference, -copy_list, -flagger, -machine_code, -map, and/or -warn, all of which affect the contents of the listing file.

Diagnostic messages provide information for you to determine the cause of an error and correct it. If the compiler creates a listing file, it writes the messages to the listing file.

1.1.4. Running a VSI COBOL Program on UNIX

The simplest form of the run command to execute a program is to type its file name at the operating system prompt, as follows:
% myprog.out

In addition to normal IO accesses, your VSI COBOL programs can read command-line arguments and access (read and write) environment variables.

1.1.4.1. Accessing Command-Line Arguments

Command-line arguments allow you to provide information to a program at run time. Your program provides the logic to parse the command line, identify command-line options, and act upon them. For example, you might develop a program that will extract a given amount of data from a specified file, where both the number of records to read and the file name are highly dynamic, changing for each activation of your program. In this case your program would contain code that reads a command-line argument for the number of records to read, and a second argument for the file specification. Your program execution command could look like the following:
% myprog 1028 powers.dat

In the preceding example the program myprog would read 1028 records from the file powers.dat.

Multiple command-line arguments are delimited by spaces, as shown in the preceding example. If an argument itself contains spaces, enclose that argument in quotation marks (" ") as follows:
% myprog2 "all of this is argument 1" argument2

You provide definitions for the command-line arguments with the SPECIAL-NAMES paragraph in your program's Environment Division, and you include ACCEPT and DISPLAY statements in the Procedure Division to parse the command line and access the arguments. Detailed information about command-line argument capability is in the ACCEPT and DISPLAY sections in the VSI COBOL Reference Manual.

1.1.4.2. Accessing Environment Variables

You can read and write environment variables at run time through your VSI COBOL program.

Example 1.1 allows you to specify a file specification by putting the directory in the value of the environment variable COBOLPATH, and the file name in a command-line argument:
Example 1.1. Accessing Environment Variables and Command-Line Arguments
  identification division.
 PROGRAM-ID. MYPROG.
 ENVIRONMENT DIVISION.
 CONFIGURATION SECTION.
 SPECIAL-NAMES.
     SYSERR              IS STANDARD-ERROR
     ENVIRONMENT-NAME    IS NAME-OF-ENVIRONMENT-VARIABLE
     ENVIRONMENT-VALUE   IS ENVIRONMENT-VARIABLE
     ARGUMENT-NUMBER     IS POS-OF-COMMAND-LINE-ARGUMENT
     ARGUMENT-VALUE      IS COMMAND-LINE-ARGUMENT.
 DATA DIVISION.
 WORKING-STORAGE SECTION.
 01 howmany-records PIC 9(5).
 01 env-dir PIC x(50).
 01 file-name PIC x(50).
 01 file-spec PIC x(100).
 PROCEDURE DIVISION.
 BEGIN.
   ACCEPT howmany-records FROM COMMAND-LINE-ARGUMENT
     ON EXCEPTION
       DISPLAY "No arguments specified"
         UPON STANDARD-ERROR
       END-DISPLAY
       STOP RUN
   END-ACCEPT.
   DISPLAY "COBOLPATH" UPON NAME-OF-ENVIRONMENT-VARIABLE.
   ACCEPT env-dir FROM ENVIRONMENT-VARIABLE
     ON EXCEPTION
       DISPLAY "Environment variable COBOLPATH is not set"
         UPON STANDARD-ERROR
       END-DISPLAY
     NOT ON EXCEPTION
       ACCEPT file-name FROM COMMAND-LINE-ARGUMENT
         ON EXCEPTION
           DISPLAY
           "Attempt to read beyond end of command line"
             UPON STANDARD-ERROR
           END-DISPLAY
         NOT ON EXCEPTION
           STRING env-dir "/" file-name delimited by " " into file-spec
           DISPLAY "Would have read " howmany-records " records from " file-spec
       END-ACCEPT
   END-ACCEPT.
This example requires that the following command has been executed to set an environment variable:
% setenv COBOLPATH /usr/files
When you execute the following command lines:
% cobol -o myprog myprog.cob
% myprog 1028 powers.dat
The following will result:
  • howmany-records will contain 1028

  • env-dir will contain /usr/files

  • file-name will contain powers.dat

  • file-spec will contain /usr/files/powers.dat

For additional information, refer to the ACCEPT and DISPLAY statements in the VSI COBOL Reference Manual.

1.1.4.3. Errors and Switches

See Section 1.4 for a discussion of errors that can cause incorrect or undesirable results when you run a program.

See Section 1.5 for a discussion of controlling program execution with switches.

1.1.5. Program Development Stages and Tools

This manual primarily addresses the program development activities associated with development and testing phases. For information about topics usually considered during application design, specification, and maintenance, refer to your operating system documentation, appropriate reference pages, or appropriate commercially published documentation.

Table 1.3 lists and describes some of the software tools you can use when developing and testing a program.
Table 1.3. Main Tools for Program Development and Testing

Task or Activity

Tool and Description

Manage source files

Use RCS or sccs to manage source files. For more information, refer to the UNIX documentation on programming support tools or the appropriate reference page.

Create and modify source files

Use a text editor, such as vi, emacs, or another editor. For more information, refer to your operating system documentation.

Analyze source code

Use searching commands such as grep and diff. For more information, refer to the UNIX documentation on programming support tools or the appropriate reference page.

Build program (compile and link)

You can use the cobol command to create small programs, perhaps using shell scripts, or use the make command to build your application in an automated fashion using a makefile. For more information on make, refer to the make (1 ) reference page and the UNIX documentation on programming support tools.

Debug and test program

Use the Ladebug Debugger to debug your program or run it for general testing. For more information on Ladebug Debugger, refer to the Ladebug Debugger Manual.

Install program

Use setld and related commands such as tar. For more information, refer to the UNIX documentation on programming support tools.

In addition, you might use the following shell commands at various times during program development:
  • To view information about an object file or an object library, use the following commands:
    • The file command shows the type of a file (such as which programming language, whether it is an object library, ASCII file, and so forth).

    • The nm command (perhaps with the -a or -o flag) shows symbol table information, including the identification field of each object file.

    • The odump command shows the contents of a file and other information.

    • The size command shows the size of the code and data sections.

    For more information on these commands, refer to the appropriate reference page or the UNIX documentation on programming support tools.

  • Use the ar command to create an archive object library ( -r flag), maintain the modules in the library, list the modules in the library ( -t), and perform other functions. Use -ts to add a table of contents to the object library for linking purposes. For more information, refer to ar (1 ) or the UNIX programmer's documentation.

  • To create shared libraries on UNIX systems, use ld, not the ar command. For more information, see Section 1.1.3.4 and refer to the UNIX programmer's documentation.

  • The strip command removes symbolic and other debugging information to minimize image size. For additional information, refer to the strip (1 ) reference page.


Note

The CALL dataname, CANCEL, and the VSI extensions to the ACCEPT and DISPLAY statements will not work correctly if you use the strip command on your image.

In most instances, use the cobol command to invoke both the VSI COBOL compiler and the ld linker. To link one or more object files created by the VSI COBOL compiler, you should use the cobol command instead of the ld command, because the cobol command automatically references the appropriate VSI COBOL Run-Time Libraries when it invokes ld. If the executable image is not in your current working directory, specify the directory path in addition to the file name.

Compilation does the following for you:

  • Detects errors in your program syntax

  • Displays compiler messages on your terminal screen

  • Generates machine language instructions from valid source statements

  • Groups the instructions into an object module for the linker

  • Launches the linker with the compiled file or files

  • Creates an executable image

You use the cobol command to compile and link your program. The cobol command invokes the VSI COBOL compiler driver that is the actual user interface to the VSI COBOL compiler. The compiler driver can accept command options and multiple file names, and normally causes the compiler and linker to process each file. A variety of qualifiers to the compile command are available to specify optional processing and to specify the names of output files.

After the VSI COBOL compiler processes the source files to create one or more object files, the compiler driver passes a list of object files and other information to the linker.

1.2. Developing Programs on OpenVMS

You use DCL commands (commands used at the OpenVMS system prompt) to create, compile, link, and run VSI COBOL programs on OpenVMS systems.

1.2.1. Creating an VSI COBOL Program on OpenVMS

To create and modify an VSI COBOL program, you must invoke a text editor. The default editor for OpenVMS is the Text Processing Utility (TPU). Other editors, such as EDT or the Language-Sensitive Editor (LSE), may be available on your system. Check with your system administrator and refer to the OpenVMS EDT Reference Manual (this manual has been archived but is available on the OpenVMS Documentation CD-ROM) for more information about EDT or the Guide to Language-Sensitive Editor for additional information about LSE.

Figure 1.2 shows the basic steps in VSI COBOL program development.

Figure 1.2. DCL Commands for Developing Programs
DCL Commands for Developing Programs

Use the text editor of your preference to create and revise your source files. For example, the following command line invokes the TPU editor and creates the source file PROG_1.COB:

$ EDIT PROG_1.COB

The file type .COB is used to indicate that you are creating an VSI COBOL program. COB is the default file type for all VSI COBOL programs.

The COPY Statement, Dictionaries, and Libraries

Including the COPY statement in your program allows separate programs to share common source text, reducing development and testing time as well as storage requirements. You can use the COPY statement to access modules in libraries. The COPY statement causes the compiler to read the file or module specified during the compilation of a program. When the compiler reaches the end of the included text, it resumes reading from the previous input file.

By using the /INCLUDE qualifier on the COBOL command line, you can set up a search list for files specified by the COPY statement. For more information, refer to the VSI COBOL Reference Manual.

You can use the COPY FROM DICTIONARY statement in your program to access a data dictionary and copy Oracle CDD/Repository record descriptions into your program as COBOL record descriptions. Before you can copy record descriptions from Oracle CDD/Repository, you must create the record descriptions using the Common Data Dictionary Language (CDDL) or Common Dictionary Operator (CDO).

For more information about using Oracle CDD/Repository and creating and maintaining text libraries, refer to the VSI COBOL Reference Manual and Using Oracle CDD/Repository on OpenVMS Systems.

1.2.2. Compiling an VSI COBOL Program on OpenVMS

To compile your program, use the COBOL command. The VSI COBOL compiler performs these primary functions:

  • Detects errors in your program.

  • Displays each compiler message on your terminal screen.

  • Generates machine language instructions from valid source statements.

  • Groups these language instructions into an object module for the linker.

  • Creates an analysis file if you request it with the /ANALYSIS_DATA qualifier. SCA uses this file to display information about program symbols and source files.

The compiler outputs an object module that provides the following information:

  • The name of the entry point. It takes this name from the program name in the first PROGRAM-ID paragraph in the program.

  • A list of variables that are declared in the module. The linker uses this information when it binds two or more modules together and must resolve references to the same names in the modules.

  • Traceback information. This information is used by the system default condition handler when an error occurs that is not handled by the program. The traceback information permits the default handler to display a list of the active blocks in the order of activation; this is an aid in program debugging.

  • A symbol table and a source line correlation table, only if you request them with the /DEBUG qualifier. A symbol table is a list of the names of all external and internal variables within a module, with definitions of their locations. The source line correlation table associates lines in your source file with lines in your program. These tables are of primary help when you use the OpenVMS Debugger.

To invoke the VSI COBOL compiler, use the COBOL command (explained in Section 1.2.2.1). You can specify qualifiers with the COBOL command. The following sections discuss the COBOL command and its qualifiers.

1.2.2.1.  Format of the COBOL Command on OpenVMS

The COBOL command has the following format:

COBOL [/qualifier] ... {file-spec [/qualifier] ...} ...

/qualifier

Specifies an action to be performed by the compiler on all files or specific files listed. When a qualifier appears directly after the COBOL command, it affects all the files listed. By contrast, when a qualifier appears after a file specification, it affects only the file that immediately precedes it. However, when files are concatenated, these rules do not apply.

file-spec

Specifies an input source file that contains the program or module to be compiled. You are not required to specify a file type; the VSI COBOL compiler assumes the default file type COB. If you do not provide a file specification with the COBOL command, the system prompts you for one.

1.2.2.2. Compiling Multiple Files

You can include more than one file specification on the same command line by separating the file specifications with either a comma (,) or a plus sign (+). If you separate the file specifications with commas, you can control which source files are affected by each qualifier. In the following example, the VSI COBOL compiler creates an object file for each source file but creates only a listing file for the source files entitled PROG_1 and PROG_3:
$ COBOL/LIST PROG_1, PROG_2/NOLIST, PROG_3

If you separate file specifications with plus signs, the VSI COBOL compiler concatenates each of the specified source files and creates one object file and one listing file. In the following example, only one object file, PROG_1.OBJ, and one listing file, PROG_1.LIS, are created. Both of these files are named after the first source file in the list, but contain all three modules.

$ COBOL PROG_1 + PROG_2/LIST + PROG_3

Any qualifiers specified for a single file within a list of files separated with plus signs affect all files in the list.

1.2.2.3. Debugging a Program

To effectively debug an VSI COBOL program, you must first make symbol and traceback information available by adding the DEBUG option to the compile command line. You specify the /DEBUG option as follows:

$ COBOL/DEBUG myprog
$ LINK/DEBUG myprog
$ RUN/DEBUG myprog

This enables you to examine and modify variables, monitor flow of control, and perform various other debugging techniques. See Section C.3 or type HELP COBOL/DEBUG or HELP DEBUG for additional information.

On Alpha and I64, when you compile a program with /DEBUG, you should also specify /NOOPTIMIZE to expedite your debugging session. (The default is /OPTIMIZE.) Optimization often changes the order of execution of the object code generated for statements in a program, and it might keep values in registers and deallocate user variables. These effects can be confusing when you use the debugger. (A diagnostic message warns you if you compile an VSI COBOL program with /DEBUG without specifying anything about optimization on the command line.)

Pay attention to compiler messages. Informational and warning messages (as well as error-level messages) do not prevent the production of an object file, which you can link and execute. However, the messages sometimes point out otherwise undetected logic errors, and the structure of the program might not be what you intended.

1.2.2.4.  Separately Compiled Programs (Alpha, I64)

If a compilation unit consists of multiple separately compiled programs (SCPs), by default the VSI COBOL compiler produces a single object file that consists of a single module with multiple embedded procedures. This object file can be inserted into an object library. If your build procedure requires that the linker extract any part of the module, the linker must extract the entire object.

If you use /SEPARATE_COMPILATION on the compile command line, VSI COBOL will compile multiple SCPs into a single object file that consists of a concatenation of modules, each containing a single procedure. This object may then be inserted into an object library from which the linker can extract just the procedures that are specifically needed.

1.2.2.5. COBOL Qualifiers

COBOL options (also known as qualifiers or flags) control the way in which the compiler processes a file. You can process your file with the COBOL command alone or you can select options that offer you alternatives for developing, debugging, and documenting programs.

If you compile parts of your program (compilation units) using multiple COBOL commands, options that affect the execution of the program should be used consistently for all compilations, especially if data will be shared or passed between procedures.

Table 1.4 lists the COBOL command options and their defaults. For more information about COBOL options, invoke online help for COBOL at the system prompt.

Note

Brackets ([]) indicate that the enclosed item is optional. If you specify more than one option for a single qualifier, you must separate each option with a comma and enclose the list of options in parentheses.

Table 1.4. COBOL Command Qualifiers

Qualifier

Default

Alpha, I64 Only

VAX Only

/ALIGNMENT[=[NO]PADDING] or /NOALIGNMENT

/NOALIGNMENTX 

/ANALYSIS_DATA[=file-spec] or /NOANALYSIS_DATA

/NOANALYSIS_DATA  
/ANSI_FORMAT or /NOANSI_FORMAT/NOANSI_FORMAT  
/ARCHITECTURE={GENERIC|HOST|EV4|EV5|EV56|EV6|EV67, EV68|PCA56}/ARCHITECTURE=GENERICX 
/ARITHMETIC={STANDARD|NATIVE}/ARITHMETIC=NATIVEX 
/AUDIT or /NOAUDIT/NOAUDIT X

/CHECK=[[NO]PERFORM|[NO]BOUNDS|[NO]DECIMAL (Alpha only)|[NO]DUPLICATES|ALL?|NONE],… or /NOCHEC

/NOCHECK or /CHECK=NONE  
/CONDITIONALS=(character,...) or /NOCONDITIONALS/NOCONDITIONALS  
/CONVERT=[NO]LEADING_BLANKS or /NOCONVERT /NOCONVERTX 
/COPY_LIST or /NOCOPY_LIST/NOCOPY_LIST  

/CROSS_REFERENCE=
[ALPHABETICAL?|DECLARED],…
or
/NOCROSS_REFERENCE

/NOCROSS_REFERENCE  

/DEBUG=[[NO]SYMBOLS|[NO]TRACEBACK|ALL|NONE],… or /NODEBUG

/DEBUG=TRACEBACK /DEBUG=ALL? /DEBUG=(TRACEBACK,SYMBOLS)?  
/DEPENDENCY_DATA or /NODEPENDENCY_DATA/NODEPENDENCY_DATA  
/DESIGN or /NODESIGN/NODESIGN X
/DIAGNOSTICS[=file-spec] or /NODIAGNOSTICS/NODIAGNOSTICS  
/DISPLAY_FORMATTED or /NODISPLAY_FORMATTED/NODISPLAY_FORMATTEDX 
/FIPS=74 or /NOFIPS/NOFIPS  

/FLAGGER=[HIGH_FIPS?|INTERMEDIATE_FIPS|MINIMUM_FIPS|OBSOLETE|OPTIONAL_FIPS|REPORT_WRITER|SEGMENTATION|SEGMENTATION_1],… or /NOFLAGGER

/NOFLAGGER  
/FLOAT=[D_FLOAT|G_FLOAT|IEEE_FLOAT]/FLOAT=D_FLOATX 
/GRANULARITY=[BYTE|LONGWORD|QUADWORD]/GRANULARITY=QUADWORD X 
/INCLUDE=file-spec or /NOINCLUDE/NOINCLUDEX 
/INSTRUCTION_SET or /NOINSTRUCTION_SET/INSTRUCTION_SET=DECIMAL_ STRING X
/KEEP or /NOKEEP/NOKEEP  
/LIST[=filename.ext] or /NOLIST/NOLIST /LIST (batch)  
/MACHINE_CODE or /NOMACHINE_CODE /NOMACHINE_CODE  

/MAP=[ALPHABETICAL?|DECLARED],… or /NOMAP

/NOMAP  
/MATH_INTERMEDIATE={CIT3|CIT4|FLOAT}/MATH_INTERMEDIATE=FLOATX 
/NAMES={AS_IS|LOWER|LOWERCASE|UPPER|UPPERCASE}/NAMES=LOWERCASEX 
/NATIONALITY=[JAPAN|US]/NATIONALITY=USX 
/OBJECT[=filename.ext] or /NOOBJECT/OBJECT  

/OPTIMIZE[={[LEVEL={0?|1|2|3|4?}|TUNE={GENERIC?|HOST|EV4|EV5|EV56|EV6|EV67, EV68|PCA56}]}] or /NOOPTIMIZE

/OPTIMIZE= (LEVEL=4,TUNE=GENERIC)X 
/RESERVED_WORDS=[[NO]200X|[NO]XOPEN|[NO]FOREIGN_EXTENSIONS],...

/RESERVED_WORDS=
(XOPEN, NO200X,
 NOFOREIGN_EXTENSIONS)

X 
/SEPARATE_COMPILATION or /NOSEPARATE_COMPILATION/NOSEPARATE_COMPILATIONX 
/SEQUENCE_CHECK or /NOSEQUENCE_CHECK/NOSEQUENCE_CHECK  
/SOURCE[=filename.ext]Source is filename.COBX 

/STANDARD=[85|[NO]MIA|[NO]SYNTAX|[NO]V3|[NO]XOPEN (Alpha)],… or /NOSTANDARD

/STANDARD=851  
/TIE or /NOTIE/NOTIEX 
/TRUNCATE or /NOTRUNCATE/NOTRUNCATE  
/VFC or /NOVFC/VFCX 

/WARNINGS=[[NO]INFORMATION|[NO]OTHER|ALL?|NONE],… or /NOWARNINGS

/WARNINGS=OTHER  

1.2.2.6. Common Command-Line Errors to Avoid

The following are some common errors to avoid when entering COBOL command lines:
  • Omitting /ANSI_FORMAT for programs that are in ANSI format (AREA A, AREA B, and so forth)

  • Including contradictory options

  • Omitting a necessary qualifier, such as /LIST if you specify /MAP

  • Omitting version numbers from file specifications when you want to compile a program that is not the latest version of a source file

  • Forgetting to use a file suffix in the file specification, or not specifying /SOURCE when your source file suffix is not .COB or .CBL

1.2.2.7. Compiling Programs with Conditional Compilation

To debug source code that contains conditional compilation lines, you can use either the /CONDITIONALS qualifier or the WITH DEBUGGING MODE clause. The /CONDITIONALS qualifier is listed in Table 1.4. For more information about the /CONDITIONALS qualifier, invoke the online help facility for VSI COBOL at the system prompt. For more information about the WITH DEBUGGING MODE clause, refer to the VSI COBOL Reference Manual.

Using the WITH DEBUGGING MODE clause as part of the SOURCE-COMPUTER paragraph causes the compiler to process all conditional compilation lines in your program as COBOL text. If you do not specify the WITH DEBUGGING MODE clause, and if the /CONDITIONALS qualifier is not in effect, all conditional compilation lines in your program are treated as comments.

The WITH DEBUGGING MODE clause applies to: (1) the program that specifies it, and (2) any contained program within a program that specifies the clause.

1.2.2.8. Interpreting Messages from the Compiler

If there are errors in your source file when you compile your program, the VSI COBOL compiler flags these errors and displays helpful messages. You can reference the message, locate the error, and, if necessary, correct the error in your program.

On Alpha and I64, the general format of compiler messages shown on your screen is as follows:

..........................^
%COBOL-s-ident, message-text
At line number n in name

%COBOL

The facility or program name of the VSI COBOL compiler. This prefix indicates that the VSI COBOL compiler issued the message.

s

The severity of the error, represented in the following way:

FFatal error. The compiler does not produce an object module. You must correct the error before you can compile the program to produce an object module.
EError. The compiler makes an assumption about what you intended and continues. However, the compiler's assumption may not relate to your intention. Correct the error.
WWarning. The compiler attempts to correct the error in the statement, but you should verify that the compiler's action is acceptable. Otherwise, your program may produce unexpected results.
IInformational. This message usually appears with other messages to inform you of specific actions taken by the compiler. No action is necessary on your part. Note that these messages are suppressed by default. You must invoke /WARN=ALL or /WARN=INFO to enable them.

ident (Alpha, I64)

The message identification. This is a descriptive abbreviation of the message text.

message-text

The compiler's message. In many cases, it consists of no more than one line of output. A message generally provides you with enough information to determine the cause of the error so that you can correct it.

At line number n in name (Alpha, I64)

The integer n is the number of the line where the diagnostic occurs. The number is relative to the beginning of the file or text library module specified by name.

On Alpha and I64, a sample compiler message with two diagnostics looks like this in the listing file:

   12          PROCEDURE DIVISION.
   13          P-NAME
   14              MOVE ABC TO XYZ.
   ................^
%COBOL-E-NODOT, Missing period is assumed


   14              MOVE ABC TO XYZ.
   ............................^
%COBOL-F-UNDEFSYM, Undefined name

In the sample, the first diagnostic pointer (^) points to the MOVE statement in source line number 14, which is the closest approximation to where the error (P-NAME is not followed by a period) occurred. The second diagnostic pointer points to XYZ, an undefined name in source line number 14. Each diagnostic pointer is followed by a message line that identifies, in this order:

  • The VSI COBOL compiler generated the message

  • The severity code of the message

  • The message identification (a mnemonic of the message text)

  • The text of the message

Although most compiler messages are self-explanatory, some require additional explanation. The online help facility for VSI COBOL contains a list and descriptions of these VSI COBOL compiler messages. Use the HELP COBOL Compiler Messages command to access this list.

To examine messages that occurred during compilation, you can search for each occurrence of %COBOL in the compiler listing file.Section 1.2.2.9 describes listing files.

1.2.2.9. Using Compiler Listing Files

A compiler listing file provides information that can help you debug or document your VSI COBOL program. It consists of the following sections:
  • Program listing

    The program listing section contains the source code plus line numbers generated by the compiler. Any diagnostics will appear in this section.

  • Storage map

    The storage map section is optional (produced by the /MAP qualifier); it contains summary information on program sections, variables, and arrays.

  • Compilation summary

    The compilation summary section lists the qualifiers used with the COBOL command and the compilation statistics.

  • Machine code

    The machine code section is optional; it displays compiler-generated object code.

To generate a listing file, specify the /LIST qualifier when you compile your VSI COBOL program interactively as in the following example for PROG_1.COB:

$  COBOL/LIST PROG_1.COB

If you compile your program as a batch job, the compiler creates a listing file by default. You can specify the /NOLIST qualifier to suppress creation of the listing file, if that suits your purposes. (In either case, however, the listing file is not automatically printed.) By default, the name of the listing file is the name of your source file followed by the file type .LIS. You can include a file specification with the /LIST qualifier to override this default.

When used with the /LIST qualifier, the following COBOL command qualifiers supply additional information in the compiler listing file:
  • /COPY_LIST—Includes source statements specified by the COPY command.

  • /CROSS_REFERENCE—Creates a cross-reference listing of user-defined names and references.

  • /MACHINE_CODE—Includes a list of compiler-generated machine code.

  • /MAP—Produces maps, data names, procedure names, file names, and external references.

For a description of each qualifier's function, invoke the online help facility for COBOL at the system prompt as follows:

$ HELP COBOL
Compiler Listing File for a Contained Program

A contained COBOL program listing file includes two additional program elements that provide nesting level information about the main program and the contained program. For additional information about contained programs, see Chapter 12: Interprogram Communication.

1.2.3. Linking an VSI COBOL Program

After you compile an VSI COBOL source program or module, use the LINK command to combine your object modules into one executable image that the OpenVMS system can execute. A source program or module cannot run until it is linked.

When you execute the LINK command, the OpenVMS Linker performs the following functions:

  • Resolves local and global symbolic references in the object code

  • Assigns values to the global symbolic references

  • Signals an error message for any unresolved symbolic reference

  • Allocates virtual memory space for the executable image

The LINK command produces an executable image by default. However, you can specify qualifiers and qualifier options with the LINK command to obtain shareable images and system images.

See Table 1.5 for a list of commonly used LINK command qualifiers. For a complete list and for more information about the LINK qualifiers, invoke the online help facility for the LINK command at the system prompt.

For a complete discussion of linker capabilities and for detailed descriptions of LINK qualifiers and qualifier options, refer to the VSI OpenVMS Linker Utility Manual.

1.2.3.1. The LINK Command

The format of the LINK command is as follows:

LINK[/qualifier] ... {file-spec[/qualifier] ...} ...

/qualifier...

Specifies output file options when it is positioned after the LINK command. Specifies input file options when it is positioned after file-spec.

file-spec...

Specifies the input files to be linked.

If you specify more than one input file, you must separate the input file specifications with a plus sign (+) or a comma (,).

By default, the linker creates an output file with the name of the first input file specified and the file type EXE. If you link multiple files, specify the file containing the main program first. Then the name of your output file will have the same name as your main program module.

The following command line links the object files MAINPROG.OBJ, SUBPROG1.OBJ, and SUBPROG2.OBJ to produce one executable image called MAINPROG.EXE:

$ LINK MAINPROG, SUBPROG1, SUBPROG2

1.2.3.2. LINK Qualifiers

LINK qualifiers allow you to control various aspects of the link operation such as modifying linker input and output and invoking the debugging and traceback facilities.

Table 1.5 summarizes some of the more commonly used LINK qualifiers. Refer to the VSI OpenVMS Linker Utility Manual for a complete list and explanations of the LINK qualifiers or invoke the online help facility for the LINK command at the OpenVMS prompt.

Note

Brackets ([]) indicate that the enclosed item is optional. If you specify more than one option for a single qualifier, you must separate each option with a comma and enclose the list of options in parentheses.

Table 1.5. Commonly Used LINK Qualifiers
FunctionQualifierDefault
Indicate that an input file is a library file./LIBRARYNot applicable.
Indicate that an input file is a linker options file./OPTIONSNot applicable.
Request output file, define a file specification, and specify whether the image is shareable./EXECUTABLE[=file-spec] /SHAREABLE[=file-spec]/EXECUTABLE=name.EXE where name is the name of the first input file. /NOSHAREABLE
Request and specify the contents of an image map (memory allocation) listing./BRIEF /[NO]CROSS_REFERENCE /FULL /MAP[=file-spec] or /NOMAP/NOCROSS_REFERENCE /NOMAP (interactive) /MAP=name.MAP (batch) where name is the name of the first input file.
Specify the amount of debugging information./DEBUG[=file-spec] or /NODEBUG /[NO]TRACEBACK/NODEBUG /TRACEBACK

1.2.3.3. Specifying Modules Other than VSI COBOL Modules

When you link VSI COBOL modules with other modules, your application will not work correctly if a non VSI COBOL module contains a LIB$INITIALIZE routine that:

  1. Is invoked before the VSI COBOL LIB$INITIALIZE routine (COB_NAME_START) and

  2. Calls an VSI COBOL program that contains CALL by data name, extended ACCEPT, or extended DISPLAY statements.

VSI COBOL uses the LIB$INITIALIZE routine, COB_NAME_START, to initialize the run-time environment for the CALL by data name and extended ACCEPT and DISPLAY statements. Therefore, the COB_NAME_START routine must be invoked before any CALL, ACCEPT, or DISPLAY statements are performed.

The order in which LIB$INITIALIZE routines are invoked is determined during the link and is shown in the image map. To ensure that the VSI COBOL LIB$INITIALIZE routine is invoked first, change your link command to the following:

$ LINK/EXE=name SYS$SHARE:STARLET/INCL=COB_NAME_START,your_modules...

See Appendix B for information on a problem with LIB$INITIALIZE when you call a C program.

1.2.3.4. Specifying Object Module Libraries

Linking against object modules allows your program to access data and routines outside of your compilation units. You can create your own object module libraries or they can be supplied by the system.

User-Created Object Module Libraries

You can make program modules accessible to other programmers by storing them in object module libraries. To link modules contained in an object module library, use the /INCLUDE qualifier with the LINK command? and specify the modules you want to link. The following example links the subprogram modules EGGPLANT, TOMATO, BROCCOLI, and ONION (contained in the VEGETABLES library) with the main program module GARDEN:

$ LINK GARDEN, VEGETABLES/INCLUDE=(EGGPLANT,TOMATO,BROCCOLI,ONION)

An object module library also contains a symbol table with the names of the global symbols in the library, and the names of the modules in which the symbols are defined. You specify the name of the object module library containing these symbol definitions with the /LIBRARY qualifier. When you use the /LIBRARY qualifier during a linking operation, the linker searches the specified library for all unresolved references found in the included modules during compilation.

The following example uses the library RACQUETS to resolve undefined symbols in the BADMINTON, TENNIS, and RACQUETBALL libraries:

$ LINK BADMINTON, TENNIS, RACQUETBALL, RACQUETS/LIBRARY

For more information about the /INCLUDE and /LIBRARY qualifiers, invoke the online help facility for the LINK command at the DCL prompt or refer to the VSI OpenVMS Linker Utility Manual.

You can define one or more of your private object module libraries as default user libraries. The following section describes how to accomplish this using the DEFINE command.

Defining Default User Object Module Libraries

You can define one or more of your private object module libraries as your default user libraries using the DCL DEFINE command, as in the following example:

$ DEFINE LNK$LIBRARY DEFLIB

The linker searches default user libraries for unresolved references after it searches modules and libraries specified in the LINK command.

In this example, LNK$LIBRARY is a logical name and DEFLIB is the name of an object module library (having the file type OLB) that you want the linker to search automatically in all subsequent link operations.

You can establish any object module library as a default user library by creating a logical name for the library. The logical names you must use are LNK$LIBRARY (as in the preceding example), LNK$LIBRARY_1, LNK$LIBRARY_2, and so on, to LNK$LIBRARY_999. When more than one of these logical names exists when a LINK command executes, the linker searches them in numeric order beginning with LNK$LIBRARY.

When one or more logical names exist for default user libraries, the linker uses the following search order to resolve references:

  • The process, group, and system logical name tables (in that order) are searched for the name LNK$LIBRARY. If the logical name exists in any of these tables and if it contains the desired reference, the search is ended.

  • The process, group, and system logical name tables (in that order) are searched for the name LNK$LIBRARY_1. If the logical name exists in any of these tables, and if it contains the desired reference, the search is ended.

Note

The /INCLUDE qualifier on the LINK command is not to be confused with the /INCLUDE qualifier on the COBOL compile command, which specifies a search list for COPY files.

This search sequence occurs for each reference that remains unresolved.

System-Supplied Object Module Libraries

All VSI COBOL programs reference system-supplied object module libraries when they are linked. These libraries contain routines that provide I/O and other system functions. Additionally, you can use your own libraries to provide application-specific object modules.

To use the contents of an object module library, you must do the following:

  • Refer to a symbol in the object module by name in your program in a CALL statement or VALUE EXTERNAL reference.

  • Make sure that the linker can locate the library that contains the object module by ensuring that required software is correctly installed.

  • Make sure that your default directory (or LINK/EXE directory) is valid and that you have write privileges to it.

To specify that a linker input file is a library file, use the /LIBRARY qualifier. This qualifier causes the linker to search for a file with the name you specify and the default file type .OLB. If you specify a file that the linker cannot locate, a fatal error occurs and linking terminates.

The sections that follow describe the order in which the linker searches libraries that you specify explicitly, default user libraries, and system libraries.

For more information about object module libraries, refer to the VSI OpenVMS Linker Utility Manual.

Defining the Search Order for Libraries

When you specify libraries as input for the linker, you can specify as many as you want; there is no practical limit. More than one library can contain a definition for the same module name. The linker uses the following conventions to search libraries specified in the command string:

  • A library is searched only for definitions that are unresolved in the previously specified input files.

  • If you specified more than one object module library, the libraries are searched in the order in which they are specified.

For example:

$ LINK METRIC,DEFLIB/LIBRARY,APPLIC

The library DEFLIB will be searched only for unresolved references in the object module METRIC. It is not searched to resolve references in the object module APPLIC. However, this command can also be entered as follows:

$ LINK METRIC,APPLIC,DEFLIB/LIBRARY

In this case, DEFLIB.OLB is searched for all references that are not resolved between METRIC and APPLIC. After the linker has searched all libraries specified in the command, it searches default user libraries, if any, and then the default system libraries.

1.2.3.5. Creating Shareable Images

You can create VSI COBOL subprograms as shareable images by using the LINK qualifier /SHARE. A shareable image is a single copy of a subprogram that can be shared by many users or applications. Using shareable images provides the following benefits:

  • Saves system resources, since one physical copy of a set of procedures can be shared by more than one application or user

  • Facilitates the linking of very large applications by allowing you to break down the whole application into manageable segments

  • Allows you to modify one or more sections of a large application without having to relink the entire program

The following steps describe one way to create an VSI COBOL subprogram as a shareable image:

  1. Create the main program used to call the subprogram.

  2. Create the subprogram.

  3. Link the subprogram as a shareable image by using the /SHARE qualifier and including the options file containing the symbol vector in the LINK command as an input file. (See the sections the section called “Using Symbol Vectors with Shareable Images (Alpha, I64)” for information about vectors.)

  4. Define a logical name to point to your shareable image.

  5. Install the shareable image subprogram, using the OpenVMS Install utility (INSTALL).

  6. Link the main program with the shareable image.

Once you have completed these steps, you can run the main program to access the subprogram installed as a shareable image.

Refer to the VSI OpenVMS Linker Utility Manual and the Guide to Creating OpenVMS Modular Procedures for more information about shareable images.

The following sample programs and command procedures provide an example of how to create and link a subprogram as a shareable image, as described in the preceding steps.

Note

Do not use the /SHARE qualifier when you link a main program. Creating a main program as a shareable image is unsupported.

Example 1.2 shows the main program CALLER.COB and the two subprograms (SUBSHR1.COB and SUBSHR2.COB). Only the subprograms are shareable images.

Example 1.2. Main Program and Subprograms
* CALLER.COB
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLER.
******************************************************************
* This program calls a subprogram installed as a shareable image.*
******************************************************************
PROCEDURE DIVISION.
0.
     CALL "SUBSHR1"
         ON EXCEPTION
             DISPLAY "First CALL failed. Program aborted."
     END-CALL.
     STOP RUN.
END PROGRAM CALLER.

* SUBSHR1.COB
IDENTIFICATION DIVISION.
PROGRAM-ID. SUBSHR1.

******************************************************************
* This subprogram is linked as a shareable image. When it is called,*
* it calls another subprogram installed as a shareable image.       *
******************************************************************
PROCEDURE DIVISION.
0.
     DISPLAY "Call to SUBSHR1 successful. Calling SUBSHR2.".
     CALL "SUBSHR2"
         ON EXCEPTION
             DISPLAY "Second call failed. Control returned to CALLER."
     END-CALL.
END PROGRAM SUBSHR1.


* SUBSHR2.COB
IDENTIFICATION DIVISION.
PROGRAM-ID. SUBSHR2.
****************************************************************
* This subprogram is linked as a shareable image and is called by *
* another shareable image.                                     *
****************************************************************
PROCEDURE DIVISION.
0.
     DISPLAY "Call to SUBSHR2 successful!".
END PROGRAM SUBSHR2.

Example 1.3 shows a command procedure that compiles and links the sample program and subprograms in Example 1.2 on an OpenVMS Alpha and I64 systems.

Example 1.3. Command Procedure to Compile and Link Subprograms as Shareable Images (Alpha, I64)
$! Create the main program and subprograms.
$! In this example CALLER.COB is the main program.
$! SUBSHR1.COB and SUBSHR2.COB are the subprograms to be installed
$! as shareable images.
$!
$! Compile the main program and subprograms.
$!
$  COBOL CALLER.COB
$  COBOL SUBSHR1.COB
$  COBOL SUBSHR2.COB
$!
$! Create an options file containing all the universal symbols
$! (entry points and other data symbols) for the subprograms.
$!
$  COPY SYS$INPUT OPTIONS1.OPT
$  DECK
   SYMBOL_VECTOR=(SUBSHR1=PROCEDURE,SUBSHR2=PROCEDURE)
$  EOD
$!
$! Link the subprograms using the /SHARE qualifier to the
$! shareable library and the options file.  For more information
$! on options files, refer to the VSI OpenVMS Linker Utility Manual.
$!
$  LINK/SHARE=MYSHRLIB SUBSHR1,SUBSHR2,OPTIONS1/OPT
$!
$! Assign a logical name for the shareable images.
$!
$  ASSIGN DEVICE:[DIRECTORY]MYSHRLIB.EXE MYSHRLIB
$!
$! Create a second options file to map the main program to the
$! shareable image library.
$!
$  COPY SYS$INPUT OPTIONS2.OPT
$  DECK
   MYSHRLIB/SHAREABLE
$  EOD
$!
$! Link the main program with the shareable image subprograms
$! through the options file.
$!
$  LINK CALLER,OPTIONS2/OPT
$!
$! Now you can run the main program.
Using Symbol Vectors with Shareable Images (Alpha, I64)

To make symbols in the shareable image available for other modules to link against, you must declare the symbols as universal. You declare universal symbols by creating a symbol vector. You create a symbol vector by specifying the SYMBOL_VECTOR=option clause in a linker options file. List all of the symbols you want to be universal in the order in which you want them to appear in the symbol vector.

If you use symbol vectors, you can modify the contents of shareable images and avoid relinking user programs bound to the shareable image when you modify the image. Once you have created the symbol vector, you can install the subprograms using the OpenVMS Install utility (INSTALL) and link the main program to the shareable library. Symbol vectors, if used according to the coding conventions, can also provide upward compatibility.

For more information about symbol vectors, refer to the VSI OpenVMS Linker Utility Manual.

For more information on transfer vectors, refer to the documentation on the OpenVMS Linker.

1.2.3.6. Interpreting Messages from the Linker

If the linker detects any errors while linking object modules, it displays system messages indicating their cause and severity. If any error or fatal error conditions occur, the linker does not produce an image file. Refer to the VSI OpenVMS Linker Utility Manual for complete information about the format of linker options.

Linker messages are self-explanatory; you do not usually need additional information to determine the specific error.

Common Linking Errors to Avoid

The following are some common errors to avoid when linking COBOL programs:

  • Trying to link a module that produced warning or error messages during compilation. Although you can usually link compiled modules for which the compiler generated system messages, you should verify that the modules actually produce the expected output during program execution.

  • Forgetting to specify a file type for an input file that has a file type other than the default on the command line. The linker searches for a file that has a file type .OBJ by default. When the linker cannot locate an object file and you have not identified your input file with the appropriate file type, the linker signals an error message and does not produce an image file.

  • Trying to link a nonexistent module. The linker signals an error message if you misspell a module name on the command line or if the compilation contains fatal messages.

  • Omitting required module or library names from the command line. The linker cannot locate the definition for a specified global symbol reference.

    Consider, for example, the following LINK command for a main program module, OCEAN.OBJ, that calls the subprograms REEF, SHELLS, and SEAWEED:

    $ LINK OCEAN,REEF,SHELLS

    If the routine SEAWEED.OBJ does not exist in the directory from which the command is issued, an error occurs and the linker issues the following diagnostic messages:

    %LINK-W-NUDFSYMS, 1 undefined symbol
    %LINK-I-UDFSYMS,        SEAWEED
    %LINK-W-USEUNDEF, undefined symbol SEAWEED referenced
            in psect $CODE offset %X0000000C
            in module OCEAN file DEVICE$:[COBOL.EXAMPLES]PROG.OBJ;1
    %LINK-W-USEUNDEF, undefined symbol SEAWEED referenced
            in psect $CODE offset %X00000021
            in module OCEAN file DEVICE$:[COBOL.EXAMPLES]PROG.OBJ;1

    If an error occurs when you link modules, you can often correct it by reentering the command string and specifying the correct modules or libraries. For a complete list of linker options, refer to the VSI OpenVMS Linker Utility Manual. For further information on a particular linker message, refer to the online OpenVMS Help Message utility.

1.2.4. Running a VSI COBOL Program

After you compile and link your program, use the RUN command to execute it. In its simplest form the RUN command has the following format:

$ RUN myprog

In the preceding example MYPROG.EXE is the file specification of the image you want to run. If you omit the file type from the file specification, the system automatically provides a default value. The default file type is .EXE. If you omit a path specification, the system will expect MYPROG.EXE to be in the current directory.

When you run your application it makes calls to the VSI COBOL Run-Time Library (RTL) installed on your system. If your application is run on a system other than the one where the application was compiled, there are two requirements that must be met:
  • The VSI COBOL Run-Time Library must be installed.

  • The RTL version must match (or be higher than) the version of the RTL on the system where the application was compiled. Otherwise, the system displays a diagnostic message each time you run the application.

1.2.4.1. Accessing Command-Line Arguments at Run Time (Alpha, I64)

Your VSI COBOL programs can read command-line arguments and access (read and write) system logicals. Command-line arguments enable you to provide information to a program at run time. Your program provides the logic to parse the command line, identify command-line options, and act upon them. For example, you might develop a program named MYPROG that will extract a given amount of data from a specified file, where both the number of records to read and the file name are highly dynamic, changing for each activation of your program. In this case your program would contain code that reads a command-line argument for the number of records to read and a second argument for the file specification.

To run the program with command-line arguments, you must define it as a foreign command, as follows:

$ MYPROG :== "$device:[dir]MYPROG.EXE"

When you use this command, you will replace device and dir with the valid device:[dir] names where MYPROG.EXE is located. Your program execution command could then look like the following:

$ MYPROG 1028 POWERS.DAT

In this hypothetical case, the program MYPROG would read 1,028 records from the file POWERS.DAT.

Multiple command-line arguments are delimited by spaces, as shown in the preceding example. If an argument itself contains spaces, enclose that argument in quotation marks (" ") as follows:

$ myprog2 "all of this is argument 1" argument2

In this example the returned value of argument1 will be the entire string "all of this is argument1", and argument2 will be simply "argument2".

You provide definitions for the command-line arguments with the SPECIAL-NAMES paragraph in your program's Environment Division, and include ACCEPT and DISPLAY statements in the Procedure Division to parse the command line and access the arguments. Detailed information about command-line argument capability is in the ACCEPT and DISPLAY sections in the VSI COBOL Reference Manual.

1.2.4.2. Accessing System Logicals at Run Time (Alpha, I64)

You can read and write system logicals at run time through your VSI COBOL program.

Example 1.4 allows the user to specify a file specification by putting the directory in the value of the logical COBOLPATH and the file name in a command-line argument.

Example 1.4. Accessing Logicals and Command-Line Arguments (Alpha, I64)
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
    SYSERR              IS STANDARD-ERROR
    ENVIRONMENT-NAME    IS NAME-OF-LOGICAL
    ENVIRONMENT-VALUE   IS LOGICAL-VALUE
    ARGUMENT-NUMBER     IS POS-OF-COMMAND-LINE-ARGUMENT
    ARGUMENT-VALUE      IS COMMAND-LINE-ARGUMENT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 howmany-records PIC 9(5).
01 env-dir PIC x(50).
01 file-name PIC x(50).
01 file-spec PIC x(100).
PROCEDURE DIVISION.
BEGIN.
    ACCEPT howmany-records FROM COMMAND-LINE-ARGUMENT
      ON EXCEPTION
        DISPLAY "No arguments specified"
          UPON STANDARD-ERROR
        STOP RUN
    END-ACCEPT.

    DISPLAY "COBOLPATH" UPON NAME-OF-LOGICAL.
    ACCEPT env-dir FROM LOGICAL-VALUE
      ON EXCEPTION
        DISPLAY "Logical COBOLPATH is not set"
          UPON STANDARD-ERROR
        END-DISPLAY
                   NOT ON EXCEPTION
        ACCEPT file-name FROM COMMAND-LINE-ARGUMENT
          ON EXCEPTION
            DISPLAY
            "Attempt to read beyond end of command line"
              UPON STANDARD-ERROR
            END-DISPLAY
          NOT ON EXCEPTION
            STRING env-dir file-name delimited by " " into file-spec
            DISPLAY "Would have read " howmany-records " records from " file-spec
        END-ACCEPT
    END-ACCEPT.

Example 1.4 assumes that the logical COBOLPATH is set as follows:

$ define COBOLPATH MYDEV:[MYDIR]

When you execute the following command line:

$ MYPROG 1028 powers.dat

The following will result:

  • howmany-records will contain 1028.

  • file-path will contain MYDEV:[MYDIR]

  • file-name will contain powers.dat

  • file-spec will contain MYDEF:[MYDIR]powers.dat

For additional information, refer to the ACCEPT and DISPLAY statements in the VSI COBOL Reference Manual.

1.2.4.3. Accessing Input and Output Devices at Run Time

ACCEPT and DISPLAY statements may interact with the input and output devices by referring to them through the environment variables COBOL_INPUT and COBOL_OUTPUT, respectively. See Chapter 11 for more information.

1.2.4.4. Debugging Environment

Perhaps the most common qualifier added to the RUN command line is DEBUG. The form of the RUN command with DEBUG is as follows:

RUN [/[NO]DEBUG] file-spec

In the preceding syntax format, file-spec is the name of the executable image to be run. A typical example would be:

$ RUN /DEBUG MYPROG

In this example, MYPROG is the name of the executable image to be run. You would specify the /DEBUG qualifier to invoke the OpenVMS Debugger if the image was not linked with it. You cannot use /DEBUG on images linked with the /NOTRACEBACK qualifier. If the image (in this case, MYPROG) was linked with the /DEBUG qualifier and you do not want the debugger to prompt you, use the /NODEBUG qualifier. The default action depends on whether or not the file was linked with the /DEBUG qualifier.

Note

Using the /DEBUG qualifier with the RUN command does not produce symbol table information if you did not specify the /DEBUG qualifier when you compiled and linked your program.

The following example executes the image MYPROG.EXE without invoking the debugger:

$ RUN MYPROG/NODEBUG

See Appendix C for more information about debugging programs.

1.2.4.5. Interpreting Run-Time Messages

During execution, an image can generate a fatal error called an exception condition. When an exception condition occurs, the system displays a message. Run-time messages can also be issued by the OpenVMS system or by other utilities such as SORT. Other kinds of errors that can occur at run time include program run errors and run-time input/output errors.

Run-time messages have the following format:

%COB-s-ident, message-text

%COB

The program name of the VSI COBOL Run-Time Library. This prefix indicates a run-time message.

s

The severity of the error. As with messages from the compiler and the linker, the severity indicator can be F (Fatal), E (Error), W (Warning), or I (Informational).

ident

The message identification. This is a descriptive abbreviation of the message text.

message-text

The run-time message. This portion may contain more than one line of output. A message generally provides you with enough information to determine the cause of the error so that you can correct it.

The following example shows a run-time message issued for an illegal divide:

%COB-E-DIVBY-ZER, divide by zero; execution continues

Both the compiler and the OpenVMS Run-Time Library include facilities for detecting and reporting errors. You can use the OpenVMS Debugger and the traceback facility to help you locate errors that occur during program execution. For a description of VSI COBOL run-time messages, use the HELP COBOL Run-Time Messages command.

Run-Time Messages

Faulty program logic can cause abnormal termination. If errors occur at run time, the Run-Time Library (RTL) displays a message with the same general format as system error messages. In addition, the system traceback facility displays a list of the routines that were active when the error occurred.

When an error occurs, TRACEBACK produces a symbolic dump of the active call frames. A call frame represents one execution of a routine. For each call frame, TRACEBACK displays the following information:

  1. The module name (program-id)

  2. The routine name (program-id)

  3. The source listing line number where the error or CALL occurred

  4. Program-counter (PC) information

You can also use the OpenVMS Debugger to examine the machine code instruction. To do this, compile and link the program using the /DEBUG qualifier. When you run the program, you automatically enter the debugger. Once in the debugger, you could use the EXAMINE/INSTRUCTION command to examine the contents of the failed instruction. You could also use the debugger in screen mode, which would indicate where the error occurred.

For more information about the OpenVMS Debugger, refer to Appendix C and the VSI OpenVMS Debugger Manual.

1.3.  VSI COBOL, Alpha and I64 Architectures System Resources

For many user applications, the VSI COBOL compiler requires significantly more system resources than VSI COBOL. In fact, unless you have adjusted your system resource parameters accordingly, the attempt to compile may fail because of insufficient virtual memory. Also, for very large programs (greater than 10,000 lines), you might experience extremely long compile times. Knowing why VSI COBOL requires more memory can help you take actions to avoid resource problems.

1.3.1. Compilation Performance

The Alpha and I64 architecture is a RISC (reduced instruction set computer) architecture. Many other processor architectures are CISC (complex instruction set computer) architectures. The main distinguishing characteristic of a RISC machine is that it has few instructions and each instruction does a small amount of work. A CISC machine generally has many instructions, most of which perform many complicated operations in one step.

By reducing the amount of work that is done in each instruction (and by reducing the number of instructions), the complexity of the hardware is reduced. These hardware changes, plus others, result in an increase in the number of instructions per second that can be completed. The result is much faster overall system performance.

A tradeoff of RISC systems is that compilers for these architectures generally must do a great deal more work than a corresponding compiler for a CISC architecture. For example, the compiler must compute the best way to use all of the functional units of the processor, and it must determine how to make the best use of registers and on-chip data cache because reads and writes to main memory are generally slow compared to the speed of the processor.

On the other hand, the VSI COBOL compiler was developed for the Alpha and I64 architectures. It is a globally optimizing compiler based on the most recent compiler technology. It does many optimizations including Peephole, loop unrolling, and instruction pipelining. Also, the compiler uses mathematical graph theory to construct an internal representation of the entire COBOL program, and it repeatedly traverses this structure at compile time, to produce the most efficient machine code for the program. This results in very high performance code, to the benefit of your users at run time. Although the VSI COBOL compiler on OpenVMS Alpha and I64 requires more resources than some other compilers to do this additional work at compile time, this cost is offset by better performance during the many run times that follow.

To reduce the impact on system resources at compile time, do the following:
  • Use /NOOPTIMIZE on the compile command line when initially developing and testing programs. The optimizer is one of the heaviest users of system resources in the COBOL compiler and is turned on by default. Also, the higher the optimization level, the more memory required by the compiler.

  • Check system tuning. Because the VSI COBOL compiler often needs a great deal of virtual memory, you may need to increase virtual memory for developers who use the compiler. This results in decreased paging and improvements in compile time.

  • Check program sizes. Larger amounts of system resources are used during compilation for large monolithic source files. It is possible that your application is already composed of several separately compiled program units (different PROGRAM IDs not nested), but all in the same .COB. On Alpha and I64 systems with VSI COBOL, compilation performance improves if you split the program units into separate (smaller) .COB files (possibly one for each separately compiled program unit).


Note

Large arrays (tables) can have a significant impact on compile time and resource requirements. In addition to the size of the program source, you should also examine the amount of space allocated in your Data Division, particularly for arrays. The number of array elements as well as the size of the array elements is significant. This impact can be minimized in two ways: by system tuning (as suggested in this section), which will optimize system resources for the compile, and by using INITIALIZE instead of VALUE in your data definitions, which will improve compilation performance.

1.3.2. Tuning OpenVMS Alpha and OpenVMS I64 for Large VSI COBOL Compiles

The recommendations that follow were determined by compiling one set of very large VSI COBOL modules on OpenVMS Alpha and I64. While your results may vary, the principles are generally applicable. For more detailed information on OpenVMS Alpha and I64 tuning, refer to the VSI OpenVMS System Manager's Manual.

Note that many tuning exercises are more beneficial if you work with a relatively quiet system, submit batch jobs, and retain the log files for later analysis.

1.3.2.1. Optimizing Virtual Memory Usage

If your system does not have enough virtual memory allocated, the compile may fail, with the "%LIB-E-INSVIRMEM, insufficient virtual memory" error reported.

OpenVMS has two parameters that control the amount of virtual memory available to a process. One is the system generation parameter VIRTUALPAGECNT, which sets an upper bound on the number of pagelets of virtual memory for any process in the system. The other control is the AUTHORIZE parameter PGFLQUOTA, which determines the number of pagelets a process can reserve in the system's page file(s).

After an "insufficient virtual memory" error, you can issue the DCL command $SHOW PROCESS/ACCOUNTING to see the "Peak virtual size" used by the process (or look at the "Peak page file size" at the end of a batch job log file). If the peak size is at the system generation parameter VIRTUALPAGECNT, you will need to raise this value. If the peak size is below VIRTUALPAGECNT, and at or above PGFLQUOTA, run AUTHORIZE to increase PGFLQUOTA for the COBOL users. (Peak size can exceed PGFLQUOTA because some virtual memory, such as read-only image code, is not allocated page file space.)

It is difficult to predict precisely how much virtual memory will be required for a compilation, but a starting point for system tuning may be computed by multiplying 250 times the size of the largest program in disk blocks (including all COPY files referenced). Alternatively, multiply 25 times the number of lines in the program (including all COPY files).

The resulting figure can then be used as a starting point for the system generation parameter VIRTUALPAGECNT. Put that figure in the parameter file SYS$SYSTEM:MODPARAMS.DAT. For example, if you estimate 370,000 pages, add the following line in MODPARAMS, run AUTOGEN and reboot:

MIN_VIRTUALPAGECNT = 400000

If the compilation now completes successfully, use the command $SHOW PROCESS/ACCOUNTING to determine the Peak Virtual Size; if the actual peak is significantly less than the value computed above, you can reduce VIRTUALPAGECNT.

When modifying VIRTUALPAGECNT and PGFLQUOTA, you may also need to increase the size of the page file.

1.3.2.2. Optimizing Physical Memory Usage

In any evaluation of your system's physical memory, two of the questions to consider are:

  • "Is there enough memory on the system?"
  • "Is enough available to the process running the compilation?"

More specifically:

  • If the physical memory on the system is too small, the command $LOGOUT/FULL (which is automatically issued at the end of a batch job) will show a high number of faults (>100,000 for a single compilation) and an elapsed time value that greatly exceeds the Charged CPU time value, as the system waits for disk I/Os to resolve page faults. In this situation, tuning attempts may be of limited benefit.

  • If the physical memory on the system is adequate, but the physical memory allotted to the process running the compilation is too small, you may still observe a large number of faults, but elapsed time may remain closer to CPU time. This is because OpenVMS Alpha and OpenVMS I64 resolve page faults from the page caches (free list, modified list) whenever possible, avoiding the relatively slow disk I/Os. In this situation, basic tuning may also be beneficial.

The amount of physical memory required will vary, but it should be a large percentage of the process peak virtual size---as close to 100% as practical. The reason is that the compiler makes multiple passes over the internal representation of the program. A page that falls out of the working set in one pass is probably going to be needed again on the very next pass.

The physical memory present on the system can be determined by the DCL command $SHOW MEMORY/PHYSICAL. The physical memory used by the compilation is reported as "Peak working set size" by the command SHOW PROCESS/ACCOUNTING or at the end of a batch log file.

More physical memory can be made available to a process by minimizing the number of competing processes on the system (for example, by compiling one module at a time or by scheduling large compiles for off-peak time periods; late at night is a good time in some situations).

More physical memory can also be made available to a process (if it is present on the machine) by adjusting the system generation parameter WSMAX and the corresponding WSEXTENT (in AUTHORIZE). Approach such adjustments with great caution, as the system may hang if memory is oversubscribed and you create a situation where OpenVMS Alpha and OpenVMS I64 effectively have no options to reclaim memory. The following guidelines can help:

  • Set the COBOL user WSEXTENT (in AUTHORIZE or INITIALIZE/QUEUE) to match WSMAX.

  • Keep WSQUOTA (in AUTHORIZE or INITIALIZE/QUEUE) low. Make sure that no process or batch queue has a WSQUOTA of more than approximately 20% of physical memory. The difference between WSEXTENT and WSQUOTA allows the operating system to manage memory to meet varying demands.

  • Use AUTOGEN. AUTOGEN will attempt to make a consistent set of changes that do not interfere with each other.

    By default, AUTOGEN will set the maximum working set (system generation parameter WSMAX) to 25% of physical memory. This value is reasonable for a workstation or multi-user system with many active processes.

    WSMAX can be increased to a somewhat larger value by editing MODPARAMS.DAT. For a system with 64 MB1 of physical memory, set WSMAX to no more than approximately 40% of physical memory, or 52000 pagelets (1 MB = 2048 pagelets). With 128 MB or more of physical memory, a setting of 50% of physical memory can be attempted.

The effects of physical memory on compilation time were studied for a set of seven large modules. These modules ranged in size from approximately 1600 to 3300 disk blocks. Your results may differ, but to give a rough appreciation for the effect of physical memory on compilation time, note that:

  • When the amount of physical memory available to the processes matched the amount of virtual memory, the elapsed times were close to the CPU times.

  • As the physical memory was reduced, CPU times rose only slightly―approximately 10%.

  • As the physical memory was reduced, elapsed times were elongated, at the rate of approximately 1 hour for each 100 MB of difference between Peak Virtual Size and the actual memory available. For example, when compiling a program that used a Peak Virtual Size of 947760 pagelets, or 463 MB, on a system where approximately 180 MB of physical memory was available to user processes, the compile required approximately 3 hours more than on a 512 MB system.

Your results may differ from those shown in this section and will be strongly affected by the speed of the devices that are used for paging.

Note that the requirements for virtual memory and physical memory can also be reduced by breaking large modules into smaller modules.

1.3.2.3. Improving Compile Performance with Separate Compilation (Alpha, I64)

The /SEPARATE_COMPILATION qualifier can improve compile-time performance for large source files that are made up of multiple separately compiled programs (SCPs). For programs compiled without this qualifier, the compiler engine parses the entire compilation unit and uses system resources (sized for the total job) for the duration of this compilation. When you use the /SEPARATE_COMPILATION qualifier, the compilation is replaced by a smaller series of operations, and memory structures that are needed for individual procedures are reclaimed and recycled. See Section 1.2.2.4 for additional information.

1.3.3. Choosing a Reference Format

You need to choose a reference format before you set out to write a VSI COBOL program, and you must be aware of the format at compile time. The VSI COBOL compiler accepts source code written in either terminal or ANSI reference format. You cannot mix reference formats in the same source file.

On OpenVMS, when copying text from Oracle CDD/Repository, the VSI COBOL compiler translates the record descriptions into the reference format of the source program.

1.3.3.1. Terminal Reference Format

VSI recommends using terminal format, a VSI optional format, when you create source files from interactive terminals. The compiler accepts terminal format as the default reference format.

Terminal format eliminates the line number and identification fields of ANSI format and allows horizontal tab characters and short lines. Terminal format saves disk space and decreases compile time. It is easier to edit source code written in terminal format.

The following table shows the structure and content of a terminal reference source line: To select ANSI format, specify at compile time. You can choose this format if your COBOL program is written for a compiler that uses ANSI format.

For ANSI format, the compiler expects 80-character program lines. The following table shows the structure and content of an ANSI reference source line:

Character Positions

Contents

1 to 6

Optional sequence numbers

7

Indicators

8 to 11

Area A

12 to 72

Area B

73 to 80

Optional Area

For more information about the two reference formats, refer to the VSI COBOL Reference Manual.

1.3.3.2. Converting Between Reference Formats

The REFORMAT utility allows you to convert a terminal format program to ANSI format and vice versa. You can also use REFORMAT to match the formats of VSI COBOL source files and library files when their formats are not the same. See Chapter 14 for a description of the REFORMAT utility.

1.4. Program Run Messages

Incorrect or undesirable program results are usually caused by data errors or program logic errors. You can resolve most of these errors by desk-checking your program and by using a debugger.

1.4.1. Data Errors

Faulty or incorrectly defined data often produce incorrect results. Data errors can sometimes be attributed to one or more of the following actions:
  • Incorrect picture size. As shown in the following sample of a partial program, if the picture size of a receiving data item is too small, your data may be truncated:
    77   COUNTER   PIC S9.
         .
         .
         .
    PROCEDURE DIVISION.
         .
         .
         .
     LOOP.
         ADD 1 TO COUNTER
         IF COUNTER < 10 GO TO LOOP.

    The IF clause will produce an infinite loop because of the one-digit size limit of COUNTER, which is PIC S9. If COUNTER were PIC S99, or if the clause used 9 instead of 10, the condition could be false, causing a proper exit from the loop.

  • Incorrect record field position. The record field positions that you specify in your program may not agree with a file's record field positions. For example, a file could have this record description:
    01  PAY-RECORD.
        03  P-NUMBER       PIC X(5).
        03  P-WEEKLY-AMT   PIC S9(5)V99  COMP-3.
        03  P-MONTHLY-AMT  PIC S9(5)V99  COMP-3.
        03  P-YEARLY-AMT   PIC S9(5)V99  COMP-3.
            .
            .
            .

    Incorrectly positioning these fields can produce faulty data.

In the following example, a program references the file incorrectly. The field described as P-YEARLY-AMT actually contains P-MONTHLY-AMT data, and vice versa.
01  PAY-RECORD.
    03  P-NUMBER       PIC X(5).
    03  P-WEEKLY-AMT   PIC S9(5)V99  COMP-3.
    03  P-YEARLY-AMT   PIC S9(5)V99  COMP-3.
    03  P-MONTHLY-AMT  PIC S9(5)V99  COMP-3.
        .
        .
        .
PROCEDURE DIVISION.
ADD-TOTALS.
    ADD P-MONTHLY-AMT TO TOTAL-MONTHLY-AMT.
        .
        .
        .

You can minimize record field position errors by writing your file and record descriptions in a library file and then using the COPY statement in your programs. On OpenVMS systems, you can also use the COPY FROM DICTIONARY statement.

Choosing your test data carefully can minimize faulty data problems. For instance, rather than using actual or ideal data, use test files that include data extremes.

Determining when a program produces incorrect results can often help your debugging effort. You can do this by maintaining audit counts (such as total master in = nnn, total transactions in = nnn, total deletions = nnn, total master out = nnn) and displaying the audit counts when the program ends. Using conditional compilation lines (see Section 1.2.2.7) in your program can also help you to debug it.

1.4.2. Program Logic Errors

When checking your program for logic errors, first examine your program for some of the more obvious bugs, such as the following:
  • Hidden periods. Periods inadvertently placed in a statement usually produce unexpected results. For example:
    050-DO-WEEKLY-TOTALS.
        IF W-CODE = "W"
           PERFORM 100-WEEKLY-SUMMARY
           ADD WEEKLY-AMT TO WEEKLY-TOTALS.
           GO TO 000-READ-A-MASTER.
        WRITE NEW-MASTER-REC.

    The period at the end of ADD WEEKLY-AMT TO WEEKLY-TOTALS terminates the scope of the IF statement and changes the logic of the program. Including the extra period before the GO TO statement transforms GO TO 000-READ-A-MASTER from a conditional statement to an unconditional statement. Because the GO TO statement is not within the scope of the IF statement, it will always be executed. In addition, the WRITE statement following the GO TO will never be executed.

  • Tests for equality, which can cause an infinite loop if the procedure is to be executed until the test condition is met, for example:
    * This is a test for equality
    PERFORM ABC-ROUTINE UNTIL A-COUNTER = 10.
    If, during execution, the program increments A-COUNTER by a value other than 1 (2 or 1.5, for example), A-COUNTER may never equal 10, causing a loop in ABC-ROUTINE. You can prevent this type of error by changing the statement to something like this:
    * This is a test for inequality
    PERFORM ABC-ROUTINE UNTIL A-COUNTER > 9
  • Testing two floating point numbers (for example, COMP-1 and COMP-2 fields) for equality. The calculations of your program might never produce exact numerical equality between two floating point values.

  • Two negative test conditions combined with an OR. The object of the following statement is to execute GO TO 200-PRINT-REPORT when TEST-FIELD contains other than an A or B. However, the GO TO always executes because no matter what TEST-FIELD contains, one of the conditions is always true.
    IF TEST-FIELD NOT = "A" OR NOT = "B"
       GO TO 200-PRINT-REPORT.
       .
       .
       .
    The following statement does not contain the logic error:
    IF TEST-FIELD NOT = "A" AND NOT = "B"
       GO TO 200-PRINT-REPORT.
       .
       .
       .

1.4.3. Run-Time Input/Output Errors

An input/output error is a condition that causes an I/O statement to fail. These I/O errors are detected at run time by the I/O system. Each time an I/O operation occurs, the I/O system generates a two-character file status value. One way to determine the nature of an I/O error is to check a file's I/O status by using file status data items. (Refer to the VSI COBOL Reference Manual for a list of file status values.) See Chapter 7: Handling Input/Output Exception Conditions for additional information about I/O exception condition handling.

Checking a file's I/O status within a Declarative USE procedure or in an INVALID KEY imperative condition can help you determine the nature of an I/O error. For example:
FD  INDEXED-MASTER
    ACCESS MODE IS DYNAMIC
    FILE STATUS IS MASTER-STATUS
    RECORD KEY IN IND-KEY.
  .
  .
  .
WORKING-STORAGE SECTION.
01  MASTER-STATUS      PIC XX  VALUE SPACES.
  .
  .
  .
PROCEDURE DIVISION.
  .
  .
  .
050-READ-MASTER.
    READ INDEXED-MASTER
      INVALID KEY PERFORM 100-CHECK-STATUS
      GO TO 200-INVALID-READ.
      .
      .
      .
100-CHECK-STATUS.
    IF MASTER-STATUS = "23"
       DISPLAY "RECORD NOT IN FILE".
    IF MASTER-STATUS = "24"
       DISPLAY "BOUNDARY VIOLATION OR RELATIVE RECORD
       NUMBER TOO LARGE".
      .
      .
      .

If your program contains a Declarative USE procedure for a file and an I/O operation for that file fails, the I/O system performs the USE procedure, but does not display an error message.

A Declarative USE procedure can sometimes avoid program termination. For example, File Status 91 indicates that the file is locked by another program; rather than terminate your program, you can perform other procedures and then try reopening the file. If program continuation is not desirable, the Declarative USE procedure can perform housekeeping functions, such as saving data or displaying program-generated diagnostic messages.

If you specify an INVALID KEY phrase for a file and the I/O operation causes an INVALID KEY condition, the I/O system performs the associated imperative statement and no other file processing for the current statement. The Declarative USE procedure (if any) is not performed. The INVALID KEY phrase processes I/O errors due to invalid key conditions only.

If you do not specify an INVALID KEY phrase but declare a Declarative USE procedure for the file, the I/O system performs the Declarative USE procedure and returns control to the program.

If a severe error occurs and you do not have a Declarative Use procedure, your program will terminate abruptly with a run-time diagnostic. For example, given a program that looks for AFILE.DAT and that file is missing:
cobrtl: severe: file AFILE.DAT not found

In this case, program run ends because you have not handled the error with a Declarative Use procedure.

1.4.4. I/O Errors and RMS (OpenVMS)

I/O errors are detected by the I/O system, which (for OpenVMS systems) consists of Record Management Services (RMS) and the Run-Time Library (RTL). You can use the RMS special registers, which contain the primary and secondary RMS completion codes of an I/O operation, to detect errors. The RMS special registers are as follows:

RMS-STS

RMS-STV

RMS-FILENAME

RMS-CURRENT-STS

RMS-CURRENT-STV

RMS-CURRENT-FILENAME

Refer to the VSI COBOL Reference Manual and the VSI OpenVMS Record Management Services Reference Manual for more information about RMS special registers.

Examples Example 1.5 and Example 1.6 show how to use RMS special registers to detect errors.

Example 1.5. Using RMS Special Registers to Detect Errors (OpenVMS)
IDENTIFICATION DIVISION.
PROGRAM-ID. RMSSPECREGS.
*
* This program demonstrates the use of RMS special registers to
* implement a different recovery for each of several errors with RMS files.
*
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT OPTIONAL EMP-FILE ASSIGN "SYS$DISK:ART.DAT".
    SELECT REPORT-FILE       ASSIGN "SYS$OUTPUT".
DATA DIVISION.
FILE SECTION.
FD  EMP-FILE VALUE OF ID IS VAL-OF-ID.
01  EMP-RECORD.
    02 EMP-ID     PIC 9(7).
    02 EMP-NAME    PIC X(15).
    02 EMP-ADDRESS PIC X(30).
FD  REPORT-FILE     REPORT IS RPT.
WORKING-STORAGE SECTION.
01  VAL-OF-ID     PIC X(20).
01  RMS$_EOF     PIC S9(9) COMP VALUE EXTERNAL RMS$_EOF.
01  SS$_BADFILENAME PIC S9(9) COMP VALUE EXTERNAL SS$_BADFILENAME.
01  RMS$_FNF     PIC S9(9) COMP VALUE EXTERNAL RMS$_FNF.
01  RMS$_DNF     PIC S9(9) COMP VALUE EXTERNAL RMS$_DNF.
01  RMS$_DEV     PIC S9(9) COMP VALUE EXTERNAL RMS$_DEV.
01  D-DATE     PIC 9(6).
01  EOF-SW     PIC X.
    88 E-O-F  VALUE "E".
    88 NOT-E-O-F VALUE "N".
01  VAL-OP-SW     PIC X.
    88 VALID-OP VALUE "V".
    88 OP-FAILED VALUE "F".
01  OP      PIC X.
    88 OP-OPEN  VALUE "O".
    88 OP-CLOSE VALUE "C".
    88 OP-READ  VALUE "R".
REPORT SECTION.
RD  RPT PAGE 26 LINES HEADING 1 FIRST DETAIL 5.
01  TYPE IS PAGE HEADING.
    02 LINE IS PLUS 1.
 03  COLUMN 1 PIC X(16) VALUE "Emplyee File on".
 03  COLUMN 18 PIC 99/99/99 SOURCE D-DATE.
    02 LINE IS PLUS 2.
 03  COLUMN 2 PIC X(5) VALUE "Empid".
 03  COLUMN 22 PIC X(4) VALUE "Name".
 03  COLUMN 43 PIC X(7) VALUE "Address".
 03  COLUMN 60 PIC X(4) VALUE "Page".
 03  COLUMN 70 PIC ZZ9  SOURCE PAGE-COUNTER.
01  REPORT-LINE TYPE IS DETAIL.
    02 LINE IS PLUS 1.
 03  COLUMN  IS 1    PIC 9(7) SOURCE EMP-ID.
 03  COLUMN  IS 20   PIC X(15) SOURCE IS EMP-NAME.
 03  COLUMN  IS 42   PIC X(30) SOURCE IS EMP-ADDRESS.
PROCEDURE DIVISION.
DECLARATIVES.
USE-SECT SECTION.
    USE AFTER STANDARD ERROR PROCEDURE ON EMP-FILE.
CHECK-RMS-SPECIAL-REGISTERS.
    SET OP-FAILED TO TRUE.
    EVALUATE RMS-STS OF EMP-FILE TRUE
 WHEN (RMS$_EOF)   OP-READ
     SET VALID-OP TO TRUE
     SET E-O-F TO TRUE
 WHEN (SS$_BADFILENAME)  OP-OPEN
 WHEN (RMS$_FNF)   OP-OPEN
 WHEN (RMS$_DNF)   OP-OPEN
 WHEN (RMS$_DEV)   OP-OPEN
     DISPLAY "File cannot be found or file spec is invalid"
     DISPLAY RMS-FILENAME OF EMP-FILE
     DISPLAY "Enter corrected file (control-Z to STOP RUN): "
      WITH NO ADVANCING
     ACCEPT VAL-OF-ID
  AT END STOP RUN
     END-ACCEPT
 WHEN ANY   OP-CLOSE
     CONTINUE
 WHEN ANY   RMS-STS OF EMP-FILE IS SUCCESS
     SET VALID-OP TO TRUE
 WHEN OTHER
     IF RMS-STV OF EMP-FILE NOT = ZERO
     THEN
  CALL "LIB$STOP" USING
      BY VALUE RMS-STS OF EMP-FILE
     END-IF
    END-EVALUATE.
END DECLARATIVES.
MAIN-PROG SECTION.
000-DRIVER.
    PERFORM 100-INITIALIZE.
    PERFORM WITH TEST AFTER UNTIL E-O-F
 GENERATE REPORT-LINE
 READ EMP-FILE
    END-PERFORM.
    PERFORM 200-CLEANUP.
    STOP RUN.
100-INITIALIZE.
    ACCEPT D-DATE FROM DATE.
    DISPLAY "Enter file spec of employee file: " WITH NO ADVANCING.
    ACCEPT VAL-OF-ID.
    PERFORM WITH TEST AFTER UNTIL VALID-OP
 SET VALID-OP TO TRUE
 SET OP-OPEN TO TRUE
 OPEN INPUT EMP-FILE
 IF OP-FAILED
 THEN
     SET OP-CLOSE TO TRUE
     CLOSE EMP-FILE
 END-IF
    END-PERFORM.
    OPEN OUTPUT REPORT-FILE.
    INITIATE RPT.
    SET NOT-E-O-F TO TRUE.
    SET OP-READ TO TRUE.
    READ EMP-FILE.
200-CLEANUP.
    TERMINATE RPT.
    SET OP-CLOSE TO TRUE.
    CLOSE EMP-FILE REPORT-FILE.
END PROGRAM RMSSPECREGS.
Example 1.6. Using RMS-CURRENT Special Registers to Detect Errors (OpenVMS)
IDENTIFICATION DIVISION.
PROGRAM ID. RMS-CURRENT-SPEC-REGISTERS.
*
* This program demonstrates the use of RMS-CURRENT special registers
* to implement a single recovery for RMS file errors with multiple files.
*
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT FILE-1
        ASSIGN TO "SYS$DISK:ART_1.DAT".
SELECT FILE-2
        ASSIGN TO "SYS$DISK:ART_2.DAT".
SELECT FILE-3
        ASSIGN TO "SYS$DISK:ART_3.DAT".
DATA DIVISION.
FILE SECTION.
FD      FILE-1.
01      FILE-1-REC.
        02      F1-REC-FIELD    PIC 9(9).
FD      FILE-2.
01      FILE-2-REC.
        02      F2-REC-FIELD    PIC 9(9).
FD      FILE-3.
01      FILE-3-REC.
        02      F3-REC-FIELD    PIC 9(9).
PROCEDURE DIVISION.
DECLARATIVES.
USE-SECT SECTION.
        USE AFTER STANDARD EXCEPTION PROCEDURE ON INPUT.
CHECK-RMS-CURRENT-REGISTERS.
        DISPLAY "************** ERROR **************".
        DISPLAY "Error on file: " RMS-CURRENT-FILENAME.
        DISPLAY "Status Values:".
        DISPLAY "      RMS-STS = " RMS-CURRENT-STS WITH CONVERSION.
        DISPLAY "      RMS-STV = " RMS-CURRENT-STV WITH CONVERSION.
        DISPLAY "***********************************".
END DECLARATIVES.
MAIN-PROG SECTION.
MAIN-PARA.
        OPEN INPUT FILE-1.
        OPEN INPUT FILE-2.
        OPEN INPUT FILE-3.
        .
        .
        .
        CLOSE FILE-1.
        CLOSE FILE-2.
        CLOSE FILE-3.
        STOP RUN.
END-PROGRAM RMS-CURRENT-SPEC-REGISTERS.

1.5. Using Program Switches

You can control program execution by defining switches in your VSI COBOL program and setting them internally (from within the image) or externally (from outside the image). Switches exist as the environment variable COBOL_SWITCHES (on the UNIX operating system) or the logical name COB$SWITCHES (on the OpenVMS operating system).

On OpenVMS systems, switches can be defined for the image, process, group, or system.

On UNIX systems, switches can be defined for the image or process.

1.5.1. Setting and Controlling Switches Internally

To set switches from within the image, define them in the SPECIAL-NAMES paragraph of the ENVIRONMENT DIVISION and use the SET statement in the PROCEDURE DIVISION to specify switches ON or OFF, as in the following example:
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
    SWITCH 10 IS MY-SWITCH
      ON IS SWITCH-ON
      OFF IS SWITCH-OFF.
    .
    .
    .
PROCEDURE DIVISION.
000-SET-SWITCH.
    SET MY-SWITCH TO ON.
    IF SWITCH-ON
       THEN
    DISPLAY "Switch 10 is on".
    .
    .
    .

On OpenVMS systems, SET in COBOL will attempt to write a user mode logical name (COB$SWITCHES) to the first entry in the LNM$FILE_DEV chain. It will therefore fail if that logical name table denies WRITE access.

To change the status of internal switches during execution, turn them on or off from within your program. However, be aware that this information is not saved between runs of the program.

Refer to the VSI COBOL Reference Manual for more information about setting internal switches.

1.5.2. Setting and Controlling Switches Externally

Switches that are set externally are handled differently on UNIX and OpenVMS, as described in this section.

Switches on UNIX

On UNIX systems, to set switches from outside the image, use the SETENV command to change the status of program switches, as follows:

% setenv COBOL_SWITCHES "switch-list"

To remove switch settings:

% unsetenv COBOL_SWITCHES

To check switch settings, enter this command:

% printenv COBOL_SWITCHES          Shows switch settings.

The switch-list can contain up to 16 switches separated by commas. To set a switch on, specify it in the switch-list. A switch is off (the default) if you do not specify it in the switch-list.

For example:
% setenv COBOL_SWITCHES "1,5,13"   Sets switches 1, 5, and 13 ON.

% setenv COBOL_SWITCHES "9,11,16"  Sets switches 9, 11, and 16 ON.

% setenv COBOL_SWITCHES " "        Sets all switches OFF
Following is a simple program that displays a message depending on the state of the environment variable COBOL_SWITCHES (on UNIX systems):
IDENTIFICATION DIVISION.
PROGRAM-ID. TSW.

ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
    SWITCH 12 IS SW12 ON IS SW12-ON OFF IS SW12-OFF.

PROCEDURE DIVISION.
01-S.
    DISPLAY "**TEST SWITCHES**".
    IF SW12-ON
       DISPLAY "SWITCH 12 IS ON".
    IF SW12-OFF
       DISPLAY "SWITCH 12 IS OFF".

    DISPLAY "**END**".
    STOP RUN.
END PROGRAM TSW.
To test this program on a UNIX system, compile and link it and then type the following:
% setenv COBOL_SWITCHES 12
% tsw
The output is as follows:
**TEST SWITCHES**
SWITCH 12 IS ON
**END**

Switches on OpenVMS

On OpenVMS systems, to set switches from outside the image or for a process, use the DCL DEFINE or ASSIGN command to change the status of program switches as follows:
$ DEFINE COB$SWITCHES "switch-list" 

The switch-list can contain up to 16 switches separated by commas. To set a switch ON, specify it in the switch-list. A switch is OFF (the default) if you do not specify it in the switch-list.

For example:
$ DEFINE COB$SWITCHES "1,5,13"   Sets switches 1, 5, and 13 ON.
$ DEFINE COB$SWITCHES "9,11,16"  Sets switches 9, 11, and 16 ON.
$ DEFINE COB$SWITCHES " "        Sets all switches OFF.

The order of evaluation for logical name assignments is image, process, group, system. System and group assignments (including VSI COBOL program switch settings) continue until they are changed or deassigned. Process assignments continue until they are changed, deassigned, or until the process ends. Image assignments end when they are changed or when the image ends.

You should know the system and group assignments for COB$SWITCHES unless you have defined them for your process or image. To check switch settings, enter this command:
$ SHOW LOGICAL COB$SWITCHES
Use the DCL DEASSIGN command to remove the switch-setting logical name from your process and reactivate the group or system logical name (if any):
$ DEASSIGN COB$SWITCHES
To change the status of external switches during execution, follow these steps:
  1. Interrupt the image with a STOP (literal-string) COBOL statement. (Refer to the VSI COBOL Reference Manual for more information.)

  2. Use the DCL DEFINE command to change switch settings.

  3. Continue execution with the DCL CONTINUE command. Be sure not to force the interrupted image to exit by entering a command that executes another image.

For information about these DCL commands, refer to the VSI OpenVMS DCL Dictionary.

Following is a simple program that displays a message depending on the state of the logical name COB$SWITCHES (on OpenVMS systems):
IDENTIFICATION DIVISION.
PROGRAM-ID. TSW.

ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
    SWITCH 12 IS SW12 ON IS SW12-ON OFF IS SW12-OFF.

PROCEDURE DIVISION.
01-S.
    DISPLAY "**TEST SWITCHES**".
    IF SW12-ON
       DISPLAY "SWITCH 12 IS ON".
    IF SW12-OFF
       DISPLAY "SWITCH 12 IS OFF".

    DISPLAY "**END**".
    STOP RUN.
END PROGRAM TSW.
On OpenVMS, to test the previous program, compile and link it and then type the following:
$ DEFINE COB$SWITCHES 12
$ RUN TSW
The output is as follows:
**TEST SWITCHES**
SWITCH 12 IS ON
**END**

1.6. Special Information for Year 2000 Programming

Even subsequent to the turn of the millennium, there still exist potential disruptions in previously problem-free software where there are instances of a two-digit year field that should be a four-digit field. Programmers need to correct all such fields, as VSI cannot prevent problems that originate in application code.

Two-digit year formats used in controlling fields, or as keys in indexed files, can cause program logic to become ambiguous. It is a fundamental rule to use four-digit years instead of two-digit years in areas where sequential operations are driven from these values or for comparison of these values.

VSI COBOL provides programmer access to four-digit and two-digit year formats:

4-digit

FUNCTION CURRENT-DATE

4-digit

FUNCTION DATE-OF-INTEGER

4-digit

FUNCTION DATE-TO-YYYYMMDD

4-digit

FUNCTION DAY-OF-INTEGER

4-digit

FUNCTION DAY-TO-YYYYDDD

4-digit

FUNCTION INTEGER-OF-DATE

4-digit

FUNCTION INTEGER-OF-DAY

4-digit

FUNCTION TEST-DATE-YYYYMMDD

4-digit

FUNCTION TEST-DAY-YYYYDDD

4-digit

FUNCTION WHEN-COMPILED

4-digit

FUNCTION YEAR-TO-YYYY

2-digit

ACCEPT FROM DATE

2-digit

ACCEPT FROM DAY

4-digit

ACCEPT FROM DATE YYYYMMDD

4-digit

ACCEPT FROM DAY YYYYDDD

VSI COBOL offers date functions that can be used in program logic that makes decisions about year order. The full four-digit year handled by the six functions listed should be used in internal program logic decisions that are based on years. External displays of year information can continue to use two-digit formats when that is appropriate.

You should check program logic in code that uses ACCEPT, to verify that millennium transition dates are properly handled.

The use of two-digit years in applications does not automatically create a problem, but a problem could exist. Programmers need to inspect each of their applications for two-digit year dependencies and change any such instances to check the full four-digit year value.

Chapter 2. Handling Numeric Data

Numeric data in VSI COBOL is evaluated with respect to the algebraic value of the operands.

This chapter describes the following topics concerning numeric data handling:

2.1. How the Compiler Stores Numeric Data

Understanding how data is stored will help you in the following situations:
  • When you define data items to participate in group moves or to be the subject of a REDEFINES clause

  • When you move a complex record consisting of several levels of subordination, to be sure that the receiving item is large enough to prevent data truncation

  • When you need to use data storage concepts to minimize storage space, particularly when the data file is large

The storage considerations applicable to tables are described in Chapter 4.

For each numeric data item, VSI COBOL stores the numeric value, and a sign (if an S appears in the PICTURE clause).

The USAGE clause of a numeric data item specifies the data's internal format in storage. When you do not specify a USAGE clause, the default usage is DISPLAY. For further information about internal representations, refer to the USAGE clause tables in the VSI COBOL Reference Manual.

2.2. Specifying Alignment

In VSI COBOL, all records, and elementary items with level 01 or 77, begin at an address that is a multiple of 8 bytes (a quadword boundary). By default, the VSI COBOL compiler will locate a subordinate data item at the next unassigned byte location. However, the SYNCHRONIZED clause, the -align flag (on UNIX), the /ALIGNMENT qualifier (on OpenVMS Alpha and I64), and alignment directives can be used to modify this behavior, causing some numeric data items to be aligned on a 2-, 4-, or 8-byte boundary. You can thus tune data alignment for optimum performance, compatibility with VSI COBOL, or flexibility. (See Chapter 16 and Chapter 15 in this manual, and refer to the SYNCHRONIZED clause in the VSI COBOL Reference Manual for a complete discussion of alignment.)

2.3. Sign Conventions

VSI COBOL numeric items can be signed or unsigned. Note the following sign conventions:
  • If you store a signed result in an unsigned item, only the absolute value is stored. Thus, unsigned items only contain the value zero or a positive value.

  • The way VSI COBOL stores signed results in signed data items depends on the usage and the presence or absence of the SIGN clause.

  • When an unsigned result is stored in a signed data item, the sign of the stored result is positive.

Do not use unsigned numeric items in arithmetic operations. They usually cause programming errors and are handled less efficiently than signed numeric items. The following example shows how unsigned numeric items can cause errors:
DATA DIVISION
.
.
.
01 A PIC 9(5) COMP VALUE 2.
01 B PIC 9(5) COMP VALUE 5.
Then:
SUBTRACT B FROM A.       (A = 3)
SUBTRACT 1 FROM A.       (A = 2)
However:
COMPUTE A = (A - B) - 1    (A = 4)

The absence of signs for the numeric items A and B results in two different answers after parallel arithmetic operations have been done. This occurs because internal temporaries (required by the COMPUTE statement) are signed. Thus, the result of (A–B) within the COMPUTE statement is –3; –3 minus 1 is –4 and the value of A then becomes 4.

2.4. Invalid Values in Numeric Items

All VSI COBOL arithmetic operations store valid values in their result items. However, it is possible, through group moves or REDEFINES, to store data in numeric items that do not conform to the data definitions of those items.

The results of arithmetic operations that use invalid data in numeric items are undefined. You can use the -check decimal flag (on the UNIX) or the /CHECK=DECIMAL qualifier (on the OpenVMS Alpha or I64 operating systems) to validate numeric digits when using display numeric items in a numeric context; note that this flag or qualifier causes a program to terminate abnormally if there is invalid data. In the case of data with blanks (typically, records in a file), you can use the -convert leading_blanks flag (on UNIX) or the /CONVERT qualifier (on OpenVMS Alpha and I64) to change all blanks to zeroes before performing the arithmetic operation. If you specify both the -check decimal and the -convert leading_blanks flags (on UNIX), or both the /CHECK=DECIMAL and the /CONVERT qualifiers (on OpenVMS Alpha or I64), the conversion of blanks will be done prior to the validation of the resulting numeric digits. Note that the use of either or both of these qualifiers increases the execution time of the program. Refer to VSI COBOL online help (at the OpenVMS Alpha or I64 system prompt), or man cobol (on UNIX) for more information.

2.5. Evaluating Numeric Items

VSI COBOL provides several kinds of conditional expressions used for evaluating numeric items. These conditional expressions include the following:
  • The numeric relation condition that compares the item's contents to another numeric value

  • The sign condition that examines the item's sign to see if it is positive or negative

  • The class condition that inspects the item's digit positions for valid numeric characters

  • The success/failure condition that checks the return status codes of COBOL and non- COBOL procedures for success or failure conditions

The following sections explain these conditional expressions in detail.

2.5.1. Numeric Relation Test

A numeric relation test compares two numeric quantities and determines if the specified relation between them is true. For example, the following statement compares item FIELD1 to item FIELD2 and determines if the numeric value of FIELD1 is greater than the numeric value of FIELD2:
IF FIELD1 > FIELD2 ... 

If the relation condition is true, the program control takes the true path of the statement.

Table 2.1 describes the relational operators.
Table 2.1. Numeric Relational Operator Descriptions

Operator

Description

IS [NOT] GREATER THAN

IS [NOT] >

The first operand is greater than (or not greater than) the second operand.

IS [NOT] LESS THAN

IS [NOT] <

The first operand is less than (or not less than) the second operand.

IS [NOT] EQUAL TO

IS [NOT] =

The first operand is equal to (or not equal to) the second operand.

IS GREATER THAN OR

EQUAL TO

IS >=

The first operand is greater than or equal to the second operand.

IS LESS THAN OR EQUAL TO

IS <=

The first operand is less than or equal to the second operand.

Comparison of two numeric operands is valid regardless of their USAGE clauses.

The length of the literal or arithmetic expression operands (in terms of the number of digits represented) is not significant. Zero is a unique value, regardless of the sign.

Unsigned numeric operands are assumed to be positive for comparison. The results of relation tests involving invalid (nonnumeric) data in a numeric item are undefined.

2.5.2. Numeric Sign Test

The sign test compares a numeric quantity to zero and determines if it is greater than (positive), less than (negative), or equal to zero. Both the relation test and the sign test can perform this function. For example, consider the following relation test:
IF FIELD1 > 0 ...
Now consider the following sign test:
IF FIELD1 POSITIVE ...

Both of these tests accomplish the same thing and always arrive at the same result. The sign test, however, shortens the statement and makes it more obvious that the sign is being tested.

Table 2.2 shows the sign tests and their equivalent relation tests.
Table 2.2. Sign Tests

Sign Test

Equivalent Relation Test

IF FIELD1 POSITIVE ...

IF FIELD1 > 0 ...

IF FIELD1 NOT POSITIVE ...

IF FIELD1 NOT > 0 ...

IF FIELD1 NEGATIVE ...

IF FIELD1 < 0 ...

IF FIELD1 NOT NEGATIVE ...

IF FIELD1 NOT < 0 ...

IF FIELD1 ZERO ...

IF FIELD1 = 0 ...

IF FIELD1 NOT ZERO ...

IF FIELD1 NOT = 0 ...

Sign tests do not execute faster or slower than relation tests because the compiler substitutes the equivalent relation test for every correctly written sign test.

2.5.3. Numeric Class Tests

The class test inspects an item to determine if it contains numeric or alphabetic data. For example, the following statement determines if FIELD1 contains numeric data:
IF FIELD1 IS NUMERIC ...

If the item is numeric, the test condition is true, and program control takes the true path of the statement.

Both relation and sign tests determine only if an item's contents are within a certain range. Therefore, certain items in newly prepared data can pass both the relation and sign tests and still contain data preparation errors.

The NUMERIC class test checks alphanumeric or numeric DISPLAY or COMP-3 usage items for valid numeric digits. If the item being tested contains a sign (whether carried as an overpunched character or as a separate character), the test checks it for a valid sign value. If the character position carrying the sign contains an invalid sign value, the NUMERIC class test rejects the item, and program control takes the false path of the IF statement.

The ALPHABETIC class test check is not valid for an operand described as numeric.

2.5.4. Success/Failure Tests

The success/failure condition tests the return status codes of COBOL and non- COBOL procedures for success or failure conditions. You test status-code-id as follows:
  • status-code-id IS
    { SUCCESS | FAILURE }
You can use the SET statement to initialize or alter the status of status-code-id (which must be a word or longword COMP integer represented by PIC 9(1 to 9) COMP or PIC S9(1 to 9) COMP), as follows:
  • SET status-code-id TO
    { SUCCESS | FAILURE }

The SET statement is typically in the called program, but the calling program may also SET the status of status-code-id. The SUCCESS class condition is true if status-code-id has been set to SUCCESS, otherwise it is false. The FAILURE class condition is true if status-code-id has been set to FAILURE, otherwise it is false. The results are unspecified if status-code is not set.

Example 2.1 shows the significant COBOL code relevant to a success/failure test.
Example 2.1. Success/Failure Test
…
PROGRAM-ID.  MAIN-PROG.
…
O1   RETURN-STATUS      PIC S9(9) COMP.
…
    CALL "PROG-1" GIVING RETURN-STATUS.
    IF RETURN-STATUS IS FAILURE PERFORM FAILURE-ROUTINE.
…
PROGRAM-ID. PROG-1.
….
WORKING-STORAGE SECTION.
01  RETURN-STATUS     PIC S9(9) COMP.
PROCEDURE DIVISION GIVING RETURN-STATUS.
…
    IF NUM-1 = NUM-2
              SET RETURN-STATUS TO SUCCESS
    ELSE
              SET RETURN-STATUS TO FAILURE.
…
    EXIT PROGRAM.
END PROGRAM PROG-1.
END PROGRAM MAIN-PROG.

2.6. Using the MOVE Statement

The MOVE statement moves the contents of one item into another item. The following sample MOVE statement moves the contents of item FIELD1 into item FIELD2:
MOVE FIELD1 TO FIELD2.

This section considers MOVE statements as applied to numeric and numeric edited data items.

2.6.1. Elementary Numeric Moves

If both items of a MOVE statement are elementary items and the receiving item is numeric, it is an elementary numeric move. The sending item can be numeric, alphanumeric, or numeric-edited. The elementary numeric move converts the data format of the sending item to the data format of the receiving item.

An alphanumeric sending item can be either of the following:
  • An elementary alphanumeric data item

  • Any alphanumeric literal other than the figurative constants SPACE, QUOTE, LOW-VALUE, or HIGH-VALUE

The elementary numeric move accepts the figurative constant ZERO and considers it to be equivalent to the numeric literal 0. It treats alphanumeric sending items as unsigned integers of DISPLAY usage.

When the sending item is numeric-edited, de-editing is applied to establish the unedited numeric value, which may be signed; then the unedited numeric value is moved to the receiving field.

If necessary, the numeric move operation converts the sending item to the data format of the receiving item and aligns the sending item's decimal point on that of the receiving item. Then it moves the sending item's digits to the corresponding receiving item's digits.

If the sending item has more digit positions than the receiving item, the decimal point alignment operation truncates the value of the sending item, with resulting loss of digits.

The end truncated (high-order or low-order) depends upon the number of sending item digit positions that find matches on each side of the receiving item's decimal point. If the receiving item has fewer digit positions on both sides of the decimal point, the operation truncates both ends of the sending item. Thus, if an item described as PIC 999V999 is moved to an item described as PIC 99V99, it loses one digit from the left end and one from the right end.

In the execution part of the following examples, the caret (^) indicates the assumed stored decimal point position:
01 AMOUNT1 PIC 99V99 VALUE ZEROS.
   .
   .
   .
   MOVE 123.321 TO AMOUNT1.
Before execution:
00^00 After execution:   23^32
If the sending item has fewer digit positions than the receiving item, the move operation supplies zeros for all unfilled digit positions.
01  TOTAL-AMT PIC 999V99 VALUE ZEROS.
    .
    .
    .
    MOVE 1 TO TOTAL-AMT.
Before execution:   000^00
After execution:    001^00
The following statements produce the same results:
MOVE 001.00 TO TOTAL-AMT.
MOVE "1" TO TOTAL-AMT.
Consider the following two MOVE statements and their truncating and zero-filling effects:
     Statement                 TOTAL-AMT After Execution
MOVE 00100 TO TOTAL-AMT                  100^00
MOVE "00100" TO TOTAL-AMT                100^00

Literals with leading or trailing zeros have no advantage in space or execution speed in VSI COBOL, and the zeros are often lost by decimal point alignment.

The MOVE statement's receiving item dictates how the sign will be moved. When the receiving item is a signed numeric item, the sign from the sending item is placed in it. If the sending item is unsigned, and the receiving item is signed, a positive sign is placed in the receiving item. If the sending item is signed and the receiving item is unsigned, the absolute value of the sending item is moved to the receiving item.

2.6.2. Elementary Numeric-Edited Moves

An elementary numeric move to a numeric-edited receiving item is considered an elementary numeric-edited move. The sending item of an elementary numeric-edited move can be numeric, numeric-edited, or alphanumeric. When the sending item is numeric-edited, de-editing is applied to establish the item's unedited numeric value, which may be signed; then the unedited numeric value is moved to the receiving field. Alphanumeric sending items in numeric-edited moves are considered unsigned DISPLAY usage integers.

A numeric-edited item PICTURE can contain 9, V, and P, but to qualify as numeric-edited, it must also contain one or more of the following editing symbols:
  • Z
  • B
  • Asterisk (*)
  • Period (.)
  • Plus sign (+)
  • Minus sign (–)
  • CR
  • DB
  • Currency symbol
  • Slash (/)
  • Comma (,)
  • Zero (0)

For a complete description of these symbols, refer to the VSI COBOL Reference Manual.

The numeric-edited move operation first converts the sending item to DISPLAY usage and aligns both items on their decimal point locations. The sending item is truncated or zero-filled until it has the same number of digit positions on both sides of the decimal point as the receiving item. The operation then moves the sending item to the receiving item, following the VSI COBOL editing rules.

The rules allow the numeric-edited move operation to perform any of the following editing functions:
  • Replace leading zeros with either spaces or asterisks.

  • Float a currency sign and a plus or minus sign through suppressed zeros, inserting the sign at either end of the item.

  • Insert zeros, spaces, slashes, and/or the symbols CR or DB.

  • Insert commas and a decimal point (or decimal points and a comma if DECIMAL-POINT IS COMMA).

Table 2.3 illustrates several of these functions, which are invoked by the statement:
MOVE FLD-B TO TOTAL-AMT.
Assume that FLD-B is described as S9999V99. Note that the caret (^) indicates an assumed decimal point in Table 2.3. In all but two of the examples, the sign of FLD-B is leading separate. Trailing overpunch signs (the sign of the number encoded into the rightmost digit) are used in the other two FLD-B data examples.
Table 2.3. Numeric Editing

FLD-B

TOTAL-AMT

 

PICTURE String

Contents After MOVE

+0023^00

ZZZZ.99

23.00

-0023^00

ZZZZ.99

23.00

0085^9P

++++.99

-85.97

+1234^00

Z,ZZZ.99

1,234.00

+0012^34

$,$$$.99

$12.34

+0000^34

$,$$9.99

$0.34

+1234^00

$$,$$$.99

$1,234.00

+0012^34

$$9,999.99

$0,012.34

+0012^34

$$$$,$$$.99

$12.34

+0000^00

$$$,$$$.$$

0012^3M

++++.99

-12.34

+0012^34

$***,***.99

$*****12.34

+1234^56

Z,ZZZ.99+

1,234.56+

-6543^21

$,$$$,$$$.99DB

$6,543.21DB?

The currency symbol ($ or other currency sign) and the editing sign control symbols (+ and –) are the only floating symbols. To float a symbol, enter a string of two or more occurrences of that symbol, one for each character position over which you want the symbol to float.

2.6.3. Subscripted Moves

Any item (other than a data item that is not subordinate to an OCCURS clause) of a MOVE statement can be subscripted, and the referenced item can be used to subscript another name in the same statement.

For additional information, see Section 3.6.4 in Chapter 3.

2.6.4. Common Move Errors

Programmers most commonly make the following errors when writing MOVE statements:
  • Placing an incorrect number of replacement characters in a numeric edited item

  • Moving nonnumeric data into numeric items with group moves

  • Trying to float the currency sign ($) or plus (+) insertion characters past the decimal point to force zero values to appear as .00 instead of spaces (use $$.99 or .99)

  • Forgetting that the currency sign ($), plus sign (+), minus sign (–), CR, or DB insertion characters require one or two additional positions on the leftmost end that cannot be replaced by a digit (unlike the asterisk (*) insertion character, which can be completely replaced)

2.7. Using the Arithmetic Statements

The VSI COBOL arithmetic statements allow programs to perform arithmetic operations on numeric data. Large values present various problems, and COBOL command qualifiers can help resolve or mitigate them. The following sections discuss these topics.

2.7.1. Temporary Work Items

VSI COBOL allows numeric items and literals with up to 31 decimal digits on Alpha and I64, and up to 18 decimal digits on VAX. (See Section 2.7.2 for more specific information.) It is quite easy to construct arithmetic expressions that produce too many digits.

Most forms of the arithmetic statements perform their operations in temporary work locations, then move the results to the receiving items, aligning the decimal points and truncating or zero-filling the resultant values. The actual size of a temporary work item (also called an intermediate result item) varies for each statement; it is determined at compile time, based on the sizes of the operands used by the statement and the arithmetic operation being performed. Should the temporary work item exceed the maximum size, truncation occurs.

On Alpha and I64 systems, the maximum temporary work item size is 31 digits for standard arithmetic and for native CIT4 arithmetic, and is 38 digits for some operations using native float or native CIT3.

Programs should not arbitrarily specify sizes significantly larger than the values actually anticipated for the lifetime of the application. Although the generous limits in VSI COBOL are useful for many applications, specifying many more digits than needed is likely to add extra processing cycles and complexity that is wasteful.

2.7.2. Standard and Native Arithmetic (Alpha, I64)

VSI COBOL supports two modes of arithmetic, standard and native. Standard arithmetic is preferable for greater precision with large values and for compatibility with other standard implementations of COBOL. These considerations are sometimes overridden by the need for compatibility with earlier versions of VSI COBOL or for compatibility with VSI COBOL, in which case native arithmetic is the appropriate mode.

Native arithmetic has three submodes: FLOAT, CIT3, and CIT4. (CIT stands for COBOL Intermediate Temporary).

You can specify the arithmetic mode and submode with the two COBOL command-line qualifiers /ARITHMETIC (or -arithmetic) and /MATH_INTERMEDIATE (or -math_intermediate). The use of these qualifiers is described in this section.

2.7.2.1. Using the /MATH_INTERMEDIATE Qualifier (Alpha , I64)

You can specify the intermediate data type to be used when the result of an arithmetic operation cannot be represented exactly. This data type affects the truncation of the intermediate result and the consequent precision. It also affects compatibility of arithmetic results with previous versions of COBOL and other implementations of COBOL.

The three options of the /MATH_INTERMEDIATE (or -math_intermediate) qualifier are FLOAT (the default), CIT3, and CIT4, as follows:

FLOAT

Selects double-precision binary floating-point for the intermediate data type. Intermediate values are truncated to the most significant 53 bits, with an 11-bit exponent, resulting in approximately 15 decimal digits of precision. FLOAT is the default, and it provides for compatibility with earlier versions of VSI COBOL, but not with VSI COBOL. FLOAT has been used since Version 1.0 of VSI COBOL on Alpha, I64.

CIT3

Selects Cobol Intermediate Temporary (design 3) for the intermediate data type. Intermediate values are truncated to the most significant 18 decimal digits, with a 2-digit exponent. CIT3 provides for increased compatibility with VSI COBOL for OpenVMS VAX; even with CIT3, however, there are still some differences, which are described in Section B.4.12.

CIT4

Selects Cobol Intermediate Temporary (design 4) for the intermediate data type. Intermediate values are truncated to the most significant 32 decimal digits, with a 2-digit exponent. CIT4 has the greatest compatibility with the draft ANSI Standard. CIT4 is the option of choice for greatest precision and for conformance to future standards and compatibility with other implementations of COBOL. CIT4 is strongly recommended for programs that use numeric items with more than 18 digits or that have complicated expressions.

In addition to the precision difference, CIT4 arithmetic has the same differences and restrictions as shown in Section B.4.12 for CIT3 arithmetic.

The default is /MATH_INTERMEDIATE=FLOAT (or -math_intermediate float). If you specify /ARITHMETIC=STANDARD (discussed in Section 2.7.2.2), this will force /MATH_INTERMEDIATE=CIT4.

Example of Different Arithmetic Results (Alpha, I64)
The following example illustrates the different results that you can get with FLOAT, CIT3, and CIT4:
 IDENTIFICATION DIVISION.
 PROGRAM-ID. MUL31.
 DATA DIVISION.
 WORKING-STORAGE SECTION.
 01  XD PIC S9(31) VALUE 3.
 01  YD PIC S9(31) VALUE 258718314234781388692555698765.
 01  ZD PIC S9(31).
 PROCEDURE DIVISION.
 0.
     MULTIPLY XD BY YD GIVING ZD
         ON SIZE ERROR DISPLAY "Size error raised"
         NOT ON SIZE ERROR DISPLAY ZD WITH CONVERSION.
The compiler relies on the number of digits implied by the pictures of decimal and integer operands. Here it assumes that XD has 31 digits and YD has 31 digits. The product could require 62 digits, which is larger than the largest fixed-point arithmetic type available to the compiler. Depending on the intermediate data type chosen, this program gets several different results.
                                                    Intermediate maintains
             MATH   ZD                              the most significant
             -----  ------------------------------  ----------------------
             FLOAT  776154942704344283789821739008  53 bits
             CIT3   776154942704344164000000000000  18 digits
             CIT4   776154942704344166077667096295  32 digits
Other Consequences of Intermediate Range Differences (Alpha, I64)

Because each intermediate data type has a different maximum magnitude, an arithmetic statement can raise the size error condition with one arithmetic mode but not with another.

For example, the value +0.999 999 999 999 999 999E+99 (spaces added for readability) is representable in any of the intermediate data types. By contrast, the larger value +0.999 999 999 999 999 999 9E+99 cannot be represented in a CIT3 intermediate data item. Such an operation would cause an overflow, raising the size error condition. This value is representable, however, in a FLOAT or CIT4 intermediate data item; the size error condition would not be raised.

The value 1.0E+99 cannot be represented in either CIT3 or CIT4 form, but is representable in FLOAT form.

Similarly, because each intermediate data type has a different minimum magnitude, an arithmetic statement can raise the size error condition for underflow with one arithmetic mode but not another. (Underflow does not raise the size error condition when FLOAT arithmetic is used.)

A literal also can be valid with one arithmetic mode but not with another, resulting in different HIGHTRUNC and LOWTRUNC informational diagnostics. When a literal cannot be represented in an intermediate data item, the value used is undefined.

Arithmetic expressions in nonarithmetic statements are also affected. Nonarithmetic statements, such as the IF statement, allow arithmetic expressions to be used, but do not provide a mechanism like the ON SIZE ERROR phrase to detect errors in evaluation. If such an error occurs, the behavior of the statement is unpredictable; in the case of an IF statement, result of the comparison is undefined.

Similar considerations apply in other contexts, such as the use of arithmetic expressions as subscript expressions or reference-modification components.

2.7.2.2. Using the /ARITHMETIC Qualifier (Alpha, I64)

You can specify /ARITHMETIC=NATIVE or STANDARD (-arithmetic native or standard) on the COBOL command line to control whether native arithmetic or standard arithmetic is used to evaluate arithmetic operations and statements. These options have the following effects:

NATIVE

Arithmetic operations will produce results that are reasonably compatible with releases for VSI COBOL for OpenVMS Alpha prior to Version 2.7 and also with VSI COBOL for OpenVMS VAX.

STANDARD

Most common arithmetic operations will produce results that are predictable, reasonable, and portable. In this context, portable means that the results will be identical from implementation to implementation. /ARITHMETIC=STANDARD forces /MATH_INTERMEDIATE=CIT4 (described in Section 2.7.2.1).

The default is /ARITHMETIC=NATIVE ( -arithmetic native).

Using the OPTIONS Paragraph (Alpha, I64)

An alternative way to specify native or standard arithmetic is to use the OPTIONS paragraph in the Identification Division of your VSI COBOL program. There you can specify ARITHMETIC IS NATIVE or STANDARD. Refer to the VSI COBOL Reference Manual for the syntax and details.

2.7.3. Specifying a Truncation Qualifier

The -trunc flag (on UNIX) or the /[NO]TRUNCATE qualifier (on OpenVMS) specifies how the VSI COBOL compiler stores values in COMPUTATIONAL receiving items.

By default (assuming that the -trunc flag is turned off, or /NOTRUNCATE is set), VSI COBOL truncates values according to the Alpha, I64 hardware storage unit (word, longword, or quadword) allocated to the receiving item.

If you specify -trunc or /TRUNCATE, the compiler truncates values according to the number of decimal digits specified by the PICTURE clause.

2.7.4. Using the ROUNDED Phrase

Rounding is an important option that you can use with arithmetic operations.

You can use the ROUNDED phrase with any VSI COBOL arithmetic statement. Rounding takes place only when the ROUNDED phrase requests it, and then only if the intermediate result has low-order digits that cannot be stored in the result.

VSI COBOL rounds off by adding a 5 to the leftmost truncated digit of the absolute value of the intermediate result before it stores that result.

Table 2.4 shows several ROUNDING examples.
Table 2.4. ROUNDING

PICTURE clause

Initial Value

03 ITEMA PIC S9(5)V9999.

12345.2222

03 ITEMB PIC S9(5)V99.

54321.11

03 ITEMC PIC S9999.

1234

03 ITEMD PIC S9999P.

0

03 ITEME PIC S99V99 VALUE 9.

9.00

03 ITEMF PIC S99V99 VALUE 24.

24.00

Arithmetic Statement

Intermediate Result

ROUNDED Result Value

ADD ITEMA TO ITEMB ROUNDED.

066666.3322

66666.33

MULTIPLY ITEMC BY 2 GIVING ITEMD ROUNDED.

02468

02470?

DIVIDE ITEME INTO ITEMF ROUNDED.

02.666

02.67

DIVIDE ITEME INTO ITEMF GIVING ITEMC ROUNDED.

02.666

0003

2.7.4.1. ROUNDED with REMAINDER

The remainder computation uses an intermediate field that is truncated, rather than rounded, when you use the DIVIDE statement with both the ROUNDED and REMAINDER options.

2.7.5. Using the SIZE ERROR Phrase

The SIZE ERROR phrase detects the loss of high-order nonzero digits in the results of VSI COBOL arithmetic operations. It does this by checking the absolute value of an arithmetic result against the PICTURE character-string of each resultant identifier. For example, if the absolute value of the result is 100.05, and the PICTURE character-string of the resultant identifier is 99V99, the SIZE ERROR phrase detects that the high-order digit, 1, will be lost, and the size error condition will be raised.

You can use the phrase in any VSI COBOL arithmetic statement.

When the execution of a statement with no ON SIZE ERROR phrase results in a size error, and native arithmetic is used, the values of all resultant identifiers are undefined. When standard arithmetic is used, or when the same statement includes an ON SIZE ERROR phrase, receiving items for which the size error exists are left unaltered; the result is stored in those receiving items for which no size error exists. The ON SIZE ERROR imperative phrase is then executed.

If the statement contains both ROUNDED and SIZE ERROR phrases, the result is rounded before a size error check is made.

The SIZE ERROR phrase cannot be used with numeric MOVE statements. Thus, if a program moves a numeric quantity to a smaller numeric item, it can lose high-order digits. For example, consider the following move of an item to a smaller item:
 01 AMOUNT-A PIC S9(8)V99.
 01 AMOUNT-B PIC S9(4)V99.
        .
        .
        .
        MOVE AMOUNT-A TO AMOUNT-B.
This MOVE operation always loses four of AMOUNT-A's high-order digits. The statement can be tailored in one of three ways, as shown in the following example, to determine whether these digits are zero or nonzero:
 1.  IF AMOUNT-A NOT > 9999.99
        MOVE AMOUNT-A TO AMOUNT-B
        ELSE ...
 2.  ADD ZERO AMOUNT-A GIVING AMOUNT-B
        ON SIZE ERROR ...
 3.  COMPUTE AMOUNT-B = AMOUNT-A
        ON SIZE ERROR ...

All three alternatives allow the MOVE operation to occur only if AMOUNT-A loses no significant digits. If the value in AMOUNT-A is too large, all three avoid altering AMOUNT-B and take the alternate execution path.

You can also use a NOT ON SIZE ERROR phrase to branch to, or perform, sections of code only when no size error occurs.

2.7.6. Using the GIVING Phrase

The GIVING phrase moves the intermediate result of an arithmetic operation to a receiving item. The phrase acts exactly like a MOVE statement in which the intermediate result serves as the sending item, and the data item following the word GIVING serves as the receiving item. When a statement contains a GIVING phrase, you can have a numeric-edited receiving item.

The receiving item can also have the ROUNDED phrase. If the receiving item is also numeric-edited, rounding takes place before the editing.

The GIVING phrase can be used with the ADD, SUBTRACT, MULTIPLY, and DIVIDE statements. For example:
ADD A,B GIVING C.

2.7.7. Multiple Operands in ADD and SUBTRACT Statements

Both the ADD and SUBTRACT statements can contain a series of operands preceding the word TO, FROM, or GIVING.

If there are multiple operands in either of these statements, the operands are added together. The intermediate result of that operation becomes a single operand to be added to or subtracted from the receiving item. In the following examples, TEMP is an intermediate result item:

1.

Statement:

ADD A,B,C,D, TO E,F,G,H.

Equivalent coding:

ADD A, B, GIVING TEMP.

ADD TEMP, C, GIVING TEMP.

ADD TEMP, D, GIVING TEMP.

ADD TEMP, E, GIVING E.

ADD TEMP, F, GIVING F.

ADD TEMP, G, GIVING G.

ADD TEMP, H, GIVING H.

2.

Statement:

SUBTRACT A, B, C, FROM D.

Equivalent coding:

ADD A, B, GIVING TEMP.

ADD TEMP, C, GIVING TEMP.

SUBTRACT TEMP FROM D, GIVING D.

3.

Statement:

ADD A,B,C,D, GIVING E.

Equivalent coding:

ADD A,B, GIVING TEMP.

ADD TEMP, C, GIVING TEMP.

ADD TEMP, D, GIVING E.

As in all VSI COBOL statements, the commas in these statements are optional.

2.7.8. Common Errors in Arithmetic Statements

Programmers most commonly make the following errors when using arithmetic statements:
  • Using an alphanumeric item in an arithmetic statement. The MOVE statement allows data movement between alphanumeric items and certain numeric items, but arithmetic statements require that all items be numeric.

  • Writing the ADD or SUBTRACT statements without the GIVING phrase, and attempting to put the result into a numeric-edited item.

  • Subtracting a 1 from a numeric counter that was described as an unsigned quantity and then testing for a value less than zero.

  • Forgetting that the MULTIPLY statement, without the GIVING phrase, stores the result back into the second operand (multiplier).

  • Performing a series of calculations that generates an intermediate result larger than 18 digits when the final result will have 18 or fewer digits. You can prevent this problem by interspersing divisions with multiplications or by dropping nonsignificant digits after multiplying large numbers or numbers with many decimal places. Also, avoid use of the COMPUTE statement to keep from performing such calculations implicitly.

  • Forgetting that when an arithmetic statement has multiple receiving items you must specify the ROUNDED phrase for each receiving item you want rounded.

  • Forgetting that the ON SIZE ERROR phrase applies to all receiving items in an arithmetic statement containing multiple receiving items. Only those receiving items for which a size error condition is raised are left unaltered. The ON SIZE ERROR imperative statement is executed after all the receiving items are processed.

  • Controlling a loop by adding to a numeric counter that was described as PIC 9, and then testing for a value of 10 or greater to exit the loop.

  • Forgetting that ROUNDING is done before the ON SIZE ERROR test.

Chapter 3. Handling Nonnumeric Data

Nonnumeric data in VSI COBOL is evaluated with respect to a specified collating sequence of the operands.

The following information is in this chapter:

3.1. How the Compiler Stores Nonnumeric Data

COBOL programs hold their data in items whose sizes are described in their source programs. The size of these items is thus fixed during compilation for the lifespan of the resulting object program.

Items in a COBOL program belong to any of the following three data classes:
  • Numeric—Can contain only numeric values.

  • Alphabetic—Can contain only A to Z (uppercase or lowercase) and space characters.

  • Alphanumeric—Can contain the following types of values:
    • All alphabetic

    • All numeric

    • A mixture of alphabetic and numeric

    • Any character from the ASCII character set

The data description of an item specifies which class that item belongs to.

Classes are further subdivided into categories. Alphanumeric items can be numeric edited, alphanumeric edited, or alphanumeric. Every elementary item, except for an index data item, belongs to one of the classes and its categories. The class of a group item is treated as alphanumeric regardless of the classes of subordinate elementary items.

If the data description of an alphanumeric item specifies that certain editing operations be performed on any value that is moved into it, that item is called an alphanumeric edited item.

As you read this chapter, keep in mind the distinction between the class or category of a data item and the actual value that the item contains.

Sometimes the text refers to alphabetic, alphanumeric, and alphanumeric edited data items as nonnumeric data items to distinguish them from items that are specifically numeric.

Regardless of the class of an item, it is usually possible at run time to store an invalid value in the item. Thus, nonnumeric ASCII characters can be placed in an item described as numeric, and an alphabetic item can be loaded with nonalphabetic characters. Invalid values can cause errors in output or run-time errors.

3.2. Data Organization

A VSI COBOL record consists of a set of data description entries that describe record characteristics; it must have an 01 or 77 level number. A data description entry can be either a group item or an elementary item.

All of the records used by VSI COBOL programs (except for certain registers and switches) must be described in the source program's Data Division. The compiler allocates memory space for these items (except for Linkage Section items) and fixes their size at compilation time.

The following sections explain how the compiler sets up storage for group and elementary data items.

3.2.1. Group Items

A group item is a data item that is followed by one or more elementary items or other group items, all of which have higher-valued level numbers than the group to which they are subordinate.

The size of a group item is the sum of the sizes of its subordinate elementary items. The compiler considers all group items to be alphanumeric DISPLAY items regardless of the class and usage of their subordinate elementary items.

3.2.2. Elementary Items

An elementary item is a data item that has no subordinate data item.

The size of an elementary item is determined by the number of symbols that represent character positions contained in the PICTURE character-string. For example, consider this record description:
 01 TRANREC.
    03 FIELD-1 PIC X(7).
    03 FIELD-2 PIC S9(5)V99.

Both elementary items require seven bytes of memory; however, item FIELD-1 contains seven alphanumeric characters while item FIELD-2 contains seven decimal digits, an operational sign, and an implied decimal point. Operations on such items are independent of the mapping of the item into memory words (32-bit words that hold four 8-bit bytes). An item can begin in the leftmost or rightmost byte of a word with no effect on the function of any operation that refers to that item. (However, the position of items in memory can have an effect on run-time performance.)

In effect, the compiler sees memory as a continuous array of bytes, not words. This becomes particularly important when you are defining a table using the OCCURS clause (see Chapter 4).

In VSI COBOL, all records, and elementary items with level 01 or 77, begin at an address that is a multiple of 8 bytes (a quadword boundary). By default, the VSI COBOL compiler will locate a subordinate data item at the next unassigned byte location.

Refer to Chapter 16, Chapter 15, and the SYNCHRONIZED clause in the VSI COBOL Reference Manual for a complete discussion of alignment.

3.3. Special Characters

VSI COBOL allows you to handle any of the 128 characters of the ASCII character set as alphanumeric data, even though many of the characters are control characters, which usually direct input/output devices. Generally, alphanumeric data manipulations attach no meaning to the 8th bit of an 8-bit byte. Thus, you can move and compare these control characters in the same manner as alphabetic and numeric characters.

Note

Some control characters have 0 in the high-order bit and are part of the ASCII character set, while others have 1 in the high order bit and are not part of the ASCII character set.

Although the object program can manipulate all ASCII characters, certain control characters cannot appear in nonnumeric literals because the compiler uses them to delimit the source text.

You can place special characters into items of the object program by defining symbolic characters in the SPECIAL-NAMES paragraph or by using the EXTERNAL clause. Refer to the VSI COBOL Reference Manual for information on these two topics.

The ASCII character set listed in the VSI COBOL Reference Manual indicates the decimal value for any ASCII character.

3.4. Testing Nonnumeric Items

The following sections describe the relation and class tests as they apply to nonnumeric items.

3.4.1. Relation Tests of Nonnumeric Items

An IF statement with a relation condition can compare the value in a nonnumeric data item with another value and use the result to alter the flow of control in the program.

An IF statement with a relation condition compares two operands. Either of these operands can be an identifier or a literal, but they cannot both be literals. If the stated relation exists between the two operands, the relation condition is true.

When coding a relational operator, leave a space before and after each reserved word. When the reserved word NOT is present, the compiler considers it and the next key word or relational character to be a single relational operator defining the comparison. Table 3.1 shows the meanings of the relational operators.
Table 3.1. Relational Operator Descriptions

Operator

Description

IS [NOT] GREATER THAN IS [NOT] >

The first operand is greater than (or not greater than) the second operand.

IS [NOT] LESS THAN IS [NOT] <

The first operand is less than (or not less than) the second operand.

IS [NOT] EQUAL TO IS [NOT] =

The first operand is equal to (or not equal to) the second operand.

IS GREATER THAN OR EQUAL TO IS >=

The first operand is greater than or equal to the second operand.

IS LESS THAN OR EQUAL TO IS <=

The first operand is less than or equal to the second operand.

3.4.1.1. Classes of Data

VSI COBOL allows comparison of both numeric class operands and nonnumeric class operands; however, it handles each class of data differently. For example, it allows a comparison of two numeric operands regardless of the formats specified in their respective USAGE clauses, but it requires that all other comparisons (including comparisons of any group items) be between operands with the same usage. It compares numeric class operands with respect to their algebraic values and nonnumeric (or numeric and nonnumeric) class operands with respect to a specified collating sequence. (See Section 2.5.1 for numeric comparisons.)

If only one of the operands is numeric, it must be an integer data item or an integer literal, and it must be DISPLAY usage. In these cases, the manner in which the compiler handles numeric operands depends on the nonnumeric operand, as follows:
  • If the nonnumeric operand is an elementary item or a literal, the compiler treats the numeric operand as if it had been moved into an alphanumeric data item the same size as the numeric operand and then compared. This causes any operational sign, whether carried as a separate character or as an overpunched character, to be stripped from the numeric item so that it appears to be an unsigned quantity.

    In addition, if the PICTURE character-string of the numeric item contains trailing P characters, indicating that there are assumed integer positions that are not actually present, they are filled with zero digits. Thus, an item with a PICTURE character-string of S9999PPP is moved to a temporary location where it is described as 9999999. If its value is 432J (–4321), the value in the temporary location will be 4321000. The numeric digits take part in the comparison.

  • If the nonnumeric operand is a group item, the compiler treats the numeric operand as if it had been moved into a group item the same size as the numeric operand and then compared. This is equivalent to a group move.

    The compiler ignores the description of the numeric item (except for length) and, therefore, includes in its length any operational sign, whether carried as a separate character or as an overpunched character. Overpunched characters are never ASCII numeric digits. They are characters ranging from A to R, left brace ({), or right brace (}). Thus, the sign and the digits, stored as ASCII bytes, take part in the comparison, and zeros are not supplied for P characters in the PICTURE character-string.

The compiler does not accept a comparison between a noninteger numeric operand and a nonnumeric operand. If you try to compare these two items, you receive a diagnostic message at compile time.

3.4.1.2. Comparison Operations

If the two operands are acceptable, the compiler compares them character by character. The compiler starts at the first byte and compares the corresponding bytes until it either encounters a pair of unequal bytes or reaches the last byte of the longer operand.

If the compiler encounters a pair of unequal characters, it considers their relative position in the collating sequence. The operand with the character that is positioned higher in the collating sequence is the greater operand.

If the operands have different lengths, the comparison proceeds as though the shorter operand were extended on the right by sufficient ASCII spaces (decimal 32) to make both operands the same length.

If all character pairs are equal, the operands are equal.

3.4.2. Class Tests for Nonnumeric Items

An IF statement with a class condition tests the value in a nonnumeric data item (USAGE DISPLAY only) to determine whether it contains numeric, alphabetic, or user-defined data and uses the result to alter the flow of control in the program. For example:
IF ITEM-1 IS NUMERIC...
IF ITEM-2 IS ALPHABETIC...
IF ITEM-3 IS NOT NUMERIC...

If the data item consists entirely of the ASCII characters 0 to 9, with or without the operational sign, the class condition is NUMERIC. If the item consists entirely of the ASCII characters A to Z (upper- or lowercase) and spaces, the class condition is ALPHABETIC.

The ALPHABETIC-LOWER test is true if the operand contains any combination of the lowercase alphabetic characters a to z, and the space. Otherwise the test is false.

The ALPHABETIC-UPPER test is true if the operand contains any combination of the uppercase alphabetical characters A to Z, and the space. Otherwise, the test is false.

You can also perform a class test on a data item that you define with the CLASS clause of the SPECIAL-NAMES paragraph.

A class test is true if the operand consists entirely of the characters listed in the definition of the CLASS-NAME in the SPECIAL-NAMES paragraph. Otherwise, the test is false.

When the reserved word NOT is present, the compiler considers it and the next key word as one class condition defining the class test to be executed. For example, NOT NUMERIC determines if an operand contains at least one nonnumeric character.

If the item being tested is described as a numeric data item, it can only be tested as NUMERIC or NOT NUMERIC. The NUMERIC test cannot examine either of the following:
  • An item described as alphabetic

  • A group item containing elementary items whose data descriptions indicate the presence of operational signs

For further information on using class conditions with numeric items, refer to the VSI COBOL Reference Manual.

3.5. Data Movement

Three VSI COBOL statements (MOVE, STRING, and UNSTRING) perform most of the data movement operations required by business-oriented programs. The MOVE statement simply moves data from one item to another. The STRING statement concatenates a series of sending items into a single receiving item. The UNSTRING statement disperses a single sending item into multiple receiving items. Section 3.6 describes the MOVE statement. Chapter 5 describes STRING and UNSTRING.

The MOVE statement handles most data movement operations on character strings. However, it is limited in its ability to handle multiple items. For example, it cannot, by itself, concatenate a series of sending items into a single receiving item or disperse a single sending item into several receiving items.

Two MOVE statements will, however, bring the contents of two items together into a third (receiving) item if the receiving item has been subdivided with subordinate elementary items that match the two sending items in size. If other items are to be concatenated into the third item, and they differ in size from the first two items, then the receiving item requires additional subdivisions (through redefinition).

Example 3.1 demonstrates item concatenation using two MOVE statements.
Example 3.1. Item Concatenation Using Two MOVE Statements
01  SEND-1        PIC X(5) VALUE "FIRST".
01  SEND-2        PIC X(6) VALUE "SECOND".
01  RECEIVE-GROUP.
    05  REC-1     PIC X(5).
    05  REC-2     PIC X(6).
PROCEDURE DIVISION.
A00-BEGIN.
    MOVE SEND-1 TO REC-1.
    MOVE SEND-2 TO REC-2.
    DISPLAY RECEIVE-GROUP.
    STOP RUN.
The result of the concatenation follows:
FIRSTSECOND

Two MOVE statements can also disperse the contents of one sending item to several receiving items. The first MOVE statement moves the leftmost end of the sending item to a receiving item; then the second MOVE statement moves the rightmost end of the sending item to another receiving item. (The second receiving item must first be described with the JUSTIFIED clause.) Characters from the middle of the sending item cannot easily be moved to any receiving item without extensive redefinitions of the sending item or a reference modification loop (as with concatenation).

The STRING and UNSTRING statements handle concatenation and dispersion more easily than compound moves. Reference modification handles substring operations more easily than compound moves, STRING, or UNSTRING.

3.6. Using the MOVE Statement

The MOVE statement moves the contents of one item into another. For example:
MOVE FIELD1 TO FIELD2
MOVE CORRESPONDING FIELD1 TO FIELD2

FIELD1 is the sending item name, and FIELD2 is the receiving item name.

The first statement causes the compiler to move the contents of FIELD1 into FIELD2. The two items need not be the same size, class, or usage; they can be either group or elementary items. If the two items are not the same length, the compiler aligns them on one end or the other. It also truncates or space-fills the other end. The movement of group items and nonnumeric elementary items is discussed in Section 3.6.1 and Section 3.6.2, respectively.

The MOVE statement alters the contents of every character position in the receiving item.

3.6.1. Group Moves

If either the sending or receiving item is a group item, the compiler considers the move to be a group move. It treats both the sending and receiving items as if they were alphanumeric items.

If the sending item is a group item, and the receiving item is an elementary item, the compiler ignores the receiving item description except for the size description, in bytes, and any JUSTIFIED clause. It conducts no conversion or editing on the sending item's data.

3.6.2. Elementary Moves

If both items of a MOVE statement are elementary items, their PICTURE character-strings control their data movement. If the receiving item was described as numeric or numeric edited, the rules for numeric moves control the data movement (see Section 2.6). Nonnumeric receiving items are alphanumeric, alphanumeric edited, or alphabetic.

Table 3.2 shows the valid and invalid nonnumeric elementary moves.
Table 3.2. Nonnumeric Elementary Moves
 

Receiving Item Category

Sending Item Category

 

Alphanumeric

Alphabetic

Alphanumeric Edited

ALPHABETIC

Valid

Valid

ALPHANUMERIC

Valid

Valid

ALPHANUMERIC EDITED

Valid

Valid

NUMERIC INTEGER (DISPLAY ONLY )

Invalid

Valid

NUMERIC EDITED

Invalid

Valid

In all valid moves, the compiler treats the sending item as though it had been described as PIC X(n). A JUSTIFIED clause in the sending item's description has no effect on the move. If the sending item's PICTURE character-string contains editing characters, the compiler uses them only to determine the item's size.

In valid nonnumeric elementary moves, the receiving item controls the movement of data. All of the following characteristics of the receiving item affect the move:
  • Its size

  • Editing characters in its description

  • The JUSTIFIED RIGHT clause in its description

The JUSTIFIED clause and editing characters are mutually exclusive.

When an item that contains no editing characters or JUSTIFIED clause in its description is used as the receiving item of a nonnumeric elementary MOVE statement, the compiler moves the characters starting at the leftmost position in the item and proceeding, character by character, to the rightmost position. If the sending item is shorter than the receiving item, the compiler fills the remaining character positions with spaces. If the sending item is longer than the receiving item, truncation occurs on the right.

Numeric items used in nonnumeric elementary moves must be integers in DISPLAY format.

If the description of the numeric data item indicates the presence of an operational sign (either as a character or an overpunched character), or if there are P characters in its character-string, the compiler first moves the item to a temporary location. It removes the sign and fills out any P character positions with zero digits. It then uses the temporary value as the sending item as if it had been described as PIC X(n). The temporary value can be shorter than the original value if a separate sign was removed, or longer than the original value if P character positions were filled with zeros.

If the sending item is an unsigned numeric class item with no P characters in its character-string, the MOVE is accomplished directly from the sending item, and a temporary item is not required.

If the numeric sending item is shorter than the receiving item, the compiler fills the receiving item with spaces.

3.6.2.1. Edited Moves

This section explains the following insertion editing characters:

B

Blank insertion position

0

Zero insertion position

/

Slash insertion position

When an item with an insertion editing character in its PICTURE character-string is the receiving item of a nonnumeric elementary MOVE statement, each receiving character position corresponding to an editing character receives the insertion byte value. Table 3.3 illustrates the use of such symbols with the following statement, where FIELD1 is described as PIC X(7):
MOVE FIELD1 TO FIELD2

Table 3.3. Data Movement with Editing Symbols

FIELD1

FIELD2

 

Character-String

Contents After MOVE

  • Legend: s = space

070476

XX/99/XX

07/04/76

04JUL76

99BAAAB99

04sJULs76

2351212

XXXBXXXX/XX/

235s1212/ss/

123456

0XB0XB0XB0X

01s02s03s04

Data movement always begins at the left end of the sending item and moves only to the byte positions described as A, 9, or X in the receiving item PICTURE character-string. When the sending item is exhausted, the compiler supplies space characters to fill any remaining character positions (not insertion positions) in the receiving item. If the receiving item is exhausted before the last character is moved from the sending item, the compiler ignores the remaining sending item characters.

Any necessary conversion of data from one form of internal representation to another takes place during valid elementary moves, along with any editing specified for, or de-editing implied by, the receiving data item.

3.6.2.2. Justified Moves

A JUSTIFIED RIGHT clause in the receiving item's data description causes the compiler to reverse its usual data movement conventions. It starts with the rightmost characters of both items and proceeds from right to left. If the sending item is shorter than the receiving item, the compiler fills the remaining leftmost character positions with spaces. If the sending item is longer than the receiving item, truncation occurs on the left. Table 3.4 illustrates various PICTURE character-string situations for the following statement:
MOVE FIELD1 TO FIELD2

Table 3.4. Data Movement with the JUSTIFIED Clause

FIELD1

FIELD2

PICTURE Character-String

Contents

PICTURE Character-String (and JUST-Clause)

Contents After MOVE

  • Legend: s = space
  

XX

AB

  

XXXXX

ABCss

XXX

ABC

XX JUST

BC

  

XXXXX JUST

ssABC

3.6.3. Multiple Receiving Items

If you write a MOVE statement containing more than one receiving item, the compiler moves the same sending item value to each of the receiving items. It has essentially the same effect as a series of separate MOVE statements, all with the same sending item.

The receiving items need have no relationship to each other. The compiler checks the validity of each one independently and performs an independent move operation on each one.

Multiple receiving items on MOVE statements provide a convenient way to set many items equal to the same value, such as during initialization code at the beginning of a section of processing. For example:
MOVE SPACES TO LIST-LINE, EXCEPTION-LINE, NAME-FLD.
MOVE ZEROS TO EOL-FLAG, EXCEPT-FLAG, NAME-FLAG.
MOVE 1 TO COUNT-1, CHAR-PTR, CURSOR.

3.6.4. Subscripted Moves

Any item (other than a data item that is not subordinate to an OCCURS clause) of a MOVE statement can be subscripted, and the referenced item can be used to subscript another name in the same statement.

For example, when more than one receiving item is named in the same MOVE statement, the order in which the compiler evaluates the subscripts affects the results of the move. Consider the following examples:
MOVE FIELD1(FIELD2) TO FIELD2 FIELD3.
In this example, the compiler evaluates FIELD1(FIELD2) only once, before it moves any data to the receiving items. It is as if the single MOVE statement were replaced with the following three statements:
MOVE FIELD1(FIELD2) TO TEMP.
MOVE TEMP TO FIELD2.
MOVE TEMP TO FIELD3.
In the following example, the compiler evaluates FIELD3(FIELD2) immediately before moving the data into it, but after moving the data from FIELD1 to FIELD2:
MOVE FIELD1 TO FIELD2 FIELD3(FIELD2).
Thus, it uses the newly stored value of FIELD2 as the subscript value. It is as if the single MOVE statement were replaced with the following two statements:
MOVE FIELD1 TO FIELD2.
MOVE FIELD1 TO FIELD3(FIELD2).

3.6.5. Common Nonnumeric Item MOVE Statement Errors

The compiler considers any MOVE statement that contains a group item (whether sending or receiving) to be a group move. If an elementary item contains editing characters or a numeric integer, these attributes of the receiving item have no effect on the action of a group move.

3.6.6. Using the MOVE CORRESPONDING Statement for Nonnumeric Items

The MOVE CORRESPONDING statement allows you to move multiple items from one group item to another group item, using a single MOVE statement. Refer to the VSI COBOL Reference Manual for rules concerning the CORRESPONDING phrase. When you use the CORRESPONDING phrase, the compiler performs an independent move operation on each pair of corresponding items from the operands and checks the validity of each. Example 3.2 shows the use of the MOVE CORRESPONDING statement.
Example 3.2. Sample Record Description Using the MOVE CORRESPONDING Statement
01 A-GROUP.                       01 B-GROUP.
   02 FIELD1.                        02 FIELD1.
      03 A PIC X.                       03 A PIC X.
      03 B PIC 9.                       03 C PIC XX.
      03 C PIC XX.                      03 E PIC XXX.
      03 D PIC 99.
      03 E PIC XXX.
     MOVE CORRESPONDING
         A-GROUP TO B-GROUP.
Equivalent MOVE statements:
MOVE A OF A-GROUP TO A OF B-GROUP.
MOVE C OF A-GROUP TO C OF B-GROUP.
MOVE E OF A-GROUP TO E OF B-GROUP.

3.6.7. Using Reference Modification

You can use reference modification to define a subset of a data item by specifying its leftmost character position and length. Reference modification is valid anywhere an alphanumeric identifier is allowed unless specific rules for a general format prohibit it. The following is an example of reference modification:
WORKING-STORAGE SECTION.
01  ITEMA  PIC X(10)  VALUE IS "XYZABCDEFG".
        .
        .
        .
    MOVE ITEMA(4:3) TO...


IDENTIFIER                 VALUE
ITEMA (4:3)                ABC

For more information on reference modification rules, refer to the VSI COBOL Reference Manual.

Chapter 4. Handling Tables

A table is one or more repetitions of one element, composed of one or more data items, stored in contiguous memory locations.

In this chapter you will find:

4.1. Defining Tables

You define a table by using an OCCURS clause following a data description entry. The literal integer value you specify in the OCCURS clause determines the number of repetitions, or occurrences, of the data description entry, thus creating a table. VSI COBOL allows you to define from 1- to 48-dimension tables.

After you have defined a table, you can load it with data. One way to load a table is to use the INITIALIZE statement or the VALUE clause to assign values to the table when you define it (see Figure 4.10).

To access data stored in tables, use subscripted or indexed procedural instructions. In either case, you can directly access a known table element occurrence or search for an occurrence based on some known condition.

You can define either fixed-length tables or variable-length tables, and they may be single or multidimensional. The following sections describe how to use the OCCURS clause and its options. For more information on tables and subscripting, refer to the VSI COBOL Reference Manual.

4.1.1. Defining Fixed-Length, One-Dimensional Tables

To define fixed-length tables, use Format 1 of the OCCURS clause (refer to the VSI COBOL Reference Manual). This format is useful when you are storing large amounts of stable or frequently used reference data. Options allow you to define single or multiple keys, or indexes, or both.

A definition of a one-dimensional table is shown in Example 4.1. The integer 2 in the OCCURS 2 TIMES clause determines the number of element repetitions. For the table to have any real meaning, this integer must be equal to or greater than 2.
Example 4.1. One-Dimensional Table
 01  TABLE-A.
     05  ITEM-B PIC X OCCURS 2 TIMES.
The organization of TABLE-A is shown in Figure 4.1.
Figure 4.1. Organization of the One-Dimensional Table in Example 4.1
Organization of the One-Dimensional Table in Example 4.1
Example 4.1 specifies only a single data item. However, you can specify as many data items as you need in the table. Multiple data items are shown in Example 4.2.
Example 4.2. Multiple Data Items in a One-Dimensional Table
 01  TABLE-A.
     05  GROUP-B OCCURS 2 TIMES.
         10  ITEMC PIC X.
         10  ITEMD PIC X.

The organization of this table is shown in Figure 4.2.

Figure 4.2. Organization of Multiple Data Items in a One-Dimensional Table
Organization of Multiple Data Items in a One-Dimensional Table

Example 4.1 and Example 4.2 both do not use the KEY IS or INDEXED BY optional phrases. The INDEXED BY phrase implicitly defines an index name. This phrase must be used if any Procedure Division statements contain indexed references to the data name that contains the OCCURS clause. The KEY IS phrase means that repeated data is arranged in ascending or descending order according to the values in the data items that contain the OCCURS clause. (The KEY IS phrase does not cause the data in the table to be placed in ascending or descending order; rather, it allows you to state how you have arranged the data.) For further information about these OCCURS clause options, refer to the VSI COBOL Reference Manual.

If you use either the SEARCH or the SEARCH ALL statement, you must specify at least one index. The SEARCH ALL statement also requires that you specify at least one key. Specify the search key using the ASCENDING/DESCENDING KEY IS phrase. (See Section 4.3.8 for information about the SEARCH statement and Section 4.3.4 for information about indexing.) When you use the INDEXED BY phrase, the index is internally defined and cannot be defined elsewhere. Example 4.3 defines a table with an ascending search key and an index.
Example 4.3. Defining a Table with an Index and an Ascending Search Key
 01  TABLE-A.
     05  ELEMENTB OCCURS 5 TIMES
                  ASCENDING KEY IS ITEMC
                  INDEXED BY INDX1.
         10  ITEMC PIC X.
         10  ITEMD PIC X.

The organization of this table is shown in Figure 4.3.

Figure 4.3. Organization of a Table with an Index and an Ascending Search Key
Organization of a Table with an Index and an Ascending Search Key

4.1.2. Defining Fixed-Length, Multidimensional Tables

VSI COBOL allows 48 levels of OCCURS nesting. If you want to define a two-dimensional table, you define another one-dimensional table within each element of the one-dimensional table. To define a three-dimensional table, you define another one-dimensional table within each element of the two-dimensional table, and so on.

A two-dimensional table is shown in Example 4.4.
Example 4.4. Defining a Two-Dimensional Table
 01  2D-TABLE-X.
     05  LAYER-Y OCCURS 2 TIMES.
         10  LAYER-Z OCCURS 2 TIMES.
             15  CELLA PIC X.
             15  CELLB PIC X.

The organization of this two-dimensional table is shown in Figure 4.4.

Figure 4.4. Organization of a Two-Dimensional Table
Organization of a Two-Dimensional Table
Example 4.5 shows a three-dimensional table.
Example 4.5. Defining a Three-Dimensional Table
 01 TABLE-A.
     05  LAYER-B OCCURS 2 TIMES.
         10  ITEMC PIC X.
         10  ITEMD PIC X OCCURS 3 TIMES.
         10  ITEME OCCURS 2 TIMES.
             15  CELLF PIC X.
             15  CELLG PIC X OCCURS 3 TIMES.

The organization of this three-dimensional table is shown in Figure 4.5.

Figure 4.5. Organization of a Three-Dimensional Table
Organization of a Three-Dimensional Table

4.1.3. Defining Variable-Length Tables

To define a variable-length table, use Format 2 of the OCCURS clause (refer to the VSI COBOL Reference Manual). Options allow you to define single or multiple keys, or indexes, or both.

Example 4.6 illustrates how to define a variable-length table.

It uses from two to four occurrences depending on the integer value assigned to NUM-ELEM. You specify the table's minimum and maximum size with the OCCURS (minimum size) TO (maximum size) clause. The minimum size value must be equal to or greater than zero and the maximum size value must be greater than the minimum size value. The DEPENDING ON clause is also required when you use the TO clause.

The data-name of an elementary, unsigned integer data item is specified in the DEPENDING ON clause. Its value specifies the current number of occurrences. The data-name in the DEPENDING ON clause must be within the minimum to maximum range.

Unlike fixed-length tables, you can dynamically alter the number of element occurrences in variable-length tables.

By generating the variable-length table in Example 4.6, you are, in effect, saying: Build a table that can contain at least two occurrences, but no more than four occurrences, and set its present number of occurrences equal to the value specified by NUM-ELEM.
Example 4.6. Defining a Variable-Length Table
 01  NUM-ELEM PIC 9.
        .
        .
        .
 01  VAR-LEN-TABLE.
     05  TAB-ELEM OCCURS 2 TO 4 TIMES DEPENDING ON NUM-ELEM.
         10 A PIC X.
         10 B PIC X.

4.1.4. Storage Allocation for Tables

The compiler maps the table elements into memory, following mapping rules that depend on the use of COMP, COMP-1, COMP-2, POINTER, and INDEX data items in the table element, the presence or absence of the SYNCHRONIZED (SYNC) clause with those data items, and the -align flag (on the UNIX operating system) or the /ALIGNMENT qualifier (on the OpenVMS Alpha and I64 operating systems).

The VSI COBOL compiler allocates storage for data items within records according to the rules of the Major-Minor Equivalence technique. This technique ensures that identically defined group items have the same structure, even when their subordinate items are aligned. Therefore, group moves always produce predictable results. For more information, refer to the description of record allocation in the VSI COBOL Reference Manual.

Note

To determine exactly how much space your tables use, specify the -map flag (on UNIX), or the /MAP qualifier (on OpenVMS). This gives you an offset map of both the Data Division and the Procedure Division.

Example 4.7 shows how to describe a sample record in a table.
Example 4.7. Sample Record Description Defining a Table
 01 TABLE-A.
     03 GROUP-G PIC X(5) OCCURS 5 TIMES.
Figure 4.6 shows how the table defined in Example 4.7 is mapped into memory.
Figure 4.6. Memory Map for Example 4.7
Memory Map for Example 4.7

Alphanumeric data items require 1 byte of storage per character. Therefore, each occurrence of GROUP-G occupies 5 bytes. The first byte of the first element is automatically aligned at the left record boundary and the first 5 bytes occupy all of word 1 and part of 2. A memory longword is composed of 4 bytes. Succeeding occurrences of GROUP-G are assigned to the next 5 adjacent bytes so that TABLE-A is composed of five 5-byte elements for a total of 25 bytes. Each table element, after the first, is allowed to start in any byte of a word with no regard for word boundaries.

4.1.4.1. Using the SYNCHRONIZED Clause

By default, the VSI COBOL compiler tries to allocate a data item at the next unassigned byte location. However, you can align some data items on a 2-, 4-, or 8-byte boundary by using the SYNCHRONIZED clause. The compiler may then have to skip one or more bytes before assigning a location to the next data item. The skipped bytes, called fill bytes, are gaps between one data item and the next.

The SYNCHRONIZED clause explicitly aligns COMP, COMP-1, COMP-2, POINTER, and INDEX data items on their natural boundaries: one-word COMP items on 2-byte boundaries, longword items on 4-byte boundaries, and quadword items on 8-byte boundaries. Thus the use of SYNC can have a significant effect on the amount of memory required to store tables containing COMP and COMP SYNC data items.

Note

The examples in this section assume compilation without the -align flag (on UNIX systems) or the /ALIGNMENT qualifier (on OpenVMS Alpha and I64 systems).

Example 4.8 describes a table containing a COMP SYNC data item. Figure 4.7 illustrates how it is mapped into memory.
Example 4.8. Record Description Containing a COMP SYNC Item
 01 A-TABLE.
    03 GROUP-G OCCURS 4 TIMES.
       05 ITEM1 PIC X.
       05 ITEM2 PIC S9(5) COMP SYNC.

Figure 4.7. Memory Map for Example 4.8
Memory Map for Example 4.8

Because a 5-digit COMP SYNC item requires one longword (or 4 bytes) of storage, ITEM2 must start on a longword boundary. This requires the addition of 3 fill bytes after ITEM1, and each GROUP-G occupies 8 bytes. In Example 4.8, A-TABLE requires 32 bytes to store four elements of 8 bytes each.

If, in the previous example, you define ITEM2 as a COMP data item of the same size without the SYNC clause, the storage required will be considerably less. Although ITEM2 will still require one longword of storage, it will be aligned on a byte boundary. No fill bytes will be needed between ITEM1 and ITEM2, and A-TABLE will require a total of 20 bytes.

If you now add a 3-byte alphanumeric item (ITEM3) to Example 4.8 and locate it between ITEM1 and ITEM2 (see Example 4.9), the new item occupies the space formerly occupied by the 3 fill bytes. This adds 3 data bytes without changing the table size, as Figure 4.8 illustrates.
Example 4.9. Adding an Item Without Changing the Table Size
 01 A-TABLE.
    03 GROUP-G OCCURS 4 TIMES.
       05 ITEM1 PIC X.
       05 ITEM3 PIC XXX.
       05 ITEM2 PIC 9(5) COMP SYNC.

Figure 4.8. Memory Map for Example 4.9
Memory Map for Example 4.9
Example 4.10. How Adding 3 Bytes Adds 4 Bytes to the Element Length
 01 A-TABLE.
    03 GROUP-G OCCURS 4 TIMES.
       05 ITEM1 PIC X.
       05 ITEM2 PIC 9(5) COMP SYNC.
       05 ITEM3 PIC XXX.
Figure 4.9. Memory Map for Example 4.10
Memory Map for Example 4.10

If, however, you place ITEM3 after ITEM2, the additional 3 bytes add their own length plus another fill byte. The additional fill byte is added after the third ITEM3 character to ensure that all occurrences of the table element are mapped in an identical manner. Now, each element requires 12 bytes, and the complete table occupies 48 bytes. This is illustrated by Example 4.10 and Figure 4.9.

Note that GROUP-G begins on a 4-byte boundary because of the way VSI COBOL allocates memory.

4.2. Initializing Values of Table Elements

You can initialize a table that contains only DISPLAY items to any desired value in either of the following ways:
  • You can specify a VALUE clause in the record level preceding the record description of the item containing the OCCURS clause.

  • You can specify a VALUE clause in a record subordinate to the OCCURS clause.

Example 4.11 and Figure 4.10 provide an example and memory map of a table initialized using the VALUE clause.
Example 4.11. Initializing Tables with the VALUE Clause
 01 A-TABLE VALUE IS "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC".
     03 MONTH-GROUP PIC XXX USAGE DISPLAY
                OCCURS 12 TIMES.

Figure 4.10. Memory Map for Example 4.11
Memory Map for Example 4.11
If each entry in the table has the same value, you can initialize the table as shown in Example 4.12.
Example 4.12. Initializing a Table with the OCCURS Clause
 01 A-TABLE.
    03 TABLE-LEG OCCURS 5 TIMES.
       05 FIRST-LEG   PIC X VALUE "A".
       05 SECOND-LEG  PIC S9(9) COMP VALUE 5.
In this example, there are five occurrences of each table element. Each element is initialized to the same value as follows:
  • FIRST-LEG occurs five times; each occurrence is initialized to A.

  • SECOND-LEG occurs five times; each occurrence is initialized to 5.

Often a table is too long to initialize using a single literal, or it contains numeric, alphanumeric, COMP, COMP-1, COMP-2, or COMP SYNC items that cannot be initialized. In these situations, you can initialize individual items by redefining the group level that precedes the level containing the OCCURS clause. Consider the sample table descriptions illustrated in Example 4.13 and Example 4.14. Each fill byte between ITEM1 and ITEM2 in Example 4.13 is initialized to X. Figure 4.11 shows how this is mapped into memory.
Example 4.13. Initializing Mixed Usage Items
 01 A-RECORD-ALT.
    05 FILLER PIC XX VALUE "AX".
    05 FILLER PIC S99 COMP VALUE 1.
    05 FILLER PIC XX VALUE "BX".
    05 FILLER PIC S99 COMP VALUE 2.
    .
    .
    .
 01 A-RECORD REDEFINES A-RECORD-ALT.
    03 A-GROUP OCCURS 26 TIMES.
       05 ITEM1 PIC X.
       05 ITEM2 PIC S99 COMP SYNC.

Figure 4.11. Memory Map for Example 4.13
Memory Map for Example 4.13
As shown in Example 4.14 and in Figure 4.12, each FILLER item initializes three 10-byte table elements.
Example 4.14. Initializing Alphanumeric Items
 01 A-RECORD-ALT.
     03 FILLER PIC X(30) VALUE IS
        "AAAAAAAAAABBBBBBBBBBCCCCCCCCCC".
     03 FILLER PIC X(30) VALUE IS
        "DDDDDDDDDDEEEEEEEEEEFFFFFFFFFF".
     .
     .
     .
 01 A-RECORD REDEFINES A-RECORD-ALT.
     03 ITEM1 PIC X(10) OCCURS 26 TIMES.

Figure 4.12. Memory Map for Example 4.14
Memory Map for Example 4.14

When redefining or initializing table elements, allow space for any fill bytes that may be added due to synchronization. You do not have to initialize fill bytes, but you can do so. If you initialize fill bytes to an uncommon value, you can use them as a debugging aid in situations where a Procedure Division statement refers to the record level preceding the OCCURS clause, or to another record redefining that level.

You can also initialize tables at run time. To initialize tables at run time, use the INITIALIZE statement. This statement allows you to initialize all occurrences of a table element to the same value. For more information about the INITIALIZE statement, refer to the VSI COBOL Reference Manual.

Sometimes the length and format of table items are such that they are best initialized using Procedure Division statements such as a MOVE statement to send a value to the table.

4.3. Accessing Table Elements

Once tables have been created using the OCCURS clause, the program must have a method of accessing the individual elements of those tables. Subscripting and indexing are the two methods VSI COBOL provides for accessing individual table elements. To refer to a particular element within a table, follow the name of that element with a subscript or index enclosed in parentheses. The following sections describe how to identify and access table elements using subscripts and indexes.

4.3.1. Subscripting

A subscript can be an integer literal, an arithmetic expression, a data name, or a subscripted data name that has an integer value. The integer value represents the desired element of the table. An integer value of 3, for example, refers to the third element of a table.

4.3.2. Subscripting with Literals

A literal subscript is an integer value, enclosed in parentheses, that represents the desired table element. In Example 4.15, the literal subscript (2) in the MOVE instruction moves the contents of the second element of A-TABLE to I-RECORD.
Example 4.15. Using a Literal Subscript to Access a Table
Table Description:
        01 A-TABLE.
          03 A-GROUP PIC X(5)
             OCCURS 10 TIMES.
Instruction:
           MOVE A-GROUP(2) TO I-RECORD.

If the table is multidimensional, follow the data name of the desired data item with a list of subscripts, one for each OCCURS clause to which the item is subordinate. The first subscript in the list applies to the first OCCURS clause to which that item is subordinate. This is the most inclusive level, and is represented by A-GROUP in Example 4.16. The second subscript applies to the next most inclusive level and is represented by ITEM3 in the example. Finally, the third subscript applies to the least inclusive level, represented by ITEM5. (Note that VSI COBOL can have 48 subscripts that follow the pattern in Example 4.15.)

In Example 4.16, the subscripts (2,11,3) in the MOVE statements move the third occurrence of ITEM5 in the eleventh repetition of ITEM3 in the second repetition of A-GROUP to I-FIELD5. ITEM5(1,1,1) refers to the first occurrence of ITEM5 in the table, and ITEM5(5,20,4) refers to the last occurrence of ITEM5.
Example 4.16. Subscripting a Multidimensional Table
Table Description:
        01 A-TABLE.
          03 A-GROUP OCCURS 5 TIMES.
             05 ITEM1           PIC X.
             05 ITEM2           PIC 99 COMP OCCURS 20 TIMES.
             05 ITEM3 OCCURS 20 TIMES.
                07 ITEM4        PIC X.
                07 ITEM5        PIC XX OCCURS 4 TIMES.
       01 I-FIELD5              PIC XX.
Procedural Instruction:
              MOVE ITEM5(2, 11, 3) TO I-FIELD5.

Note

Because ITEM5 is not subordinate to ITEM2, an occurrence number for ITEM2 is not permitted in the subscript list (when referencing ITEM3, ITEM4, or ITEM5). The ninth occurrence of ITEM2 in the fifth occurrence of A-GROUP will be selected by ITEM2(5,9).

Table 4.1 shows the subscripting rules that apply to Example 4.16.
Table 4.1. Subscripting Rules for a Multidimensional Table

Name of Item

Number of Subscripts Required to Refer to the Name Item

Size of Item in Bytes (Each Occurrence )

A-TABLE

NONE

1105

A-GROUP

ONE

221

ITEM1

ONE

1

ITEM2

TWO

2

ITEM3

TWO

9

ITEM4

TWO

1

ITEM5

THREE

2

4.3.3. Subscripting with Data Names

You can also use data names to specify subscripts. To use a data name as a subscript, define it with COMP, COMP-1, COMP-2, COMP-3, or DISPLAY usage and with a numeric integer value. If the data name is signed, the sign must be positive at the time the data name is used as a subscript.

A data name that is a subscript can also be subscripted; for example, A(B(C)). Note that for efficiency your subscripts should be S9(5) to S9(9) COMP.

The sample subscripts and data names used in Table 4.2 refer to the table defined in Example 4.16.
Table 4.2. Subscripting with Data Names

Data Descriptions of Subscript Data Names

Procedural Instructions

01 SUB1 PIC 99 USAGE DISPLAY.

MOVE 2 TO SUB1.

01 SUB2 PIC S9(9) USAGE COMP.

MOVE 11 TO SUB2.

01 SUB3 PIC S99.

MOVE 3 TO SUB3.

 

MOVE ITEM5(SUB1,SUB2,SUB3) TO I-FIELD5.

4.3.4. Subscripting with Indexes

The same rules apply for specifying indexes as for subscripts, except that the index must be named in the INDEXED BY phrase of the OCCURS clause.

You cannot access index items as normal data items; that is, you cannot use them, redefine them, or write them to a file. However, the SET statement can change their values, and relation tests can examine their values. The index integer you specify in the SET statement must be in the range of one to the integer value in the OCCURS clause. The sample MOVE statement shown in Example 4.17 moves the contents of the third element of A-GROUP to I-FIELD.
Example 4.17. Subscripting with Index Name Items
Table Description:
        01 A-TABLE.
          03 A-GROUP OCCURS 5 TIMES
             INDEXED BY IND-NAME
             05   ITEMC   PIC X  VALUE "C".
             05   ITEMD   PIC X  VALUE "D".
        01 I-FIELD     PIC X(5).
Procedural Instructions:
       SET IND-NAME TO 3.
       MOVE A-GROUP(IND-NAME) TO I-FIELD.

Note

VSI COBOL initializes the value of all indexes to 1. Initializing indexes is an extension to the ANSI COBOL standard. Users who write COBOL programs that must adhere to standard COBOL should not rely on this feature.

4.3.5. Relative Indexing

To perform relative indexing when referring to a table element, you follow the index name with a plus or minus sign and an integer literal. Although it is easy to use, relative indexing generates additional overhead each time a table element is referenced in this way. The run-time overhead for relative indexing of variable-length tables is significantly greater than that required for fixed-length tables. If any of the range checks reveals an out-of-range index value, program execution terminates, and an error message is issued. You can use the -check flag (on UNIX systems) or the /CHECK qualifier (on OpenVMS systems) to check the range when you compile the program.

On UNIX, see Chapter 1 or the cobol man page for more information about the -check flag.

On OpenVMS, invoke the online help facility for VSI COBOL at the OpenVMS system prompt for more information about the /CHECK qualifier.

The following sample MOVE statement moves the fourth repetition of A-GROUP to I-FIELD:
 SET IND-NAME TO 1.
 MOVE A-GROUP(IND-NAME + 3) TO I-FIELD.

4.3.6. Index Data Items

Often a program requires that the value of an index be stored outside of that item. VSI COBOL provides the index data item to fulfill this requirement.

Index data items are stored as longword COMP items and must be declared with a USAGE IS INDEX phrase in the item description. Index data items can be explicitly modified only with the SET statement.

4.3.7. Assigning Index Values Using the SET Statement

You can use the SET statement to assign values to indexes associated with tables to reference particular table elements. The following sections discuss the two relevant SET statement formats. (All six SET statement formats are shown in the VSI COBOL Reference Manual.)

4.3.7.1. Assigning an Integer Index Value with a SET Statement

When you use the SET statement, the index is set to the value you specify. The most straightforward use of the SET statement is to set an index name to an integer literal value. This example assigns a value of 5 to IND-5:
 SET IND-5 TO 5.
You can also set an index name to an integer data item. For example:
 SET INDEX-A TO COUNT-1.
More than one index can be set with a single SET statement. For example:
 SET TAB1-IND TAB2-IND TO 15.
Table indexes specified in INDEXED BY phrases can be displayed by using the WITH CONVERSION option with the DISPLAY statement. Also, you can display, move, and manipulate the value of the table index with an index data item. You do this by setting an index data item to the present value of an index. You can, for example, set an index data item and then display its value as shown in the following example:
 SET INDEX-ITEM TO TAB-IND.
          .
          .
          .
 DISPLAY INDEX-ITEM WITH CONVERSION.

4.3.7.2. Incrementing an Index Value with the SET Statement

You can use the SET statement with the UP BY/DOWN BY clause to arithmetically alter the value of a index. A numeric literal is added to (UP BY) or subtracted from (DOWN BY) a table index. For example:
 SET TABLE-INDEX UP BY 12.
 SET TABLE-INDEX DOWN BY 5.

4.3.8. Identifying Table Elements Using the SEARCH Statement

The SEARCH statement is used to search a table for an element that satisfies a known condition. The statement provides for sequential and binary searches, which are described in the following sections.

4.3.8.1. Implementing a Sequential Search

The SEARCH statement allows you to perform a sequential search of a table. The OCCURS clause of the table description entry must contain the INDEXED BY phrase. If more than one index is specified in the INDEXED BY phrase, the first index is the controlling index for the table search unless you specify otherwise in the SEARCH statement.

The search begins at the current index setting and progresses through the table, checking each element against the conditional expression. The index is incremented by 1 as each element is checked. If the conditional expression is true, the associated imperative statement executes; otherwise, program control passes to the next procedural sentence. This terminates the search, and the index points to the current table element that satisfied the conditional expression.

If no table element is found that satisfies the conditional expression, program control passes to the AT END exit path; otherwise, program control passes to the next procedural sentence.

You can use the optional VARYING phrase of the SEARCH statement by specifying any of the following:
  • VARYING index name associated with table search

  • VARYING index data item or integer data item

  • VARYING index name not associated with table search

Regardless of which method you use, the index specified in the INDEXED BY phrase of the table being searched is incremented. This controlling index, when compared against the allowable number of occurrences in the table, dictates the permissible search range. When the search terminates, either successfully or unsuccessfully, the index remains at its current setting. At this point, you can reference the data in the table element pointed to by the index, unless the AT END condition is true. If the AT END condition is true, and if the -check flag (on UNIX systems) or the /CHECK qualifier (on OpenVMS systems) has been specified, the compiler issues a run-time error message indicating that the subscript is out of range.

When you vary an index associated with the table being searched, the index name can be any index you specify in the INDEXED BY phrase. It becomes the controlling index for the search and is the only index incremented. Example 4.18 and Example 4.20 show how to vary an index other than the first index.

When you vary an index data item or an integer data item, either the index data item or the integer data item is incremented. The first index name you specify in the INDEXED BY phrase of the table being searched becomes the controlling index and is also incremented. The index data item or the integer data item you vary does not function as an index; it merely allows you to maintain an additional pointer to elements within a table. Example 4.18 and Example 4.21 show how to vary an index data item or an integer data item.

When you vary an index associated with a table other than the one you are searching, the controlling index is the first index you specify in the INDEXED BY phrase of the table you are searching. Each time the controlling index is incremented, the index you specify in the VARYING phrase is incremented. In this manner, you can search two tables in synchronization. Example 4.18 and Example 4.22 show how to vary an index associated with a table other than the one you are searching.

When you omit the VARYING phrase, the first index you specify in the INDEXED BY phrase becomes the controlling index. Only this index is incremented during a serial search. Example 4.18 and Example 4.23 show how to perform a serial search without using the VARYING phrase.

4.3.8.2. Implementing a Binary Search

You can use the SEARCH statement to perform a nonsequential (binary) table search.

To perform a binary search, you must specify an index name in the INDEXED BY phrase and a search key in the KEY IS phrase of the OCCURS clause of the table being searched.

A binary search depends on the ASCENDING/DESCENDING KEY attributes. If you specify an ASCENDING KEY, the data in the table must either be stored in ascending order or sorted in ascending order prior to the search. For a DESCENDING KEY, data must be stored or sorted in descending order prior to the search.

On Alpha and I64 systems, you can sort an entire table in preparation for a binary search. Use the SORT statement (Format 2, a VSI extension), described in the VSI COBOL Reference Manual.

During a binary search, the first (or only) index you specify in the INDEXED BY phrase of the OCCURS clause of the table being searched is the controlling index. You do not have to initialize an index in a binary search because index manipulation is automatic.

In addition to being generally faster than a sequential search, a binary search allows multiple equality checks.

The following search sequence lists the capabilities of a binary search. At program execution time, the system:
  1. Examines the range of permissible index values, selects the median value, and assigns this value to the index.

  2. Checks for equality in WHEN and AND clauses.

  3. Terminates the search if all equality statements are true. If you use the imperative statement after the final equality clause, that statement executes; otherwise, program control passes to the next procedural sentence, the search exits, and the index retains its current value.

  4. Takes the following actions if the equality test of a table element is false:
    1. Executes the imperative statement associated with the AT END statement (if present) when all table elements have been tested. If there is no AT END statement, program control passes to the next procedural statement.

    2. Determines which half of the table is to be eliminated from further consideration. This is based on whether the key being tested was specified as ASCENDING or DESCENDING and whether the test failed because of a greater-than or less-than comparison. For example, if the key values are stored in ascending order, and the median table element being tested is greater than the value of the argument, then all key elements following the one being tested must also be greater. Therefore, the upper half of the table is removed from further consideration and the search continues at the median point of the lower half.

    3. Begins processing all over again at Step 1.

A useful variation of the binary search is that of specifying multiple search keys. Multiple search keys allow you to select a specified table element from among several elements that have duplicate low-order keys. An example is a telephone listing where several people have the same last and first names, but different middle initials. All specified keys must be either ascending or descending. Example 4.24 shows how to use multiple search keys.

The table in Example 4.18 is followed by several examples (Examples 4.19, 4.20, 4.21, 4.22, and 4.23) of how to search it.
Example 4.18. Sample Table
DATA DIVISION.
WORKING-STORAGE SECTION.
01  TEMP-IND                            USAGE IS INDEX.
01  FED-TAX-TABLES.
    02  ALLOWANCE-DATA.
        03  FILLER                      PIC X(70) VALUE
            "0101440
-           "0202880
-           "0304320
-           "0405760
-           "0507200
-           "0608640
-           "0710080
-           "0811520
-           "0912960
-           "1014400".
    02  ALLOWANCE-TABLE REDEFINES ALLOWANCE-DATA.
        03  FED-ALLOWANCES OCCURS 10 TIMES
            ASCENDING KEY IS ALLOWANCE-NUMBER
            INDEXED BY IND-1.
            04  ALLOWANCE-NUMBER        PIC XX.
            04  ALLOWANCE               PIC 99999.
    02 SINGLES-DEDUCTION-DATA.
        03  FILLER                      PIC X(112) VALUE
            "0250006700000016
-           "0670011500067220
-           "1150018300163223
-           "1830024000319621
-           "2400027900439326
-           "2790034600540730
-           "3460099999741736".
   02   SINGLE-DEDUCTION-TABLE REDEFINES SINGLES-DEDUCTION-DATA.
        03  SINGLES-TABLE OCCURS 7 TIMES
            ASCENDING KEY IS S-MIN-RANGE S-MAX-RANGE
            INDEXED BY IND-2, TEMP-INDEX.
            04  S-MIN-RANGE             PIC 99999.
            04  S-MAX-RANGE             PIC 99999.
            04  S-TAX                   PIC 9999.
            04  S-PERCENT               PIC V99.
    02  MARRIED-DEDUCTION-DATA.
        03  FILLER                      PIC X(119) VALUE
            "04800096000000017
-           "09600173000081620
-           "17300264000235617
-           "26400346000390325
-           "34600433000595328
-           "43300500000838932
-           "50000999991053336".
    02  MARRIED-DEDUCTION-TABLE REDEFINES MARRIED-DEDUCTION-DATA.
        03 MARRIED-TABLE OCCURS 7 TIMES
           ASCENDING KEY IS M-MIN-RANGE M-MAX-RANGE
           INDEXED BY IND-0, IND-3.
           04  M-MIN-RANGE              PIC 99999.
           04  M-MAX-RANGE              PIC 99999.
           04  M-TAX                    PIC 99999.
           04  M-PERCENT                PIC V99.
Example 4.19 shows how to perform a serial search.
Example 4.20 shows how to use SEARCH while varying an index other than the first index.
Example 4.20. Using SEARCH and Varying an Index Other than the First Index
01   TAXABLE-INCOME PIC 9(6) VALUE 50000.
01   FED-TAX-DEDUCTION PIC 9(6).
PROCEDURE DIVISION.
BEGIN.
       PERFORM MARRIED.
       DISPLAY FED-TAX-DEDUCTION.
       STOP RUN.
MARRIED.
       IF TAXABLE-INCOME < 04800
               MOVE ZEROS TO FED-TAX-DEDUCTION
               GO TO END-FED-COMP.
       SET IND-3 TO 1.
       SEARCH MARRIED-TABLE VARYING IND-3 AT END
               GO TO TABLE-3-ERROR
          WHEN TAXABLE-INCOME = M-MIN-RANGE(IND-3)
               MOVE M-TAX(IND-3) TO FED-TAX-DEDUCTION
          WHEN TAXABLE-INCOME < M-MAX-RANGE(IND-3)
               COMPUTE FED-TAX-DEDUCTION =
                   M-TAX(IND-3) + (TAXABLE-INCOME - M-TAX(IND-3)) *
                   M-PERCENT(IND-3).
   .
   .
   .
Example 4.21 shows how to use SEARCH while varying an index data item.
Example 4.21. Using SEARCH and Varying an Index Data Item
01   TAXABLE-INCOME PIC 9(6) VALUE 50000.
01   FED-TAX-DEDUCTION PIC 9(6).
PROCEDURE DIVISION.
BEGIN.
       PERFORM SINGLE.
       DISPLAY FED-TAX-DEDUCTION.
       STOP RUN.
SINGLE.
       IF TAXABLE-INCOME < 02500
               GO TO END-FED-COMP.
       SET IND-2 TO 1.
       SEARCH SINGLES-TABLE VARYING TEMP-IND AT END
               GO TO TABLE-2-ERROR
          WHEN TAXABLE-INCOME = S-MIN-RANGE(IND-2)
               MOVE S-TAX(IND-2) TO FED-TAX-DEDUCTION
          WHEN TAXABLE-INCOME < S-MAX-RANGE(IND-2)
               MOVE S-TAX(IND-2) TO FED-TAX-DEDUCTION
               SUBTRACT S-MIN-RANGE(IND-2) FROM TAXABLE-INCOME
               MULTIPLY TAXABLE-INCOME BY S-PERCENT(IND-2) ROUNDED
               ADD TAXABLE-INCOME TO FED-TAX-DEDUCTION.

   .
   .
   .
Example 4.22 shows how to use SEARCH while varying an index not associated with the target table.
Example 4.22. Using SEARCH and Varying an Index not Associated with the Target Table
01   TAXABLE-INCOME PIC 9(6) VALUE 50000.
01   FED-TAX-DEDUCTION PIC 9(6).
PROCEDURE DIVISION.
BEGIN.
       PERFORM SINGLE.
       DISPLAY FED-TAX-DEDUCTION.
       STOP RUN. SINGLE.
        IF TAXABLE-INCOME < 02500
               GO TO END-FED-COMP.
        SET IND-2 TO 1.
        SEARCH SINGLES-TABLE VARYING IND-0 AT END
               GO TO TABLE-2-ERROR
          WHEN TAXABLE-INCOME = S-MIN-RANGE(IND-2)
               MOVE S-TAX(IND-2) TO FED-TAX-DEDUCTION
          WHEN TAXABLE-INCOME < S-MAX-RANGE(IND-2)
               MOVE S-TAX(IND-2) TO FED-TAX-DEDUCTION
               SUBTRACT S-MIN-RANGE(IND-2) FROM TAXABLE-INCOME
               MULTIPLY TAXABLE-INCOME BY S-PERCENT(IND-2) ROUNDED
               ADD TAXABLE-INCOME TO FED-TAX-DEDUCTION.

   .
   .
   .
Example 4.23 shows how to perform a serial search without using the VARYING phrase.
Example 4.23. Doing a Serial Search Without Using the VARYING Phrase
01   NR-DEPENDENTS     PIC 9(2)  VALUE 3.
01   GROSS-WAGE        PIC 9(6)  VALUE 50000.
01   TAXABLE-INCOME    PIC 9(6)  VALUE 50000.
01   FED-TAX-DEDUCTION PIC9(6).
01   MARITAL-STATUS    PIC X     VALUE "M".
PROCEDURE DIVISION.
BEGIN.
       PERFORM FED-DEDUCT-COMPUTATION.
       DISPLAY TAXABLE-INCOME.
       STOP RUN.
FED-DEDUCT-COMPUTATION.
          SET IND-1 TO 1.
          SEARCH FED-ALLOWANCES AT END
                 GO TO TABLE-1-ERROR
            WHEN ALLOWANCE-NUMBER(IND-1) = NR-DEPENDENTS
                 SUBTRACT ALLOWANCE(IND-1) FROM GROSS-WAGE
                     GIVING TAXABLE-INCOME ROUNDED.
          IF MARITAL-STATUS = "M"
                 GO TO MARRIED.
MARRIED.

   .
   .
   .
Example 4.24 shows how to perform a multiple-key, binary search.

Chapter 5. Using the STRING, UNSTRING, and INSPECT Statements

The STRING, UNSTRING, and INSPECT statements give your VSI COBOL programs the following capabilities:
  • Concatenating data using the STRING statement (Section 5.1)

  • Separating data using the UNSTRING statement (Section 5.2)

  • Examining and replacing characters using the INSPECT statement (Section 5.3)

5.1. Concatenating Data Using the STRING Statement

The STRING statement concatenates the contents of one or more sending items into a single receiving item.

The statement has many forms; the simplest is equivalent in function to a nonnumeric MOVE statement. Consider the following example:
STRING FIELD1 DELIMITED BY SIZE INTO FIELD2. 
If the two items are the same size, or if the sending item (FIELD1) is larger, the statement is equivalent to the following statement:
MOVE FIELD1 TO FIELD2.

If the sending item of the string is shorter than the receiving item, the compiler does not replace unused positions in the receiving item with spaces. Thus, the STRING statement can leave some portion of the receiving item unchanged.

The receiving item of the string must be an elementary alphanumeric item with no JUSTIFIED clause or editing characters in its description. Thus, the data movement of the STRING statement always fills the receiving item with the sending item from left to right and with no editing insertions.

5.1.1. Multiple Sending Items

The STRING statement can concatenate a series of sending items into one receiving item. Consider the following example:
STRING FIELD1A FIELD1B FIELD1C DELIMITED BY SIZE
                           INTO FIELD2.

In this sample STRING statement, FIELD1A, FIELD1B, and FIELD1C are all sending items. The compiler moves them to the receiving item (FIELD2) in the order in which they appear in the statement, from left to right, resulting in the concatenation of their values.

If FIELD2 is not large enough to hold all three items, the operation stops when it is full. If the operation stops while moving one of the sending items, the compiler ignores the remaining characters of that item and any other sending items not yet processed. For example, if FIELD2 is filled while it is receiving FIELD1B, the compiler ignores the rest of FIELD1B and all of FIELD1C.

If the sending items do not fill the receiving item, the operation stops when the last character of the last sending item (FIELD1C) is moved. It does not alter the contents nor space-fill the remaining character positions of the receiving item.

The sending items can be nonnumeric literals and figurative constants (except for ALL literal). Example 5.1 sets up an address label by stringing the data items CITY, STATE, and ZIP into ADDRESS-LINE. The figurative constant SPACE and the literal period (.) are used to separate the information.
Example 5.1. Using the STRING Statement and Literals
01 ADDRESS-GROUP.
   03 CITY           PIC X(20).
   03 STATE          PIC XX.
   03 ZIP            PIC X(5).
01 ADDRESS-LINE      PIC X(31).
      .
      .
      .
PROCEDURE DIVISION.
BEGIN.
   STRING CITY SPACE STATE ". " SPACE ZIP
        DELIMITED BY SIZE INTO ADDRESS-LINE.
      .
      .
      .

5.1.2. Using the DELIMITED BY Phrase

Although the sending items of the STRING statement are fixed in size at compile time, they are frequently filled with spaces. For example, if a 20-character city item contains the text MAYNARD followed by 13 spaces, the STRING statement using the DELIMITED BY SIZE phrase would move the text (MAYNARD) and the unwanted 13 spaces (assuming the receiving item is at least 20 characters long). The DELIMITED BY phrase, written with a data name or literal, eliminates this problem.

The delimiter can be a literal, a data item, a figurative constant, or the word SIZE. It cannot, however, be ALL literal, because ALL literal has an indefinite length. When the phrase contains the word SIZE, the compiler moves each sending item in total, until it either exhausts the characters in the sending item or fills the receiving item.

If you use the code in Example 5.1, and CITY is a 20-character item, the result of the STRING operation might look like Figure 5.1.

Figure 5.1. Results of the STRING Operation
Results of the STRING Operation
A more attractive and readable report can be produced by having the STRING operation produce this line:
AYER, MA. 01432 
To accomplish this, use the figurative constant SPACE as a delimiter on the sending item:
MOVE 1 TO P.
STRING CITY DELIMITED BY SPACE
        INTO ADDRESS-LINE WITH POINTER P.
STRING ", " STATE ". " ZIP
        DELIMITED BY SIZE
        INTO ADDRESS-LINE WITH POINTER P.

This example makes use of the POINTER phrase (see Section 5.1.3). The first STRING statement moves data characters until it encounters a space character - a match of the delimiter SPACE. The second STRING statement supplies the literal, the 2-character STATE item, another literal, and the 5-character ZIP item.

The delimiter can be varied for each item within a single STRING statement by repeating the DELIMITED BY phrase after each of the sending item names to which it applies. Thus, the shorter STRING statement in the following example has the same effect as the two STRING statements in the preceding example. (Placing the operands on separate source lines has no effect on the operation of the statement, but it improves program readability and simplifies debugging.)
STRING CITY DELIMITED BY SPACE
        ", " STATE ". "
        ZIP DELIMITED BY SIZE
        INTO ADDRESS-LINE.
The sample STRING statement cannot handle 2-word city names, such as San Francisco, because the compiler considers the space between the two words as a match for the delimiter SPACE. A longer delimiter, such as two or three spaces (nonnumeric literal), can solve this problem. Only when a sequence of characters matches the delimiter does the movement stop for that data item. With a 2-character delimiter, the same statement can be rewritten in a simpler form:
STRING CITY ", " STATE ". " ZIP
        DELIMITED BY "  " INTO ADDRESS-LINE. 

Because only the CITY item contains two consecutive spaces, the delimiter's search of the other items will always be unsuccessful, and the effect is the same as moving the full item (delimiting by SIZE).

Data movement under control of a data name or literal generally executes more slowly than data movement delimited by SIZE.

Remember, the remainder of the receiving item is not space-filled, as with a MOVE statement. If ADDRESS-LINE is to be printed on a mailing label, for example, the STRING statement should be preceded by the statement:
MOVE SPACES TO ADDRESS-LINE.

This statement guarantees a space-fill to the right of the concatenated result. Alternatively, the last item concatenated by the STRING statement can be an item previously set to SPACES. This sending item must either be moved under control of a delimiter other than SPACE or use the value of POINTER and reference modification.

5.1.3. Using the POINTER Phrase

Although the STRING statement normally starts scanning at the leftmost position of the receiving item, the POINTER phrase makes it possible to start scanning at another point within the item. The scanning, however, continues left to right. Consider the following example:
MOVE 5 TO P.
STRING FIELD1A FIELD1B DELIMITED BY SIZE
        INTO FIELD2 WITH POINTER P.

The value of P determines the starting character position in the receiving item. In this example, the 5 in P causes the program to move the first character of FIELD1A into character position 5 of FIELD2 (the leftmost character position of the receiving item is character position 1), and leave positions 1 to 4 unchanged.

When the STRING operation is complete, P points to one character position beyond the last character replaced in the receiving item. If FIELD1A and FIELD1B are both four characters long, P contains a value of 13 (5+4+4) when the operation is complete (assuming that FIELD2 is at least 13 characters long).

5.1.4. Using the OVERFLOW Phrase

When the SIZE option of the DELIMITED BY phrase controls the STRING operation, and the pointer value is either known or the POINTER phrase is not used, you can add the PICTURE sizes of sending items together at program development time to see if the receiving item is large enough to hold the sending items. However, if the DELIMITED BY phrase contains a literal or an identifier, or if the pointer value is not predictable, it can be difficult to tell whether or not the size of the receiving item will be large enough at run time. If the size of the receiving item is not large enough, an overflow can occur.

An overflow occurs when the receiving item is full and the program is either about to move a character from a sending item or is considering a new sending item. Overflow can also occur if, during the initialization of the statement, the pointer contains a value that is either less than 1 or greater than the length of the receiving item. In this case, the program moves no data to the receiving item and terminates the operation immediately.

The ON OVERFLOW phrase at the end of the STRING statement tests for an overflow condition:
STRING FIELD1A FIELD1B DELIMITED BY "C"
        INTO FIELD2 WITH POINTER PNTR
        ON OVERFLOW GO TO 200-STRING-OVERFLOW.

The ON OVERFLOW phrase cannot distinguish the overflow caused by a bad initial value in the pointer from the overflow caused by a receiving item that is too short. Only a separate test preceding the STRING statement can distinguish between the two.

Additionally, even if an overflow condition does not exist, you can use the NOT ON OVERFLOW phrase to branch to or execute other sections of code.

Example 5.2 illustrates the overflow condition.
Example 5.2. Sample Overflow Condition
DATA DIVISION.
     .
     .
     .
01 FIELD1 PIC XXX VALUE "ABC".
01 FIELD2 PIC XXXX.
PROCEDURE DIVISION.
           .
           .
           .
1.    STRING FIELD1 QUOTE DELIMITED BY SIZE INTO FIELD2
             ON OVERFLOW DISPLAY "overflow at 1".
2.    STRING FIELD1 FIELD1 DELIMITED BY SIZE INTO FIELD2
             ON OVERFLOW DISPLAY "overflow at 2".
3.    STRING FIELD1 FIELD1 DELIMITED BY "C" INTO FIELD2
             ON OVERFLOW DISPLAY "overflow at 3".
4.    STRING FIELD1 FIELD1 FIELD1 FIELD1
             DELIMITED BY "B" INTO FIELD2 ON OVERFLOW DISPLAY "overflow at 4".
5.    STRING FIELD1 FIELD1 "D" DELIMITED BY "C"
             INTO FIELD2 ON OVERFLOW DISPLAY "overflow at 5".
6.    MOVE 2 TO P.
       MOVE ALL QUOTES TO FIELD2.
       STRING FIELD1 "AC" DELIMITED BY "C"
             INTO FIELD2 WITH POINTER P ON OVERFLOW DISPLAY "overflow at 6". 
The STRING statement numbers in Example 5.2 point to the line number results shown in Table 5.1.
Table 5.1. Results of Sample Overflow Statements

Value of FIELD2 After the STRING Operation

Overflow?

1. ABC "

No

2. ABCA

Yes

3. ABAB

No

4. AAAA

No

5. ABAB

Yes

6. "ABA

No

5.1.5. Common STRING Statement Errors

The following are common errors made when writing STRING statements:
  • Using the word TO instead of INTO

  • Failing to include the DELIMITED BY SIZE phrase

  • Failing to initialize the pointer

  • Initializing the pointer to 0 instead of 1

  • Permitting the pointer to get out of range (negative or larger than the size of the receiving field)

  • Failing to provide for space-filling of the receiving item when it is desirable

  • Using the pointer as a subscript without fully understanding subscript evaluation

5.2. Separating Data Using the UNSTRING Statement

The UNSTRING statement disperses the contents of a single sending item into one or more receiving items.

The statement has many forms; the simplest is equivalent in function to a nonnumeric MOVE statement. Consider the following example:
UNSTRING FIELD1 INTO FIELD2.
Regardless of the relative sizes of the two items, the sample statement is equivalent to the following MOVE statement:
MOVE FIELD1 TO FIELD2.

The sending item (FIELD1) can be either (1) a group item, or (2) an alphanumeric or alphanumeric edited elementary item. The receiving item (FIELD2) can be alphabetic, alphanumeric, or numeric, but it cannot specify any type of editing.

If the receiving item is numeric, it must be DISPLAY usage. The PICTURE character-string of a numeric receiving item can contain any of the legal numeric description characters except P and the editing characters. The UNSTRING statement moves the sending item to the numeric receiving item as if the sending item had been described as an unsigned integer. It automatically truncates or zero-fills as required.

If the receiving item is not numeric, the statement follows the rules for elementary nonnumeric MOVE statements. It left-justifies the data in the receiving item, truncating or space-filling as required. If the data description of the receiving item contains a JUSTIFIED clause, the compiler right-justifies the data, truncating or space-filling to the left as required.

5.2.1. Multiple Receiving Items

The UNSTRING statement can disperse one sending item into several receiving items. Consider the following example of the UNSTRING statement written with multiple receiving items:
UNSTRING FIELD1 INTO FIELD2A FIELD2B FIELD2C.

The compiler-generated code performs the UNSTRING operation by scanning across FIELD1, the sending item, from left to right. When the number of characters scanned equals the number of characters in the receiving item, the scanned characters are moved into that item and the next group of characters is scanned for the next receiving item.

If each of the receiving items in the preceding example (FIELD2A, FIELD2B, and FIELD2C) is 5 characters long, and FIELD1 is 15 characters long, FIELD1 is scanned until the number of characters scanned equals the size of FIELD2A (5). Those first five characters are moved to FIELD2A, and scanning is resumed at the sixth character position in FIELD1. Next, FIELD1 is scanned from character position 6, until the number of scanned characters equals the size of FIELD2B (five). The sixth through the tenth characters are then moved to FIELD2B, and the scanner is set to the next (eleventh) character position in FIELD1. For the last move in this example, characters 11 to 15 of FIELD1 are moved into FIELD2C.

Each data movement acts as an individual MOVE statement, the sending item of which is an alphanumeric item equal in size to the receiving item. If the receiving item is numeric, the move operation converts the data to numeric form. For example, consider what would happen if the items under discussion had the data descriptions and were manipulating the values shown in Table 5.2.
Table 5.2. Values Moved into the Receiving Items Based on the Sending Item Value

FIELD1
PIC X(15)
VALUE IS:

FIELD2A
PIC X(5)

FIELD2B
PIC S9(5)
LEADING
SEPARATE

FIELD2C
PIC S999V99

ABCDE1234512345

ABCDE

+12345

3450{

XXXXX0000100123

XXXXX

+00001

1230{

FIELD2A is an alphanumeric item. Therefore, the statement simply conducts an elementary nonnumeric move with the first five characters.

FIELD2B, however, has a leading separate sign that is not included in its size. Thus, the compiler moves only five numeric characters and generates a positive sign (+) in the separate sign position.

FIELD2C has an implied decimal point with two character positions to the right of it, plus an overpunched sign on the low-order digit. The sending item should supply five numeric digits. However, because the sending item is alphanumeric, the compiler treats it as an unsigned integer; it truncates the two high-order digits and supplies two zero digits for the decimal positions. Furthermore, it supplies a positive overpunch sign, making the low-order digit a +0 (ASCII { ). There is no way to have the UNSTRING statement recognize a sign character or a decimal point in the sending item in a single statement.

If the sending item is shorter than the sum of the sizes of the receiving items, the compiler ignores the remaining receiving items. If the compiler reaches the end of the sending item before it reaches the end of one of the receiving items, it moves the scanned characters into that receiving item. It either left-justifies and fills the remaining character positions with spaces for alphanumeric data, or else it decimal point-aligns and zero-fills the remaining character positions for numeric data.

Consider the following statement with reference to the corresponding PICTURE character-strings and values in Table 5.3:
UNSTRING FIELD1 INTO FIELD2A FIELD2B.
FIELD2A is a 3-character alphanumeric item. It receives the first three characters of FIELD1 (ABC) in every operation. FIELD2B, however, runs out of characters every time before filling, as Table 5.3 illustrates.
Table 5.3. Handling a Short Sending Item

FIELD1
PIC X(6)
VALUE IS:

FIELD2B
PICTURE IS

FIELD2B
Value After UNSTRING
Operation

ABCDEF

XXXXX

DEF

S99999

0024F

ABC246

S9V999

600{

S9999 LEADING SEPARATE

+0246

5.2.2. Controlling Moved Data Using the DELIMITED BY Phrase

The size of the data to be moved can be controlled by a delimiter, rather than by the size of the receiving item. The DELIMITED BY phrase supplies the delimiter characters.

UNSTRING delimiters can be literals, figurative constants (including ALL literal), or identifiers (identifiers can even be subscripted data names). This section describes the use of these three types of delimiters. Subsequent sections cover multiple delimiters, the COUNT phrase, and the DELIMITER phrase.

Consider the following sample UNSTRING statement with the figurative constant SPACE as a delimiter:
UNSTRING FIELD1 DELIMITED BY SPACE
         INTO FIELD2.

In this example, the compiler scans the sending item (FIELD1), searching for a space character. If it encounters a space, it moves all of the scanned (nonspace) characters that precede that space to the receiving item (FIELD2). If it finds no space character, it moves the entire sending item. When the compiler has determined the size of the sending item, it moves the contents of that item following the rules for the MOVE statement, truncating or zero-filling as required.

Table 5.4 shows the results of the following UNSTRING operation that uses a literal asterisk delimiter:
UNSTRING FIELD1 DELIMITED BY "*"
         INTO FIELD2.

Table 5.4. Results of Delimiting with an Asterisk

FIELD1
PIC X(6)
VALUE IS:

FIELD2
PICTURE IS:

FIELD2
Value After
UNSTRING

  • Legend: # = space
 

XXX

ABC

ABCDEF

X(7)

ABCDEF

 

XXX JUSTIFIED

DEF

******

XXX

###

*ABCDE

XXX

###

A*****

XXX JUSTIFIED

##A

246***

S9999

024F

12345*

S9999 TRAILING SEPARATE

2345+

2468**

S999V9 LEADING SEPARATE

+4680

*246**

9999

0000

If the delimiter matches the first character in the sending item, the compiler considers the size of the sending item to be zero. The operation still takes place, however, and fills the receiving item with spaces (if it is nonnumeric) or zeros (if it is numeric).

A delimiter can also be applied to an UNSTRING statement that has multiple receiving items:
UNSTRING FIELD1 DELIMITED BY SPACE
         INTO FIELD2A FIELD2B. 

The compiler generates code that scans FIELD1 searching for a character that matches the delimiter. If it finds a match, it moves the scanned characters to FIELD2A and sets the scanner to the next character position to the right of the character that matched. The compiler then resumes scanning FIELD1 for a character that matches the delimiter. If it finds a match, it moves all of the characters between the character that first matched the delimiter and the character that matched on the second scan, and sets the scanner to the next character position to the right of the character that matched.

The DELIMITED BY phrase handles additional items in the same manner as it handled FIELD2B.

Table 5.5 illustrates the results of the following delimited UNSTRING operation into multiple receiving items:
UNSTRING FIELD1 DELIMITED BY "*"
         INTO FIELD2A FIELD2B.

Table 5.5. Results of Delimiting Multiple Receiving Items
 

Values After UNSTRING Operation

FIELD1
PIC X(8)
VALUE IS:

FIELD2A
PIC X(3)

FIELD2B
PIC X(3)

  • Legend: # = space

ABC*DEF*

ABC

DEF

ABCDE*FG

ABC

FG#

A*B****

A##

B##

*AB*CD**

###

AB#

**ABCDEF

###

###

A*BCDEFG

A##

BCD

ABC**DEF

ABC

###

A******B

A##

###

The previous examples illustrate the limitations of a single-character delimiter. To overcome these limitations, a delimiter of more than one character or a delimiter preceded by the word ALL may be used.

Table 5.6 shows the results of the following UNSTRING operation using a 2-character delimiter:
UNSTRING FIELD1 DELIMITED BY "**"
         INTO FIELD2A FIELD2B.

Table 5.6. Results of Delimiting with Two Asterisks
 

Values After UNSTRING Operation

FIELD1
PIC X(8)
VALUE IS:

FIELD2A
PIC XXX

FIELD2B
PIC XXX
JUSTIFIED

  • Legend: # = space

ABC**DEF

ABC

DEF

A*B*C*D*

A*B

###

AB***C*D

AB#

C*D

AB**C*D*

AB#

*D*

AB**CD**

AB#

#CD

AB***CD*

AB#

CD*

AB*****CD

AB#

###

Unlike the STRING statement, the UNSTRING statement accepts the ALL literal as a delimiter. When the word ALL precedes the delimiter, the action of the UNSTRING statement remains essentially the same as with one delimiter until the scanning operation finds a match. At this point, the compiler scans farther, looking for additional consecutive strings of characters that also match the delimiter item. It considers the ALL delimiter to be one, two, three, or more adjacent repetitions of the delimiter item. Table 5.7 shows the results of the following UNSTRING operation using an ALL delimiter:
UNSTRING FIELD1 DELIMITED BY ALL "*"
         INTO FIELD2A FIELD2B.

Table 5.7. Results of Delimiting with ALL Asterisks
 

Values After UNSTRING Operation

FIELD1
PIC X(8)
VALUE IS:

FIELD2A
PIC XXX

FIELD2B
PIC XXX
JUSTIFIED

  • Legend: # = space

ABC*DEF*

ABC

DEF

ABC**DEF

ABC

DEF

A******F

A##

##F

A*F*****

A##

##F

A*CDEFG

A##

EFG

Table 5.8 shows the results of the following UNSTRING operation that combines ALL with a 2-character delimiter:
UNSTRING FIELD1 DELIMITED BY ALL "**"
         INTO FIELD2A FIELD2B.

Table 5.8. Results of Delimiting with ALL Double Asterisks
 

Values After UNSTRING Operation

FIELD1
PIC X(8)
VALUE IS:

PIC XX

PIC XXX
JUSTIFIED

  • Legend: # = space

ABC**DEF

ABC

DEF

AB**DE**

AB#

#DE

A***D***

A##

#*D

A*******

A##

##*

In addition to unchangeable delimiters, such as literals and figurative constants, delimiters can be designated by identifiers. Identifiers permit variable delimiting. Consider the following sample statement:
UNSTRING FIELD1 DELIMITED BY DEL1
         INTO FIELD2A FIELD2B.

The data name DEL1 must be alphanumeric; it can be either a group or an elementary item. If the delimiter contains a subscript, the subscript may vary as a side effect of the UNSTRING operation.

5.2.2.1. Multiple Delimiters

The UNSTRING statement scans a sending item, searching for a match from a list of delimiters. This list can contain ALL delimiters and delimiters of various sizes. Delimiters in the list must be connected by the word OR.

The following sample statement unstrings a sending item into three receiving items. The sending item consists of three strings separated by one of the following: (1) any number of spaces, (2) a comma followed by a single space, (3) a single comma, (4) a tab character, or (5) a carriage-return character. The comma and space must precede the single comma in the list if the comma and space are to be recognized.
UNSTRING FIELD1 DELIMITED BY ALL SPACE
         OR ", "
         OR ","
         OR TAB
         OR CR
         INTO FIELD2A FIELD2B FIELD2C.
Table 5.9 shows the potential of this statement. The tab and carriage-return characters represent single-character items containing the ASCII horizontal tab and carriage-return characters.
Table 5.9. Results of Multiple Delimiters

FIELD1
PIC X(12)

FIELD2A
PIC XXX

FIELD2B
PIC 9999

FIELD2C PIC XXX

  • Legend: # = space

A,0,C Return

A##

0000

C##

A Tab456, E

A##

0456

E##

A 3 9

A##

0003

9##

A Tab Tab B Return

A##

0000

B##

A,,C

A##

0000

C##

ABCD, 4321,Z

ABC

4321

Z##

5.2.3. Using the COUNT Phrase

The COUNT phrase keeps track of the size of the sending string and stores the length in a user-supplied data area.

The length of a delimited sending item can vary from zero to the full length of the item. Some programs require knowledge of this length. For example, some data is truncated if it exceeds the size of the receiving item, so the program's logic requires this information.

The COUNT phrase follows the receiving item. Consider the following example:
UNSTRING FIELD1 DELIMITED BY ALL "*"
         INTO FIELD2A COUNT IN COUNT2A
         FIELD2B COUNT IN COUNT2B
         FIELD2C.

The compiler generates code that counts the number of characters between the leftmost position of FIELD1 and the first asterisk in FIELD1 and places the count into COUNT2A. The delimiter is not included in the count because it is not a part of the string. The data preceding the first asterisk is then moved into FIELD2A.

The compiler then counts the number of characters between the last contiguous asterisk in the first scan and the next asterisk in the second scan, and places the count in COUNT2B. The data between the delimiters of the second scan is moved into FIELD2B.

The third scan begins at the first character after the last contiguous asterisk in the second scan. Any data between the delimiters of this scan is moved to FIELD2C.

The COUNT phrase should be used only where it is needed. In this example, the length of the string moved to FIELD2C is not needed, so no COUNT phrase follows it.

If the receiving item is shorter than the value placed in the count item, the code truncates the sending string. If the number of integer positions in a numeric item is smaller than the value placed into the count item, high-order numeric digits have been lost. If a delimiter match is found on the first character examined, a zero is placed in the count item.

The COUNT phrase can be used only in conjunction with the DELIMITED BY phrase.

5.2.4. Saving UNSTRING Delimiters Using the DELIMITER Phrase

The DELIMITER phrase causes the actual character or characters that delimited the sending item to be stored in a user-supplied data area. This phrase is most useful when:
  • The UNSTRING statement contains a delimiter list.

  • Any one of the delimiters in the list might have delimited the item.

  • Program logic flow depends on the delimiter match found.

By using the DELIMITER and COUNT phrases, you can make the flow of program logic dependent on both the size of the sending string and the delimiter terminating the string.

To use the DELIMITER phrase, follow the receiving item name with the words DELIMITER IN and an identifier. The compiler generates code that places the delimiter character in the area named by the identifier. Consider the following sample UNSTRING statement:
UNSTRING FIELD1 DELIMITED BY ","
         OR TAB
         OR ALL SPACE
         OR CR
         INTO FIELD2A DELIMITER IN DELIMA
         FIELD2B DELIMITER IN DELIMB
         FIELD2C.

After moving the first sending string to FIELD2A, the character (or characters) that delimited that string is placed in DELIMA. In this example, DELIMA contains either a comma, a tab, a carriage return, or any number of spaces. Because the delimiter string is moved under the rules of the elementary nonnumeric MOVE statement, the compiler truncates or space-fills with left or right justification.

The second sending string is then moved to FIELD2B and its delimiting character is placed into DELIMB.

When a sending string is delimited by the end of the sending item rather than by a match on a delimiter, the delimiter string is of zero length and the DELIMITER item is space-filled. The phrase should be used only where needed. In this example, the character that delimits the last sending string is not needed, so no DELIMITER phrase follows FIELD2C.

The data item named in the DELIMITER phrase must be described as an alphanumeric item. It can contain editing characters, and it can also be a group item.

When you use both DELIMITER and COUNT phrases, the DELIMITER phrase must precede the COUNT phrase. Both of the data items named in these phrases can be subscripted or indexed. If they are subscripted, the subscript can be varied as a side effect of the UNSTRING operation.

5.2.5. Controlling UNSTRING Scanning Using the POINTER Phrase

Although the UNSTRING statement scan usually starts at the leftmost position of the sending item, the POINTER phrase lets you control the character position where the scan starts. Scanning, however, remains left to right.

When a sending item is to be unstrung into multiple receiving items, the choice of delimiters and the size of subsequent receiving items depends on the size of the first sending string and the character that delimited that string. Thus, the program needs to move the first sending item, hold its scanning position in the sending item, and examine the results of the operation to determine how to handle the sending items that follow.

This is done by using an UNSTRING statement with a POINTER phrase that fills only the first receiving item. When the first string has been moved to a receiving item, the compiler begins the next scanning operation one character beyond the delimiter that caused the interruption. The program examines the new position, the receiving item, the delimiter value, and the sending string size. It resumes the scanning operation by executing another UNSTRING statement with the same sending item and pointer data item. In this way, the UNSTRING statement moves one sending string at a time, with the form of each succeeding move depending on the context of the preceding string of data.

The POINTER phrase must follow the last receiving item in the UNSTRING statement. You are responsible for initializing the pointer before the UNSTRING statement executes. Consider the following two UNSTRING statements with their accompanying POINTER phrases and tests:
MOVE 1 TO PNTR. UNSTRING FIELD1 DELIMITED BY ":"
          OR TAB
          OR CR
          OR ALL SPACE
          INTO FIELD2A DELIMITER IN DELIMA COUNT IN LSIZEA
          WITH POINTER PNTR.
 IF LSIZEA = 0 GO TO NO-LABEL-PROCESS.
 IF DELIMA =  ":"
          IF PNTR > 8 GO TO BIG-LABEL-PROCESS
          ELSE GO TO LABEL-PROCESS.
 IF DELIMA = TAB GO TO BAD-LABEL PROCESS.
          .
          .
          .
 UNSTRING FIELD1 DELIMITED BY ... WITH POINTER PNTR.

PNTR contains the current position of the scanner in the sending item. The second UNSTRING statement uses PNTR to begin scanning the additional sending strings in FIELD1.

Because the compiler considers the leftmost character to be character position 1, the value of PNTR can be used to examine the next character. To do this, describe the sending item as a table of characters and use PNTR as a sending item subscript. This is shown in the following example:
 01 FIELD1.
    02 FIELD1-CHAR OCCURS 40 TIMES.
    .
    .
    .
    UNSTRING FIELD1
             .
             .
             .
             WITH POINTER PNTR.
    IF FIELD1-CHAR(PNTR) = "X" ...
Another way to examine the next character of the sending item is to use the UNSTRING statement to move the character to a 1-character receiving item:
 UNSTRING FIELD1
          .
          .
          .
          WITH POINTER PNTR.
 UNSTRING FIELD1 INTO CHAR1 WITH POINTER PNTR.
 SUBTRACT 1 FROM PNTR.
 IF CHAR1 = "X" ... 

The program must decrement PNTR by 1 to work, because the second UNSTRING statement increments the pointer by 1.

The program must initialize the POINTER phrase data item before the UNSTRING statement uses it. The compiler will terminate the UNSTRING operation if the initial value of the pointer is less than one or greater than the length of the sending item. Such a pointer value causes an overflow condition. Overflow conditions are discussed in Section 5.2.7.

5.2.6. Counting UNSTRING Receiving Items Using the TALLYING Phrase

The TALLYING phrase counts the number of receiving items that received data from the sending item.

When an UNSTRING statement contains several receiving items, there are not always as many sending strings as there are receiving items. The TALLYING phrase provides a convenient method for keeping a count of how many receiving items actually received strings. The following example shows how to use the TALLYING phrase:
 MOVE 0 TO RCOUNT.
 UNSTRING FIELD1 DELIMITED BY ","
          OR ALL SPACE
          INTO FIELD2A
               FIELD2B
               FIELD2C
               FIELD2D
               FIELD2E
               TALLYING IN RCOUNT.

If the compiler has moved only three sending strings when it reaches the end of FIELD1, it adds 3 to RCOUNT. The first three receiving items (FIELD2A, FIELD2B, and FIELD2C) contain data from the UNSTRING operation, but the last two (FIELD2D and FIELD2E) do not.

The UNSTRING statement does not initialize the TALLYING data item. The TALLYING data item always contains the sum of its initial contents plus the number of receiving items receiving data. Thus, you might want to initialize the tally count before each use.

You can use the POINTER and TALLYING phrases together in the same UNSTRING statement, but the POINTER phrase must precede the TALLYING phrase. Both phrases must follow all of the item names, the DELIMITER phrase, and the COUNT phrase. The data items for both phrases must contain numeric integers without editing characters or the symbol P in their PICTURE character-strings; both data items can be either COMP or DISPLAY usage. They can be signed or unsigned and, if they are DISPLAY usage, they can contain any desired sign option.

5.2.7. Exiting an UNSTRING Statement Using the OVERFLOW Phrase

The OVERFLOW phrase detects the overflow condition and causes an imperative statement to be executed when it detects the condition. An overflow condition exists when:
  • The UNSTRING statement is about to execute and its pointer data item contains a value less than one or greater than the size of the sending item. The compiler generates code that executes the OVERFLOW phrase before it moves any data, and the values of all the receiving items remain unchanged.

  • Data still remains in the sending item after the UNSTRING statement has filled all the receiving items. The compiler executes the OVERFLOW phrase after it has executed the UNSTRING statement. The value of each receiving item is updated, but some data is still unmoved.

If the UNSTRING operation causes the scan to move past the rightmost position of the sending item (thus exhausting it), the compiler does not execute the OVERFLOW phrase.

The following set of instructions causes program control to execute the UNSTRING statement repeatedly until it exhausts the sending item. The TALLYING data item is a subscript that indexes the receiving item. Compare this loop with the previous loop, which accomplishes the same thing:
       MOVE 1 TO TLY PNTR.
 PAR1. UNSTRING FIELD1 DELIMITED BY ","
                OR CR
                INTO FIELD2(TLY) WITH POINTER PNTR
                TALLYING IN TLY
                ON OVERFLOW GO TO PAR1.

5.2.8. Common UNSTRING Statement Errors

The most common errors made when writing UNSTRING statements are as follows:
  • Leaving the OR connector out of a delimiter list

  • Misspelling or interchanging the words DELIMITED and DELIMITER

  • Writing the DELIMITER and COUNT phrases in the wrong order when both are present (DELIMITER must precede COUNT)

  • Omitting the word INTO (or writing it as TO) before the receiving item list

  • Repeating the word INTO in the receiving item list as shown in this example:
     UNSTRING FIELD1 DELIMITED BY SPACE
              OR TAB
              INTO FIELD2A DELIMITER IN DELIMA
              INTO FIELD2B DELIMITER IN DELIMB
              INTO FIELD2C DELIMITER IN DELIMC.
  • Writing the POINTER and TALLYING phrases in the wrong order (POINTER must precede TALLYING)

  • Failing to understand the rules concerning subscript evaluation

5.3. Examining and Replacing Characters Using the INSPECT Statement

The INSPECT statement examines the character positions in an item and counts or replaces certain characters (or groups of characters) in that item.

Like the STRING and UNSTRING operations, INSPECT operations scan across the item from left to right. Included in the INSPECT statement is an optional phrase that allows scanning to begin or terminate upon detection of a delimiter match. This feature allows scanning to begin within the item, as well as at the leftmost position.

5.3.1. Using the TALLYING and REPLACING Options of the INSPECT Statement

The TALLYING operation, which counts certain characters in the item, and the REPLACING operation, which replaces certain characters in the item, can be applied either to the characters in the delimited area of the item being inspected, or to only those characters that match a given character string or strings under stated conditions. Consider the following sample statements, both of which cause a scan of the complete item:
 INSPECT FIELD1 TALLYING TLY FOR ALL "B".
 INSPECT FIELD1 REPLACING ALL SPACE BY ZERO.

The first statement causes the compiler to scan FIELD1 looking for the character B. Each time a B is found, TLY is incremented by 1.

The second statement causes the compiler to scan FIELD1 looking for spaces. Each space found is replaced with a zero.

The TALLYING and REPLACING phrases support both single and multiple arguments. For example, both of the following statements are valid:
 INSPECT FIELD1 TALLYING TLY FOR ALL "A" "B" "C".
 INSPECT FIELD1 REPLACING ALL "A" "B" "C" BY "D".

You can use both the TALLYING and REPLACING phrases in the same INSPECT statement. However, when used together, the TALLYING phrase must precede the REPLACING phrase. An INSPECT statement with both phrases is equivalent to two separate INSPECT statements. In fact, the compiler compiles such a statement into two distinct INSPECT statements. To simplify debugging, write the two phrases in separate INSPECT statements.

5.3.2. Restricting Data Inspection Using the BEFORE/AFTER Phrase

The BEFORE/AFTER phrase acts as a delimiter and can restrict the area of the item being inspected.

The following sample statement counts only the zeros that precede the percent sign (%) in FIELD1:
 INSPECT FIELD1 TALLYING TLY
         FOR ALL ZEROS BEFORE "%".
The delimiter (the percent sign in the preceding sample statement) can be a single character, a string of characters, or any figurative constant. Furthermore, it can be either an identifier or a literal.
  • If the delimiter is an identifier, it must be an elementary data item of DISPLAY usage. It can be alphabetic, alphanumeric, or numeric, and it can contain editing characters. The compiler always treats the item as if it had been described as an alphanumeric string. It does this by implicit redefinition of the item, as described in Section 5.3.3.

  • If the delimiter is a literal, it must be nonnumeric.

The compiler repeatedly compares the delimiter characters against an equal number of characters in the item being inspected. If none of the characters matches the delimiter, or if too few characters remain in the rightmost position of the item for a full comparison, the compiler considers the comparison to be unequal.

The examples of the INSPECT statement in Figure 5.2 illustrate the way the delimiter character finds a match in the item being inspected. The underlined characters indicate the portion of the item the statement inspects as a result of the delimiters of the BEFORE and AFTER phrases. The remaining portion of the item is ignored by the INSPECT statement.

Figure 5.2. Matching Delimiter Characters to Characters in a Field
Matching Delimiter Characters to Characters in a Field

The ellipses represent the position of the TALLYING or REPLACING phrase. The compiler generates code that scans the item for a delimiter match before it scans for the inspection operation (TALLYING or REPLACING), thus establishing the limits of the operation before beginning the actual inspection. Section 5.3.4.1 further describes the separate scan.

5.3.3. Implicit Redefinition

The compiler requires that certain items referred to by the INSPECT statement be alphanumeric items. If one of these items is described as another data class, the compiler implicitly redefines that item so the INSPECT statement can handle it as an alphanumeric string as follows:
  • If the item is alphabetic, alphanumeric edited, or unsigned numeric, the item is redefined as alphanumeric. This is a compile-time operation; no data movement occurs at run time.

  • If the item is signed numeric, the compiler generates code that first removes the sign and then redefines the item as alphanumeric. If the sign is a separate character, that character is ignored, essentially shortening the item, and that character does not participate in the implicit redefinition. If the sign is an overpunch on the leading or trailing digit, the sign value is removed and the character is left with only the numeric value that was stored in it.

The compiler alters the digit position containing the sign before beginning the INSPECT operation and restores it to its former value after the operation. If the sign's digit position does not contain a valid ASCII signed numeric digit, redefinition causes the value to change.

Table 5.10 shows these original, altered, and restored values.

The compiler never moves an implicitly redefined item from its storage position. All redefinition occurs in place.

The position of an implied decimal point on numeric quantities does not affect implicit redefinition.
Table 5.10. Values Resulting from Implicit Redefinition

Original Value

Altered Value

Restored Value

} (173)

0 (60)

} (173)

A (101)

1 (61)

A (101)

B (102)

2 (62)

B (102)

C (103)

3 (63)

C (103)

D (104)

4 (64)

D (104)

 

E (105)

5 (65)

E (105)

F (106)

6 (66)

F (106)

G (107)

7 (67)

G (107)

H (110)

8 (70)

H (110)

I (111)

9 (71)

I (111)

 

{ (175)

0 (60)

{ (175)

J (112)

1 (61)

J (112)

K (113)

2 (62)

K (113)

L (114)

3 (63)

L (114)

M (115)

4 (64)

M (115)

 

N (116)

5 (65)

N (116)

O (117)

6 (66)

O (117)

P (120)

7 (67)

P (120)

Q (121)

8 (70)

Q (121)

R (122)

9 (71)

R (122)

 

0 (60)

0 (60)

} (173)

1 (61)

1 (61)

A (101)

2 (62)

2 (62)

B (102)

3 (63)

3 (63)

C (103)

4 (64)

4 (64)

D (104)

 

5 (65)

5 (65)

E (105)

6 (66)

6 (66)

F (106)

7 (67)

7 (67)

G (107)

8 (70)

8 (70)

H (110)

9 (71)

9 (71)

I (111)

 

All other values

0 (60)

} (173)

5.3.4. Examining the INSPECT Operation

Regardless of the type of inspection (TALLYING or REPLACING), the INSPECT statement has only one method for inspecting the characters in the item. This section analyzes the INSPECT statement and describes this inspection method.

Figure 5.3 shows an example of the INSPECT statement. The item to be inspected must be named (FIELD1 in our example), and the item name must be followed by a TALLYING phrase (TALLYING TLY). The TALLY phrase must be followed by one or more identifiers or literals (B). These identifiers or literals comprise the arguments. More than one argument makes up the argument list.

Figure 5.3. Sample INSPECT Statement
Sample INSPECT Statement

Each argument in an argument list can have other items associated with it. Thus, each argument that is used in a TALLYING operation must have a tally counter (such as TLY in the example) associated with it. The tally counter is incremented each time it matches the argument with a character or group of characters in the item being inspected.

Each argument in an argument list used in a REPLACING operation must have a replacement item associated with it. The compiler generates code that uses the replacement item to replace each string of characters in the item that matches the argument. Figure 5.4 shows a typical REPLACING phrase (with $ as the replacement item).

Figure 5.4. Typical REPLACING Phrase
Typical REPLACING Phrase

Each argument in an argument list used with either a TALLYING or REPLACING operation can have a delimiter item (BEFORE/AFTER phrase) associated with it. If the delimiter item is not present, the argument is applied to the entire item. If the delimiter item is present, the argument is applied only to that portion of the item specified by the BEFORE/AFTER phrase.

5.3.4.1. Setting the Scanner

The INSPECT operation begins by setting the scanner to the leftmost character position of the item being inspected. It remains on this character until an argument has been matched with a character (or characters) or until all arguments have failed to find a match at that position.

5.3.4.2. Active/Inactive Arguments

When an argument has a BEFORE/AFTER phrase associated with it, that argument has a delimiter and may not be eligible to participate in a comparison at every position of the scanner. Thus, each argument in the argument list has an active/inactive status at any given setting of the scanner.

For example, an argument that has an AFTER phrase associated with it starts the INSPECT operation in an inactive state. The delimiter of the AFTER phrase must find a match before the argument can participate in the comparison. When the delimiter finds a match, the compiler generates code that retains the character position beyond the matched character string; then, when the scanner reaches or passes this position, the argument becomes active. This is shown in the following example:
 INSPECT FIELD1 TALLYING TLY
         FOR ALL "B" AFTER "X". 

If FIELD1 has a value of ABABXZBA, the argument B remains inactive until the scanner finds a match for delimiter X. Thus, argument B remains inactive while the compiler generates code that scans character positions 1 to 5. At character position 5, delimiter X finds a match, and because the character position beyond the matched delimiter character is the point at which the argument becomes active, argument B is compared for the first time at character position 6. It finds a successful match at character position 7, causing TLY to be incremented by 1.

Table 5.11 shows an INSPECT...TALLYING statement that is scanning FIELD1, tallying in TLY, and looking for the arguments and delimiters listed in the left column. Assume that TLY is initialized to 0.
Table 5.11. Relationship Among INSPECT Argument, Delimiter, Item Value, and Argument Active Position

Argument and Delimiter

FIELD1 Value

Argument Active at Position

Contents of TLY After Scan

ALL

BXBXXXXBB

6

2

B AFTER XX

XXXXXXXX

3

0

BXBXBBBBXX

never

0

 

BXBXXBXXB

6

2

X AFTER XX

XXXXXXXX

3

6

BBBBBBXX

never

0

 

BXYBXBXX

7

0

B AFTER XB

XBXBXBXB

3

3

BBBBBBXB

never

0

 

XXXXBXXXX

6

0

BX AFTER XB

XXXXBBXXX

6

1

XXBXXXXBX

4

1

When an argument has an associated BEFORE delimiter, the inactive/active states reverse roles: the argument is in an active state when the scanning begins and becomes inactive at the character position that matches the delimiter. Regardless of the presence of the BEFORE delimiter, an argument becomes inactive when the scanner approaches the rightmost position of the item and the remaining characters are fewer in number than the characters in the argument. In such a case, the argument cannot possibly find a match in the item, so it becomes inactive.

Because the BEFORE/AFTER delimiters are found on a separate scan of the item, the compiler generates code that recognizes and sets up the delimiter boundaries before it scans for an argument match; therefore, the same characters can be used as arguments and delimiters in the same phrase.

5.3.4.3. Finding an Argument Match

The compiler generates code that selects arguments from the argument list in the order in which they appear in the list. If the first one it selects is an active argument, and the conditions stated in the INSPECT statement allow a comparison, the compiler generates code that compares it to the character at the scanner's position. If the active argument does not find a match, the compiler generates code that takes the next active argument from the list and compares that to the same character. If none of the active arguments finds a match, the scanner moves one position to the right and begins the inspection operation again with the first active argument in the list. The inspection operation terminates at the rightmost position of the item.

When an active argument finds a match, the compiler ignores any remaining arguments in the list and conducts the TALLYING or REPLACING operation on the character. The scanner moves to a new position and the next inspection operation begins with the first argument in the list. The INSPECT statement can contain additional conditions, which are described later in this section; without them, however, the argument match is allowed to take place, and inspection continues following the match.

The compiler updates the scanner by adding the size of the matching argument to it. This moves the scanner to the next character beyond the string of characters that matched the argument. Thus, once an active argument matches a string of characters, the statement does not inspect those character positions again unless program control executes the entire statement again.

5.3.5. The TALLYING Phrase

An INSPECT statement that contains a TALLYING phrase counts the occurrences of various character strings under certain stated conditions. It keeps the count in a user-designated item called a tally counter.

5.3.5.1. The Tally Counter

The identifier following the word TALLYING designates the tally counter. The identifier can be subscripted or indexed. The data item must be a numeric integer without any editing or P characters; it can be COMP or DISPLAY usage, and it can be signed (separate or overpunched).

Each time the tally argument matches the delimited string being inspected, the compiler adds 1 to the tally counter.

You can initialize the tally counter to any numeric value. The INSPECT statement does not initialize it.

5.3.5.2. The Tally Argument

The tally argument specifies a character-string (or strings) and a condition under which that string should be compared to the delimited string being inspected.

The CHARACTERS form of the tally argument specifies that every character in the delimited string being inspected should be considered to match an imaginary character that serves as the tally argument. This increments the tally counter by a value that equals the size of the delimited string. For example, the following statement causes TLY to be incremented by the number of characters that precede the first comma, regardless of what those characters are:
 INSPECT FIELD1 TALLYING TLY FOR
         CHARACTERS BEFORE ",".
The ALL and LEADING forms of the tally argument specify a particular character-string (or strings), which can be represented by either a literal or an identifier. The tally argument character-string can be any length; however, each character of the argument must match a character in the delimited string before the compiler considers the argument matched.
  • A literal character-string must be either nonnumeric or a figurative constant (other than ALL literal). A figurative constant, such as SPACE or ZERO, represents a single character and can be written as " " or 0 with the same effect.

  • An identifier must be an elementary item of DISPLAY usage. It can be any data class. However, if it is not alphanumeric, the compiler performs an implicit redefinition of the item. This redefinition is identical to the BEFORE/AFTER delimiter redefinition discussed in Section 5.3.2.

The words ALL and LEADING supply conditions that further delimit the inspection operation:
  • ALL specifies that every match that the search argument finds in the delimited character string be counted in the tally counter. When a literal follows the word ALL, it does not have the same meaning as the figurative constant, ALL literal. The ALL literal meaning of ALL , is a string of consecutive commas (as many as the context of the statement requires). ALL , used as a tally argument means count each comma without regard to adjacent characters.

  • LEADING specifies that only adjacent matches of the TALLY argument at the leftmost position of the delimited character string be counted. At the first failure to match the tally argument, the compiler terminates counting and causes the argument to become inactive. The sample statement INSPECT...TALLYING (scanning FIELD1, tallying in TLY, and looking for the arguments and delimiters listed in the left column) gives the results in Table 5.12 (if the program initializes TLY to 0).


Table 5.12. LEADING Delimiter of the Inspection Operation

Argument and Delimiter

FIELD1 Value

Contents of TLY After Scan

F***0**F

2

F**0F**

0

LEADING * AFTER 0.

F**F**0

0

0***F**

3

 

F**0**F***

1

F**F0***FF

1

LEADING ** AFTER 0.

F**F0****F**

2

F**F**0*

0

5.3.5.3. The Tally Argument List

One INSPECT...TALLYING statement can contain more than one tally argument, and each argument can have a separate BEFORE/AFTER phrase and tally counter associated with it. These tally arguments with their associated tally counters and BEFORE/AFTER phrases form an argument list. The manner in which this list is processed affects the action of any given tally argument.

The following examples show INSPECT statements with argument lists. The text with each example explains how that list is processed.
 INSPECT FIELD1 TALLYING T FOR
         ALL ","
         ALL "."
         ALL ";".
These three tally arguments have the same tally counter, T, and are active over the entire item being inspected. Thus, the preceding statement adds the total number of commas, periods, and semicolons in FIELD1 to the initial value of T. Because the TALLYING phrase supports multiple arguments and only one counter is used, the previous statement could have been written as follows:
 INSPECT FIELD1 TALLYING T FOR ALL "," "." ";". 

 INSPECT FIELD1 TALLYING
         T1 FOR ALL ","
         T2 FOR ALL "."
         T3 FOR ALL ";".
Each tally argument in this statement has its own tally counter and is active over the entire item being inspected. Thus, the preceding statement adds the total number of commas in FIELD1 to the initial value of T1, the total number of periods to the initial value of T2, and the number of semicolons to T3.
 INSPECT FIELD1 TALLYING
         T1 FOR ALL "," AFTER "A"
         T2 FOR ALL "." BEFORE "B"
         T3 FOR ALL ";".

Each tally argument in the preceding statement has its own tally counter; the first two arguments have delimiter phrases, and the last one is active over the entire item being inspected. Thus, the first argument is initially inactive and becomes active only after the scanner encounters an A; the second argument begins the scan in the active state but becomes inactive after a B has been encountered; and the third argument is active during the entire scan of FIELD1.

Table 5.13 shows various values of FIELD1 and the contents of the three tally counters after the scan of the previous statements. Assume that the counters are initialized to 0 before the INSPECT statement.
Table 5.13. Results of the Scan with Separate Tallies
 

Contents of Tally Counters After Scan

FIELD1 Value

T1

T2

T3

A.C;D.E,F

1

2

1

A.B.C.D

0

1

0

A,B,C,D

3

0

0

A;B;C;D

0

0

3

*,B,C,D

0

0

0

The BEFORE/AFTER phrase applies only to the argument that precedes it and delimits the item for that argument only. Each BEFORE/AFTER phrase causes a separate scan of the item to determine the limits of the item for its corresponding argument.

5.3.5.4. Interference in Tally Argument Lists

When several tally arguments contain one or more identical characters active at the same time, they may interfere with each other, so that when one of the arguments finds a match, the scanner steps past any other matching characters, preventing those characters from being considered for a match.

The following two identical tally arguments do not interfere with each other because they are not active at the same time. The first A in FIELD1 causes the first argument to become inactive and the second argument to become active:
 MOVE 0 TO T1 T2. INSPECT FIELD1 TALLYING
         T1 FOR ALL "," BEFORE "A"
         T2 FOR ALL "," AFTER "A".
However, the next identical tally arguments interfere with each other since both are active at the same time:
 INSPECT FIELD1 TALLYING
         T1 FOR ALL ","
         T2 FOR ALL "," AFTER "A".

For any given position of the scanner, the arguments are applied to FIELD1 in the order in which they appear in the statement. When one of them finds a match, the scanner moves to the next position and ignores the remaining arguments in the argument list. Each comma in FIELD1 causes T1 to be incremented by 1 and the second argument to be ignored. Thus, T1 always contains an accurate count of all the commas in FIELD1, and T2 is always unchanged.

The following INSPECT statement arguments only partially interfere with each other:
 INSPECT FIELD1 TALLYING
         T2 FOR ALL "," AFTER "A"
         T1 FOR ALL ",".

The first argument does not become active until the scanner encounters an A. The second argument tallies all commas that precede the A. After the A, the first argument counts all commas and causes the second argument to be ignored. Thus, T1 contains the number of commas that precede the first A, and T2 contains the number of commas that follow the first A. This statement works well as written, but it could be difficult to debug.

The following three examples show that one INSPECT statement cannot count any character more than once. Thus, when you use the same character in more than one argument of an argument list, consider the possibility of interference and choose the order of the arguments carefully. The solution may require two or more INSPECT statements. Consider the following problem:
 INSPECT FIELD1 TALLYING
         T1 FOR ALL "AB"
         T2 FOR ALL "BC".

If FIELD1 contains ABCABC after the scan, T1 is incremented by 2, and T2 is unaltered. The successful matching of the argument includes each B in the item. Each match resets the scanner to the character position to the right of the B, so that the second argument is never successfully matched. The results remain the same even if the order of the arguments is reversed. Only separate INSPECT statements can develop the desired counts.

Sometimes you can use the interference characteristics of the INSPECT statement to your advantage. Consider the following sample argument list:
 MOVE 0 TO T4 T3 T2 T1. INSPECT FIELD1 TALLYING
         T4 FOR ALL "****"
         T3 FOR ALL "***"
         T2 FOR ALL "**"
         T1 FOR ALL "*".

The argument list counts all of the asterisks in FIELD1 in four different tally counters. T4 counts the number of times that four asterisks occur together; T3 counts the number of times three asterisks appear together; T2 counts double asterisks; and T1 counts singles.

If FIELD1 contains a string of more than four consecutive asterisks, the argument list breaks the string into groups of four and counts them in T4. It then counts the less-than-four remainder in T3, T2, or T1.

Reversing the order of the arguments in this list causes T1 to count all of the asterisks, and T2, T3, and T4 to remain unchanged.

When the LEADING condition is used with an argument in the argument list, that argument becomes inactive as soon as it fails to be matched in the item being inspected. Therefore, when two arguments in an argument list contain one or more identical characters and one of the arguments has a LEADING condition, the argument with the LEADING condition should appear first. Consider the following sample statement:
 MOVE 0 TO T1 T2. INSPECT FIELD1 TALLYING
         T1 FOR LEADING "*"
         T2 FOR ALL "*".

T1 counts only leading asterisks in FIELD1; the occurrence of any other character causes the first tally argument to become inactive. T2 keeps a count of any remaining asterisks in FIELD1.

Reversing the order of the arguments in the following statement results in an argument list that can never increment T1:
 INSPECT FIELD1 TALLYING
         T2 FOR ALL "*"
         T1 FOR LEADING "*".

If the first character in FIELD1 is not an asterisk, neither argument can match it, and the second argument becomes inactive. If the first character in FIELD1 is an asterisk, the first argument matches it and causes the second argument to be ignored. The first character in FIELD1 that is not an asterisk fails to match the first argument, and the second argument becomes inactive because it has not found a match in any of the preceding characters.

An argument with both a LEADING condition and a BEFORE phrase can sometimes successfully delimit the item being inspected, as in the following example:
 MOVE 0 TO T1 T2. INSPECT FIELD1 TALLYING
         T1 FOR LEADING SPACES
         T2 FOR ALL "   " BEFORE "."
         T2 FOR ALL "  " BEFORE "."
         T2 FOR ALL " " BEFORE ".".
 IF T2 > 0 ADD 1 TO T2. 

These statements count the number of words in the English statement in FIELD1, assuming that no more than three spaces separate the words in the sentence, that the sentence ends with a period, and that the period immediately follows the last word. When FIELD1 has been scanned, T2 contains the number of spaces between the words. Because a count of the spaces renders a number that is one less than the number of words, the conditional statement adds 1 to the count.

The first argument removes any leading spaces, counting them in a different tally counter. This shortens FIELD1 by preventing the application of the second to the fourth arguments until the scanner finds a nonspace character. The BEFORE phrase on each of the other arguments causes them to become inactive when the scanner reaches the period at the end of the sentence. Thus, the BEFORE phrases shorten FIELD1 by making the second to the fourth arguments inactive before the scanner reaches the rightmost position of FIELD1. If the sentence in FIELD1 is indented with tab characters instead of spaces, a second LEADING argument can count the tab characters. For example:
 INSPECT FIELD1 TALLYING
         T1 FOR LEADING SPACES
         T1 FOR LEADING TAB
         T2 FOR ALL "    "
         .
         .
         .
When an argument list contains a CHARACTERS argument, it should be the last argument in the list. Because the CHARACTERS argument always matches the item, it prevents the application of any arguments that follow in the list. However, as the last argument in an argument list, it can count the remaining characters in the item being inspected. Consider the following example.
 MOVE 0 TO T1 T2 T3 T4 T5.
 INSPECT FIELD1 TALLYING
         T1 FOR LEADING SPACES
         T2 FOR ALL "." BEFORE ","
         T3 FOR ALL "+" BEFORE ","
         T4 FOR ALL "-" BEFORE ","
         T5 FOR CHARACTERS BEFORE ",".
If FIELD1 is known to contain a number in the form frequently used to input data, it can contain a plus or minus sign, and a decimal point; furthermore, the number can be preceded by spaces and terminated by a comma. When this statement is compiled and executed, it delivers the following results:
  • T1 contains the number of leading spaces.

  • T2 contains the number of periods.

  • T3 contains the number of plus signs.

  • T4 contains the number of minus signs.

  • T5 contains the number of remaining characters (assumed to be numeric).

The sum of T1 to T5, plus 1, gives the character position occupied by the terminating comma.

5.3.6. Using the REPLACING Phrase

When an INSPECT statement contains a REPLACING phrase, that statement selectively replaces characters or groups of characters in the designated item.

The REPLACING phrase names a search argument of one or more characters and a condition under which the string can be applied to the item being inspected. Associated with the search argument is the replacement value, which must be the same length as the search argument. Each time the search argument finds a match in the item being inspected, under the condition stated, the replacement value replaces the matched characters.

A BEFORE/AFTER phrase can be used to delimit the area of the item being inspected. A search argument applies only to the delimited area of the item.

5.3.6.1. The Search Argument

The search argument of the REPLACING phrase names a character string and a condition under which the character string should be compared to the delimited string being inspected.

The CHARACTERS form of the search argument specifies that every character in the delimited string being inspected should be considered to match an imaginary character that serves as the search argument. Thus, the replacement value replaces each character in the delimited string. For example:
 INSPECT ITEMA REPLACING CHARACTERS ...
The ALL, LEADING, and FIRST forms of the search argument specify a particular character string, which can be represented by a literal or an identifier. The search argument character string can be any length. However, each character of the argument must match a character in the delimited string before the compiler considers the argument matched. For example:
 INSPECT ITEMA REPLACING ALL ...
The necessary literal and identifier characteristics are as follows:
  • A literal character string must be either nonnumeric or a figurative constant (other than ALL literal). A figurative constant, such as SPACE or ZERO, represents a single character and can be written as or 0 with the same effect. Because a figurative constant represents a single character, the replacement value must be one character long.

  • An identifier must represent an elementary item of DISPLAY usage. It can be any class. However, if it is not alphabetic, the compiler performs an implicit redefinition of the item. This redefinition is identical to the BEFORE/AFTER delimiter redefinition discussed in Section 5.3.2.

The words ALL, LEADING, and FIRST supply conditions that further delimit the inspection operation:
  • ALL specifies that each match the search argument finds in the delimited character string is replaced by the replacement value. When a literal follows the word ALL, it does not have the same meaning as the figurative constant, ALL literal. The figurative constant meaning of ALL , is a string of consecutive commas, as many as the context of the statement requires. ALL , as a search argument of the REPLACING phrase means replace each comma without regard to adjacent characters.

  • LEADING specifies that only adjacent matches of the search argument at the leftmost position of the delimited character-string be replaced. At the first failure to match the search argument, the compiler terminates the replacement operation and causes the argument to become inactive.

  • FIRST specifies that only the leftmost character string that matches the search argument be replaced. After the replacement operation, the search argument containing this condition becomes inactive.

5.3.6.2. The Replacement Value

Whenever the search argument finds a match in the item being inspected, the matched characters are replaced by the replacement value. The word BY followed by an identifier or literal specifies the replacement value. For example:
 INSPECT ITEMA REPLACING ALL "A" BY "X" ALL "D" BY "X".

The replacement value must always be the same size as its associated search argument.

If the replacement value is a literal character-string, it must be either a nonnumeric literal or a figurative constant (other than ALL literal). A figurative constant represents as many characters as the length of the search argument requires.

If the replacement value is an identifier, it must be an elementary item of DISPLAY usage. It can be any class. However, if it is not alphanumeric, the compiler conducts an implicit redefinition of the item. This redefinition is the same as the BEFORE/AFTER redefinition discussed in Section 5.3.2.

5.3.6.3. The Replacement Argument

The replacement argument consists of the search argument (with its condition and character-string), the replacement value, and an optional BEFORE/AFTER phrase, as shown in Figure 5.5.

Figure 5.5. The Replacement Argument
The Replacement Argument

5.3.6.4. The Replacement Argument List

One INSPECT...REPLACING statement can contain more than one replacement argument. Several replacement arguments form an argument list, and the manner in which the list is processed affects the action of any given replacement argument.

The following examples show INSPECT statements with replacement argument lists. The text following each one tells how that list will be processed.
 INSPECT FIELD1 REPLACING
         ALL "," BY SPACE
         ALL "." BY SPACE
         ALL ";" BY SPACE.
The previous three replacement arguments all have the same replacement value, SPACE, and are active over the entire item being inspected. The statement replaces all commas, periods, and semicolons with space characters and leaves all other characters unchanged.
 INSPECT FIELD1 REPLACING
         ALL "0" BY "1"
         ALL "1" BY "0". 
Each of these two replacement arguments has its own replacement value and is active over the entire item being inspected. The statement exchanges zeros for 1s and 1s for zeros. It leaves all other characters unchanged.
 INSPECT FIELD1 REPLACING
         ALL "0" BY "1" BEFORE SPACE
         ALL "1" BY "0" BEFORE SPACE.

Note

When a search argument finds a match in the item being inspected, the code replaces that character-string and scans to the next position beyond the replaced characters. It ignores the remaining arguments and applies the first argument in the list to the character-string in the new position. Thus, it never inspects the new value that was supplied by the replacement operation. Because of this, the search arguments can have the same values as the replacement arguments with no chance of interference.

The statement also exchanges zeros and 1s. Here, however, the first space in FIELD1 causes both arguments to become inactive.
 INSPECT FIELD1 REPLACING
         ALL "0" BY "1" BEFORE SPACE
         ALL "1" BY "0" BEFORE SPACE
         CHARACTERS BY "*" BEFORE SPACE.

The first space causes the three replacement arguments to become inactive. This argument list exchanges zeros for 1s, 1s for zeros, and asterisks for all other characters in the delimited area. If the BEFORE phrase is removed from the third argument, that argument will remain active across all of FIELD1. Within the area delimited by the first space character, the third argument replaces all characters except 1s and zeros with asterisks. Beyond this area, it replaces all characters (including the space that delimited FIELD1 for the first two arguments, and any zeros and 1s) with asterisks.

5.3.6.5. Interference in Replacement Argument Lists

When several search arguments, all active at the same time, contain one or more identical characters, they can interfere with each other - and consequently affect the replacement operation. This interference is similar to the interference that occurs between tally arguments.

The action of a search argument is never affected by the BEFORE/AFTER delimiters of other arguments, because the compiler scans for delimiter matches before it scans for replacement operations.

The action of a search argument is never affected by the characters of any replacement value, because the scanner does not inspect the replaced characters again during execution of the INSPECT statement. Interference between search arguments, therefore, depends on the order of the arguments, the values of the arguments, and the active/inactive status of the arguments. The discussion in Section 5.3.5.4 about interference in tally argument lists generally applies to replacement arguments as well.

The following rules help minimize interference in replacement argument lists:
  1. Place search arguments with LEADING or FIRST conditions at the start of the list.

  2. Place any arguments with the CHARACTERS condition at the end of the list.

  3. Consider the order of appearance of any search arguments that contain identical characters.

5.3.7. Using the CONVERTING Option

When an INSPECT statement contains a CONVERTING phrase, that statement selectively replaces characters or groups of characters in the designated item; it executes as if it were a Format 2 INSPECT statement with a series of ALL phrases. (Refer to the INSPECT statement formats in the VSI COBOL Reference Manual.)

An example of the use of the CONVERTING phrase follows:
 IDENTIFICATION DIVISION.
 PROGRAM-ID.  PROGX.
 ENVIRONMENT DIVISION.
 DATA DIVISION.
 WORKING-STORAGE SECTION.
 01 X PIC X(28).
 PROCEDURE DIVISION.
 A.
     MOVE "ABC*ABC*ABC ABC@ABCABC" TO X.
     INSPECT X CONVERTING "ABC" TO "XYZ"
             AFTER "*" BEFORE "@".
     DISPLAY X.
     STOP RUN.
      X before INSPECT executes      X after INSPECT executes
       ABC*ABC*ABC ABC@ABCABC         ABC*XYZ*XYZ XYZ@ABCABC

5.3.8. Common INSPECT Statement Errors

Programmers most commonly make the following errors when writing INSPECT statements:
  • Leaving the FOR out of an INSPECT...TALLYING statement

  • Using the word WITH instead of BY in the REPLACING phrase

  • Failing to initialize the tally counter

  • Omitting the word ALL before the comparison character-string

Chapter 6. Processing Files and Records

The VSI COBOL I/O system offers you a wide range of record management techniques while remaining transparent to you. You can select one of several file organizations and access modes, each of which is suited to a particular application. The file organizations available through VSI COBOL are sequential, line sequential, relative, and indexed. The access modes are sequential, random, and dynamic.

This chapter introduces you to the following VSI COBOL I/O features:

For information about low-volume or terminal screen I/O using the ACCEPT and DISPLAY statements, see Chapter 11 and refer to the VSI COBOL Reference Manual.

The operating system provides you with I/O services for handling, controlling, and spooling your I/O needs or requests. VSI COBOL, through the I/O system, provides you with extensive capabilities for data storage, retrieval, and modification.

On the OpenVMS Alpha and OpenVMS I64, the VSI COBOL I/O system consists of the Run-Time Library (RTL), which accesses Record Management Services (RMS). (On OpenVMS VAX, COBOL-generated code accesses RMS directly.) Refer to the VSI OpenVMS Record Management Utilities Reference Manual and the VSI OpenVMS Record Management Services Reference Manual for more information about RMS.

On the UNIX, the VSI COBOL I/O system consists of the Run-Time Library (RTL) and facilities of UNIX. In addition, the facilities of a third-party ISAM package are required for any use of ORGANIZATION INDEXED.

6.1. Defining Files and Records

A file is a collection of related records. You can specify the organization and size of a file as well as the record format and physical record size. The system creates a file with these characteristics and stores them with the file. Any program that accesses a file must specify the same characteristics as those that the system stored for that file when creating it.

A record is a group of related data elements. The space a record needs on a physical device depends on the file organization, the record format, and the number of bytes the record contains.

File organization is described in Section 6.1.1. Record format is described in Section 6.1.2.

6.1.1. File Organization

VSI COBOL supports the following four types of file organization:
  • SEQUENTIAL—This organization requires that records be referenced in sequence from the first record to the last. This organization is useful for programs that normally access each record serially. (See the the section called “Sequential File Organization” section in this chapter.)

  • LINE SEQUENTIAL (Alpha, I64)— This organization is essentially the same as sequential. Line sequential allows you to treat files as collections of variable length records, with each record containing one line of printable characters. This organization is useful for programs that access files created by text editors and similar programs. (See the the section called “Line Sequential File Organization (Alpha, I64)” section in this chapter.)

  • RELATIVE—This organization lets you access records randomly, or sequentially by record number values. While this organization is more flexible than sequential organization, it is less flexible than indexed organization because you cannot insert a record in the middle of your file unless you have an empty cell to contain it. (See the the section called “Relative File Organization” section in this chapter.)

  • INDEXED—This organization lets you access records randomly or sequentially, by primary and alternate key values. This is a useful way to organize a file in which records will be added, changed, or deleted upon demand. (See the the section called “Indexed File Organization” section in this chapter.)


Note

On UNIX, a third-party product is required for INDEXED runtime support. Refer to the Read Before Installing … letter for up-to-date details on how to obtain the INDEXED runtime support.

Table 6.1 summarizes the advantages and disadvantages of these file organizations.
Table 6.1. VSI COBOL File Organizations – Advantages and Disadvantages

File Organizations

Advantages

Disadvantages

Sequential

Uses disk and memory efficiently

Allows sequential access only

Provides optimal usage if the application accesses all records sequentially on each run

Allows records to be added only to the end of a file

Provides the most flexible record format

 

Allows READ/WRITE sharing

 

Allows data to be stored on many types of media, in a device-independent manner

 

Allows easy file extension

 

Line Sequential (Alpha, I64)

Most efficient storage format

Allows sequential access only

Compatible with text editors

Used for printable characters only

 

Open Mode I/O is not allowed

Relative

Allows sequential, random, and dynamic access

Allows data to be stored on disk only

Provides random record deletion and insertion

Requires that record cells be the same size

Allows READ/WRITE sharing

Indexed

Allows sequential, random, and dynamic access

Allows data to be stored on disk only

Allows random record deletion and insertion on the basis of a user-supplied key

Requires more disk space

Allows READ/WRITE sharing

Uses more memory to process records

Allows variable-length records to change length on update

Generally requires multiple disk accesses to randomly process a record

Allows easy file extension

 

Sequential File Organization

Sequential input/output, in which records are written and read in sequence, is the simplest and most common form of I/O. It can be performed on all I/O devices, including magnetic tape, disk, terminals, and line printers.

Sequential files consist of records that are arranged in the order in which they were written to the file. Figure 6.1 illustrates sequential file organization.

Figure 6.1. Sequential File Organization
Sequential File Organization

Sequential files always contain an end-of-file (EOF) indication. On magnetic tapes, it is the EOF mark; on disk, it is a counter in the file header that designates the end of the file. VSI COBOL statements can write over the EOF mark and, thus, extend the length of the file. Because the EOF indicates the end of useful data, VSI COBOL provides no method for reading beyond it, even though the amount of space reserved for the file exceeds the amount actually used.

Occasionally a file with sequential organization is so large that it requires more than one volume. An end-of-volume (EOV) label marks the end of recorded information on each volume and signals the file system to switch to a new volume. On multiple-volume files, the EOF mark appears only once, at the end of the last record on the last volume. Figure 6.2 depicts a multiple-volume, sequential file.

Figure 6.2. A Multiple-Volume, Sequential File
A Multiple-Volume, Sequential File
When you select the medium for your sequential file, consider the following:
  • Speed of access—Tape is significantly slower than disk. In general, most removable media storage (magnetic, optical, and so forth) devices are slower than your fixed disks.

  • Frequency of use—Use removable media devices to store relatively static files, and save your fixed disk space for more dynamic files.

  • Cost—Fixed disks are generally more expensive than removable media devices. The more frequently you plan to access the data, the easier it is to justify maintaining the data on your fixed disks. For example, data that is accessed daily must be kept on readily available disks; quarterly or annual data could be offloaded to removable media.

  • Transportability—Use removable media if you need to use the file across systems that have no common disk devices (this technique is commonly referred to as sneakernetting).

Refer to the VSI OpenVMS I/O User's Reference Manual or the ltf(4) manpage for more information on magnetic tape formats.

Line Sequential File Organization (Alpha, I64)

Line sequential file structure is essentially similar to the structure of sequential files, with the major difference being record length. Figure 6.3 illustrates line sequential file organization.

Figure 6.3. Line Sequential File Organization (Alpha, I64)
Line Sequential File Organization (Alpha, I64)

A line sequential file consists of records of varying lengths arranged in the order in which they were written to the file. Each record is terminated with a newline character. The newline character is a line feed record terminator ('0A' hex).

Each record in a line sequential file should contain only printable characters and should not be written with a WRITE statements that contains either a BEFORE ADVANCING or AFTER ADVANCING statement.

Record length is determined by the maximum record length in the FD entry in the FILE-CONTROL section and the number of characters in a line (not including the record terminator).

When your VSI COBOL program reads a line from a line sequential file that is shorter than the record area, it reads up to the record terminator, discards the record terminator, and pads the rest of the record with a number of spaces necessary to equal the record's specified length. When your program reads a line from a line sequential file that is longer than the record area, it reads the number of characters necessary to fill the record area. The next READ, if any, will begin at the next printable character in the file that is not a record terminator.

Line sequential file organization is useful in reading and printing files that were created by an editor or word processor, which typically do not write fixed-length records.

Relative File Organization

A relative file consists of fixed-size record cells and uses a key to retrieve its records. The key, called a relative key, is an integer that specifies the record's storage cell or record number within the file. It is analogous to the subscript of a table. Relative file processing is available only on disk devices.

Any record on a relative file (unlike a sequential file) can be accessed with one READ operation. Also, relative files allow the program to read forward with respect to the current relative key. In addition to random access by relative key, relative files also permit you to delete and update records by relative key. Relative files are used primarily when records must be accessed in random order and the records can easily be associated with numbers that give the relative positions in the file.

In relative file organization, not every cell must contain a record. Although each cell occupies one record space, a field preceding the record on the storage medium indicates whether or not that cell contains a valid record. Thus, a file can contain fewer records than it has cells, and the empty cells can be anywhere in the file.

The numerical order of the cells remains the same during all operations on a relative file. However, accessing statements can move a record from one cell to another, delete a record from a cell, insert new records into empty cells, or rewrite existing cells.

With relative file processing, the I/O system organizes a file as a series of fixed-sized record cells. Cell size is based on the size specified as the maximum permitted length for a record in the file. The I/O system considers these cells as successively numbered from 1 (the first) to n (the last). A cell's relative record number (RRN) represents its location relative to the beginning of the file.

Because cell numbers in a relative file are unique, they can be used to identify both the cell and the record (if any) occupying that cell. Thus, record number 1 occupies the first cell in the file, record number 21 occupies the twenty-first cell, and so forth. Figure 6.4 illustrates relative file organization.

Figure 6.4. Relative File Organization
Relative File Organization

Relative files are used like tables. Their advantage over tables is that their size is limited to disk space rather than memory space. Also, their information can be saved from run to run. Relative files are best for records that are easily associated with ascending, consecutive numbers (so that the program conversion from data to cell number is easy), such as months (record keys 1 to 12), or the 50 U.S. states (record keys 1 to 50).

Indexed File Organization

An indexed file uses primary and alternate keys in the record to retrieve the contents of that record. VSI COBOL allows sequential, random, and dynamic access to records. You access each record by one of its primary or alternate keys. Indexed file processing is available only on disk devices.

Unlike the sequential ordering of records in a sequential file or the relative positioning of records in a relative file, the physical location of records in indexed file organization is transparent to the program. You can add new records to an indexed file without recreating the file. You can also delete records, making room for new records.

Indexed file organization allows you to use a key to uniquely identify a record within the file. The location and length of the key are identical in all records. When creating an indexed file, you must select the data items to be the keys. Selecting such a data item indicates to the I/O system that the contents (key value) of that data item in any record written to the file can be used by the program to identify that record for subsequent retrieval. For more information, refer to the Environment Division clauses RECORD KEY IS and ALTERNATE RECORD KEY IS in the VSI COBOL Reference Manual.

You must define at least one main key, called the primary key, for an indexed file. You may also optionally define from 1 to 254 additional keys called alternate keys. Each alternate key represents an additional data item in each record of the file. You can also use the key value in any of these alternate keys as a means of identifying the record for retrieval.

You define primary and alternate key values in the Record Description entry. Primary and alternate key values need not be unique if you specify the WITH DUPLICATES phrase in the file description entry (FD). When duplicate key values are present, you can retrieve the first record written in the logical sort order of the records with the same key value and any subsequent records using the READ NEXT phrase. The logical sort order controls the order of sequential processing of the record. (For more information about retrieving records with duplicate key values, refer to the information about the READ statement in the VSI COBOL Reference Manual.)

When you open a file, you must specify the same number and type of keys that were specified when the file was created. (This situation is subject to modification by the check duplicate keys and relax key checking options, as well as a duplicate key specification on an FD.) If the number or type of keys does not match, the system will issue a run-time diagnostic when you try to open the file.

As your program writes records into an indexed file, the I/O system locates the values contained in the primary and alternate keys. The I/O system builds these values into a tree-structured table or index, which consists of a series of entries. Each entry contains a key value copied from a record. With each key value is a pointer to the location in the file of the record from which the value was copied.

Figure 6.5 shows the general structure of an indexed file defined with a primary key only.

Figure 6.5. Indexed File Organization
Indexed File Organization

For information about specifying file organization in your program, see Section 6.2.2.

6.1.2. Record Format

VSI COBOL provides four record format types: fixed, variable, print-control, and stream. Table 6.2 shows the record format availability.
Table 6.2. Record Format Availability
 

Sequential

Line

Sequential

Relative

Indexed

Disk

Tape

Fixed length

yes

yes

no

yes

yes

Variable length

yes

yes

no

yes

yes

Print control

yes

no

no

no

no

Stream

no

no

yes

no

no

The compiler determines the record format from the information that you specify as follows:
  • Fixed record format—Use the RECORD CONTAINS clause. This is the VSI COBOL default.

  • Variable record format—Use the RECORD CONTAINS TO clause or the RECORD VARYING clause.

  • Print-control (VFC on OpenVMS systems or ASCII on UNIX systems)—use the Procedure Division ADVANCING phrase, the Environment Division APPLY PRINT-CONTROL or (on UNIX) ASSIGN TO PRINTER clauses, or the Data Division LINAGE clause, or use Report Writer statements and phrases.

  • Stream (Alpha, I64 only)—Use the FILE-CONTROL ORGANIZATION IS LINE SEQUENTIAL clause. On OpenVMS Alpha and OpenVMS I64 you also get this format with /NOVFC.

If a file has more than one record description, the different record descriptions automatically share the same record area in memory. The I/O system does not clear this area before it executes the READ statement. Therefore, if the record read by the latest READ statement does not fill the entire record area, the area not overlaid by the incoming record remains unchanged.

The record format type that was specified when the file was created must be used for all subsequent accesses to the file.

In Example 6.1, a file contains a company's stock inventory information (part number, supplier, quantity, price). Within this file, the information is divided into records. All information for a single piece of stock constitutes a single record.
Example 6.1. Sample Record Description
 01  PART-RECORD.
     02  PART-NUMBER                PIC 9999.
     02  PART-SUPPLIER              PIC X(20).
     02  PART-QUANTITY              PIC 99999.
     02  PART-PRICE                 PIC S9(5)V99.

Each record in the stock file is itself divided into discrete pieces of information referred to as elementary items (02 level items). You give each elementary item a specific location in the record, give it a name, and define its size and type. The part number is an elementary item in the part record, as are supplier, quantity, and price. In this example, PART-RECORD contains four elementary items: PART-NUMBER, PART-SUPPLIER, PART-QUANTITY, and PART-PRICE.

Fixed-Length Records

Files with a fixed-length record format contain the same size records. The compiler generates the fixed-length format when either of the following conditions is true:
  • The RECORD CONTAINS clause specifies a fixed number of characters.

  • The RECORD CONTAINS clause is omitted.

The compiler does not generate fixed-length format when any of the following conditions exist:
  • The file description contains a RECORD CONTAINS TO clause or a RECORD VARYING clause.

  • The program specifies a print-control file by referring to the file with:
    • The ADVANCING phrase in a WRITE statement

    • An APPLY PRINT-CONTROL clause in the Environment Division

    • A LINAGE clause in the file description

    • Report Writer statements and phrases

    • ASSIGN TO PRINTER

  • LINE SEQUENTIAL organization is specified.

Fixed-length record size is determined by either the largest record description or the record size specified by the RECORD CONTAINS clause, whichever is larger. Example 6.2 shows how fixed-length record size is determined.
Example 6.2. Determining Fixed-Length Record Size
 FD  FIXED-FILE
     RECORD CONTAINS 100 CHARACTERS.
 01  FIXED-REC
     PIC X(75).

For the file, FIXED-FILE, the RECORD CONTAINS clause specifies a record size larger than the record description; therefore, the record size is 100 characters.

In Example 6.2, the following warning message is generated when the file FIXED-FILE is used:
 "Record contains value is greater than length of longest record."
If the multiple record descriptions are associated with the file, the size of the largest record description is used as the size. In Example 6.3, for the file REC-FILE, the FIXED-REC2 record specifies the largest record size; therefore, the record size is 90 characters.
Example 6.3. Determining Fixed-Length Record Size for Files with Multiple Record Descriptions
 FD  REC-FILE
     RECORD CONTAINS 80 CHARACTERS.
 01  FIXED-REC1    PIC X(75).
 01  FIXED-REC2    PIC X(90).
When the file REC-FILE is used, the following warning message is generated:
 "Longest record is longer than RECORD CONTAINS value -
     longest record size used."

Variable-Length Records

Files with a variable-length record format can contain records of different length. The compiler generates the variable-length attribute for a file when the file description contains a RECORD VARYING clause or a RECORD CONTAINS TO clause.

Each record is written to the file with a 32-bit integer that specifies the size of the record. This integer is not counted in the size of the record.

Examples 6.4, 6.5, and 6.6 show you the three ways you can create a variable-length record file.

In Example 6.4, the DEPENDING ON phrase sets the OUT-REC record length. The IN-TYPE data field determines the OUT-LENGTH field's contents.
Example 6.4. Creating Variable-Length Records with the DEPENDING ON Phrase
 FILE SECTION.               FD  INFILE.
 01  IN-REC.
     03  IN-TYPE       PIC X.
     03  REST-OF-REC   PIC X(499).
 FD  OUTFILE
     RECORD VARYING FROM 200 TO 500 CHARACTERS
     DEPENDING ON OUT-LENGTH.
 01  OUT-REC           PIC X(500).
 WORKING-STORAGE SECTION.
 01  OUT-LENGTH        PIC 999 COMP VALUE ZEROES.
Example 6.5 shows how to create variable-length records using the RECORD VARYING phrase.
Example 6.5. Creating Variable-Length Records with the RECORD VARYING Phrase
 FILE SECTION.
 FD  OUTFILE
     RECORD VARYING FROM 200 TO 500 CHARACTERS.
 01  OUT-REC-1         PIC X(200).
 01  OUT-REC-2         PIC X(500).
Example 6.6. Creating Variable-Length Records and Using the OCCURS Clause with the DEPENDING ON Phrase
      .
      .
      .
 FILE SECTION.
 FD  PARTS-MASTER
     RECORD VARYING 118 TO 163 CHARACTERS.
 01  PARTS-REC.
     03  P-PART-NUM     PIC X(10).
     03  P-PART-INFO    PIC X(100).
     03  P-BIN-INDEX    PIC 999.
     03  P-BIN-NUMBER   PIC X(5)
         OCCURS 1 TO 10 TIMES DEPENDING ON P-BIN-INDEX.
      .
      .
      .

Example 6.6 creates variable-length records by using the OCCURS clause with the DEPENDING ON phrase in the record description. VSI COBOL determines record length by adding the sum of the variable record's fixed portion to the size of the table described by the number of table occurrences at execution time.

In this example, the variable record's fixed portion size is 113 characters. (This is the sum of P-PART-NUM, P-PART-INFO, and P-BIN-INDEX.) If P-BIN-INDEX contains a 7 at execution time, P-BIN-NUMBER will be 35 characters long. Therefore, PARTS-REC's length will be 148 characters; the fixed portion's length is 113 characters, and the table entry's length at execution time is 35 characters.

If you describe a record with both the RECORD VARYING...DEPENDING ON phrase on data-name-1 and the OCCURS clause with the DEPENDING ON phrase on data-name-2, VSI COBOL specifies record length as the value of data-name-1.

If you have multiple record-length descriptions for a file and omit either the RECORD VARYING clause or the RECORD CONTAINS TO clause, all records written to the file will have a fixed length equal to the length of the longest record described for the file, as in Example 6.7.
Example 6.7. Defining Fixed-Length Records with Multiple Record Descriptions
     .
     .
     .
 FD PARTS-MASTER.
 01  PARTS-REC-1   PIC X(200).
 01  PARTS-REC-2   PIC X(300).
 01  PARTS-REC-3   PIC X(400).
 01  PARTS-REC-4   PIC X(500).
     .
     .
     .
 PROCEDURE DIVISION.
     .
     .
     .
 100-WRITE-REC-1.
     MOVE IN-REC TO PARTS-REC-1.
     WRITE PARTS-REC-1.
     GO TO ...
 200-WRITE-REC-2.
     MOVE IN-REC TO PARTS-REC-2.
     WRITE PARTS-REC-2.
     GO TO ...
     .
     .
     .

Writing PARTS-REC-1, PARTS-REC-2, PARTS-REC-3 or PARTS-REC-4 produces records equal in length to the longest record, PARTS-REC-4. Note that this is not variable-length I/O.

6.1.3. Print-Control Records

Print-control files contain record-advancing information with each record. These files are intended for eventual printing, but are created on disk by your VSI COBOL program. The compiler generates print-control records when you use the WRITE AFTER ADVANCING, the LINAGE, or the APPLY PRINT-CONTROL clause, or if you create a Report Writer file or use ASSIGN TO PRINTER (on UNIX systems).

On OpenVMS Alpha and I64, in any of the preceding cases, if you compile /NOVFC, the compiler does not generate print-control records, but generates stream files instead.

On OpenVMS, VSI COBOL places explicit form-control bytes directly into the file. You must use the /NOFEED option on the DCL PRINT command to print a print-control file.

Stream (Alpha, I64)

Stream files contain records of different length, delimited by a record terminator.

The compiler generates a stream record formatted file when you use the ORGANIZATION IS LINE SEQUENTIAL clause in the File-Control Division. This record format is useful for files created by text editors.

On OpenVMS Alpha and I64, a stream file will also be generated under certain situations if you compiled /NOVFC. SeeSection B.4.3 for more information.

6.1.4. File Design

The difficulty of design is proportional to the complexity of the file organization. Before you create your sequential, relative, or indexed file applications, you should design your files based on these design considerations:

On OpenVMS, for more information about file design, see Chapter 15. Chapter 15 contains instructions on optimizing the file design for indexed files. With indexed files, in particular, if you accept all the file defaults instead of carefully designing your file, your application may run more slowly than you expect.

6.2. Identifying Files and Records from Within Your VSI COBOL Program

Before your program can perform I/O on a file, your program must identify the file to the operating system and specify the file's organization and access modes. A program must follow these steps whenever creating a new file or processing an existing file.

You use a file description entry to define a file's logical structure and associate the file with a file name that is unique within the program. The program uses this file name in the following COBOL statements:
  • OPEN

  • READ

  • START

  • UNLOCK

  • DELETE

  • CLOSE

The program uses the record name for the WRITE and REWRITE statements.

6.2.1. Defining a File Connector

You must establish a link between the file connector your program uses and the file specification that the I/O system uses. You create this link and define a file connector by using the SELECT statement with the ASSIGN clause and optionally specifying the VALUE OF ID clause or by using logical names or environment variables.

A file connector is a VSI COBOL data structure that contains information about a file. The file connector links a file name and its associated record area to a physical file.

Defining a File Connector with SELECT and ASSIGN

Your program must include a SELECT statement, including an ASSIGN clause, for every file description entry (FD) it contains. The file name you specify in the SELECT statement must match the file name in the file description entry.

In the ASSIGN clause, you specify a nonnumeric literal or data name that associates the file name with a file specification. This value must be a complete file specification.

Example 6.8 and Example 6.9 show the relationships between the SELECT statement, the ASSIGN clause, and the FD entry.

In Example 6.8, because the file name specified in the FD entry is DAT-FILE, all I/O statements in the program referring to that file or to its associated record must use the file name DAT-FILE or the record name DAT-RECORD. The I/O system uses the ASSIGN clause to interpret DAT-FILE as REPORT.DAT on OpenVMS systems, and REPORT on UNIX systems. The default directory will be used on OpenVMS systems, and the current working directory will be used on UNIX systems.
Example 6.8. Defining a Disk File
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT DAT-FILE
            ASSIGN TO "REPORT".
            .
            .
            .
 DATA DIVISION.
 FILE SECTION.
 FD  DAT-FILE.
 01  DAT-RECORD PIC X(100).
            .
            .
            .

Note

On OpenVMS systems, if no file type is supplied, VSI COBOL supplies the default file extension DAT. On UNIX systems, the extensions dat and idx are appended, but only in the case of indexed files.

The I/O statements in Example 6.9 refer to MYFILE-PRO, which the ASSIGN clause identifies to the operating system as MARCH.311. Additionally, the operating system looks for the file in the current directory on the magnetic tape mounted on MTA0: on an OpenVMS system.
Example 6.9. Defining a Magnetic Tape File (OpenVMS)
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT MYFILE-PRO
            ASSIGN TO "MTA0:MARCH.311"
            .
            .
            .
 DATA DIVISION.
 FILE SECTION.
 FD  MYFILE-PRO.
 01  DAT-RECORD PIC X(100).
            .
            .
            .
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT MYFILE-PRO.
            .
            .
            .
     READ MYFILE-PRO AT END DISPLAY "end".
            .
            .
            .
     CLOSE MYFILE-PRO.
Example 6.10 achieves the same result as Example 6.9, but on UNIX. The I/O statements in Example 6.10 refer to MYFILE-PRO, which the ASSIGN clause identifies to the operating system as a magnetic tape file. The file is named in the Data Division VALUE OF ID clause as MARCH.311.
Example 6.10. Defining a Magnetic Tape File (UNIX)
 ENVIRONMENT DIVISION INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT MYFILE-PRO
            ASSIGN TO REEL.
            .
            .
            .
 DATA DIVISION.
 FILE SECTION.
 FD MYFILE-PRO VALUE OF ID "MARCH.311".
 01  DAT-RECORD PIC X(100).
            .
            .
            .
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT MYFILE-PRO.
            .
            .
            .
     READ MYFILE-PRO AT END DISPLAY "end".
            .
            .
            .
     CLOSE MYFILE-PRO.

For each OPEN verb referring to a file assigned to magnetic tape, the user is prompted to assign the file to a magnetic tape device. These device names are in the form /dev/rmt0(a,l,m,h) ... /dev/rmt31(a,l,m,h) and correspond to special files on the system that refer to mass storage tape devices. For more information on tape devices, refer to the mtio(7) UNIX manual page.

As an alternative to prompting, each file assigned to a magnetic tape can have its associated tape device defined through a shell environment variable. The name of this environment variable is the concatenation of COBOL_TAPE_ and the base of the file name used in the COBOL program. The value of this environment variable is the name of the desired tape device. The environment variable needed in Example 6.10 to assign the MARCH.311 file to tape device /dev/rmt0a is:

% setenv COBOL_TAPE_MARCH /dev/rmt0a

Establishing File Names with ASSIGN and VALUE OF ID

If the file specification is subject to change, you can use a partial file specification in the ASSIGN clause and complete it by using the optional VALUE OF ID clause of the FD entry. In the VALUE OF ID clause, you can specify a nonnumeric literal or an alphanumeric WORKING-STORAGE item to supplement the file specification.

VALUE OF ID can complete a file name specified in ASSIGN TO:
   ASSIGN TO "filename"
   VALUE OF ID ".ext" 

In the above example, OPEN would create a file with the name filename.ext.

VALUE OF ID can override a file name specified in ASSIGN TO:
   ASSIGN TO "oldname"
   VALUE OF "newname"
In the above example, OPEN would create a file with the name newname.
VALUE OF ID can be a directory/device specification and ASSIGN TO can provide the file name, as in the following example:
  ASSIGN TO "filename.dat"
  VALUE OF ID "/usr/"

       or

  ASSIGN TO "filename"
  VALUE OF ID "DISK:[DIRECTORY]"

On OpenVMS, with this code OPEN would create a file with the name DISK:[DIRECTORY]FILENAME.DAT.

On UNIX, with this code OPEN would create a file with the name "/usr/filename.dat".

Establishing Device and File Independence with Logical Names on OpenVMS

On OpenVMS, logical names let you write programs that are device and file independent and provide a brief way to refer to frequently used files.

You can assign logical names with the ASSIGN command. When you assign a logical name, the logical name and its equivalence name (the name of the actual file or device) are placed in one of three logical name tables; the choice depends on whether they are assigned for the current process, on the group level, or on a systemwide basis. Refer to the VSI OpenVMS DCL Dictionary for more information on DCL and a description of logical name tables.

To translate a logical name, the system searches the three tables in this order: (1) process, (2) group, (3) system. Therefore, you can override a systemwide logical name by defining it for your group or process.

Logical name translation is a recursive procedure: when the system translates a logical name, it uses the equivalence name as the argument for another logical name translation. It continues in this way until it cannot translate the equivalence name.

Assume that your program updates monthly sales files (for example, JAN.DAT, FEB.DAT, MAR.DAT, and so forth). Your SELECT statement could look like either of these:
SELECT SALES-FILE ASSIGN TO "MOSLS"
SELECT SALES-FILE ASSIGN TO MOSLS
To update the January sales file, you can use this ASSIGN command to equate the equivalence name JAN.DAT with the logical name MOSLS:
$ ASSIGN JAN.DAT MOSLS
To update the February sales file, you can use this ASSIGN command:
$ ASSIGN FEB.DAT MOSLS

In the same way, all programs that access the monthly sales file can use the logical name MOSLS.

To disassociate the relationship between the file and the logical name, you can use this DEASSIGN command:
$ DEASSIGN MOSLS

If MOSLS is not set as a logical name, the system uses it as a file specification and looks for a file named MOSLS.DAT.

Using Environment Variables for File Specification on UNIX

On UNIX, environment variables can be used as aliases for file specification at run time. File name resolution follows these rules:
  • Use contents of the ASSIGN TO clause or VALUE OF ID clause to find a match against an environment variable.

  • If a match is found, substitute the value of the environment variable in the construction of the file specification.

  • If a match was not found, take the file name as specified.

On UNIX, you can also use the literal or alphanumeric item to specify a run-time environment variable set. Refer to setenv(3) in the reference page.

The program in Example 6.11 and the commands that follow it illustrate how to use the ASSIGN TO clause in conjunction with an environment variable or logical name.
Example 6.11. Using Environment Variables (UNIX) or Logical Names (OpenVMS) for File Specification
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ENVVAR-EXAMPLE.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT F-DISK ASSIGN TO "MYENV".
       DATA DIVISION.
       FILE SECTION.
       FD  F-DISK.
       01  DAT-RECORD PIC X(100).

       PROCEDURE DIVISION.
       P0. OPEN OUTPUT F-DISK.
           CLOSE F-DISK.

       PE. STOP RUN.
       END PROGRAM ENVVAR-EXAMPLE.

       On UNIX, set an environment variable as follows:

       % cobol -o envtest envvar-example.cob
       % setenv MYENV hello.dat
       % envtest
       % ls *.dat
       hello.dat
       % unsetenv MYENV
       % envtest
       % ls MY*
       MYENV

Setting environment variables at run time can help in moving applications between OpenVMS Alpha and UNIX platforms without having to modify their source COBOL programs. You can define environment variables that access files in a way similar to that in which you access files using logical names on OpenVMS systems. Thus, in Example 6.11, the program is applicable to either UNIX or to OpenVMS, because MYENV can refer to an environment variable or to a logical name.

Example 6.12 is another program that can be used on either system, depending on the definition at system level of an environment variable or logical name, as appropriate.
Example 6.12. Using Environment Variables
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ENVVAR-EXAMPLE2.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT F-DISK ASSIGN TO "SYS$SCRATCH:envtest.dat".
       DATA DIVISION.
       FILE SECTION.
       FD  F-DISK
           VALUE OF ID "SYS$DISK:".
       01  DAT-RECORD PIC X(100).
       PROCEDURE DIVISION.
       P0. OPEN OUTPUT F-DISK.
           CLOSE F-DISK.
       PE. STOP RUN.
       END PROGRAM ENVVAR-EXAMPLE2.
Example 6.12, on OpenVMS, would produce a file with the name ENVTEST.DAT. On UNIX, SYS$SCRATCH: has no meaning because it is a OpenVMS logical. OpenVMS logicals are not defined on UNIX However, the SYS$SCRATCH: in the ASSIGN clause can be defined as an environment variable with the following command:
   % setenv 'SYS$SCRATCH:' ./

This would make SYS$SCRATCH point to the home directory. This can be used for any OpenVMS logicals used in the VSI COBOL source. When you declare an environment variable you should be careful to match the case of what is in the VSI COBOL source with the setenv(3) line.

6.2.2. Specifying File Organization and Record Access Mode

Your program must state—either explicitly or implicitly—a file's organization and record access mode before the program opens the file. The Environment Division ORGANIZATION and ACCESS MODE clauses, if present, specify these two characteristics.

In a VSI COBOL program, each file is given a file name in a separate Environment Division SELECT statement. The compiler determines the file organization from the SELECT statement and its associated clauses.

For relative and indexed files, you must specify the ORGANIZATION IS RELATIVE or the ORGANIZATION IS INDEXED phrase, respectively. For sequential files you need not specify the ORGANIZATION IS SEQUENTIAL phrase. For line sequential files (Alpha, I64), you must explicitly declare ORGANIZATION IS LINE SEQUENTIAL. When you omit the ORGANIZATION IS clause the file organization is sequential.

The ASSIGN clause, in the SELECT statement, associates the file name with a file specification. The file specification points the operating system to the file's physical and logical location on a specific hardware device.

The SELECT statement and the ASSIGN clause are further described in Section 6.2.1. For further information, refer to the VSI COBOL Reference Manual.

Each file is further described with a file description (FD) entry in the Data Division File Section. The FD entry is followed immediately by the file's record description.

You can specify additional file characteristics in the Environment and Data Divisions as follows:
  • Use the Environment Division APPLY clause to specify file characteristics such as lock-holding, file extension factors, and preallocation factors. (See Chapter 15.)

  • Use file description entries to specify record format and record blocking.

  • Use record description entries to specify physical record size or sizes.

Examples 6.13, 6.14, and Example 6.15 illustrate how to specify the file organization and access mode for sequential, relative, and indexed files.
Example 6.13. Specifying Sequential File Organization and Sequential Access Mode for a Sequential File
 IDENTIFICATION DIVISION.
 PROGRAM-ID.
  SEQ01.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT   MASTER-FILE   ASSIGN   TO   "MASTER.DAT".
     SELECT   TRANS-FILE    ASSIGN   TO   "TRANS.DAT".
     SELECT   REPRT-FILE    ASSIGN   TO   "REPORT.DAT".
 DATA DIVISION.
 FILE SECTION.
 FD  MASTER-FILE.
 01  MASTER-RECORD.
     02  MASTER-DATA       PIC X(80).
     02  MASTER-SIZE       PIC 99.
     02  MASTER-TABLE      OCCURS 0 to 50 TIMES
                           DEPENDING ON MASTER-SIZE.
         03  MASTER-YEAR   PIC 99.
         03  MASTER-COUNT  PIC S9(5)V99.
 FD  TRANS-FILE.
 01  TRANSACTION-RECORD    PIC X(25).
 FD  REPRT-FILE.
 01  REPORT-LINE           PIC X(132).

Example 6.14. Specifying Relative File Organization and Random Access Mode for a Relative File
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL01.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS RANDOM
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER            PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY        PIC 99.
Example 6.15 defines a dynamic access mode indexed file with one primary key and two alternate record keys. Note that one alternate record key allows duplicates. Any program using the identical entries in the SELECT clause as shown in Example 6.15 can reference the DAIRY file sequentially and randomly. Refer to the VSI COBOL Reference Manual for information relating to the RECORD KEY and ALTERNATE RECORD KEY clauses.
Example 6.15. Specifying Indexed File Organization and Dynamic Access Mode for an Indexed File
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX01.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "DAIRY"
              ORGANIZATION IS INDEXED
              ACCESS MODE IS DYNAMIC
              RECORD KEY IS ICE-CREAM-MASTER-KEY
              ALTERNATE RECORD KEY IS ICE-CREAM-STORE-STATE
                               WITH DUPLICATES
              ALTERNATE RECORD KEY IS ICE-CREAM-STORE-CODE.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-MASTER-KEY          PIC XXXX.
     02 ICE-CREAM-MASTER-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
 PROCEDURE DIVISION. A00-BEGIN.
   .
   .
   .
Example 6.16 defines a line sequential (Alpha, I64) file.
Example 6.16. Specifying Line Sequential File Organization with Sequential Access Mode (Alpha, I64)
 IDENTIFICATION DIVISION.
 PROGRAM ID. EX0616.
 ENVIRONMENT DIVISION.
 INOUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT MUSIC ASSIGN TO "CLASSICAL"
            ORGANIZATION IS LINE SEQUENTIAL.
 DATA DIVISION.
 FILE SECTION.
 FD  MUSIC.
 01  OPERA        PIC X(9).
 PROCEDURE DIVISION.
 A00-BEGIN.

   .
   .
   .

File organization is discussed in more detail in Section 6.1.1. Record access mode is discussed in the following section.

Record Access Mode

The methods for retrieving and storing records in a file are called record access modes. VSI COBOL supports the following three types of record access modes:
  • ACCESS MODE IS SEQUENTIAL
    • With sequential files, sequential access mode retrieves the records in the same sequence established by the WRITE statements that created the file.

    • With relative files, sequential access mode retrieves the records in the order of ascending record key values (or relative record numbers).

    • With indexed files, sequential access mode retrieves records in the order of record key values.

  • ACCESS MODE IS RANDOM—The value of the record key your program specifies indicates the record to be accessed in Indexed and Relative files.

  • ACCESS MODE IS DYNAMIC—With relative and indexed files, dynamic access mode allows you to switch back and forth between sequential access mode and random access mode while reading a file by using the the NEXT phrase on the READ statement. For more information about dynamic access mode, refer to READ and REWRITE statements in the VSI COBOL Reference Manual.

When you omit the ACCESS MODE IS clause in the SELECT statement, the access mode is sequential.

Example 6.17 shows sample SELECT statements for sequential files with sequential access modes.
Example 6.17. SELECT Statements for Sequential Files with Sequential Access Mode
                   (1)                                (2)
 FILE-CONTROL.                         FILE-CONTROL.
     SELECT LIST-FILE                      SELECT PAYROLL
            ASSIGN TO "MAIL.LIS"                  ASSIGN TO "PAYROL.DAT".
            ORGANIZATION IS SEQUENTIAL       ACCESS IS SEQUENTIAL.
Sample SELECT statements for relative files with sequential and dynamic access modes are shown in Example 6.18.
Example 6.18. SELECT Statements for Relative Files with Sequential and Dynamic Access Modes
                   (1)                                (2)
 FILE-CONTROL.                        FILE-CONTROL.
     SELECT MODEL                         SELECT PARTS
            ASSIGN TO "ACTOR.DAT"                ASSIGN TO "PART.DAT"
            ORGANIZATION IS RELATIVE             ORGANIZATION IS RELATIVE
            ACCESS MODE IS SEQUENTIAL.           ACCESS MODE IS DYNAMIC
                                                 RELATIVE KEY IS PART-NO.
Sample SELECT statements for indexed files with dynamic and sequential access modes are shown in Example 6.19.
Example 6.19. SELECT Statements for Indexed Files with Dynamic and Default Sequential Access Modes
                   (1)                                (2)
 FILE-CONTROL.                           FILE-CONTROL.
     SELECT A-GROUP                          SELECT TEAS
            ASSIGN TO "RFCBA.PRO"                   ASSIGN TO "TEA"
            ORGANIZATION IS INDEXED                 ORGANIZATION IS INDEXED
            ACCESS MODE IS DYNAMIC                  RECORD KEY IS LEAVES.
            RECORD KEY IS WRITER
            ALTERNATE RECORD KEY IS EDITOR.

Because the default file organization is also sequential, both the relative and indexed examples require the ORGANIZATION IS clause.

Sample SELECT statements for line sequential files with sequential access modes are shown in Example 6.20.
Example 6.20. SELECT Statements for Line Sequential Files with Sequential Access Modes (Alpha, I64)
           (1)                                     (2)
 FILE-CONTROL.                           FILE-CONTROL.
       SELECT MAMMALS                          SELECT VACATION-SPOTS
            ASSIGN TO "DOLPHINS"                    ASSIGN TO "BAHAMAS"
            ORGANIZATION IS LINE SEQUENTIAL         ORGANIZATION IS LINE SEQUENTIAL.
            ACCESS MODE IS SEQUENTIAL.

6.3. Creating and Processing Files

Creating and processing sequential, line sequential, relative, and indexed files includes the following tasks:
  1. Opening the file

  2. Executing valid I/O statements

  3. Closing the file

Sections 6.3.2, 6.3.3, and 6.3.4 describe the specific tasks involved in creating and processing sequential, relative, and indexed files.

6.3.1. Opening and Closing Files

A VSI COBOL program must open a file with an OPEN statement before any other I/O or Report Writer statement can reference it. Files can be opened more than once in the same program as long as they are closed before being reopened.

Sample OPEN and CLOSE statements are shown in Example 6.21.
Example 6.21. OPEN and CLOSE Statements
⋮
 OPEN INPUT MASTER-FILE.
 OPEN OUTPUT REPORT-FILE.
 OPEN I-O   MASTER-FILE2
            TRANS-FILE
      OUTPUT REPORT-FILE2.
 CLOSE MASTER-FILE.
 CLOSE TRANS-FILE, MASTER-FILE2
       REPORT-FILE, REPORT-FILE2.

   .
   .
   .
The OPEN statement must specify one of the following four open modes:
  • INPUT
  • OUTPUT
  • I-O {Not for LINE SEQUENTIAL}
  • EXTEND

Your choice, along with the file's organization and access mode, determines which I/O statements you can use. Sections 6.3.2, 6.3.3, and 6.3.4 discuss the I/O statements for sequential, relative, and indexed files, respectively.

When your program performs an OPEN statement, the following events take place:
  1. The I/O system builds a file specification by using the contents of the VALUE OF ID clause, if any, to alter or complete the file specification in the ASSIGN clause. Logicals and environment variables are translated.

  2. The I/O system checks the file's current status. If the file is unavailable, or if it was closed WITH LOCK, the OPEN statement fails. (See Chapter 8 for information on file sharing.)

  3. If the file specification names an invalid device, or contains any other errors, the I/O system generates an error message and the OPEN statement fails.

  4. The I/O system takes one of the following actions if it cannot find the file:
    1. If the file's OPEN mode is OUTPUT, the file is created.

    2. If the file's OPEN mode is EXTEND, or I-O, the OPEN statement fails, unless the file's SELECT clause includes the OPTIONAL phrase. If the file's SELECT clause includes the OPTIONAL phrase, the file is created.

    3. If the file's OPEN mode is INPUT, and its SELECT clause includes the OPTIONAL phrase, the OPEN statement is successful. The first read on that file causes the AT END or INVALID KEY condition.

    4. If none of the previous conditions is met, the OPEN fails and the Declarative USE procedure (if any) gains control. If no Declarative USE procedure exists, the I/O system aborts the program.

  5. If the file's OPEN mode is OUTPUT, and a file by the same name already exists, a new version is created.

  6. If the file characteristics specified by the program attempting an OPEN operation differ from the characteristics specified when the file was created, the OPEN statement fails.

If the file is on magnetic tape, the I/O system rewinds the tape. (To close a file on tape without rewinding the tape, use the NO REWIND phrase.) This speeds processing when you want to write another file beyond the end of the first file, as in the following example:

CLOSE MASTER-FILE NO REWIND.
You can also close a file and prevent your program from opening that file again in the same run, as in the following example:
CLOSE MASTER-FILE WITH LOCK.

6.3.2. File Handling for Sequential and Line Sequential (Alpha, I64) Files

Creating a sequential or (on Alpha and I64 only) line sequential file involves the following:
  1. Opening the file for OUTPUT or EXTEND

  2. Executing valid I/O statements

  3. Closing the file

By default, VSI COBOL assumes sequential organization and sequential access mode. (See Example 6.22.)
Example 6.22. Creating a Sequential File
 IDENTIFICATION DIVISION.
 PROGRAM-ID. SEQ01.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT TRANS-FILE ASSIGN TO "TRANS.DAT".
 DATA DIVISION.
 FILE SECTION.
 FD  TRANS-FILE.
 01  TRANSACTION-RECORD    PIC X(25).
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN OUTPUT TRANS-FILE.
     PERFORM A010-PROCESS-TRANS
        UNTIL TRANSACTION-RECORD = "END".
     CLOSE TRANS-FILE.
     STOP RUN.
 A010-PROCESS-TRANS.
     DISPLAY "Enter next record  - X(25)".
     DISPLAY "enter END to terminate the session".
     DISPLAY "-------------------------".
     ACCEPT TRANSACTION-RECORD.
     IF TRANSACTION-RECORD NOT = "END"
        WRITE TRANSACTION-RECORD.

Example 6.23. Creating a Line Sequential File (Alpha, I64)
         IDENTIFICATION DIVISION.
         PROGRAM-ID. LINESEQ01.
         ENVIRONMENT DIVISION.
         INPUT-OUTPUT SECTION.
         FILE-CONTROL.
             SELECT LINESEQ-FILE ASSIGN TO "LINESEQ.DAT".
         DATA DIVISION.
         FILE SECTION.
         FD  LINESEQ-FILE.
         01  LINESEQ-RECORD    PIC X(25).
          PROCEDURE DIVISION.
         A000-BEGIN.
             OPEN OUTPUT LINESEQ-FILE.
             CLOSE LINESEQ-FILE.
             STOP RUN.

By default, VSI COBOL assumes sequential access mode when the line sequential organization is specified. (See Example 6.23.)

Statements for Sequential and Line Sequential (Alpha, I64) File Processing

Processing a sequential file or line sequential file (Alpha, I64) involves the following:
  1. Opening the file

  2. Processing the file with valid I/O statements

  3. Closing the file

Table 6.3 lists the valid I/O statements for sequential files, and Table 6.4 lists the valid I/O statements for line sequential files. Both tables illustrate the following relationships:
  • Organization determines valid access modes.

  • Organization and access mode determine valid open modes.

  • All three (organization, access, and open mode) enable or disable I/O statements.


Table 6.3. Valid I/O Statements for Sequential Files
 

Open Mode

File Organization

Access Mode

Statement

INPUT

OUTPUT

I/O

EXTEND

SEQUENTIAL

SEQUENTIAL

READ

Yes

No

Yes

No

  

REWRITE

No

No

Yes

No

  

WRITE

No

Yes

No

Yes

  

UNLOCK

Yes

Yes

Yes

Yes

Writing a Sequential File

Each WRITE statement appends a logical record to the end of an output file, thereby creating an entirely new record in the file. The WRITE statement appends records to files that are OPEN for the following modes:
  • OUTPUT—Output mode can create the following two kinds of files:
    • Storage files—A storage file remains on tape or disk for future reference or processing.

    • Print-control files—The Data Division LINAGE clause, the Environment Division APPLY PRINT-CONTROL clause, the Procedure Division ADVANCING phrase (in the WRITE statement), or Report Writer statements and phrases designates a file as a print-control file.

      On OpenVMS Alpha, each record in a print-control file contains a header that performs line spacing. On UNIX, line spacing is done with blank records in print-control files.

  • EXTEND—Extend mode permits new records to be added in sequence after the last record of an existing file (see the section called “Extending a Sequential File or Line Sequential File (Alpha, I64)” in Section 6.5.1).


Table 6.4. Valid I/O Statements for Line Sequential Files (Alpha, I64)
 

Open Mode

File Organization

Access Mode

Statement

INPUT

OUTPUT

EXTEND

LINE SEQUENTIAL

SEQUENTIAL

READ

Yes

No

No

  

WRITE

No

Yes

Yes

  

UNLOCK

Yes

Yes

Yes

Writing a Line Sequential File (Alpha, I64)

Each WRITE statement appends a logical record to the end of an output file, thereby creating an entirely new record in the file. The WRITE statement appends records to files that are OPEN for the following modes:

Writing a Record

You can write records in the following two ways:
  • WRITE record-name FROM source-area

  • WRITE record-name

The first way provides easier program readability with multiple record types. For example, statements (1) and (2) in the following example are logically equivalent:
 FILE SECTION.
 FD  STOCK-FILE.
 01  STOCK-RECORD       PIC X(80).
 WORKING-STORAGE SECTION.
 01  STOCK-WORK         PIC X(80).
  ----------------(1)----------------    --------------(2)---------------
 WRITE STOCK-RECORD FROM STOCK-WORK.     MOVE STOCK-WORK TO STOCK-RECORD.
                                         WRITE STOCK-RECORD.

When you omit the FROM phrase, you process the records directly in the record area or buffer (for example, STOCK-RECORD).

The following example writes the record PRINT-LINE to the device assigned to that record's file, then skips three lines. At the end of the page (as specified by the LINAGE clause), it causes program control to transfer to HEADER-ROUTINE.
 WRITE PRINT-LINE BEFORE ADVANCING 3 LINES
       AT END-OF-PAGE PERFORM HEADER-ROUTINE.

For a WRITE FROM statement, if the destination area is shorter than the file's record length, the destination area is padded on the right with spaces; if longer, the destination area is truncated on the right. This follows the rules for a group move.

6.3.3. File Handling for Relative Files

Creating a relative file involves the following tasks:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL (or RANDOM) in the Environment Division SELECT clause

    Each of these two access modes requires a different processing technique. (Refer to the the section called “Creating a Relative File in Sequential Access Mode” and the section called “Creating a Relative File in Random Access Mode” sections in this chapter for information about those techniques.)

  3. Opening the file for OUTPUT or I-O

  4. Initializing the relative key data name for each new record

  5. Executing a WRITE statement for each new relative record

  6. Closing the file

Creating a Relative File in Sequential Access Mode

When your program creates a relative file in sequential access mode, the I/O system does not use the relative key. Instead, it writes the first record in the file at relative record number 1, the second record at relative record number 2, and so on, until the program closes the file. If you use the RELATIVE KEY IS clause, the compiler moves the relative record number of the record being written to the relative key data item. Example 6.24 writes 10 records with relative record numbers 1 to 10.
Example 6.24. Creating a Relative File in Sequential Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL02.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS SEQUENTIAL.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER.
     02  FILLER            PIC X(14).
     02  REC-NUM           PIC 9(05).
     02  FILLER            PIC X(31).
     02  FILLER            PIC X(31).
 WORKING-STORAGE SECTION.
 01  REC-COUNT             PIC S9(5) VALUE 0.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN OUTPUT FLAVORS.
     PERFORM A010-WRITE 10 TIMES.
     CLOSE FLAVORS.
     STOP RUN. A010-WRITE.
     MOVE "Record number" TO KETCHUP-MASTER.
     ADD 1 TO REC-COUNT.
     MOVE REC-COUNT TO REC-NUM.
     WRITE KETCHUP-MASTER
           INVALID KEY DISPLAY "BAD WRITE"
                       STOP RUN.

Creating a Relative File in Random Access Mode

When a program creates a relative file using random access mode, the program must place a value in the RELATIVE KEY data item before executing a WRITE statement. Example 6.25 shows how to supply the relative key. It writes 10 records in the cells numbered: 2, 4, 6, 8, 10, 12, 14, 16, 18, and 20. Record cells 1, 3, 5, 7, 9, 11, 13, 15, 17, and 19 are also created, but contain no valid records.
Example 6.25. Creating a Relative File in Random Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL03.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS RANDOM
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION. FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER.
     02  FILLER            PIC X(14).
     02  REC-NUM           PIC 9(05).
     02  FILLER            PIC X(31).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY    PIC 99.
 01  REC-COUNT             PIC S9(5) VALUE 0.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN OUTPUT FLAVORS.
     MOVE 0 TO KETCHUP-MASTER-KEY.
     PERFORM A010-CREATE-RELATIVE-FILE 10 TIMES.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A010-CREATE-RELATIVE-FILE.
     ADD 2 TO KETCHUP-MASTER-KEY.
     MOVE "Record number" TO KETCHUP-MASTER.
     ADD 2 TO REC-COUNT.
     MOVE REC-COUNT TO REC-NUM.
     WRITE KETCHUP-MASTER
           INVALID KEY DISPLAY "BAD WRITE"
                       STOP RUN.

Statements for Relative File Processing

Processing a relative file involves the following:
  1. Opening the file

  2. Setting the relative record number

  3. Processing the file with valid I/O statements

  4. Closing the file

Table 6.5 lists the valid I/O statements and illustrates the following relationships:
  • Organization determines valid access modes.

  • Organization and access mode determine valid open modes.

  • All three (organization, access, and open mode) enable or disable I/O statements.


Table 6.5. Valid I/O Statements for Relative Files
 

Open Mode

File Organization

Access Mode

Statement

INPUT

OUTPUT

I-O

EXTEND

RELATIVE

SEQUENTIAL

DELETE

READ

REWRITE

START

WRITE

UNLOCK

No

Yes

No

Yes

No

Yes

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

No

Yes

No

No

No

No

Yes

Yes

RANDOM

DELETE

READ

REWRITE

WRITE

UNLOCK

No

Yes

No

No

Yes

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

No

No

No

No

No

DYNAMIC

DELETE

READ

READ NEXT

REWRITE

START

WRITE

UNLOCK

No

Yes

Yes

No

Yes

No

Yes

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

No

No

No

No

No

No

No

Writing a Relative File

Each WRITE statement places a record into a cell that contains no valid data. If the cell does not already exist, the I/O system creates it. To change the contents of a cell that already contains valid data, use the REWRITE statement.

6.3.4. File Handling for Indexed Files

Creating an indexed file involves the following tasks:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL (or RANDOM or DYNAMIC) in the Environment Division SELECT clause

  3. Opening the file for OUTPUT (to create and add records) or for I-O (to add, change, delete, or extend records)

  4. Initializing the key values

  5. Executing a WRITE statement

  6. Closing the file

One way to populate an indexed file is to sequentially write the records in ascending order by primary key. Example 6.26 creates and populates an indexed file from a sequential file, which has been sorted in ascending sequence on the primary key field. Notice that the primary and alternate keys are initialized in ICE-CREAM-MASTER when the contents of the fields in INPUT-RECORD are read into ICE-CREAM-MASTER before the record is written.
Example 6.26. Creating and Populating an Indexed File
IDENTIFICATION DIVISION.
PROGRAM-ID. INDEX02.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
     SELECT INPUT-FILE ASSIGN TO "DAIRYI".
     SELECT FLAVORS
    ASSIGN TO "DAIRY"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS SEQUENTIAL
                       RECORD KEY IS ICE-CREAM-MASTER-KEY
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-STATE
                                            WITH DUPLICATES
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-CODE.
 DATA DIVISION.
 FILE SECTION.
 FD  INPUT-FILE.
 01  INPUT-RECORD.
     02  INPUT-RECORD-KEY             PIC 9999.
     02  INPUT-RECORD-DATA            PIC X(47).
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-MASTER-KEY          PIC XXXX.
     02 ICE-CREAM-MASTER-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
 WORKING-STORAGE SECTION.
 01  END-OF-FILE                      PIC X.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT INPUT-FILE.
     OPEN OUTPUT FLAVORS.
 A010-POPULATE.
     PERFORM A100-READ-INPUT UNTIL END-OF-FILE = "Y".
 A020-EOJ.
     DISPLAY "END OF JOB".
     STOP RUN.
 A100-READ-INPUT.
     READ INPUT-FILE INTO ICE-CREAM-MASTER
          AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y"
        WRITE ICE-CREAM-MASTER INVALID KEY DISPLAY "BAD WRITE"
                                           STOP RUN.
The program can add records to the file until it reaches the physical limitations of its storage device. When this occurs, you should follow these steps:
  1. Delete unnecessary records.

  2. Back up the file.

  3. Recreate the file either by using the OpenVMS Alpha and I64 CONVERT Utility to optimize file space, or by using a VSI COBOL program.

Statements for Indexed File Processing

Processing an indexed file involves the following:
  1. Opening the file

  2. Processing the file with valid I/O statements

  3. Closing the file

Table 6.6 lists the valid I/O statements and illustrates the following relationships:
  • File organization determines valid access modes.

  • File organization and access mode determine valid open modes.

  • All three (organization, access, and open mode) enable or disable I/O statements.


Table 6.6. Valid I/O Statements for Indexed Files
 

Open Mode

File Organization

Access Mode

Statement

INPUT

OUTPUT

I-O

EXTEND

INDEXED

SEQUENTIAL

DELETE

READ

REWRITE

START

WRITE

UNLOCK

No

Yes

No

Yes

No

Yes

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

No

Yes

No

No

No

No

Yes

Yes

RANDOM

DELETE

READ

REWRITE

WRITE

UNLOCK

No

Yes

No

No

Yes

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

No

No

No

No

No

DYNAMIC

DELETE

READ

READ NEXT

REWRITE

START

WRITE

UNLOCK

No

Yes

Yes

No

Yes

No

Yes

No

No

No

No

No

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

Yes

No

No

No

No

No

No

No

Writing an Indexed File

You specify sequential access mode in the Environment Division SELECT clause when you want to write records in ascending or descending order by primary key, depending on the sort order. Specify random or dynamic access mode to enable your program to write records in any order.

Using Segmented Keys in Indexed Files

Segmented keys are a form of primary or alternate keys. A segmented key can be made up of multiple pieces, or segments. These segments are data items that you define in the record description entry for a file. They are concatenated, in order of specification in the ALTERNATE RECORD KEY or RECORD KEY clause, to form the segmented key, which will be treated like any "simple" primary or alternate key.

With segmented keys, you have more flexibility in defining record description entries for indexed files. A segmented key is made up of between one and eight data items, which can be defined anywhere and in any order within the record description, and which can even overlap. For example, you might use the following record definition in your program:
 01 EMPLOYEE.
     02 FORENAME    PIC X(10).
     02 BADGE-NO    PIC X(6).
     02 DEPT        PIC X(2).
     02 SURNAME     PIC X(20).
     02 INITIAL     PIC X(1).
Then the following line in your program, which specifies the segmented key name and three of its segments:
     RECORD KEY IS NAME = SURNAME FORENAME INITIAL 
causes VSI COBOL to treat name as if it were an explicitly defined group item consisting of the following:
     02 SURNAME   PIC X(20).
     02 FORENAME  PIC X(10).
     02 INITIAL   PIC X(1).

You define a segmented key in either the RECORD KEY clause or the ALTERNATE RECORD KEY clause. You use the START or READ statement to reference a segmented key.

Each segment is a data-name of a data item in a record description entry. A segment can be an alphanumeric or alphabetic item, a group item, or an unsigned numeric display item. A segment can be qualified, but it cannot be a group item containing a variable-occurrence item.

Refer to the chapters on the Data Division and the Procedure Division in the VSI COBOL Reference Manual for more information on segmented keys.

Example 6.27 shows how you might use segmented keys. In this example, SEG-ICE-CREAM-KEY is a segmented-key name. ICE-CREAM-STORE-KIND and ICE-CREAM-STORE-ZIP are the segments. Notice that the segmented-key name is referenced in the READ statement.
Example 6.27. Using Segmented Keys
 IDENTIFICATION DIVISION.
 PROGRAM-ID. MANAGER.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
    SELECT FLAVORS    ASSIGN TO "STORE"
       ORGANIZATION IS INDEXED
       ACCESS MODE IS RANDOM
       RECORD KEY IS
     SEG-ICE-CREAM-KEY =
       ICE-CREAM-STORE-KIND,
       ICE-CREAM-STORE-ZIP.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-DATA.
        03  ICE-CREAM-STORE-KIND      PIC XX.
        03  ICE-CREAM-STORE-MANAGER   PIC X(40).
        03  ICE-CREAM-STORE-SIZE      PIC XX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
        03  ICE-CREAM-STORE-ZIP       PIC XXXXX. WORKING-STORAGE SECTION.
 01  PROGRAM-STAT                     PIC X.
     88  OPERATOR-STOPS-IT            VALUE "1".
 PROCEDURE DIVISION.
 A000-BEGIN.
       OPEN I-O FLAVORS.
       PERFORM A020-INITIAL-PROMPT.
       IF OPERATOR-STOPS-IT
          PERFORM A005-TERMINATE.
       PERFORM A030-RANDOM-READ.
       PERFORM A025-SUBSEQUENT-PROMPTS UNTIL OPERATOR-STOPS-IT.
       PERFORM A005-TERMINATE. A005-TERMINATE.
       DISPLAY "END OF JOB".
       STOP RUN. A020-INITIAL-PROMPT.
       DISPLAY "Do you want to see the manager of a store?".
       PERFORM A040-GET-ANS UNTIL PROGRAM-STAT = "Y" OR "y" OR "N" OR "n".
       IF PROGRAM-STAT = "N" OR "n"
       THEN
           MOVE "1" TO PROGRAM-STAT.
 A025-SUBSEQUENT-PROMPTS.
       MOVE SPACE TO PROGRAM-STAT.
       DISPLAY "Do you want to see the manager of another store?".
       PERFORM A040-GET-ANS UNTIL PROGRAM-STAT = "Y" OR "y" OR "N" OR "n".
       IF PROGRAM-STAT = "Y" OR "y"
       THEN
           PERFORM A030-RANDOM-READ
       ELSE
           MOVE "1" TO PROGRAM-STAT.
 A030-RANDOM-READ.
       DISPLAY "Enter store kind: ".
       ACCEPT ICE-CREAM-STORE-KIND.
       DISPLAY "Enter zip code: " AT LINE PLUS 2.
       ACCEPT ICE-CREAM-STORE-ZIP.
       PERFORM A100-READ-INPUT-BY-KEY.
 A040-GET-ANS.
       DISPLAY "Please answer Y or N"
       ACCEPT PROGRAM-STAT.
 A100-READ-INPUT-BY-KEY.
       READ FLAVORS KEY IS SEG-ICE-CREAM-KEY
       INVALID KEY
         DISPLAY "Store does not exist - Try again"
       NOT INVALID KEY
         DISPLAY "The manager is: ", ICE-CREAM-STORE-MANAGER.

6.4. Reading Files

Reading sequential, line sequential, relative, and indexed files includes the following tasks:
  1. Opening the file

  2. Executing a READ or START statement

Sections 6.4.1, 6.4.2, and 6.4.3 describe the specific tasks involved in reading sequential, line sequential, relative, and indexed files.

6.4.1. Reading a Sequential or Line Sequential (Alpha, I64) File

Reading a sequential or (on Alpha and I64 only) line sequential file involves the following:
  1. Opening the file for INPUT or I/O for sequential files, or INPUT for line sequential files (I/O is not permitted for line sequential files)

  2. Executing a READ statement

Each READ statement reads a single logical record and makes its contents available to the program in the record area. There are two ways of reading records:
  • READ file-name INTO destination-area

  • READ file-name

Statements (1) and (2) in the following example are logically equivalent:
 FILE SECTION.
 FD  STOCK-FILE.
 01  STOCK-RECORD     PIC X(80).
 WORKING-STORAGE SECTION.
 01  STOCK-WORK       PIC X(80).
 -------------(1)---------------    -------------(2)---------------
 READ STOCK-FILE INTO STOCK-WORK.   READ STOCK-FILE.
                                    MOVE STOCK-RECORD TO STOCK-WORK.

When you omit the INTO phrase, you process the records directly in the record area or buffer (for example, STOCK-RECORD). The record is also available in the record area if you use the INTO phrase.

In a READ INTO clause, if the destination area is shorter than the length of the record area being read, the record is truncated on the right and a warning is issued; if longer, the destination area is filled on the right with blanks.

If the data in the record being read is shorter than the length of the record (for example, a variable-length record), the contents of the record beyond that data are undefined.

Generally speaking, if the recordtype is fixed, the prolog and epilog are zero. The exceptions to this are: for relative files there is a 1 byte record status flag prolog; for sequential files there is a 1 byte epilog if the record length is odd.

Example 6.28 reads a sequential file and displays its contents on the terminal.
Example 6.28. Reading a Sequential File
 IDENTIFICATION DIVISION.
 PROGRAM-ID. SEQ02.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT TRANS-FILE ASSIGN TO "TRANS".
 DATA DIVISION. FILE SECTION.
 FD  TRANS-FILE.
 01  TRANSACTION-RECORD    PIC X(25).
 PROCEDURE DIVISION. A000-BEGIN.
     OPEN INPUT TRANS-FILE.
     PERFORM A100-READ-TRANS-FILE
        UNTIL TRANSACTION-RECORD = "END".
     CLOSE TRANS-FILE.
     STOP RUN. A100-READ-TRANS-FILE.
     READ TRANS-FILE
        AT END MOVE "END" TO TRANSACTION-RECORD.
     IF TRANSACTION-RECORD NOT = "END"
        DISPLAY TRANSACTION-RECORD.

6.4.2. Reading a Relative File

Your program can read a relative file sequentially, randomly, or dynamically. The following three sections describe the specific tasks involved in reading a relative file sequentially, randomly, and dynamically.

Reading a Relative File Sequentially

Reading relative records sequentially involves the following:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL (or DYNAMIC) in the Environment Division SELECT clause (and using the READ NEXT phrase)

  3. Opening the file for INPUT or I-O

  4. Reading records as you would a sequential file, or using a START statement

The READ statement makes the next logical record of an open file available to the program. The system reads the file sequentially from either cell 1 or wherever you START the file, up to cell n. It skips the empty cells and retrieves only valid records. Each READ statement updates the contents of the file's RELATIVE KEY data item, if specified. The data item contains the relative number of the available record. When the at end condition occurs, execution of the READ statement is unsuccessful (see Chapter 7).

Sequential processing need not begin at the first record of a relative file. The START statement specifies the next record to be read and positions the file position indicator for subsequent I/O operations.

Example 6.29 reads a relative file sequentially, displaying every record on the terminal.
Example 6.29. Reading a Relative File Sequentially
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL04.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS SEQUENTIAL
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99.
 01  END-OF-FILE              PIC X.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT FLAVORS.
     PERFORM A010-DISPLAY-RECORDS UNTIL END-OF-FILE = "Y".
 A005-EOJ.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A010-DISPLAY-RECORDS.
     READ FLAVORS AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y" DISPLAY KETCHUP-MASTER.

Reading a Relative File Randomly

Reading relative records randomly involves the following:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS RANDOM (or DYNAMIC) in the Environment Division SELECT clause

  3. Opening the file for INPUT or I-O

  4. Moving the relative record number value to the RELATIVE KEY data name

  5. Reading the record from the cell identified by the relative record number

The READ statement selects a specific record from an open file and makes it available to the program. The value of the relative key identifies the specific record. The system reads the record identified by the RELATIVE KEY data name clause. If the cell does not contain a valid record, the invalid key condition occurs, and the READ operation fails (see Chapter 7).

Example 6.30 reads a relative file randomly, displaying every record on the terminal.
Example 6.30. Reading a Relative File Randomly
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL05.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS RANDOM
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99 VALUE 99.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT FLAVORS.
     PERFORM A100-DISPLAY-RECORD UNTIL KETCHUP-MASTER-KEY = 00.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A100-DISPLAY-RECORD.
     DISPLAY "TO DISPLAY A RECORD ENTER ITS RECORD NUMBER (0 to END)".
     ACCEPT KETCHUP-MASTER-KEY WITH CONVERSION.
     IF KETCHUP-MASTER-KEY > 00
        READ FLAVORS
         INVALID KEY DISPLAY "BAD KEY"
                         CLOSE FLAVORS
                         STOP RUN
        END-READ
        DISPLAY KETCHUP-MASTER.

Reading a Relative File Dynamically

The READ statement has two formats so that it can select the next logical record (sequential access) or select a specific record (random access) and make it available to the program. In dynamic mode, the program can switch from random access I/O statements to sequential access I/O statements in any order, without closing and reopening files. However, you must use the READ NEXT statement to sequentially read a relative file open in dynamic mode.

Sequential processing need not begin at the first record of a relative file. The START statement repositions the file position indicator for subsequent I/O operations.

A sequential read of a dynamic file is indicated by the NEXT phrase of the READ statement. A READ NEXT statement should follow the START statement since the READ NEXT statement reads the next record indicated by the current record pointer. Subsequent READ NEXT statements sequentially retrieve records until another START statement or random READ statement executes.

Example 6.31 processes a relative file containing 10 records. If the previous program examples in this chapter have been run, each record has a unique even number from 2 to 20 as its key. The program positions the record pointer (using the START statement) to the cell corresponding to the value in INPUT-RECORD-KEY. The program's READ...NEXT statement retrieves the remaining valid records in the file for display on the terminal.
Example 6.31. Reading a Relative File Dynamically
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL06.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS DYNAMIC
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION. FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99.
 01  END-OF-FILE              PIC X   VALUE "N".
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     DISPLAY "Enter number".
     ACCEPT KETCHUP-MASTER-KEY.
     START FLAVORS KEY = KETCHUP-MASTER-KEY
           INVALID KEY DISPLAY "Bad START statement"
           GO TO A005-END-OF-JOB.
     PERFORM A010-DISPLAY-RECORDS UNTIL END-OF-FILE = "Y".
 A005-END-OF-JOB.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A010-DISPLAY-RECORDS.
     READ FLAVORS NEXT RECORD AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y" DISPLAY KETCHUP-MASTER.

6.4.3. Reading an Indexed File

Your program can read an indexed file sequentially, randomly, or dynamically.

Reading an Indexed File Sequentially

Reading indexed records sequentially involves the following:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL in the Environment Division SELECT clause

  3. Opening the file for INPUT or I-O

  4. Reading records from the beginning of the file as you would a sequential file (using a READ...AT END statement)

The READ statement makes the next logical record of an open file available to the program. It skips deleted records and sequentially reads and retrieves only valid records. When the at end condition occurs, execution of the READ statement is unsuccessful (see Chapter 7).

Example 6.32 reads an entire indexed file sequentially beginning with the first record in the file, displaying every record on the terminal.
Example 6.32. Reading an Indexed File Sequentially
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX03.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS
    ASSIGN TO "DAIRY"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS SEQUENTIAL
                       RECORD KEY IS ICE-CREAM-MASTER-KEY
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-STATE
                                            WITH DUPLICATES
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-CODE.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-MASTER-KEY          PIC XXXX.
     02 ICE-CREAM-MASTER-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX. WORKING-STORAGE SECTION.
 01  END-OF-FILE                      PIC X.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN INPUT FLAVORS.
 A010-SEQUENTIAL-READ.
     PERFORM A100-READ-INPUT UNTIL END-OF-FILE = "Y". A020-EOJ.
     DISPLAY "END OF JOB".
     STOP RUN. A100-READ-INPUT.
     READ  FLAVORS AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y"
        DISPLAY ICE-CREAM-MASTER
        STOP "Type CONTINUE to display next master".

Reading an Indexed File Randomly

Reading indexed records randomly involves the following:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS RANDOM in the Environment Division SELECT clause

  3. Opening the file for INPUT or I-O

  4. Initializing the RECORD KEY or ALTERNATE RECORD KEY data name before reading the record

  5. Reading the record using the KEY IS clause

To read the file randomly, the program must initialize either the primary key data name or the alternate key data name before reading the target record, and specify that data name in the KEY IS phrase of the READ statement.

The READ statement selects a specific record from an open file and makes it available to the program. The value of the primary or alternate key identifies the specific record. The system randomly reads the record identified by the KEY clause. If the I/O system does not find a valid record, the invalid key condition occurs, and the READ statement fails (see Chapter 7).

Example 6.33 reads an indexed file randomly, displaying its contents on the terminal.
Example 6.33. Reading an Indexed File Randomly
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX04.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS    ASSIGN TO "DAIRY"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS RANDOM
                       RECORD KEY IS ICE-CREAM-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-KEY                 PIC XXXX.
     02 ICE-CREAM-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
 WORKING-STORAGE SECTION.
 01  PROGRAM-STAT                     PIC X.
     88  OPERATOR-STOPS-IT            VALUE "1".
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM A020-INITIAL-PROMPT.
     IF OPERATOR-STOPS-IT
        PERFORM A005-TERMINATE.
     PERFORM A030-RANDOM-READ.
     PERFORM A025-SUBSEQUENT-PROMPTS UNTIL OPERATOR-STOPS-IT.
     DISPLAY "END OF JOB".
     STOP RUN.
 A020-INITIAL-PROMPT.
     DISPLAY "Do you want to see a store?".
     PERFORM A040-GET-ANSWER UNTIL PROGRAM-STAT = "Y" OR "y" OR "N" OR "n".
     IF PROGRAM-STAT = "N" OR "n"
        MOVE "1" TO PROGRAM-STAT.
 A025-SUBSEQUENT-PROMPTS.
     MOVE SPACE TO PROGRAM-STAT.
     DISPLAY "Do you want to see another store ?".
     PERFORM A040-GET-ANSWER UNTIL PROGRAM-STAT = "Y" OR "y" OR "N" OR "n".
     IF PROGRAM-STAT = "Y" OR "y"
        PERFORM A030-RANDOM-READ
     ELSE
        MOVE "1" TO PROGRAM-STAT.
 A030-RANDOM-READ.
     DISPLAY "Enter key".
     ACCEPT ICE-CREAM-KEY.
     PERFORM A100-READ-INPUT-BY-KEY.
 A040-GET-ANSWER.
     DISPLAY "Please answer Y or N"
     ACCEPT PROGRAM-STAT.
 A100-READ-INPUT-BY-KEY.
     READ FLAVORS KEY IS ICE-CREAM-KEY
          INVALID KEY DISPLAY "Record does not exist - Try again"
          NOT INVALID KEY DISPLAY "The record is: ", ICE-CREAM-MASTER.
 A005-TERMINATE.
     DISPLAY "terminated".

Reading an Indexed File Dynamically

The READ statement has two formats, so it can select the next logical record (sequential access) or select a specific record (random access) and make it available to the program. In dynamic mode, the program can switch from using random access I/O statements to sequential access I/O statements, in any order and any number of times, without closing and reopening files. However, the program must use the READ NEXT statement to sequentially read an indexed file opened in dynamic mode.

Sequential processing need not begin at the first record of an indexed file. The START statement specifies the next record to be read sequentially, selects which key to use to determine the logical sort order, and repositions the file position indicator for subsequent I/O operations anywhere within the file.

A sequential read of a dynamic file is indicated by the NEXT phrase of the READ statement. A READ NEXT statement should follow the START statement since the READ NEXT statement reads the next record indicated by the file position indicator. Subsequent READ NEXT statements sequentially retrieve records until another START statement or random READ statement executes.

Example 6.34 processes an indexed file containing 26 records. Each record has a unique letter of the alphabet as its primary key. The program positions the file to the first record whose INPUT-RECORD-KEY is equal to the specified letter of the alphabet. The program's READ NEXT statement sequentially retrieves the remaining valid records in the file for display on the terminal.
Example 6.34. Reading an Indexed File Dynamically
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX05.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT IND-ALPHA  ASSIGN TO "ALPHA"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS DYNAMIC
                       RECORD KEY IS INPUT-RECORD-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  IND-ALPHA.
 01  INPUT-RECORD.
     02  INPUT-RECORD-KEY             PIC X.
     02  INPUT-RECORD-DATA            PIC X(50).
 WORKING-STORAGE SECTION.
 01  END-OF-FILE                      PIC X.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O IND-ALPHA.
     DISPLAY "Enter letter"
     ACCEPT INPUT-RECORD-KEY.
     START IND-ALPHA KEY = INPUT-RECORD-KEY
           INVALID KEY DISPLAY "BAD START STATEMENT"
           NOT INVALID KEY
     PERFORM A100-GET-RECORDS THROUGH A100-GET-RECORDS-EXIT
            UNTIL END-OF-FILE = "Y" END-START.
 A010-END-OF-JOB.
     DISPLAY "END OF JOB".
     CLOSE IND-ALPHA.
     STOP RUN.
 A100-GET-RECORDS.
     READ IND-ALPHA NEXT RECORD AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y" DISPLAY INPUT-RECORD.
 A100-GET-RECORDS-EXIT.
     EXIT.
On Alpha and I64, READ PRIOR retrieves from an Indexed file a record that logically precedes the one made current by the previous file access operation, if such a logically previous record exists. READ PRIOR can only be used with a file whose organization is INDEXED and whose access mode is DYNAMIC. The file must be opened for INPUT or I-O. Example 6.35 is an example of READ PRIOR in a program.
Example 6.35. Reading an Indexed File Dynamically, with READ PRIOR (Alpha, I64)
 IDENTIFICATION DIVISION.
 PROGRAM-ID. READ_PRIOR.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT F  ASSIGN TO "READPR"
         ORGANIZATION IS INDEXED
         ACCESS IS DYNAMIC
         RECORD KEY       IS K0
         ALTERNATE RECORD IS K2 DUPLICATES.
 DATA DIVISION.
 FILE SECTION.
 FD F.
 01 R.
     02  K0     PIC  X(3).
     02  FILLER PIC  X(5).
     02  K2     PIC  X(2).
        PROCEDURE DIVISION.
        P0. DISPLAY "***READ_PRIOR***".
       *+
       * Indexed file creation: After this load, the indexed file
       * contains the following records : 0123456789, 1234567890,
        * 2345678990, and 9876543291
 PROCEDURE DIVISION.
 P0. DISPLAY "***READ_PRIOR***".
 *+
 * Indexed file creation: After this load, the indexed file
 * contains the following records : 0123456789, 1234567890,
 * 2345678990, and 9876543291
       *+
            OPEN OUTPUT F.
            MOVE "0123456789" TO R.
            WRITE R INVALID KEY DISPLAY "?1".
            MOVE "1234567890" TO R.
            WRITE R INVALID KEY DISPLAY "?2".
            MOVE "2345678990" TO R.
            WRITE R INVALID KEY DISPLAY "?3".
            MOVE "9876543291" TO R.
            WRITE R INVALID KEY DISPLAY "?4".
            CLOSE F.
 *+
    OPEN OUTPUT F.
    MOVE "0123456789" TO R.
    WRITE R INVALID KEY DISPLAY "?1".
    MOVE "1234567890" TO R.
    WRITE R INVALID KEY DISPLAY "?2".
    MOVE "2345678990" TO R.
    WRITE R INVALID KEY DISPLAY "?3".
    MOVE "9876543291" TO R.
    WRITE R INVALID KEY DISPLAY "?4".
    CLOSE F.
       *+
       * READ PREVIOUS immediately after file open for IO
       *+
            OPEN I-O F.
            MOVE "000" TO K0.
            READ F PREVIOUS AT END GO TO P1 END-READ.
            DISPLAY "?5 " R.
        P1. CLOSE F.
 *+
 * READ PREVIOUS immediately after file open for IO
 *+
    OPEN I-O F.
    MOVE "000" TO K0.
    READ F PREVIOUS AT END GO TO P1 END-READ.
    DISPLAY "?5 " R. P1. CLOSE F.
       *+
       * READ PREVIOUS after file open for IO, from a middle
       * record to beginning record on primary key.
       *+
            OPEN I-O F.
            MOVE "2345678990" TO R.
            READ F INVALID KEY DISPLAY "?6" GO TO P2 END-READ.
            IF R NOT = "2345678990" THEN DISPLAY "?7 " R.
            READ F PREVIOUS AT END DISPLAY "?8" GO TO P2 END-READ.
            IF R NOT = "1234567890" THEN DISPLAY "?9 " R.
            READ F PREVIOUS AT END DISPLAY "?10" GO TO P2 END-READ.
            IF R NOT = "0123456789" THEN DISPLAY "?11 " R.
            READ F PREVIOUS AT END GO TO P2.
            DISPLAY "?12 " R.
 *+
 * READ PREVIOUS after file open for IO, from a middle
 * record to beginning record on primary key.
 *+
    OPEN I-O F.
    MOVE "2345678990" TO R.
    READ F INVALID KEY DISPLAY "?6" GO TO P2 END-READ.
    IF R NOT = "2345678990" THEN DISPLAY "?7 " R.
    READ F PREVIOUS AT END DISPLAY "?8" GO TO P2 END-READ.
    IF R NOT = "1234567890" THEN DISPLAY "?9 " R.
    READ F PREVIOUS AT END DISPLAY "?10" GO TO P2 END-READ.
    IF R NOT = "0123456789" THEN DISPLAY "?11 " R.
    READ F PREVIOUS AT END GO TO P2.
    DISPLAY "?12 " R.
       *+
       * Multiple READ PREVIOUS on a display alternate key with
        * duplicates.
        *+
        P2. MOVE "91" TO K2.
            READ F KEY K2 INVALID KEY DISPLAY "?13" GO TO P5 END-READ.
            IF R NOT = "9876543291" THEN DISPLAY "?14 " R.
            READ F PREVIOUS AT END DISPLAY "?15" GO TO P5 END-READ.
            IF R NOT = "2345678990" THEN DISPLAY "?16 " R.
            READ F PREVIOUS AT END DISPLAY "?17" GO TO P5 END-READ.
            IF R NOT = "1234567890" THEN DISPLAY "?18 " R.
            READ F PREVIOUS AT END DISPLAY "?19" GO TO P5 END-READ.
            IF R NOT = "0123456789" THEN DISPLAY "?20 " R.
            READ F PREVIOUS AT END GO TO P5.
             DISPLAY "?21 " R.
 *+
 * Multiple READ PREVIOUS on a display alternate key with
 * duplicates.
 *+
 P2. MOVE "91" TO K2.
    READ F KEY K2 INVALID KEY DISPLAY "?13" GO TO P5 END-READ.
    R NOT = "9876543291" THEN DISPLAY "?14 " R.
    READ F PREVIOUS AT END DISPLAY "?15" GO TO P5 END-READ.
    IF R NOT = "2345678990" THEN DISPLAY "?16 " R.
    READ F PREVIOUS AT END DISPLAY "?17" GO TO P5 END-READ.
    IF R NOT = "1234567890" THEN DISPLAY "?18 " R.
    READ F PREVIOUS AT END DISPLAY "?19" GO TO P5 END-READ.
    IF R NOT = "0123456789" THEN DISPLAY "?20 " R.
    READ F PREVIOUS AT END GO TO P5.
    DISPLAY "?21 " R.
 P5. CLOSE F.
    DISPLAY "***END***".
    STOP RUN.
Example 6.36 is another example of READ PRIOR. This example contrasts how duplicates are handled with a DESCENDING key and with READ PRIOR. Also, this example shows how to use START before initiating a sequence of either READ NEXT statements or READ PRIOR statements. This example highlights how to use START, if you switch between READ NEXT and READ PRIOR.
Example 6.36. Another Example of READ PRIOR (Alpha, I64)
 ***READ_PRIOR2***
 Read ascending key
 a1
 b2
 c2
 d2
 e3
 Read descending key
 e3
 b2
 c2
 d2
 a1
 Read prior
 e3
 d2
 c2
 b2
 a1
 ***END***
 IDENTIFICATION DIVISION.
 PROGRAM-ID. READ_PRIOR2.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT OPTIONAL F1
     ASSIGN TO "READPR"
     ORGANIZATION IS INDEXED
     ACCESS MODE IS DYNAMIC
     RECORD KEY IS K1 = W2  ASCENDING WITH DUPLICATES
     ALTERNATE
  RECORD KEY IS K2 = W2 DESCENDING WITH DUPLICATES.
 DATA DIVISION.
 FILE SECTION.
 FD F1.
 01 R1.
    02 W1 PIC X.
    02 W2 PIC X.
 PROCEDURE DIVISION.
 P0. DISPLAY "***READ_PRIOR2***".
 *+
 * Indexed file creation.
 *-
     OPEN OUTPUT F1.
     MOVE "a1" TO R1.
     WRITE R1 INVALID KEY DISPLAY "?a1".
     MOVE "b2" TO R1.
     WRITE R1 INVALID KEY DISPLAY "?b2".
     MOVE "c2" TO R1.
     WRITE R1 INVALID KEY DISPLAY "?c2".
     MOVE "d2" TO R1.
     WRITE R1 INVALID KEY DISPLAY "?d2".
     MOVE "e3" TO R1.
     WRITE R1 INVALID KEY DISPLAY "?e3".
     CLOSE F1.
 *+
 * Read using ascending key.
 *-
     OPEN INPUT F1.
     DISPLAY "Read ascending key".
     MOVE "0" TO W2.
     START F1 KEY IS GREATER THAN K1 INVALID KEY DISPLAY "?S1".
     PERFORM 5 TIMES
       READ F1 NEXT AT END DISPLAY "?R2" END-READ
       DISPLAY R1     END-PERFORM.
     CLOSE F1.
 *+
 * Read using descending key.
 *-
     OPEN INPUT F1.
     DISPLAY "Read descending key".
     MOVE "4" TO W2.
     START F1 KEY IS GREATER THAN K2 INVALID KEY DISPLAY "?S2".
     PERFORM 5 TIMES
       READ F1 NEXT AT END DISPLAY "?R2" END-READ
       DISPLAY R1
     END-PERFORM.
 *+
 * READ PRIOR - note the difference in duplicate order from
 * Read with a descending key.
 *-
     DISPLAY "Read prior".
     MOVE "4" TO W2.
     START F1 KEY IS LESS THAN K1 INVALID KEY DISPLAY "?S3".
     PERFORM 5 TIMES
       READ F1 PRIOR AT END DISPLAY "?R3" END-READ
       DISPLAY R1
     END-PERFORM.
     CLOSE F1.
     DISPLAY "***END***".
     STOP RUN.

Reading an Indexed File from Other Languages on UNIX

COBOL supports more data types for indexed keys than are supported in the ISAM definition. For keys in any of the data types not supported in the ISAM definition, the run-time system will translate those keys to strings. Table 6.7 specifies the appropriate mapping to create or use indexed files outside of COBOL (for example, if you are using the C language on UNIX and you need to access COBOL files). Refer to the ISAM package documentation for details of the file format.
Table 6.7. Indexed File – ISAM Mapping
COBOL Data TypeMaps ToTransformation Method

character string PIC x(n)

CHARTYPE

None.

short signed int PIC S9(4) COMP

INTTYPE

C-ISAM

long signed int PIC S9(9) COMP

LONGTYPE

C-ISAM

signed quadword PIC S9(18) COMP

CHARTYPE

Reverse the bytes (integers: most significant byte (msb) last; character strings: msb first). If the data type is not _UNSIGNED, then complement the sign bit. This causes negative values to sort correctly with respect to each other, and precede positive values.

unsigned quadword PIC 9(18) COMP

CHARTYPE

Same as signed quadword.

packed decimal PIC S9(n) COMP-3

CHARTYPE

(Note that sign nibble after is the only case allowed in COBOL.) If the sign nibble is minus, complement all bits. This will give a sign nibble of 1 for a minus, which will come before the plus. Copy the nibbles so the sign nibble is placed on the left and all the other nibbles are shifted one to the right.

Note that any data type not directly supported by ISAM is translated to a character string, which will sort as a character string in the correct order.

6.5. Updating Files

Updating sequential, line sequential, relative, and indexed files includes the following tasks:
  1. Opening the file

  2. Executing a READ or START statement

  3. Executing a REWRITE and a DELETE statement

Sections 6.5.1, 6.5.2, and 6.5.3 describe how to update sequential, relative, and indexed files.

6.5.1. Updating a Sequential File or Line Sequential (Alpha, I64) File

Updating a record in a sequential file involves the following:
  1. Opening the file for I/O

  2. Reading the target record

  3. Rewriting the target record

The REWRITE statement places the record just read back into the file. The REWRITE statement completely replaces the contents of the target record with new data. You can use the REWRITE statement for files on mass storage devices only (for example, disk units). There are two ways of rewriting records:
  • REWRITE record-name FROM source-area

  • REWRITE record-name

Statements (1) and (2) in the following example are logically equivalent:
 FILE SECTION.
 FD  STOCK-FILE.
 01  STOCK-RECORD     PIC X(80). WORKING-STORAGE SECTION.
 01  STOCK-WORK       PIC X(80).
 ---------------(1)------------------    --------------(2)--------------
 REWRITE STOCK-RECORD FROM STOCK-WORK.   MOVE STOCK-WORK TO STOCK-RECORD.
                                         REWRITE STOCK-RECORD.

When you omit the FROM phrase, you process the records directly in the record area or buffer (for example, STOCK-RECORD).

For a REWRITE statement on a sequential file, the record being rewritten must be the same length as the record being replaced.

Example 6.37 reads a sequential file and rewrites as many records as the operator wants.
Example 6.37. Rewriting a Sequential File
 IDENTIFICATION DIVISION.
 PROGRAM-ID. SEQ03.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT TRANS-FILE ASSIGN TO "TRANS".
 DATA DIVISION.
 FILE SECTION.
 FD  TRANS-FILE.
 01  TRANSACTION-RECORD    PIC X(25).
 WORKING-STORAGE SECTION.
 01  ANSWER                PIC X.
 PROCEDURE DIVISION. A000-BEGIN.
     OPEN I-O TRANS-FILE.
     PERFORM A100-READ-TRANS-FILE
        UNTIL TRANSACTION-RECORD = "END".
     CLOSE TRANS-FILE.
     STOP RUN. A100-READ-TRANS-FILE.
     READ TRANS-FILE AT END
        MOVE "END" TO TRANSACTION-RECORD.
     IF TRANSACTION-RECORD NOT = "END"
        PERFORM A300-GET-ANSWER UNTIL ANSWER = "Y" OR "N"
         IF ANSWER = "Y" DISPLAY "Please enter new record content"
            ACCEPT TRANSACTION-RECORD
            REWRITE TRANSACTION-RECORD.
 A300-GET-ANSWER.
     DISPLAY "Do you want to replace this record? – "
              TRANSACTION-RECORD.
     DISPLAY "Please answer Y or N".
     ACCEPT ANSWER.

You cannot open a line sequential file (Alpha, I64) for I-O or use the REWRITE statement.

Extending a Sequential File or Line Sequential File (Alpha, I64)

To position a file to its current end, and to allow the program to write new records beyond the last record in the file, use both:
  • The EXTEND phrase of the OPEN statement

  • The WRITE statement

Example 6.38 shows how to extend a sequential file.
Example 6.38. Extending a Sequential File or Line Sequential File (Alpha, I64)
 IDENTIFICATION DIVISION.
 PROGRAM-ID. SEQ04.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
    SELECT TRANS-FILE ASSIGN TO "TRANS".
 DATA DIVISION.
 FILE SECTION.
 FD  TRANS-FILE.
 01  TRANSACTION-RECORD    PIC X(25).
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN EXTEND TRANS-FILE.
     PERFORM A100-WRITE-RECORD
        UNTIL TRANSACTION-RECORD = "END".
     CLOSE TRANS-FILE.
     STOP RUN.
 A100-WRITE-RECORD.
     DISPLAY "Enter next record  - X(25)".
     DISPLAY "Enter END to terminate the session".
     DISPLAY "-------------------------".
     ACCEPT TRANSACTION-RECORD.
     IF TRANSACTION-RECORD NOT = "END"
        WRITE TRANSACTION-RECORD.

Without the EXTEND mode, a VSI COBOL program would have to open the input file, copy it to an output file, and add records to the output file.

6.5.2. Updating a Relative File

A program updates a relative file with the WRITE, REWRITE, and DELETE statements. The WRITE statement adds a record to the file. Only the REWRITE and DELETE statements change the contents of records already existing in the file. In either case, adequate backup must be available in the event of error. Sections 6.5.2.1 and 6.5.2.2 explain how to rewrite and delete relative records, respectively.

6.5.2.1. Rewriting a Relative File

The REWRITE statement logically replaces a record in a relative file; the original contents of the record are lost. Two options are available for rewriting relative records:
  • Sequential access mode rewriting

  • Random access mode rewriting

Rewriting Relative Records in Sequential Access Mode
Rewriting relative records in sequential access mode involves the following:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Using a START statement and then a READ statement to read the target record

  5. Updating the record

  6. Rewriting the record into its cell

Example 6.39 reads a relative record sequentially and displays the record on the terminal. The program then passes the record to an update routine that is not included in the example. The update routine updates the record, and passes the updated record back to the program illustrated in Example 6.39, which displays the updated record on the terminal and rewrites the record in the same cell.
Example 6.39. Rewriting Relative Records in Sequential Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL07.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS SEQUENTIAL
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99 VALUE 99.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM A100-UPDATE-RECORD UNTIL KETCHUP-MASTER-KEY = 00.
 A005-EOJ.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN. A100-UPDATE-RECORD.
     DISPLAY "TO UPDATE A RECORD ENTER ITS RECORD NUMBER (ZERO to END)".
     ACCEPT KETCHUP-MASTER-KEY WITH CONVERSION.
     IF KETCHUP-MASTER-KEY IS NOT EQUAL TO 00
        START FLAVORS KEY IS EQUAL TO KETCHUP-MASTER-KEY
              INVALID KEY DISPLAY "BAD START"
                          STOP RUN.
                          END-START
        PERFORM A200-READ-FLAVORS
        DISPLAY  "*********BEFORE UPDATE*********"
        DISPLAY KETCHUP-MASTER
 ************************************************************
 *
 *      Update routine code here
 *
 ************************************************************
        DISPLAY
  "*********AFTER UPDATE*********"
        DISPLAY KETCHUP-MASTER
        REWRITE KETCHUP-MASTER.
 A200-READ-FLAVORS.
     READ FLAVORS
          AT END DISPLAY "END OF FILE"
                 GO TO A005-EOJ.
Rewriting Relative Records in Random Access Mode
Rewriting relative records in random access mode involves the following:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS RANDOM (or DYNAMIC) in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Moving the relative record number value of the record you want to read to the RELATIVE KEY data name

  5. Reading the record from the cell identified by the relative record number

  6. Updating the record

  7. Rewriting the record into the cell identified by the relative record number

During execution of the REWRITE statement, the I/O system randomly reads the record identified by the RELATIVE KEY IS clause. The REWRITE statement then places the successfully read record back into its cell in the file.

If the cell does not contain a valid record, or if the REWRITE operation is unsuccessful, the invalid key condition occurs, and the REWRITE operation fails (see Chapter 7).

Example 6.40 reads a relative record randomly, displays its contents on the terminal, updates the record, displays its updated contents on the terminal, and rewrites the record in the same cell.
Example 6.40. Rewriting Relative Records in Random Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL08.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS RANDOM
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM A100-UPDATE-RECORD UNTIL KETCHUP-MASTER-KEY = 00.
 A005-EOJ.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A100-UPDATE-RECORD.
     DISPLAY "TO UPDATE A RECORD ENTER ITS RECORD NUMBER".
     ACCEPT KETCHUP-MASTER-KEY.
     READ FLAVORS INVALID KEY DISPLAY "BAD READ"
                         GO TO A005-EOJ.
     DISPLAY  "*********BEFORE UPDATE*********".
     DISPLAY KETCHUP-MASTER.
 ********************************************************
 *
 *               Update routine
 *
 ********************************************************
     DISPLAY  "*********AFTER UPDATE*********".
     DISPLAY KETCHUP-MASTER.
     REWRITE KETCHUP-MASTER INVALID KEY DISPLAY "BAD REWRITE"
                                        GO TO A005-EOJ.

6.5.2.2. Deleting Records from a Relative File

The DELETE statement logically removes an existing record from a relative file. After successfully removing a record from a file, the program cannot later access it. Two options are available for deleting relative records:
  • Sequential access mode deletion

  • Random access mode deletion

Deleting a Relative Record in Sequential Access Mode
Deleting a relative record in sequential access mode involves the following:
  1. Specifying ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Using a START statement to position the record pointer, or sequentially reading the file up to the target record

  5. Deleting the last read record

Example 6.41 deletes relative records in sequential access mode.
Example 6.41. Deleting Relative Records in Sequential Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL09.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
    SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS SEQUENTIAL
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99 VALUE 1.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM A010-DELETE-RECORDS UNTIL KETCHUP-MASTER-KEY = 00.
 A005-EOJ.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
      STOP RUN.
 A010-DELETE-RECORDS.
     DISPLAY "TO DELETE A RECORD ENTER ITS RECORD NUMBER".
     ACCEPT KETCHUP-MASTER-KEY.
     IF KETCHUP-MASTER-KEY NOT = 00 PERFORM A200-READ-FLAVORS
                                    DELETE FLAVORS RECORD.
 A200-READ-FLAVORS.
     START FLAVORS
         INVALID KEY DISPLAY "INVALID START"
                         STOP RUN.
     READ FLAVORS AT END DISPLAY "FILE AT END"
                         GO TO A005-EOJ.
Deleting a Relative Record in Random Access Mode
Deleting a relative record in random access mode involves the following:
  1. Specifing ORGANIZATION IS RELATIVE in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS RANDOM in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Moving the relative record number value to the RELATIVE KEY data name

  5. Deleting the record identified by the relative record number

If the file does not contain a valid record, an invalid key condition exists.

Example 6.42 deletes relative records in random access mode.
Example 6.42. Deleting Relative Records in Random Access Mode
 IDENTIFICATION DIVISION.
 PROGRAM-ID. REL10.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS ASSIGN TO "BRAND"
                    ORGANIZATION IS RELATIVE
                    ACCESS MODE IS RANDOM
                    RELATIVE KEY IS KETCHUP-MASTER-KEY.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  KETCHUP-MASTER           PIC X(50).
 WORKING-STORAGE SECTION.
 01  KETCHUP-MASTER-KEY       PIC 99 VALUE 1.
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM A010-DELETE-RECORDS UNTIL KETCHUP-MASTER-KEY = 00.
 A005-EOJ.
     DISPLAY "END OF JOB".
     CLOSE FLAVORS.
     STOP RUN.
 A010-DELETE-RECORDS.
     DISPLAY "TO DELETE A RECORD ENTER ITS RECORD NUMBER".
     ACCEPT KETCHUP-MASTER-KEY.
     IF KETCHUP-MASTER-KEY NOT = 00
        DELETE FLAVORS RECORD
               INVALID KEY DISPLAY "INVALID DELETE"
                           STOP RUN.

6.5.3. Updating an Indexed File

Updating a record in an indexed file in sequential access mode involves the following:
  1. Reading the target record

  2. Verifying that the record is the one you want to change

  3. Changing the record

  4. Rewriting or deleting the target record

A program updates an indexed file in random access mode by rewriting or deleting the record.

Three options are available for updating indexed records:
  • Sequential access mode updating

  • Random access mode updating

  • Dynamic access mode updating


Note

A program cannot rewrite an existing record if it changes the contents of the primary key in that record. Instead, the program must delete the record and write a new record. Alternate key values can be changed at any time. However, the value of alternate keys must be unique unless the WITH DUPLICATES phrase is present.

Updating an Indexed File Sequentially

Updating indexed records in sequential acess mode involves the following:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS SEQUENTIAL in Environment Division SELECT clause

  3. Opening the file for I-O

  4. Reading records as you would a sequential file (use the READ statement with the AT END phrase)

  5. Rewriting or deleting records using the INVALID KEY phrase

The READ statement makes the next logical record of an open file available to the program. It skips deleted records and sequentially reads and retrieves only valid records. When the at end condition occurs, execution of the READ statement is unsuccessful (see Chapter 7).

The REWRITE statement replaces the record just read, while the DELETE statement logically removes the record just read from the file.

Example 6.43 updates an indexed file sequentially.
Example 6.43. Updating an Indexed File Sequentially
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX06.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS
    ASSIGN TO "DAIRY"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS SEQUENTIAL
                       RECORD KEY IS ICE-CREAM-MASTER-KEY
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-STATE
                                            WITH DUPLICATES
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-CODE.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-MASTER-KEY          PIC XXXX.
     02 ICE-CREAM-MASTER-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
         03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
 WORKING-STORAGE SECTION.
 01  END-OF-FILE                      PIC X.
 01  REWRITE-KEY                      PIC XXXXX.
 01  DELETE-KEY                       PIC XX.
 01  NEW-ADDRESS                      PIC X(20).
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     DISPLAY "Which store code do you want to find?".
     ACCEPT REWRITE-KEY.
     DISPLAY "What is its new address?".
     ACCEPT NEW-ADDRESS.
     DISPLAY "Which state do you want to delete?".
     ACCEPT DELETE-KEY.
     PERFORM A100-READ-INPUT UNTIL END-OF-FILE = "Y".
 A020-EOJ.
     DISPLAY "END OF JOB".
     STOP RUN. A100-READ-INPUT.
     READ  FLAVORS AT END MOVE "Y" TO END-OF-FILE.
     IF END-OF-FILE NOT = "Y" AND
        REWRITE-KEY = ICE-CREAM-STORE-CODE
        PERFORM A200-REWRITE-MASTER.
     IF END-OF-FILE NOT = "Y" AND
        DELETE-KEY  = ICE-CREAM-STORE-STATE
        PERFORM A300-DELETE-MASTER.
 A200-REWRITE-MASTER.
     MOVE NEW-ADDRESS TO ICE-CREAM-STORE-ADDRESS.
     REWRITE ICE-CREAM-MASTER
             INVALID KEY DISPLAY "Bad rewrite - ABORTED"
                         STOP RUN.
 A300-DELETE-MASTER.
     DELETE FLAVORS.

Updating an Indexed File Randomly

Updating indexed records in random access mode involves the following:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS RANDOM in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Initializing the RECORD KEY or ALTERNATE RECORD KEY data name

  5. Writing, rewriting, or deleting records using the INVALID KEY phrase

You do not need to first read a record to update or delete it. If the primary or alternate key you specify allows duplicates, only the first occurrence of a record with a matching value will be updated.

Example 6.44 updates an indexed file randomly.
Example 6.44. Updating an Indexed File Randomly
 IDENTIFICATION DIVISION.
 PROGRAM-ID. INDEX07.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT FLAVORS
    ASSIGN TO "DAIRY"
                       ORGANIZATION IS INDEXED
                       ACCESS MODE IS RANDOM
                       RECORD KEY IS ICE-CREAM-MASTER-KEY
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-STATE
                                            WITH DUPLICATES
                       ALTERNATE RECORD KEY IS ICE-CREAM-STORE-CODE.
 DATA DIVISION.
 FILE SECTION.
 FD  FLAVORS.
 01  ICE-CREAM-MASTER.
     02 ICE-CREAM-MASTER-KEY          PIC XXXX.
     02 ICE-CREAM-MASTER-DATA.
        03  ICE-CREAM-STORE-CODE      PIC XXXXX.
        03  ICE-CREAM-STORE-ADDRESS   PIC X(20).
        03  ICE-CREAM-STORE-CITY      PIC X(20).
        03  ICE-CREAM-STORE-STATE     PIC XX.
 WORKING-STORAGE SECTION.
 01  HOLD-ICE-CREAM-MASTER            PIC X(51).
 01  PROGRAM-STAT                     PIC X.
     88  OPERATOR-STOPS-IT            VALUE "1".
     88  LETS-SEE-NEXT-STORE          VALUE "2".
     88  NO-MORE-DUPLICATES           VALUE "3".
 PROCEDURE DIVISION.
 A000-BEGIN.
     OPEN I-O FLAVORS.
     PERFORM
 A030-RANDOM-READ UNTIL OPERATOR-STOPS-IT.
 A020-EOJ.
     DISPLAY "END OF JOB".
     STOP RUN. A030-RANDOM-READ.
     DISPLAY "Enter key".
     ACCEPT ICE-CREAM-MASTER-KEY.
     PERFORM A100-READ-INPUT-BY-PRIMARY-KEY
             THROUGH A100-READ-INPUT-EXIT.
     DISPLAY " Do you want to terminate the session?".
     PERFORM A040-GET-ANSWER UNTIL PROGRAM-STAT   = "Y" OR "N".
     IF PROGRAM-STAT   = "Y" MOVE "1" TO PROGRAM-STAT.
 A040-GET-ANSWER.
         DISPLAY "Please answer Y or N"
         ACCEPT PROGRAM-STAT.
 A100-READ-INPUT-BY-PRIMARY-KEY.
     READ FLAVORS KEY IS ICE-CREAM-MASTER-KEY
          INVALID KEY DISPLAY "Master does not exist - Try again"
          GO TO A100-READ-INPUT-EXIT.
     DISPLAY ICE-CREAM-MASTER.
     PERFORM A200-READ-BY-ALTERNATE-KEY UNTIL NO-MORE-DUPLICATES.
 A100-READ-INPUT-EXIT.
     EXIT.
 A200-READ-BY-ALTERNATE-KEY.
     DISPLAY "Do you want to see the next store in this state?".
     PERFORM A040-GET-ANSWER UNTIL PROGRAM-STAT   = "Y" OR "N".
     IF PROGRAM-STAT   = "Y"
        MOVE "2" TO PROGRAM-STAT
        READ FLAVORS KEY IS ICE-CREAM-STORE-STATE
                     INVALID KEY DISPLAY "No more stores in this state"
                                 MOVE "3" TO PROGRAM-STAT.
     IF LETS-SEE-NEXT-STORE AND
        ICE-CREAM-STORE-STATE = "NY"
              PERFORM A500-DELETE-RANDOM-RECORD.
     IF LETS-SEE-NEXT-STORE AND
        ICE-CREAM-STORE-STATE = "NJ"
              MOVE "Monmouth" TO ICE-CREAM-STORE-CITY
              PERFORM A400-REWRITE-RANDOM-RECORD.
     IF LETS-SEE-NEXT-STORE AND
        ICE-CREAM-STORE-STATE = "CA"
              MOVE ICE-CREAM-MASTER TO HOLD-ICE-CREAM-MASTER
              PERFORM A500-DELETE-RANDOM-RECORD
              MOVE HOLD-ICE-CREAM-MASTER TO ICE-CREAM-MASTER
              MOVE "AZ" TO ICE-CREAM-STORE-STATE
              PERFORM A300-WRITE-RANDOM-RECORD.
     IF PROGRAM-STAT   = "N"
        MOVE "3" TO PROGRAM-STAT.
 A300-WRITE-RANDOM-RECORD.
     WRITE ICE-CREAM-MASTER
           INVALID KEY DISPLAY "Bad write - ABORTED"
                       STOP RUN.
 A400-REWRITE-RANDOM-RECORD.
     REWRITE ICE-CREAM-MASTER
             INVALID KEY DISPLAY "Bad rewrite - ABORTED"
                         STOP RUN.
 A500-DELETE-RANDOM-RECORD.
     DELETE FLAVORS
            INVALID KEY DISPLAY "Bad delete - ABORTED"
                        STOP RUN.

Updating an Indexed File Dynamically

Updating indexed records in dynamic access mode involves the following:
  1. Specifying ORGANIZATION IS INDEXED in the Environment Division SELECT clause

  2. Specifying ACCESS MODE IS DYNAMIC in the Environment Division SELECT clause

  3. Opening the file for I-O

  4. Reading the records sequentially (using the START statement to position the record pointer and then using the READ...NEXT statement) or randomly (initializing the RECORD KEY or ALTERNATE RECORD KEY data name and then reading records in any order you want using the INVALID KEY phrase) (See Example 6.44.)

  5. Rewriting or deleting records using the INVALID KEY phrase

For indexed files with duplicate primary keys values, rewriting and deleting work as if the file was opened in sequential access mode. You first read the record, then update or delete the record just read.

For indexed files without duplicates allowed on the primary key, rewriting and deleting work as if the file was opened in random access mode. Specify the value of the primary key data item to indicate the target record, then update or delete that record.

In dynamic access mode, the program can switch from using random access I/O statements to sequential access I/O statements in any order without closing and reopening files.

6.6. Backing Up Your Files

Files can become unusable if either of the following situations occur:
  • Your disk file becomes corrupted by a hardware error.

  • Your disk file becomes corrupted with bad data.

Proper backup procedures are the key to successful recovery. You should back up your disk file at some reasonable point (daily, weekly, or monthly, depending on file activity and value of data), and save all transactions until you create a new backup. In this way, you can easily recreate your disk file from your last backup file and transaction files whenever the need arises.

Chapter 7. Handling Input/Output Exception Conditions

Many types of exception conditions can occur when a program processes a file; not all of them are errors. The three categories of exception conditions are as follows:
  • AT END condition—This is a normal condition when you access a file sequentially. However, if your program tries to read the file any time after having read the last logical record in the file, and there is no applicable Declarative USE procedure or AT END phrase, the program abnormally terminates when the next READ statement executes.

  • Invalid key condition—When you process relative and indexed files, the invalid key condition is a normal condition if you plan for it with a Declarative USE procedure or INVALID KEY phrase. It is an abnormal condition that causes your program to terminate if there is no applicable Declarative USE procedure or INVALID KEY phrase.

  • All other conditions—These can also be either normal conditions (if you plan for them with Declarative USE procedures) or abnormal conditions that cause your program to terminate.

Planning for exception conditions effectively increases program and programmer efficiency. A program with exception handling routines is more flexible than a program without them. Exception handling routines minimize operator intervention and often reduce or eliminate the time you need to spend debugging and rerunning your program.

This chapter introduces you to the tools you need to execute exception handling routines for sequential, relative, and indexed files as a normal part of your program. These tools are the AT END phrase, the INVALID KEY phrase, file status values, and Declarative USE procedures. The topics that follow explain how to use these tools in your programs:

7.1. Planning for the AT END Condition

VSI COBOL provides you the option of testing for this condition with the AT END phrase of the READ statement (for sequential, relative, and indexed files) and the AT END phrase of the ACCEPT statement.

Programs often read sequential files from beginning to end. They can produce reports from the information in the file or even update it. However, the program must be able to detect the end of the file, so that it can continue normal processing at that point. If the program does not test for this condition when it occurs, and if no applicable Declarative USE procedure exists (see Section 7.4), the program terminates abnormally. The program must detect when no more data is available from the file so that it can perform its normal end-of-job processing and then close the file.

Example 7.1 shows the use of the AT END phrase with the READ statement for sequential, relative, and indexed files.
Example 7.1. Handling the AT END Condition
 READ SEQUENTIAL-FILE AT END PERFORM A600-TOTAL-ROUTINES
                             PERFORM A610-VERIFY-TOTALS-ROUTINES
                             MOVE "Y" TO END-OF-FILE.
 READ RELATIVE-FILE NEXT RECORD AT END PERFORM A700-CLEAN-UP-ROUTINES
                                       CLOSE RELATIVE-FILE
                                       STOP RUN.
 READ INDEXED-FILE NEXT RECORD AT END DISPLAY "End of file"
                                      DISPLAY "Do you want to continue?"
                                      ACCEPT REPLY
                                      PERFORM A700-CLEAN-UP-ROUTINES.

7.2. Planning for the Invalid Key Condition

The INVALID KEY clause is available for the VSI COBOL DELETE, READ, REWRITE, START, and WRITE statements. (It does not apply to the READ NEXT statement.) An invalid key condition occurs whenever the I/O system cannot complete a DELETE, READ, REWRITE, START, or WRITE statement. When the condition occurs, execution of the statement that recognized it is unsuccessful, and the file is not affected.

For example, relative and indexed files use keys to access (retrieve or update) records. The program specifying random access must initialize a key before executing a DELETE, READ, REWRITE, START, or WRITE statement. If the key does not result in the successful execution of any one of these statements, the invalid key condition exists. This condition is fatal to the program, if the program does not check for the condition when it occurs and if no applicable Declarative USE procedure exists (see Section 7.4).

The invalid key condition, although fatal if not planned for, can be to your advantage when used properly. You can, as shown in Example 7.2, read through an indexed file for all records with a specific duplicate key and produce a report from the information in those records. You can also plan for an invalid key condition on the first attempt to find a record with a specified key value that is not present in the file. In this case, planning for the invalid key condition allows the program to continue its normal processing. You can also plan for the AT END condition when you have read and tested for the last of the duplicate records in the file, or when you receive the AT END condition for a subsequent read operation, indicating that no more records exist in the file.
Example 7.2. Handling the Invalid Key Condition
      .
      .
      .
      MOVE "SMITH" TO LAST-NAME TEST-LAST-NAME.
      MOVE "Y" TO ANY-MORE-DUPLICATES.
      PERFORM A500-READ-DUPLICATES
              UNTIL ANY-MORE-DUPLICATES = "N".
      .
      .
      . 
      STOP RUN. 
 A500-READ-DUPLICATES.
      READ INDEXED-FILE RECORD INTO HOLD-RECORD
           KEY IS LAST-NAME
           INVALID KEY
                 MOVE "N" TO ANY-MORE-DUPLICATES
                 DISPLAY "Name not in file!"
           NOT INVALID KEY
                 PERFORM A510-READ-NEXT-DUPLICATES
                     UNTIL ANY-MORE-DUPLICATES = "N"
      END-READ. 
 A510-READ-NEXT-DUPLICATES.
      READ INDEXED-FILE NEXT RECORD
           AT END MOVE "N" TO ANY-MORE-DUPLICATES
           NOT AT END
                  PERFORM A520-VALIDATE
      END-READ.
           IF ANY-MORE-DUPLICATES = "Y" PERFORM A700-PRINT.
 A520-VALIDATE.
      IF LAST-NAME NOT EQUAL TEST-LAST-NAME
         MOVE "N" TO ANY-MORE-DUPLICATES.
      END READ. 
 A700-PRINT.
      .
      .
      .

7.3. Using File Status Values and OpenVMS RMS Completion Codes

Your program can check for the specific cause of the failure of a file operation by checking for specific file status values in its exception handling routines. To obtain VSI COBOL file status values, use the FILE STATUS clause in the file description entry.

On OpenVMS, to access RMS completion codes, use the VSI COBOL special registers RMS-STS and RMS-STV, or RMS-CURRENT-STS and RMS-CURRENT-STV.

7.3.1. File Status Values

The run-time execution of any VSI COBOL file processing statement results in a two-digit file status value that reports the success or failure of the COBOL statement. To access this file status value, you must specify the FILE STATUS clause in the file description entry, as shown in Example 7.3.
Example 7.3. Defining a File Status for a File
 DATA DIVISION.
 FILE SECTION.
 FD  INDEXED-FILE
 *     FILE STATUS IS INDEXED-FILE-STATUS.
 * 01  INDEXED-RECORD         PIC X(50).
 WORKING-STORAGE SECTION.
 01  INDEXED-FILE-STATUS    PIC XX.
 01  ANSWER                 PIC X.
The program can access this file status variable, INDEXED-FILE-STATUS, anywhere in the Procedure Division, and depending on its value, take a specific course of action without terminating the program. Notice that in Example 7.4 the file status that was defined in Example 7.3 is used. However, not all statements allow you to access the file status value as part of the statement. Your program has two options:
  • Build an error recovery routine into the statement. The relative and indexed file processing statements that allow you to do this within the INVALID KEY phrase are DELETE, READ, REWRITE, START, and WRITE (that is, all the record I-O verbs except READ NEXT). See Example 7.4.

  • Define a Declarative USE procedure to handle the condition. This option is available for all file organizations and their I/O statements. (See Example 7.6, Example 7.7, and Example 7.8.)


Example 7.4. Using the File Status Value in an Exception Handling Routine
 PROCEDURE DIVISION.
 A000-BEGIN.
     .
     .
     .
     DELETE INDEXED-FILE
          INVALID KEY MOVE "Bad DELETE" to BAD-VERB-ID
                PERFORM A900-EXCEPTION-HANDLING-ROUTINE.
     .
     .
     .
     READ INDEXED-FILE NEXT RECORD
          AT END MOVE "Bad READ" TO BAD-VERB-ID
                PERFORM A900-EXCEPTION-HANDLING-ROUTINE.
     .
     .
     . 
     REWRITE INDEXED-RECORD
          INVALID KEY MOVE "Bad REWRITE" TO BAD-VERB-ID
                PERFORM A900-EXCEPTION-HANDLING-ROUTINE.
     .
     .
     .
     START INDEXED-FILE
           INVALID KEY MOVE "Bad START" TO BAD-VERB-ID
                PERFORM A900-EXCEPTION-HANDLING-ROUTINE.
     .
     .
     .
     WRITE INDEXED-RECORD
          INVALID KEY MOVE "Bad WRITE" TO BAD-VERB-ID
                PERFORM A900-EXCEPTION-HANDLING-ROUTINE.
     .
     .
     .
 A900-EXCEPTION-HANDLING-ROUTINE.
     DISPLAY BAD-VERB-ID " - File Status Value = " INDEXED-FILE-STATUS.
     PERFORM A905-GET-ANSWER UNTIL ANSWER = "Y" OR "N".
     IF ANSWER = "N" STOP RUN. A905-GET-ANSWER.
     DISPLAY "Do you want to continue?"
     DISPLAY "Please answer Y or N"
     ACCEPT ANSWER.

See Soft Record Locks for information about inspecting variables with soft record locks and Declarative USE procedures.

Each file processing statement described in the Procedure Division section of the VSI COBOL Reference Manual contains a specific list of file status values in its Technical Notes section. In addition, all file status values are listed in an appendix in the VSI COBOL Reference Manual.

7.3.2. RMS Completion Codes (OpenVMS)

VSI COBOL on OpenVMS checks for RMS completion codes after each file and record operation. If the code indicates anything other than unconditional success, VSI COBOL maps the RMS completion code to a file status value. However, not all RMS completion codes map to distinct file status values. Many RMS completion codes map to File Status 30, a COBOL code for errors that have no specific file status value.

VSI COBOL provides the following six special exception condition registers, four of which are shown in Example 7.5:
  • RMS-STS

  • RMS-STV

  • RMS-FILENAME

  • RMS-CURRENT-STS

  • RMS-CURRENT-STV

  • RMS-CURRENT-FILENAME

These special registers supplement the file status values already available and allow the VSI COBOL program to directly access RMS completion codes. For more information on RMS completion codes, refer to the VSI COBOL Reference Manual and the VSI OpenVMS Record Management Services Reference Manual.

You do not define these special registers in your program. As special registers, they are available whenever and wherever you need to use them in the Procedure Division. RMS-CURRENT-STS contains the RMS completion codes for the most recent file or record operation for any file. RMS-CURRENT-FILENAME contains the name of the current file by which it is known to the system, which can be the full file specification (directory, device, file name, and extension). RMS-CURRENT-STV contains other relevant information (refer to the OpenVMS System Messages and Recovery Procedures Reference Manual, an archived manual that is available on the OpenVMS Documentation CD-ROM.). When you access these three special registers, you must not qualify your reference to them. However, if you define more than one file in the program and intend to access RMS-STS, RMS-STV, and RMS-FILENAME, you must qualify your references to them by using the internal COBOL program's file name for the file that you intend to reference.

Notice the use of the WITH CONVERSION phrase of the DISPLAY statement in Example 7.5. This converts the PIC S9(9) COMP contents of the RMS Special Registers from binary to decimal digits for terminal display.
Example 7.5. Referencing RMS-STS, RMS-STV, RMS-CURRENT-STS, and RMS-CURRENT-STV Codes (OpenVMS)
   .
   .
   .
DATA DIVISION.
FILE SECTION.
FD  FILE-1.
01  RECORD-1         PIC X(50).
FD  FILE-2.
01  RECORD-2         PIC X(50).
WORKING-STORAGE SECTION.
01  ANSWER           PIC X.
01  STS              PIC S9(9)  COMP.
01  STV              PIC S9(9)  COMP.

PROCEDURE DIVISION.
A000-BEGIN.
    .
    .
    WRITE RECORD-1 INVALID KEY PERFORM A901-REPORT-FILE1-STATUS.
*
*   The following PERFORM statement displays the RMS completion
*   codes resulting from the above WRITE statement for FILE-1.
*
    PERFORM A903-REPORT-RMS-CURRENT-STATUS.
    .
    .
    .
    WRITE RECORD-2 INVALID KEY PERFORM A902-REPORT-FILE2-STATUS.
*
*   The following PERFORM statement displays the RMS completion
*   codes resulting from the above WRITE statement for FILE-2.
*
    PERFORM A903-REPORT-RMS-CURRENT-STATUS.
    .
    .
    .
*
*   The following PERFORM statement moves the RMS completion codes
*   resulting from the above WRITE statement for FILE-2 to data
*   fields that are explicitly defined within your program.
*
    PERFORM A904-MOVE-RMS-STS-STV.
    .
    .
    .
A901-REPORT-FILE1-STATUS.
*******************************************
*
    DISPLAY "RMS-STS = " RMS-STS OF FILE-1 WITH CONVERSION.
    DISPLAY "RMS-STV = " RMS-STV OF FILE-1 WITH CONVERSION.
    DISPLAY "RMS-FILENAME = " RMS-FILENAME OF FILE-1.
*
*******************************************
    PERFORM A999-GET-ANSWER UNTIL ANSWER = "Y" OR "N".
    IF ANSWER = "N" STOP RUN.
A902-REPORT-FILE2-STATUS.
*******************************************
*
    DISPLAY "RMS-STS = " RMS-STS OF FILE-2 WITH CONVERSION.
    DISPLAY "RMS-STV = " RMS-STV OF FILE-2 WITH CONVERSION.
    DISPLAY "RMS-FILENAME = " RMS-FILENAME OF FILE-2.
*
*******************************************
    PERFORM A999-GET-ANSWER UNTIL ANSWER = "Y" OR "N".
    IF ANSWER = "N" STOP RUN.
A903-REPORT-RMS-CURRENT-STATUS.
*******************************************
*
    DISPLAY "RMS-CURRENT-STS = " RMS-CURRENT-STS WITH CONVERSION.
    DISPLAY "RMS-CURRENT-STV = " RMS-CURRENT-STV WITH CONVERSION.
    DISPLAY "RMS-CURRENT-FILENAME = " RMS-CURRENT-FILENAME.
*
*******************************************
    PERFORM A999-GET-ANSWER UNTIL ANSWER = "Y" OR "N".
    IF ANSWER = "N" STOP RUN.
A904-MOVE-RMS-STS-STV.
*******************************************
*
    MOVE RMS-STS OF FILE-1 TO STS.
    MOVE RMS-STV OF FILE-1 TO STV.
*
*******************************************
    PERFORM A999-GET-ANSWER UNTIL ANSWER = "Y" OR "N".
    IF ANSWER = "N" STOP RUN.
A999-GET-ANSWER.
    DISPLAY "Do you want to continue?"
    DISPLAY "Please answer Y or N"
    ACCEPT ANSWER.

7.4. Using Declarative USE Procedures

An applicable Declarative USE procedure executes whenever an I/O statement results in an exception condition (a file status value that does not begin with a zero (0)) and the I/O statement does not contain an AT END or INVALID KEY phrase. The AT END and INVALID KEY phrases take precedence over a Declarative USE procedure, but only for the I/O statement that includes the clause. For example, the AT END phrase takes effect only with File Status 10 and the INVALID KEY phrase takes effect only with File Status 23. Therefore, you can have specific I/O statement exception condition handling for a file and also include a Declarative USE procedure for general exception handling.

A Declarative USE procedure is a set of one or more special-purpose sections at the beginning of the Procedure Division. As shown in Example 7.6, the key word DECLARATIVES precedes the first of these sections, and the key words END DECLARATIVES follow the last.
Example 7.6. The Declaratives Skeleton
 PROCEDURE DIVISION.
 DECLARATIVES.
      .
      .
      .
 END DECLARATIVES.
 MAIN-BODY SECTION.
 BEGIN.
      .
      .
      .
As shown in Example 7.7, a Declarative procedure consists of a section header, followed, in order, by a USE statement and one or more paragraphs.
Example 7.7. A Declarative USE Procedure Skeleton
      .
      .
      .
 PROCEDURE DIVISION.
 DECLARATIVES.
 D0-00-FILE-A-PROBLEM SECTION.
     USE AFTER STANDARD ERROR PROCEDURE ON FILE-A. 
     D0-01-FILE-A-PROBLEM.
      .
      .
      .
     D0-02-FILE-A-PROBLEM.
      .
      .
      .
     D0-03-FILE-A-PROBLEM.
      .
      .
      .
 END DECLARATIVES.
 MAIN-BODY SECTION.
 BEGIN.
      .
      .
      .

Declarative USE procedures can be either ordinary or global. Ordinary Declarative USE procedures have a limited scope; you can use them only in programs where they are originally introduced. Global Declarative USE procedures have a wider scope; you can use them in programs that introduce them as well as in programs that are contained within the introducing program.

In VSI COBOL Declarative procedures, the conditions in the USE statements indicate when they execute. There are five conditions. One USE statement can have only one condition; therefore, if you need all five conditions in one program, you must use five separate USE procedures. These procedures and their corresponding conditions are as follows:
  • File name—You can define a file name Declarative USE procedure for each file name. This procedure takes precedence over the next four procedures. It executes for any unsuccessful exception condition. (One USE statement can specify multiple file names.)

  • INPUT—You can define only one INPUT Declarative USE procedure for each program. This procedure executes for any unsuccessful exception condition if: (1) the file is open for INPUT and (2) a file name Declarative USE procedure does not exist for that file.

  • OUTPUT—You can define only one OUTPUT Declarative USE procedure for each program. This procedure executes for any unsuccessful exception condition if: (1) the file is open for OUTPUT and (2) a file name Declarative USE procedure does not exist for that file.

  • INPUT-OUTPUT—You can define only one INPUT-OUTPUT Declarative USE procedure for each program. This procedure executes for any unsuccessful exception condition if: (1) the file is open for INPUT-OUTPUT (I-O) and (2) a file name Declarative USE procedure does not exist for that file.

  • EXTEND—You can define only one EXTEND Declarative USE procedure for each program. This procedure executes for any unsuccessful exception condition if: (1) the file is open for EXTEND and (2) a file name Declarative USE procedure does not exist for that file.

Note that the USE statement itself does not execute; it defines the condition that causes the Declarative procedure to execute. Refer to the VSI COBOL Reference Manual for more information about specifying Declarative procedures with the USE statement.

Example 7.8 shows you how to include a USE procedure for each of the conditions in your program. The example also contains explanatory comments for each.
Example 7.8. Five Types of Declarative USE Procedures
     .
     .
     .
 PROCEDURE DIVISION.
 DECLARATIVES.
 ********************************************************
 D1-00-FILE-A-PROBLEM SECTION.
     USE AFTER STANDARD ERROR PROCEDURE ON FILE-A.
 *
 *
 * If any file-access statement for FILE-A results in an
 * error, D1-00-FILE-A-PROBLEM executes.
 *
 *
 D1-01-FILE-A-PROBLEM.
     PERFORM D9-00-REPORT-FILE-STATUS.
     .
     .
     . 
 ********************************************************
 D2-00-FILE-INPUT-PROBLEM SECTION.
     USE AFTER STANDARD EXCEPTION PROCEDURE ON INPUT.
 *
 *
 * If an error occurs for any file open 
 * in the INPUT mode except FILE-A,
 * D2-00-FILE-INPUT-PROBLEM executes.
 *
 *
 D2-01-FILE-INPUT-PROBLEM.
     PERFORM D9-00-REPORT-FILE-STATUS.
     .
     .
     . 
 ********************************************************
 D3-00-FILE-OUTPUT-PROBLEM SECTION.
     USE AFTER STANDARD EXCEPTION PROCEDURE ON OUTPUT.
 *
 *
 * If an error occurs for any file open 
 * in the OUTPUT mode except FILE-A,
 * D3-00-FILE-OUTPUT-PROBLEM executes.
 *
 *
 D3-01-FILE-OUTPUT-PROBLEM.
     PERFORM D9-00-REPORT-FILE-STATUS.
     .
     .
     . 
 ********************************************************
 D4-00-FILE-I-O-PROBLEM SECTION.
     USE AFTER STANDARD EXCEPTION PROCEDURE ON I-O.
 *
 *
 * If an error occurs for any file open 
 * in the INPUT-OUTPUT mode except FILE-A,
 * D4-00-FILE-I-O-PROBLEM executes.
 *
 *
 *
 D4-01-FILE-I-O-PROBLEM.
     PERFORM D9-00-REPORT-FILE-STATUS.
     .
     .
     . 
 ********************************************************
 D5-00-FILE-EXTEND-PROBLEM SECTION.
     USE AFTER STANDARD EXCEPTION PROCEDURE ON EXTEND.
 *
 *
 * If an error occurs for any file open 
 * in the EXTEND mode except FILE-A,
 * D5-00-FILE-EXTEND-PROBLEM executes.
 *
 *
 D5-01-FILE-EXTEND-PROBLEM.
     PERFORM D9-00-REPORT-FILE-STATUS.
     .
     .
     .
 D9-00-REPORT-FILE-STATUS.
     .
     .
     .
 END DECLARATIVES.
 ********************************************************
 A000-BEGIN SECTION.
 BEGIN.
     .
     .
     .

Chapter 8. Sharing Files and Locking Records

This chapter includes the following information about sharing files and protecting records for sequential, relative, and indexed files:
  • Controlling access to files and records (Section 8.1)

  • Choosing X/Open standard (OpenVMS Alpha and I64) or VSI standard file sharing and record locking (Section 8.2)

  • Ensuring successful file sharing (Section 8.3)

  • Using record locking to control access to records (Section 8.4)

8.1. Controlling Access to Files and Records

In a data manipulation environment where many users and programs access the same data, file control must be applied to protect files from nonprivileged users, to permit the desired degree of file sharing, and to preserve data integrity in the files. For example, in Figure 8.1 many users and programs want to access data found in FILE-A.

Figure 8.1. Multiple Access to a File
Multiple Access to a File

File sharing and record locking allow you to control file and record operations when more than one access stream (the series of file and record operations being performed by a single user, using a single file connector) is concurrently accessing a file, as in Figure 8.1.

A VSI COBOL program, via the I/O system, can define one or more access streams. You create one access stream with each OPEN file-name statement. The access stream remains active until you terminate it with the CLOSE file-name statement or until your program terminates.

File sharing allows multiple users (or access streams) to access a single file concurrently. The protection level of the file, set by the file owner, determines which users can share a file.

Record locking controls simultaneous record operations in files that are accessed concurrently. Record locking ensures that when a program is writing, deleting, or rewriting a record in a given access stream, another access stream is allowed to access the same record in a specified manner.

Figure 8.2 illustrates the relationship of record locking to file sharing.

Figure 8.2. Relationship of Record Locking to File Sharing
Relationship of Record Locking to File Sharing

File sharing is a function of the file system, while record locking is a function of the I/O system. The file system manages file placement and the file-sharing process, in which multiple access streams simultaneously access a file. The I/O system manages the record-sharing process and provides access methods to records within a file. This includes managing the record-locking process, in which multiple access streams simultaneously access a record.

You must have successful file sharing before you can consider record locking.

In VSI COBOL, the file operations begin with an OPEN statement and end with a CLOSE statement. The OPEN statement initializes an access stream and specifies the mode. The CLOSE statement terminates an access stream and can be either explicit (stated in the program) or implicit (on program termination).

Note

The first access stream to open a file determines how other access streams can access the file concurrently (if at all).

The record operations for VSI COBOL that are associated with record locking are as follows:
  • READ
  • START
  • WRITE
  • REWRITE
  • DELETE
  • UNLOCK

8.2. Choosing a File Sharing and Record Locking Standard (OpenVMS Alpha and I64)

On OpenVMS Alpha and I64 systems, VSI COBOL offers two methods of controlling potential conflicts of multi-user file access between simultaneously running processes:
  • VSI standard, which is compatible with the behavior of VSI COBOL

  • X/Open standard (OpenVMS Alpha and I64), which conforms to the X/Open CAE Specification: COBOL Language and which offers X/Open portability

Both effectively control potential conflicts of file access between simultaneously running COBOL processes. Both offer locking for all file types: sequential, relative, and indexed.

Note

If you choose X/Open standard file sharing and record locking for a file connector, you must not use VSI standard syntax anywhere in your program for the same file connector. The two are mutually exclusive.

The VSI COBOL compiler determines whether to apply X/Open standard behavior or VSI standard behavior for any file connector on the basis of the syntax used for that file connector. The following syntax identifies X/Open standard:
  • LOCK MODE (SELECT statement)
  • WITH LOCK (OPEN statement)
  • WITH [NO] LOCK (READ statement)
  • UNLOCK RECORDS
The following syntax identifies VSI standard:
  • APPLY LOCK-HOLDING (Environment Division)
  • ALLOWING?
  • REGARDLESS ? (Procedure Division)
  • UNLOCK ALL

For any given file connector, any subsequent I-O locking syntax in your program must be consistent: X/Open standard and VSI standard file sharing/record locking (implicit or explicit) cannot be mixed for the same file connector.

If a program includes any ambiguous semantics for I-O verbs (that is, no locking syntax for verbs for which the two standards provide different default behavior) and the previous code does not use VSI or X/Open standard-specific syntax for that file connector, the compiler determines which standard to use by applying the specification (or default) from your compile command line, as follows:
  • The -std [no]xopen flag on the cobol command for the UNIX operating system

  • The /STANDARD=[NO]XOPEN qualifier on the COBOL command for the OpenVMS Alpha and I64 operating system

If you do not specify the flag or qualifier, the default is noxopen (VSI standard) file sharing and record locking.

If you want X/Open file sharing and record locking and have not used the LOCK MODE clause, therefore, you should specify /STANDARD=XOPEN or -std xopen to ensure X/Open standard behavior in instances of conflicting default semantics. Note, however, that the qualifier/flag comes into effect only when the explicit syntax has not determined the usage.

8.3. Ensuring Successful File Sharing

Successful file sharing requires that you:
  • Provide disk residency for the file.

  • Use the operating system file protection facility, namely the contents of /etc/groups (on UNIX systems) or the UIC (on OpenVMS systems).

  • Determine the intended access mode to the file (VSI COBOL open modes).

  • Indicate the file access allowed by other streams, using X/Open standard (on OpenVMS Alpha and I64 only) or VSI standard syntax to specify file sharing.

The remainder of this section describes these requirements in more detail.

8.3.1. Providing Disk Residency

Only files that reside on a disk can be shared. In VSI COBOL you can share sequential, relative, and indexed files.

8.3.2. Using File Protection

By applying the appropriate file permissions at the operating system level, the owner of a file determines how other users can access the file. An owner can permit different types of file access for different users or groups.

Note

The following OpenVMS operating system file protection access types are not a part of VSI COBOL syntax.

The four types of file access are as follows:
  • READ—Permits the reading of the records in the file.

  • WRITE—Permits updating or extending the records in the file.

  • EXECUTE—Applies to on-disk volume protection and image execution and is therefore not applicable to a VSI COBOL program except through system service routines.

  • DELETE—Permits deletion of the file and is therefore not applicable to a VSI COBOL program (since VSI COBOL has no delete file facility) except through system service routines.

In the OpenVMS file protection facility, four different categories of users exist with respect to data structures and devices. A file owner determines which of the following user categories can share the file:
  • SYSTEM—Users of the system whose group numbers are in the range 0 to the value of the MAXSYSGROUP parameter or who have certain I/O-related privileges

  • OWNER—Users of the system whose UIC group and member numbers are identical to the UIC of the file owner

  • GROUP—Users of the system whose group number is identical to the group number of the file owner

  • WORLD—All other users of the system who are not included in the previous categories

The OpenVMS operating system applies a default protection to each newly created file unless the owner specifically requests modified protection.

For more information on file protection, refer to the VSI OpenVMS User's Manual.

Note

The following UNIX operating system file access types are not a part of VSI COBOL syntax.

On UNIX systems, the three types of file access are as follows:
  • Read—Permits the reading of the records in the file.

  • Write—Permits updating or extending the records in the file.

  • Execute—Applies to image execution and is therefore not applicable to a VSI COBOL program.

There are three categories of users:
  • User—Owner of the file

  • Group—Users in the same group as the owner

  • Others—All other users

VSI COBOL determines the access permission for newly created files in the following manner:
  1. The default access permissions are granted:
    • User and Group are granted read and write access.

    • Others are granted read access.

  2. Then the file mode creation mask of the process creating the file is taken into account.

Additional information on file permission can be found in the UNIX man pages for chmod, ls, open, and umask.

8.3.3. Determining the Intended Access Mode to a File

Once you establish disk residency and permission for a file, you can consider how the stream intends to access the file. You specify this intention by using the VSI COBOL open and access modes.

The VSI COBOL open modes are INPUT, OUTPUT, EXTEND, and I-O. The VSI COBOL access modes are SEQUENTIAL, RANDOM, and DYNAMIC. The combination of open and access modes determines the operations intended on the file.

You must validate your VSI COBOL intention against the file protection assigned by the file owner. For example, to use an OPEN INPUT clause requires that the file owner has granted read access privileges to the file. To use an OPEN OUTPUT or EXTEND clause requires write access privileges to the file. To use an OPEN I-O clause requires both read and write access privileges.

The following chart shows the relationship between open and access modes and intended VSI COBOL operations. The word ANY indicates that all three access methods result in the same intentions.

Open Mode

Access Mode

Intended COBOL Operations

INPUT

ANY

READ, START

OUTPUT

ANY

WRITE

I-O

SEQUENTIAL

READ, START, REWRITE, DELETE

 

RANDOM/DYNAMIC

READ, START, REWRITE, DELETE, WRITE

EXTEND

SEQUENTIAL

WRITE


Note

If the file protection does not permit the intended operations, file access is not granted, even if open and access modes are compatible.

File protection and open mode access apply to both the unshared and shared (multiple access stream) file environments. A file protection and intent check is made when the first access stream opens a file (in the unshared file environment), and again when the second and subsequent access streams open the file (in the shared file environment).

After you provide disk residency, specify permission, and determine the access mode to a file, you can specify the access allowed to other streams through file-sharing and record-locking techniques. The remainder of this chapter describes this access control.

8.3.4. Specifying File Access Using X/Open Standard File Sharing (OpenVMS Alpha and I64)

X/Open standard file sharing is summarized in this section and fully described in the VSI COBOL Reference Manual (Environment Division and Procedure Division chapters) and the CAE Specification: COBOL Language.

If you want a file in your COBOL program to utilize X/Open standard file sharing (probably for purposes of portability), you should include X/Open-specific syntax for the file in the Environment Division. Use one of the following:
  • LOCK MODE IS AUTOMATIC
  • LOCK MODE IS MANUAL
  • LOCK MODE IS EXCLUSIVE

You can also select X/Open file sharing by just specifying WITH LOCK on the OPEN or READ statements. However, it is recommended that you use the LOCK MODE clause to avoid ambiguity and maintain readability. If this is not done and any I-O verbs rely on default behavior that might result in ambiguity, you should compile your program with the X/Open option added to the compile command line.

Opened files can be exclusive or shareable, as specified by the LOCK MODE option of the SELECT clause (in the FILE-CONTROL paragraph of the Environment Division) or the OPEN statement. However, files opened in OUTPUT mode cannot be shared. To make a file shareable, specify one of the following:
  • LOCK MODE IS AUTOMATIC [WITH LOCK ON RECORD]

  • LOCK MODE IS MANUAL WITH LOCK ON MULTIPLE RECORDS (allowed only for indexed or relative files)

These forms allow other access streams to open the file.

To make the file unavailable to other processes, specify one of the following:
  • LOCK MODE IS EXCLUSIVE

  • WITH LOCK on the OPEN statement

This locks the file. Attempts by other access streams to open the file cause a file lock condition.

If the LOCK MODE clause and WITH LOCK phrase are both omitted, the default file sharing is as follows:
  • Opened in INPUT mode: shareable

  • Opened in I-O, EXTEND, or OUTPUT mode: exclusive

The WITH LOCK phrase overrides any LOCK MODE clause. This is useful to create an exclusive access stream for a file declared as shareable.

You can protect a shareable file's data by using record-locking syntax (described in Section 8.4.1).

Example 8.1 shows the use of X/Open standard file-sharing code and the results when files are opened.

Example 8.1. X/Open Standard Lock Modes and Opening Files (Alpha, I64)
     FILE-CONTROL.
         SELECT employee-file ASSIGN TO "EMPFIL"
                LOCK MANUAL LOCK ON MULTIPLE RECORDS.

         SELECT master-file ASSIGN TO "MASTFIL"
                LOCK AUTOMATIC.

         SELECT tran-file ASSIGN TO "TRANFIL"
                LOCK MODE IS EXCLUSIVE.

         SELECT job-codes ASSIGN TO "JOBFIL".
                .
                .
                .
     PROCEDURE-DIVISION.
     BEGIN.
     * The file is shareable per LOCK MODE specification:

          OPEN I-O employee-file.

     * The file is exclusive during this access stream, overriding the
     * LOCK MODE specification:

          OPEN I-O master-file WITH LOCK.

     * The file is exclusive per LOCK MODE; others cannot access it:

          OPEN INPUT tran-file.

     * The file defaults to exclusive; others cannot access it:

          OPEN EXTEND job-codes.

8.3.5. Specifying File Access Using VSI Standard File Sharing

VSI standard file sharing is summarized in this section and fully described in the VSI COBOL Reference Manual (Environment Division and Procedure Division chapters).

You use the ALLOWING clause of the OPEN statement to specify what other access streams are allowed to access that file. The forms of OPEN ALLOWING are as follows:
  • OPEN ALLOWING NO OTHERS―Locks the file for exclusive access. Attempts by other access streams to access the file cause a file lock condition.

  • OPEN ALLOWING READERS―Locks the file against operations that indicate intended write access (OPEN I-O and OPEN EXTEND). Other streams can use the OPEN INPUT statement to view the file. No updaters are permitted.

    On UNIX, this lock is limited for INDEXED files, as follows:
    • Any stream

      If automatic record locking was requested, the file has now been opened with manual record locking in an attempt to process READERS.

    • First stream

      If the open mode was INPUT (reader), subsequent non-exclusive updaters will get access to the file at OPEN time, but they will not be able to update the file at the record level.

      If the mode is EXTEND, I-O, or OUTPUT (updater), the file lock acquired will not exclude other updaters that have specified full sharing of the file (with ALLOWING {ALL,UPDATERS,WRITERS}).

    • Subsequent stream

      If the mode is EXTEND or OUTPUT (updater), access to the file is granted instead of denied when a previous updater stream has specified full sharing of the file (with ALLOWING {ALL,UPDATERS,WRITERS}).

      If the mode is INPUT (reader), access to the file is granted instead of denied when a previous updater stream has specified full (ALL/UPDATERS/WRITERS) or partial (READERS) sharing of the file.

  • OPEN ALLOWING WRITERS or UPDATERS or ALL―Allows access by other streams. Other access streams can open the file in INPUT, EXTEND, and I-O modes.

VSI COBOL also permits a list of OPEN ALLOWING options, separated by commas. The list results in the following equivalent ALLOWING specifications:
  • ALLOWING WRITERS, UPDATERS becomes ALLOWING ALL

  • ALLOWING READERS, UPDATERS becomes ALLOWING UPDATERS

The first access stream uses the ALLOWING clause to specify what other access streams can do. When the second and subsequent access streams attempt to open the file, the following checks occur:
  1. The allowed options of this access stream are checked against the intended access of the previous streams.

  2. The intended access of this access stream is checked against the allowed access of the previous streams.

For example, if the first access stream specifies the ALLOWING READERS clause, then a subsequent access stream that opens the file ALLOWING NO OTHERS would fail. Also, if the first access stream opens the file ALLOWING READERS, the following access stream that opens the file ALLOWING ALL and WITH I-O mode would fail, because the clause option and the I-O mode declare write intent to the file.

If you do not specify an ALLOWING clause on the OPEN statement, the default for files opened for INPUT is ALLOWING READERS, and the default for files opened for I-O, OUTPUT, or EXTEND mode is ALLOWING NO OTHERS.

Describing Types of Access Streams

You can establish several types of access streams. For example, two programs opening the same file represent two access streams to that file. Both programs begin with the file open, perform record operations, and then close the file.

Combining Related File-Sharing Criteria

This section summarizes the relationships among three of the file-sharing criteria (the first file-sharing requirement, disk residency, is not included).

The following chart shows the file protection and open mode requirements. For example, the file protection privilege READ (R) permits OPEN INPUT.

File ProtectionOpen Mode
RINPUT
WOUTPUT, EXTEND
RWI-O, INPUT, OUTPUT, EXTEND

Remember that you specify intended operations through the first access stream. For the second and subsequent shared access to a file, you use the access intentions (open modes) and the ALLOWING clause to determine if and how a file is shared. Note that some streams can be locked out if their intentions are not compatible with those of the streams that have already been allowed entry to the file.

On OpenVMS, Table 8.1 shows the valid and invalid OPEN ALLOWING combinations between first and subsequent access streams.

Table 8.1. File-Sharing Options (UNIX)

FIRST STREAM

SUBSEQUENT STREAM

Open mode:

UPDATEUPDATEUPDATEINPUTINPUTINPUTOUTPUT ALL

Allowing:

ALLREADERSNONEALLREADERSNONEREADERS NONE

UPDATE

ALL

G52G525

UPDATE

READERS

63,42G525

UPDATE

NONE

11,31,211,31,25

INPUT

ALL

GG2GG25

INPUT

READERS

772GG25

INPUT

NONE

111,2111,25

Legend

UPDATE

OPEN EXTEND or OPEN I-O

INPUT

OPEN INPUT

OUTPUT

OPEN OUTPUT

ALL

ALLOWING ALL or ALLOWING UPDATERS or ALLOWING WRITERS

READERS

ALLOWING READERS

NONE

ALLOWING NO OTHERS

G

Second stream successfully opens and file sharing is granted.

1

Second stream is denied access to the file because the first stream requires exclusive access (the first stream specified NO OTHERS).

2

Second stream is denied access to the file because the second stream requires exclusive access (the second stream specified NO OTHERS).

3

Second stream is denied access to the file because the first stream intends to write, while the second stream specifies read-only sharing.

4

Second stream is denied access to the file because the second stream intends to write, while the first stream specifies read-only sharing.

On UNIX, Table 8.2 shows the valid and invalid OPEN ALLOWING combinations between first and subsequent access streams. The table assumes no file protection violations on the first stream.
Table 8.2. File-Sharing Options (UNIX)

FIRST STREAM

SUBSEQUENT STREAM

Open mode: Allowing:

UPDATE ALL

UPDATE READERS

UPDATE NONE

INPUT ALL

INPUT READERS

INPUT NONE

UPDATE ALL

G

5

2

G

5

2

UPDATE READERS

6

3,4

2

G

5

2

UPDATE NONE

1

1,3

1,2

1

1,3

1,2

INPUT ALL

G

G

2

G

G

2

INPUT READERS

7

7

2

G

G

2

INPUT NONE

1

1

1,2

1

1

1,2

In the following example, three streams illustrate some of the file-sharing rules:
 STREAM 1           OPEN INPUT ALLOWING ALL
 STREAM 2           OPEN INPUT ALLOWING READERS
 STREAM 3           OPEN I-O ALLOWING UPDATERS

Stream 1 permits ALLOWING ALL; thus stream 2 can read the file. However, the third stream violates the intent of the second stream, because OPEN I-O implies a write intention that stream 2 disallows. Consequently, the third access stream receives a file locked error.

8.3.6. Error Handling for File Sharing

This section describes error conditions, checking file operations for success or failure, some considerations when you specify the OPEN EXTEND statement, and related potential errors.

Error Conditions

Whether the syntax is X/Open standard (Alpha, I64) or VSI standard, any file contention error results in an unsuccessful statement for which a USE procedure will be invoked. A file-locked condition results in an I-O status code of 91.

On OpenVMS Alpha and I64, it is invalid to specify both X/Open and VSI standard file sharing for the same file connector. Any attempts are flagged by the compiler when they are detectable in a single compilation unit. Across compilation units, the run-time system detects and reports such violations. This restriction is true for explicit and implicit (default) usage.

Checking File Operations

You can check the success or failure of a file open operation by using the File Status value (or, on OpenVMS systems, the RMS-STS value in a VSI COBOL special register called RMS-STS).

Table 8.3 illustrates the file status values you frequently use in a file-sharing environment.
Table 8.3. File Status Values Used in a File-Sharing Environment

File Status Value

Meaning

00

Successful operation

30File protection violation
91File is locked

File Status 00 indicates the completion of a successful operation.

File Status 30 might result from a violation of the file protection codes described in Section 8.3.2. To correct this condition, the file owner must reset the protection on the file or the directory that contains the file.

File Status 91 indicates that a previous access stream has denied access to the file. That previous access stream opened the file with locking attributes that conflict with the OPEN statement of the subsequent stream.

You can obtain the values that apply to file-sharing exceptions (or to successful file-sharing operations), as shown in Example 8.2.

Example 8.2. Program Segment for File Status Values
 FILE-CONTROL.
     SELECT FILE-NAME ASSIGN TO "fshare.dat"
            FILE STATUS IS FILE-STAT.
 WORKING-STORAGE SECTION.
 01  FILE-STAT PIC XX.
     88 FILE-OPENED VALUES "00", "05", "07".
     88 FILE-LOCKED VALUE  "91".
 01  RETRY-COUNT  PIC 9(2).
 01  MAX-RETRY    PIC 9)2)  VALUE 10.
     .
     .
     .
     PROCEDURE DIVISION.
 DECLARATIVES.
 FILE-USE SECTION.
       USE AFTER STANDARD EXCEPTION PROCEDURE ON FILE-NAME.
 FILE-ERR.
 * need declaratives to trap condition, but let main code process it
         IF FILE-LOCKED
            CONTINUE
         ELSE
         .
         .
         .
         END-IF.
 END DECLARATIVES.
     .
     .
     .
     OPEN-FILES.
       OPEN I-O FILE-NAME.
       IF NOT FILE-OPENED
          PERFORM CHECK-OPEN.
       .
       .
       .
     CHECK-OPEN.
       IF FILE-LOCKED
          MOVE 1 to RETRY-COUNT
          PERFORM RETRY-OPEN UNTIL FILE-OPENED OR
                                   RETRY-COUNT > MAX-RETRY
          IF FILE-LOCKED AND RETRY-COUNT > MAX-RETRY
             DISPLAY "File busy...please try again later"
             STOP RUN
          END-IF
       END-IF.
     * handle other possible errors here
       .
       .
       .
     RETRY-OPEN.
      OPEN I-O FILE-NAME.
      add 1 to RETRY-COUNT.
On OpenVMS, Table 8.4 describes RMS-STS values used in a file-sharing environment.
Table 8.4. RMS-STS Values Used in a File-Sharing Environment (OpenVMS)

RMS-STS Value

Meaning

RMS$_DIR

Error in directory name

RMS$_DNF

Directory not found

RMS$_DNR

Device not ready or not mounted

RMS$_DUP

Duplicate key detected (DUP not set)

RMS$_ENQ

System service request failed

RMS$_EOF

End of file detected

RMS$_FLK?

File is locked

RMS$_FNF

File not found

RMS$_FUL

Device full (insufficient space)

RMS$_KEY

Invalid record number key or key value

RMS$_KRF

Invalid key of reference for $GET/$FIND

RMS$_KSZ

Invalid key size for $GET/$FIND

RMS$_OK_RLK

Record locked but read anyway

RMS$_OK_RRL

Record locked against read but read anyway

RMS$_PRV?

File protection violation

RMS$_RAC

Invalid record access mode

RMS$_REX

Record already exists

RMS$_RLK

Record currently locked by another stream

RMS$_RNF

Record not found

RMS$_RNL

Record not locked

RMS$_RSZ

Invalid record size

RMS$_SNE

File sharing not enabled

RMS$_SPE

File$_sharing page count exceeded

RMS$_SUC?

Successful operation

RMS$_WLK

Device currently write locked

You can obtain the values that apply to file-sharing exceptions (or to successful file-sharing operations) by using the VALUE IS EXTERNAL clause, as shown in Example 8.3:
Example 8.3. Program Segment for RMS-STS Values (OpenVMS)
WORKING-STORAGE SECTION.

01 RMS-SUC     PIC S9(9) COMP  VALUE IS EXTERNAL RMS$_SUC.
01 RMS-FLK     PIC S9(9) COMP  VALUE IS EXTERNAL RMS$_FLK.
   .
   .
   .
PROCEDURE DIVISION.
DECLARATIVES.
FILE-1-ERR SECTION.
    USE AFTER STANDARD EXCEPTION PROCEDURE ON FILE-1.
FILE-1-USE.
    EVALUATE RMS-STS OF FILE-1
      WHEN RMS-SUC      DISPLAY "successful operation"
      WHEN RMS-FLK      DISPLAY "file is locked - access denied".
   .
   .
   .

Specifying the OPEN EXTEND Statement in a File-Sharing Environment

If you specify an OPEN EXTEND in a file-sharing environment, be aware that the EXTEND results differ depending upon what file organization you use.

OPEN EXTEND with a Shared Sequential File

In a shared sequential file environment, when two concurrent access streams open the file in EXTEND mode, and both streams issue a write to the end of the file (EOF), the additional data will come from both streams, and the data will be inserted into the file in the order in which it was written to the file.

OPEN EXTEND with a Shared Relative File

You must use the sequential access mode when you open a relative file in extend mode. Sequential access mode for a relative file indicates that the record order is by ascending relative record number.

In sequential access mode for a relative file, the RELATIVE KEY clause of the WRITE statement is not used on record insertion; instead, the RELATIVE KEY clause acts as a receiving field. Consequently, after the completion of a write by the first access stream, the relative key field is set to the actual relative record number.

Figure 8.3 illustrates why this condition occurs.

Figure 8.3. Why a Record-Already-Exists Error Occurs
Why a Record-Already-Exists Error Occurs

As the file operations begin, both access streams point to the end of file by setting record 4 as the highest relative record number in the file. When access stream 1 writes to the file, record 5 is created as the next ascending relative record number and 5 is returned as the RELATIVE KEY number.

When access stream 2 writes to the file, it also tries to write the fifth record. Record 5 already exists (inserted by the first stream), and the second access stream gets a record-exists error. Thus, in a file-sharing environment, the second access stream always receives a record-exists error. To gain access to the current highest relative record number, stream 2 must close the file and reopen it.

OPEN EXTEND with a Shared Indexed File

You must use the sequential file access mode when you open an indexed file in extend mode. Sequential access mode requires that the first additional record insertion have a prime record key whose value is greater than the highest prime record key value in the file.

In a file-sharing environment, you should be aware of and prepared for duplicate key errors (by using INVALID KEY and USE procedures), especially on the first write to the file by the second access stream.

Subsequent writes should also allow for duplicate key errors, although subsequent writes are not constrained to use keys whose values are greater than the highest key value that existed at file open time. If you avoid duplicate key errors, you will successfully insert all access stream records.

8.4. Ensuring Successful Record Locking

Once you meet all of the file-sharing criteria and you access a file, you can consider two record-locking modes that control access to records in a file:
  • Automatic record locking—The system automatically releases an existing record lock whenever a new record is accessed and acquires a record lock whenever it reads a record in the file.

  • Manual record locking—A file connector can hold a number of record locks simultaneously. Manual record locking is available only for relative or indexed files.


Note

You must use the same method for record locking as for file sharing. For any single file connector, you cannot mix the X/Open standard (OpenVMS Alpha and I64) and the VSI standard methods.

8.4.1. X/Open Standard Record Locking (OpenVMS Alpha and I64)

This section describes the X/Open standard method of specifying automatic or manual record locking.

Specifying Automatic Record Locking (X/Open Standard) (OpenVMS Alpha and I64)

You specify X/Open standard automatic record locking in the Environment Division by using LOCK MODE IS AUTOMATIC [WITH LOCK ON RECORD] on the SELECT statement. (The optional WITH LOCK ON RECORD clause has no effect and is for documentation only.) Subsequently, a record lock is acquired by the successful execution of a READ statement. (The WITH LOCK clause is not necessary on the READ; it is implied.)

A record lock is released by one of the following events:
  • The successful execution of a subsequent I-O statement

  • Using the UNLOCK statement

  • Closing the file, implicitly or explicitly

In X/Open standard record locking, only the READ statement can acquire a lock. You can use the WITH NO LOCK phrase of the READ statement to prevent the acquiring of an automatic record lock.

For files opened in INPUT mode, READ and READ WITH LOCK statements do not acquire a record lock.

Specifying Manual Record Locking (X/Open Standard) (OpenVMS Alpha and I64)

You specify X/Open standard manual record locking in the Environment Division by using LOCK MODE IS MANUAL WITH LOCK ON MULTIPLE RECORDS on the SELECT statement. Manual record locking is available only for relative and indexed files.

For manual record locking, a record lock is acquired by specifying the WITH LOCK phrase on the READ statement. READ is the only operation that can acquire a lock. The record lock is released by one of the following events:
  • Using the UNLOCK statement (any form of the UNLOCK statement unlocks all record locks held by the current access stream; there is no singular option)

  • Closing the file, implicitly or explicitly

The WITH LOCK clause is ignored for files opened in INPUT mode. Locks are detected but not acquired.

Example 8.4 is a partial example of using both methods of X/Open standard record locking.
Example 8.4. X/Open Standard Record Locking (OpenVMS Alpha and I64)
 User 1 (Automatic Record Locking):
 ---------------------------------
 FILE-CONTROL.
          SELECT FILE-1
               ORGANIZATION IS RELATIVE
               ASSIGN TO "SHAREDAT.DAT"
               LOCK MODE AUTOMATIC.
               .
               .
               .
 PROCEDURE DIVISION.
 BEGIN.
 OPEN I-O FILE-1.
 READ FILE-1.
               .
               .
               .
 REWRITE FILE-1-REC.
 CLOSE FILE-1.
 STOP RUN.
 User 2 (Manual Record Locking):
 ------------------------------
 FILE-CONTROL
          SELECT FILE-1
               ORGANIZATION IS RELATIVE
               ASSIGN "SHAREDAT.DAT"
               LOCK MODE MANUAL LOCK ON MULTIPLE RECORDS.
               .
               .
               .

 PROCEDURE DIVISION.
 BEGIN.
 OPEN I-O FILE-1.
               .
               .
               .
 READ FILE-1 WITH LOCK.
 REWRITE FILE-1-REC.
 UNLOCK FILE-1.
 CLOSE FILE-1.
 STOP RUN.

Note that User 2 could have employed AUTOMATIC record locking just as well. In this case, manual and automatic locking work similarly.

8.4.2. VSI Standard Record Locking

Automatic Record Locking (VSI Standard)

You specify automatic record locking by using the ALLOWING phrase of the OPEN statement. The lock is applied when you access the record and released when you deaccess the record. In automatic record locking the access stream can have only one record locked at a time and can apply only one type of lock to the records of the file.

You deaccess a record by using the next READ operation, a REWRITE or a DELETE operation on the record, or by closing the file. In addition, you can release locks applied by automatic record locking by using the UNLOCK statement.

In automatic record-locking mode, you can release the current record lock by using an UNLOCK RECORD statement or an UNLOCK ALL RECORDS statement. (On UNIX systems for indexed files only, there is no current record lock.) However, because in automatic record locking you can lock only one record at a time, the UNLOCK ALL RECORDS statement unnecessarily checks all records for additional locks.

The sample program in Example 8.5 uses automatic record locking. The program opens the file with I-O ALLOWING ALL. Another access stream in another program also opens the file with INPUT ALLOWING ALL.

Note that two parallel access streams use the program in Example 8.5.

If the first access stream is updating records in random order, a record lock can occur to the second stream from the READ until the REWRITE statement of the first stream. Record locks can also occur to the first stream when the second stream reads a record and the first stream tries to read the same record.
Example 8.5. Automatic Record Locking (VSI Standard)
 SELECT FILE-1
      ORGANIZATION IS RELATIVE
      ASSIGN TO "SHAREDAT.DAT"
      .
      .
      .
 PROCEDURE DIVISION.
      OPEN I-O FILE-1 ALLOWING ALL.
      READ FILE-1  AT END DISPLAY "end".
           .
           .
           .
      REWRITE FILE-1-REC.
      CLOSE FILE-1.
      STOP RUN.

When you close a file, any existing record lock is released automatically. The UNLOCK RECORD statement releases the lock only on the current record on OpenVMS systems, which is the last record you successfully accessed. On UNIX systems for indexed files only, there is no current record lock.

Manual Record Locking (VSI Standard)

You specify manual record locking by using the APPLY LOCK-HOLDING clause (in the I-O-CONTROL paragraph), the OPEN ALLOWING statement, and the ALLOWING clauses on the VSI COBOL record operations (except DELETE). Manual record locking allows greater control of locking options by permitting users to lock multiple records in a file and by permitting different types of locking to apply to different records.

Manual record locking applies the specified lock when you access the record and releases the lock when you unlock the record.

When you specify manual record locking, you must use all of the following clauses:
  • An APPLY LOCK-HOLDING clause in the I-O CONTROL paragraph

  • An OPEN ALLOWING clause at file open time

  • An ALLOWING clause on each record operation (except DELETE)

The possible ALLOWING clauses for the record operations (that is, the READ, WRITE, REWRITE, and START statements) are as follows:
  • ALLOWING NO OTHERS?—Locks records for exclusive access. Others cannot perform READ, WRITE, DELETE, or UPDATE statements. This clause constitutes a lock for write and does not allow readers.

  • ALLOWING READERS—Locks records against WRITE, REWRITE, and DELETE access by all streams including the stream that issues the statement. Others can perform READ statements.

  • ALLOWING UPDATERS?—Does not apply any locks to the records. Others can perform READ, REWRITE, and DELETE statements. This clause constitutes a no record lock condition?.

However, if the file's OPEN mode is INPUT, using the ALLOWING clause on the record operation does not lock the record.

On UNIX systems, for indexed files only, the WRITE, REWRITE, and START statements do not acquire a record lock.

On UNIX systems for indexed files only, ALLOWING READERS is treated as ALLOWING NO OTHERS if the file is opened in I-O mode or as ALLOWING ALL if the file is opened in INPUT mode.

Table 8.5 shows the valid and invalid ALLOWING combinations for manual record locking. The columns represent the lock held, and the rows represent the lock requested.
Table 8.5. Manual Record Locking Combinations
  

Lock Held (for first stream)

I-O Attempt (for subsequent stream)

Updaters

Readers

No Others

READ

Allowing Updaters

Y

Y

N

Allowing Readers

Y

Y

N

Allowing no others

Y

N

N

REWRITE

Allowing no others

Y

N

N

DELETE

 

Y

N

N

START

Allowing Updaters

Y

Y

N

Allowing Readers

Y

Y

N

Allowing no others

Y

Y

N

WRITE

Allowing no others

N/A

N/A

N/A


 Legend: Y = Subsequent stream executes successful I-O operation
         N = Subsequent stream I-O operation is unsuccessful (File Status 92)
Example 8.6 uses manual record locking. The file is opened with the ALLOWING ALL clause. The records are read but do not become available to other access streams because of the lock applied by the READ statement (READ...ALLOWING NO OTHERS). When the UNLOCK is executed, the records can be read by another access stream if that stream opens the file allowing writers.
Example 8.6. Sample Program Using Manual Record Locking (VSI Standard)
 FILE-CONTROL.
     SELECT FILE-1
          ORGANIZATION IS RELATIVE
          ASSIGN "SHAREDAT.DAT".
           .
           .
           .
     I-O-CONTROL.
          APPLY LOCK-HOLDING ON FILE-1.
           .
           .
           .
 PROCEDURE DIVISION.
 BEGIN.
      OPEN I-O FILE-1 ALLOWING ALL.
           .
           .
           .
      READ FILE-1 ALLOWING NO OTHERS AT END DISPLAY "end".
           .
           .
           .
      REWRITE FILE-1-REC ALLOWING NO OTHERS.
           .
           .
           .
      UNLOCK FILE-1 ALL RECORDS.
      CLOSE FILE-1.
      STOP RUN.

In manual record locking, you release record locks by the UNLOCK statement or when you close the file (either explicitly or implicitly; when you close a file, any existing record lock is released automatically). The UNLOCK statement provides for either releasing the lock on the current record (on OpenVMS systems with UNLOCK RECORD) or releasing all locks currently held by the access stream on the file (UNLOCK ALL RECORDS). (On UNIX systems for indexed files only, there is no current record lock.)

When you access a shared file with ACCESS MODE IS SEQUENTIAL and use manual record locking, the UNLOCK statement can cause you to violate either of the following statements: (1) the REWRITE statement rule that states that the last input-output statement executed before the REWRITE must be a READ or START statement, or (2) the DELETE statement rule that states that the last input/output statement executed before the DELETE statement must be a READ. You must lock the record before it can be rewritten or deleted.

Releasing Locks on Deleted Records

In automatic record locking, the DELETE operation releases the lock on the record. In manual record-locking mode, you can delete a record using the DELETE statement but still retain a record lock. You must use the UNLOCK ALL RECORDS statement to release the lock on a deleted record.

If a second stream attempts to access a deleted record that retains a lock, the second stream will receive either a record not found exception or a hard lock condition. (See Section 8.4.3 for information on hard lock conditions.)

On OpenVMS, if another stream attempts to REWRITE a deleted record for which there is a retained lock, the type of exception that access stream receives depends on its file organization. If the file organization is RELATIVE, the access stream receives the record locked status. If the file organization is INDEXED, the access stream succeeds (receives the success status).

In relative files, the lock is on the relative cell (record) and cannot be rewritten until the lock is released. On indexed files, the lock is on the record's file address (RFA) of the deleted record, so a new record (with a new RFA) can be written to the file.

Bypassing a Record Lock

When you use manual record locking, you can apply a READ REGARDLESS or START REGARDLESS statement to bypass any record lock that exists. READ REGARDLESS reads the record and applies no locks to the record. START REGARDLESS positions to the record and applies no locks to the record. If the record is currently locked by another access stream, a soft record lock condition can be detected by a USE procedure. (See Section 8.4.3 for information on soft record locks.)

You use READ REGARDLESS or START REGARDLESS when: (1) a record is locked against readers because the record is about to be written, but (2) your access program needs the existing record regardless of the possible change in its data.

Note

You should recognize that READ REGARDLESS and START REGARDLESS are of limited usefulness. They can only reliably tell the user whether a record exists with a given key value. They cannot guarantee the current contents of the data in the record. You prevent the use of READ REGARDLESS or START REGARDLESS at the file protection level when you prevent readers from referencing the file.

You can use READ REGARDLESS and START REGARDLESS during sequential file access to force the File Position Indicator.

When you close a file, any existing record lock is released automatically. The UNLOCK RECORD statement releases the lock only on the current record on OpenVMS systems, which is the last record you successfully accessed. On UNIX systems for indexed files only, there is no current record lock.

8.4.3. Error Handling for Record Locking

This section describes the locking error conditions and the two kinds of locks: hard and soft.

Note

Soft record locks are available for VSI standard record locking but are not part of X/Open standard (OpenVMS Alpha and I64). Soft record lock conditions also do not occur on the UNIX system for indexed files.

Any record contention error results in an unsuccessful statement for which a USE procedure will be invoked. A record-locked condition results in an I-O status code of 92.

Interpreting Locking Error Conditions

Two record-locking conditions (hard and soft record lock) indicate whether a record was transferred to the record buffer. VSI COBOL provides the success, failure, or informational status of an I/O operation in the file status variable.

Hard Record Locks

A hard record lock condition, which causes the file status variable to be set to 92, indicates that the record operation failed and the record was not transferred to the buffer. A hard record lock results from a scenario such as the one shown in the following steps, which uses VSI standard manual record-locking mode:

  1. Program A opens the file I-O ALLOWING ALL.

  2. Program A reads a record ALLOWING NO OTHERS.

  3. Program B opens the file I-O ALLOWING ALL.

  4. Program B tries to access the same record as A.

  5. Program B receives a hard record lock condition.

  6. The record is not accessible to Program B.

  7. Program B's File Status variable is set to 92.

  8. Program B's USE procedure is invoked.

  9. Program A continues.

    The record was not available to Program B.

On UNIX, for INDEXED files, READ with the ALLOWING UPDATERS clause as well as any START statement will not detect a locked record. Potential conflicts do not trigger a hard lock condition, only actual conflicts do.

Soft Record Locks

Soft record locks can occur only with VSI standard record locking. A soft record lock condition, which causes the file status variable to be set to 90, indicates that the record operation was successful, the record was transferred to the buffer, and a prior access stream holds a lock on that record. A soft record lock can be detected by a USE procedure. This condition occurs in either of the following two situations:

  • When a record is accessed in a file that has been opened in INPUT mode, a soft record lock condition may occur if the record has been locked by a prior stream. This depends on the type of lock held by the first stream and requested by the subsequent stream.

  • In the second situation, a stream attempts to access a record that has been locked by another stream. The second stream employs a READ REGARDLESS or START REGARDLESS statement (see the section called “Bypassing a Record Lock”), which overrides the hard record lock and allows access to the record. The second stream sucessfully reads the record and receives a soft record lock. (The second stream cannot update the record.)

For example, a soft record lock results from a situation such as the following, which uses automatic record-locking mode:

  1. Program A opens the file I-O ALLOWING READERS.

  2. Program A reads a record.

  3. Program B opens the file INPUT ALLOWING ALL.

  4. Program B reads the same record.