File Concepts: SELECT and FD
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), where the file-control entry, the file description and the OPEN and CLOSE statements come from COBOL 85 and earlier, record locking was added by COBOL 2002 and LINE SEQUENTIAL organization and DELETE FILE by COBOL 2023; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every complete program on it was compiled with cobc -x -Wall and run on 2026-10-20 (programs that differ under -std=ibm were run both ways). The z/OS JCL on the page is not run here; it is checked against the Language Reference and the Programming Guide. The examples use the Circulation public-library scenario: the book master BOOKMAST, the member master MEMBMAST and the loan transactions LOANTRAN, whose record layouts are the copybooks BOOKREC, MEMBREC and LOANREC. Their sample data are line-sequential text files with one record per line.
A COBOL program never mentions a disk, a directory or a data set name in its statements. It names a file with a file-name that two entries describe: a SELECT entry in the ENVIRONMENT DIVISION that says how the file is organized, accessed and found, and an FD entry in the DATA DIVISION that says what its records look like. The OPEN statement then connects the two descriptions to a real file. This page covers those four things in that order, because each one fixes a part of the contract that the statements on the Sequential Files, Indexed Files and VSAM and Relative Files pages rely on. The status codes that every file statement leaves behind are on File Status Codes, and what to do with them is on Error Handling and Declaratives.
Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 5) introduces SELECT and FD for a customer file, and the idea is still current. Three details are wrong, and the correct rules are these. A file is opened with OPEN INPUT, not "OPEN READ". The FILE STATUS item is a separate two-character item in WORKING-STORAGE, not a field declared inside the record group (which would make it part of the data of the file). And a PC-style name such as ASSIGN TO "Customers.dat" with LINE SEQUENTIAL is how a GnuCOBOL program names a file on a PC; it is not how an Enterprise COBOL batch program names a data set. On z/OS the assignment name is a name of at most eight characters that stands for a JCL DD statement, and Enterprise COBOL supports line-sequential files only for z/OS UNIX files. The sections below show both worlds side by side.
The file model
Four words are used loosely in everyday talk and precisely by the language.
| Term | Meaning | In the Circulation scenario |
|---|---|---|
Logical record |
The unit that one |
One book, one member or one loan transaction; |
Physical record or block |
The unit that the operating system transfers to and from the device; it holds one or more logical records and, for variable-length records, control bytes |
On z/OS, a block of many 60-byte records on disk; with GnuCOBOL’s line-sequential files there is no block, only lines |
File |
A named collection of logical records, with an organization that was fixed when the file was created |
|
Data set |
The z/OS word for a file that the system catalogs and allocates; a QSAM sequential data set, a PDS member or a VSAM cluster |
|
The organization is the permanent logical structure of the file and cannot be changed after the file has been created. The access mode is how one program reads or writes it. Four organizations exist in the language.
ORGANIZATION |
Structure | Access modes allowed | Notes |
|---|---|---|---|
|
Records follow one another in the order they were written; each one except the first has a predecessor |
Sequential only |
Fixed or variable length; the default when the clause is omitted. On z/OS it is a QSAM data set, or a VSAM ESDS when the assignment name starts with |
|
Text lines; each record ends with a delimiter that is not part of the record |
Sequential only |
The text-file organization. Standardised in COBOL 2023, a GnuCOBOL staple, and in Enterprise COBOL limited to z/OS UNIX files |
|
Records are found through an index built on a unique prime |
Sequential, random, dynamic |
On z/OS a VSAM KSDS (or |
|
Record areas numbered 1, 2, 3 and so on; the position is the relative record number |
Sequential, random, dynamic |
On z/OS a VSAM RRDS |
Records of a sequential file can be fixed or variable in length and carry no keys. The statements that are specific to each organization are on the three file pages named above; this page covers what they all share.
Program, FD, DD name and data set
The figure shows the chain from a statement to the bytes on disk. The program refers to the file-name BOOK-FILE. The FD of that file-name gives its record layout, and the SELECT entry gives its assignment name, here BOOKMAST. What the assignment name means is decided by the run-time system: on z/OS it is the name of a DD statement in the JCL of the job step, and in GnuCOBOL it is a key that is looked up in the environment and then taken as a path.
The figure follows the Language Reference rules for the assignment name and the GnuCOBOL manual and Programmer’s Guide rules for file-name mapping; the sections below test each arrow.
The SELECT entry
SELECT lives in the FILE-CONTROL paragraph of the INPUT-OUTPUT SECTION. Each entry starts with the file-name and then lists clauses ending with one period. The clause forms for Enterprise COBOL depend on the organization (the Language Reference has one format per organization), and GnuCOBOL accepts the union of them.
SELECT [OPTIONAL] file-name
ASSIGN TO assignment-name
[ORGANIZATION IS SEQUENTIAL | LINE SEQUENTIAL | INDEXED | RELATIVE]
[ACCESS MODE IS SEQUENTIAL | RANDOM | DYNAMIC]
[RECORD KEY IS key] [ALTERNATE RECORD KEY IS key [WITH DUPLICATES]] (indexed)
[RELATIVE KEY IS data-name] (relative)
[FILE STATUS IS status-item].
The clauses can follow the file-name in any order. A few rules from the Language Reference matter from the first program on.
-
file-namemust be described by anFD(orSD, for sort and merge files) in theDATA DIVISION, must contain at least one letter and must be unique in the program. -
ORGANIZATIONdefaults toSEQUENTIALandACCESS MODEtoSEQUENTIAL. Always write both, because the line-sequential, indexed and relative organizations are not what a reader expects from a missing clause. -
OPTIONALis allowed only for files openedINPUT,I-OorEXTEND, and marks an input file that is not always there. Without it, a missing input file is an error; with it, the open succeeds with status05and the firstREADreachesAT END(shown in theOPENsection below). -
FILE STATUS ISnames a two-characterWORKING-STORAGEitem that receives a code after every operation on the file. It is not optional in practice: without it the program has no way to find out why anOPENfailed. -
RECORD KEY,ALTERNATE RECORD KEYandRELATIVE KEYare covered on the indexed and relative pages. The relative key must not be a field of the record.
Source: Enterprise COBOL for z/OS 6.5 Language Reference: SELECT clause; FILE-CONTROL paragraph; FILE STATUS clause; GnuCOBOL Programmer’s Guide: SELECT statement syntax
A first complete program
The program lists the book master. It has one SELECT with a literal assignment name, one FD that pulls in the BOOKREC copybook, and the usual read loop, with FILE STATUS examined after OPEN and CLOSE.
IDENTIFICATION DIVISION.
PROGRAM-ID. LISTBOOK.
* List the book master: one SELECT, one FD, one read loop.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BOOK-FILE ASSIGN TO "BOOKMAST"
ORGANIZATION IS LINE SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-BOOK-STATUS.
DATA DIVISION.
FILE SECTION.
FD BOOK-FILE.
COPY BOOKREC.
WORKING-STORAGE SECTION.
01 WS-BOOK-STATUS PIC XX.
01 WS-EOF PIC X VALUE "N".
88 END-OF-BOOKS VALUE "Y".
PROCEDURE DIVISION.
0000-MAIN-LINE.
OPEN INPUT BOOK-FILE
DISPLAY "OPEN status " WS-BOOK-STATUS
PERFORM UNTIL END-OF-BOOKS
READ BOOK-FILE
AT END SET END-OF-BOOKS TO TRUE
NOT AT END
DISPLAY BOOK-ID " " BOOK-TITLE " " BOOK-STATUS
END-READ
END-PERFORM
CLOSE BOOK-FILE
DISPLAY "CLOSE status " WS-BOOK-STATUS
STOP RUN.
Compiled with cobc -x -Wall and run in the directory that holds BOOKMAST:
OPEN status 00
000001 DATA STRUCTURES A
000002 COBOL BASICS A
000003 BATCH PROCESSING A
000004 OLD LEDGERS W
CLOSE status 00
Source: Enterprise COBOL for z/OS 6.5 Language Reference: FILE-CONTROL paragraph; GnuCOBOL 3.2 manual
On z/OS the same SELECT is written with an unquoted ddname, and the job supplies the file. Compare the two ways of writing the assignment name before moving on.
SELECT BOOK-FILE ASSIGN TO BOOKMAST
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-BOOK-STATUS.
//LISTBOOK EXEC PGM=LISTBOOK
//STEPLIB DD DISP=SHR,DSN=USERID.CIRC.LOADLIB
//BOOKMAST DD DISP=SHR,DSN=USERID.CIRC.BOOKMAST
//SYSOUT DD SYSOUT=*
The JCL above is z/OS; not run here. The BOOKMAST DD statement is what the assignment name BOOKMAST refers to; the data set name never appears in the program. On z/OS the book master is a fixed-length QSAM data set, so the example uses ORGANIZATION IS SEQUENTIAL; the line-sequential organization would need a z/OS UNIX file. The Language Reference allows exactly two meanings for the name: the name on the DD statement for the file, or the name of an environment variable that holds file allocation information.
ASSIGN and the DD name
The ASSIGN clause connects the file-name to an external name. The two systems read the clause differently, and a program that is meant to run on both must be written with that in mind.
Enterprise COBOL on z/OS
The assignment name can be a user-defined word or an alphanumeric literal, and has an optional organization prefix.
| Form | File type | Meaning |
|---|---|---|
|
QSAM |
|
|
VSAM sequential (ESDS) |
The |
|
VSAM indexed (KSDS), VSAM relative (RRDS), line sequential |
The organization field must be omitted |
|
|
The prefix is required |
|
any |
A |
The name is one to eight characters, starts with a letter and uses letters and digits (a literal may also use @, # and $). It cannot contain an underscore, and the compiler folds it to uppercase to form the ddname. This is the first thing a GnuCOBOL programmer must unlearn: BOOK_MASTER is a fine GnuCOBOL name and an illegal Enterprise COBOL assignment name. When there are several assignment names, only the first one has any effect.
The name is looked up in this order. It is first treated as a ddname. If the job step has no DD statement by that name, Enterprise COBOL treats it as the name of an environment variable, which is read at every OPEN. The variable must be named in uppercase and must hold a DSN(...) option for a QSAM or VSAM data set, or a PATH(...) option with an absolute path for a z/OS UNIX file. A line-sequential file accepts only PATH. If the variable is missing, malformed, or the dynamic allocation fails, the OPEN leaves file status 98.
export BOOKMAST="DSN(USERID.CIRC.BOOKMAST),SHR"
export NOTEFILE="PATH(/u/circ/notes.txt)"
The two lines are z/OS UNIX shell syntax in the style of the Language Reference; z/OS; not run here. The Language Reference recommends always coding a disposition (NEW, OLD, SHR or MOD) with DSN rather than relying on a system default.
GnuCOBOL
GnuCOBOL accepts three kinds of assignment name, and a compiler dialect decides how an unquoted word is read.
| Form | Example | File that is opened |
|---|---|---|
Literal |
|
The literal is the file location string, after the environment lookup below |
Data-name |
|
The value of the item at the moment of |
|
|
The value of the environment variable |
The next program opens the same file the three ways and shows the first record’s title each time. The data file is in a data directory next to the program.
IDENTIFICATION DIVISION.
PROGRAM-ID. ASSIGNFORMS.
* Three ways to name the same data file in GnuCOBOL.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LIT-FILE ASSIGN TO "data/BOOKMAST"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
SELECT VAR-FILE ASSIGN USING WS-PATH
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
SELECT EXT-FILE ASSIGN TO EXTERNAL BOOKEXT
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD LIT-FILE.
01 LIT-LINE PIC X(60).
FD VAR-FILE.
01 VAR-LINE PIC X(60).
FD EXT-FILE.
01 EXT-LINE PIC X(60).
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX.
01 WS-PATH PIC X(40).
PROCEDURE DIVISION.
0000-MAIN-LINE.
OPEN INPUT LIT-FILE
READ LIT-FILE
DISPLAY "literal : " LIT-LINE(20:30)
CLOSE LIT-FILE
MOVE "data/BOOKMAST" TO WS-PATH
OPEN INPUT VAR-FILE
READ VAR-FILE
DISPLAY "variable : " VAR-LINE(20:30)
CLOSE VAR-FILE
DISPLAY "BOOKEXT" UPON ENVIRONMENT-NAME
DISPLAY "data/BOOKMAST" UPON ENVIRONMENT-VALUE
OPEN INPUT EXT-FILE
READ EXT-FILE
DISPLAY "EXTERNAL : " EXT-LINE(20:30)
CLOSE EXT-FILE
STOP RUN.
literal : DATA STRUCTURES
variable : DATA STRUCTURES
EXTERNAL : DATA STRUCTURES
The output was identical with -std=ibm. DISPLAY ... UPON ENVIRONMENT-NAME and UPON ENVIRONMENT-VALUE set the environment variable from inside the program; in a shell script one would run export BOOKEXT=data/BOOKMAST before the program instead.
How GnuCOBOL turns a name into a path
When the compiler configuration has filename-mapping: yes (the default, and the setting in the default, -std=ibm and -std=mf dialects tried here), the run-time system does not use the assignment name as a path straight away. It looks for the first existing environment variable in this order, using the assignment name as the base:
-
DD_followed by the base, for exampleDD_BOOKS -
dd_followed by the base, for exampledd_BOOKS -
the base itself, for example
BOOKS
The value of the variable that is found is the path and file name. If none exists, or if filename mapping is off (-fno-filename-mapping), the assignment name itself is the path. A relative path is then prefixed with the directory in the run-time setting COB_FILE_PATH (the current directory when it is not set), and an absolute path is used as it is. These are the same two ideas as the ddname and the DD statement on z/OS, with the shell standing in for the JCL: export DD_BOOKMAST=/data/circ/bookmast.dat is the GnuCOBOL spelling of a BOOKMAST DD statement.
The next program shows the order. BOOKS is a file that exists in the current directory; plain.dat, lower.dat and upper.dat are three other one-line files made for the test. The program adds one variable at a time and opens the file again, then points DD_BOOKS at a file that does not exist.
IDENTIFICATION DIVISION.
PROGRAM-ID. MAPDEMO.
* Which file does ASSIGN TO "BOOKS" open? Set variables, look.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BOOK-FILE ASSIGN TO "BOOKS"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD BOOK-FILE.
01 BOOK-LINE PIC X(40).
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX.
01 WS-NAME PIC X(8).
01 WS-VALUE PIC X(20).
PROCEDURE DIVISION.
0000-MAIN-LINE.
DISPLAY "nothing set : " WITH NO ADVANCING
PERFORM 1000-SHOW-FILE
MOVE "BOOKS" TO WS-NAME
MOVE "plain.dat" TO WS-VALUE
DISPLAY "BOOKS set : " WITH NO ADVANCING
PERFORM 2000-SET-AND-SHOW
MOVE "dd_BOOKS" TO WS-NAME
MOVE "lower.dat" TO WS-VALUE
DISPLAY "dd_BOOKS added : " WITH NO ADVANCING
PERFORM 2000-SET-AND-SHOW
MOVE "DD_BOOKS" TO WS-NAME
MOVE "upper.dat" TO WS-VALUE
DISPLAY "DD_BOOKS added : " WITH NO ADVANCING
PERFORM 2000-SET-AND-SHOW
MOVE "DD_BOOKS" TO WS-NAME
MOVE "missing.dat" TO WS-VALUE
DISPLAY "DD_BOOKS missing: " WITH NO ADVANCING
PERFORM 2000-SET-AND-SHOW
STOP RUN.
1000-SHOW-FILE.
OPEN INPUT BOOK-FILE
IF WS-STATUS = "00"
READ BOOK-FILE
DISPLAY BOOK-LINE
CLOSE BOOK-FILE
ELSE
DISPLAY "OPEN failed, status " WS-STATUS
END-IF.
2000-SET-AND-SHOW.
DISPLAY WS-NAME UPON ENVIRONMENT-NAME
DISPLAY WS-VALUE UPON ENVIRONMENT-VALUE
PERFORM 1000-SHOW-FILE.
nothing set : FROM LITERAL FILE NAME BOOKS
BOOKS set : FROM VARIABLE BOOKS
dd_BOOKS added : FROM VARIABLE dd_BOOKS
DD_BOOKS added : FROM VARIABLE DD_BOOKS
DD_BOOKS missing: OPEN failed, status 35
Each variable overrides the previous one, in the documented order, and the environment is read again at every OPEN. A variable that is set always wins: when DD_BOOKS points at missing.dat, GnuCOBOL does not fall back to the file called BOOKS, it fails with status 35. That is the behaviour of a DD statement that names a data set that does not exist, and it is usually what a batch job wants.
Source: GnuCOBOL Programmer’s Guide: SELECT statement, file location strings; GnuCOBOL 3.2 manual: compiler configuration (filename-mapping, assign-clause)
A relative path is also affected by COB_FILE_PATH. With BOOKMAST moved into a directory data, a program that has ASSIGN TO BOOKMAST found nothing in the current directory and, run as COB_FILE_PATH=data ./prog, opened data/BOOKMAST. When DD_BOOKMAST held an absolute path, COB_FILE_PATH was ignored (it was set to a directory that does not exist, and the file was still opened); when DD_BOOKMAST held the relative name BOOKMAST, COB_FILE_PATH=data was put in front of it.
export DD_BOOKMAST=/data/circ/bookmast.dat # absolute: COB_FILE_PATH is not used
export DD_BOOKMAST=bookmast.dat # relative: COB_FILE_PATH/bookmast.dat
export COB_FILE_PATH=/data/circ
The three lines are alternatives, not a script; the first two are two ways to write DD_BOOKMAST, and the last one sets the directory for relative names.
A portability pitfall: the label prefix
Enterprise COBOL lets the assignment name start with a documentation label such as UT-S- or WS-, and ignores it. GnuCOBOL applies a similar rule only under the IBM dialect, and that rule can swallow part of a data-name. The program below is deliberately wrong for -std=ibm: it writes the variable form as ASSIGN TO WS-PATH instead of ASSIGN USING WS-PATH.
The only change to the assignforms.cob program above is the second SELECT:
SELECT VAR-FILE ASSIGN TO WS-PATH
Compiled with cobc -x -Wall -std=ibm, the program reports the problem as a warning and then reads nothing from that file:
assignforms.cob:11: warning: ASSIGN WS-PATH interpreted as 'PATH' [-Wadditional]
literal : DATA STRUCTURES
variable :
EXTERNAL : DATA STRUCTURES
Under the IBM rules WS- is taken as a label and the assignment name becomes PATH, so the item WS-PATH is never used; the default dialect reads the word as a data-name. The fix is to use ASSIGN USING for the variable form (the assignforms.cob program above uses it and behaves the same in both dialects) and ASSIGN TO name only for the ddname form. A prefix is honoured only for an unquoted name under -std=ibm. With the unquoted ASSIGN TO S-BOOKMAST, cobc -std=ibm printed warning: ASSIGN S-BOOKMAST interpreted as 'BOOKMAST' [-Wadditional] and opened the file, the default dialect ended with status 35, and a quoted "S-BOOKMAST" ended with status 35 in both dialects even with BOOKMAST set in the environment. On GnuCOBOL write the plain name.
ORGANIZATION and ACCESS MODE together
The access mode says how the statements in the procedure division reach the records. It is limited by the organization.
| Organization | Access modes | How a record is selected |
|---|---|---|
|
|
By position: the order in which the records were written |
|
|
Sequential: ascending record key. Random: the key placed in the |
|
|
Sequential: ascending relative record number among the records that exist. Random: the number placed in the |
DYNAMIC is a convenience, not a faster mode. The Language Reference says it is tuned for positioning to a record and then reading a few records forward, and recommends RANDOM when records are only ever accessed by key. The statements that each open mode permits are in the OPEN section below.
The three SELECT entries below show the organizations next to each other for the Circulation book data: the line-sequential master that is read in order, an indexed copy keyed on the book id and read dynamically, and a relative copy whose slot number is the book id and that is read randomly. They are shown as entries only; the statements that use them are on the indexed and relative pages.
SELECT BOOK-IN ASSIGN TO "BOOKMAST"
ORGANIZATION IS LINE SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-STATUS.
SELECT BOOK-IDX ASSIGN TO "BOOKIDX"
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS IDX-ID
FILE STATUS IS WS-STATUS.
SELECT BOOK-REL ASSIGN TO "BOOKREL"
ORGANIZATION IS RELATIVE
ACCESS MODE IS RANDOM
RELATIVE KEY IS WS-REL-KEY
FILE STATUS IS WS-STATUS.
The indexed file needs a back end in GnuCOBOL: cobc --info reports the indexed file handler of the build used here as BDB (Berkeley DB), and a build without one cannot open ORGANIZATION IS INDEXED files. The Language Reference states the equivalent z/OS facts: indexed and relative files must be on direct-access storage, and the access mode must be the same in every file-control entry that refers to the same external file.
Source: Enterprise COBOL for z/OS 6.5 Language Reference: ACCESS MODE clause; Relationship between data organizations and access modes; GnuCOBOL Programmer’s Guide: ORGANIZATION clauses
The organization also decides what an assignment name has to be on z/OS and which data-set type the file is. The Programming Guide pages that start the file chapters are the place to read how each is defined: QSAM and line-sequential files, VSAM KSDS and VSAM RRDS.
The FD entry
The file description entry, FD, is in the FILE SECTION of the DATA DIVISION. It names the file-name from the SELECT, may carry clauses about the physical file, and is followed by one or more level-01 record descriptions. The record descriptions use the same PICTURE and USAGE rules as the rest of the data division (see Data Division and Level Numbers), and the copybook BOOKREC is typically COPY-ed under the FD (see Copybooks and REPLACE).
| Clause | What it does | Notes |
|---|---|---|
|
Format 1: the number of bytes of every record, regardless of |
|
|
Format 2: smallest and largest record |
Never required: the compiler derives the limits from the 01 records |
|
Format 3: variable-length records, with the length in a separate unsigned integer item |
The item must be set before every |
|
The size of the physical record |
|
|
The format of the physical records of a QSAM file |
Ignored for VSAM; when omitted, the compiler works the mode out from the record lengths |
|
Document the presence of labels and the names of the records |
Obsolete: syntax checked and without effect |
|
Page layout of a print file |
|
|
Share the file connector between the programs of a run unit, or with nested programs |
All descriptions of an external file must agree |
The Language Reference gives the four recording modes these meanings. Mode F (fixed): every record has the same length. Mode V (variable): records may differ, and each record carries a four-byte record-length field and each block a four-byte block descriptor, which are not described in the data division and not visible to the program. Mode U (undefined): one record per block, with no length fields. Mode S (spanned): a record may be larger than a block and is split into segments. Without a RECORDING MODE clause the compiler chooses F when every record has the same length (a RECORD CONTAINS n clause, or equal 01 records without OCCURS DEPENDING ON) and V when the lengths differ, when RECORD IS VARYING is used, or when RECORD CONTAINS n TO m gives different limits.
LABEL RECORDS ARE STANDARD and DATA RECORD IS name appear in a great deal of older COBOL. They are old code you may meet: Enterprise COBOL still accepts them, ignores them, and documents them as describing the labels, so remove them when they are not needed. There is no modern replacement because there is no modern need; the labels are decided by the DD statement.
Several 01 records under one FD
An FD may be followed by more than one level-01 record description. All of them describe the same storage: the record area of the file. The program does not choose among them; it uses whichever fits the record just read. This is an implicit redefinition, and it is how files with several record types (header, detail, trailer) are processed. If the records have different lengths the file is a variable-length file with the shortest and longest record derived from the descriptions, and the record area is as big as the longest.
The next program gives the loan transaction file two views of the same record: the structured LOAN-RECORD from the copybook and an alphanumeric LOAN-RAW that sees the whole 30 bytes.
IDENTIFICATION DIVISION.
PROGRAM-ID. FDRECORDS.
* Two 01 records under one FD share the same storage.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LOAN-FILE ASSIGN TO "LOANTRAN"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD LOAN-FILE.
COPY LOANREC.
01 LOAN-RAW PIC X(30).
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX.
01 WS-EOF PIC X VALUE "N".
88 END-OF-LOANS VALUE "Y".
PROCEDURE DIVISION.
0000-MAIN-LINE.
OPEN INPUT LOAN-FILE
PERFORM UNTIL END-OF-LOANS
READ LOAN-FILE
AT END SET END-OF-LOANS TO TRUE
NOT AT END
DISPLAY "raw: [" LOAN-RAW "] type: " LOAN-TYPE
" member: " LOAN-MEMBER-ID
END-READ
END-PERFORM
CLOSE LOAN-FILE
STOP RUN.
raw: [O00010000000120261020 ] type: O member: 000100
raw: [O00010100000220261020 ] type: O member: 000101
raw: [R00010000000120261020 ] type: R member: 000100
raw: [O00010200000320261020 ] type: O member: 000102
The same record area is reached through both names, so a MOVE to LOAN-RAW changes LOAN-TYPE and vice versa. Two things follow. First, the record area belongs to the file, so after a READ it is valid only while the file is open and the record has not been replaced by the next READ; copy what the program needs with READ ... INTO or a MOVE to WORKING-STORAGE. Second, nothing in the 01 records tells the program which record type it holds; the program tests a field (here LOAN-TYPE) and then uses the matching description.
Variable-length records
A file whose records differ in length is declared with the RECORD IS VARYING format, optionally with a DEPENDING ON item. The item is an unsigned integer that carries the length between the program and the file: the program sets it before every output statement, and the run time sets it after every successful READ. The program below writes three notes of different lengths to a record-sequential file, then reads them back, using the item to display only the bytes that belong to each record.
IDENTIFICATION DIVISION.
PROGRAM-ID. VARREC.
* Variable-length records: RECORD VARYING ... DEPENDING ON.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT NOTE-FILE ASSIGN TO "NOTES.BIN"
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD NOTE-FILE
RECORD IS VARYING IN SIZE FROM 10 TO 40 CHARACTERS
DEPENDING ON WS-LENGTH.
01 NOTE-RECORD PIC X(40).
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX.
01 WS-LENGTH PIC 9(4) BINARY.
01 WS-EOF PIC X VALUE "N".
88 END-OF-NOTES VALUE "Y".
PROCEDURE DIVISION.
0000-MAIN-LINE.
OPEN OUTPUT NOTE-FILE
MOVE "Late return" TO NOTE-RECORD
MOVE 11 TO WS-LENGTH
WRITE NOTE-RECORD
MOVE "Member moved, new address pending" TO NOTE-RECORD
MOVE 33 TO WS-LENGTH
WRITE NOTE-RECORD
MOVE "Damaged copy" TO NOTE-RECORD
MOVE 12 TO WS-LENGTH
WRITE NOTE-RECORD
CLOSE NOTE-FILE
OPEN INPUT NOTE-FILE
PERFORM UNTIL END-OF-NOTES
READ NOTE-FILE
AT END SET END-OF-NOTES TO TRUE
NOT AT END
DISPLAY WS-LENGTH " [" NOTE-RECORD(1:WS-LENGTH)
"]"
END-READ
END-PERFORM
CLOSE NOTE-FILE
STOP RUN.
0011 [Late return]
0033 [Member moved, new address pending]
0012 [Damaged copy]
With -std=ibm the lengths were printed with five digits (00011), because the dialect makes PIC 9(4) BINARY a halfword and changes how DISPLAY formats it; the records are the same. The first 48 bytes of the file, dumped with od -A d -c, show how GnuCOBOL stores variable records: each record is preceded by a prefix of two length bytes (big-endian) and two zero bytes, and the length does not include the prefix.
0000000 \0 \v \0 \0 L a t e r e t u r n \0
0000016 ! \0 \0 M e m b e r m o v e d ,
0000032 n e w a d d r e s s p e n
The prefix layout is the COB_VARSEQ_FORMAT setting of the run time (value 0 is the default; other values give four-byte lengths), and the manual states that the length it records does not include the prefix. A z/OS format-V file has its own four-byte record-length field in front of each record and a four-byte block descriptor in front of each block, which the Language Reference says are not described in the data division and not available to the program. The two layouts are therefore not interchangeable, which matters when files are moved between the platforms (see Data Sets and Mainframe Utilities).
BLOCK CONTAINS and RECORDING MODE in practice
On z/OS the physical layout of a QSAM file is a property of the data set, and the program should not hard-code it. The usual FD for a fixed-length batch file therefore has RECORDING MODE IS F, BLOCK CONTAINS 0 RECORDS and the record length, and the DD statement supplies RECFM, LRECL and the block size. A typical z/OS FD for the book master therefore looks like this (z/OS; not run here). GnuCOBOL, which has no blocks, is not expected to use BLOCK CONTAINS or RECORDING MODE.
FD BOOK-FILE
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 60 CHARACTERS.
COPY BOOKREC.
For a line-sequential file the RECORD CONTAINS clause has no meaning: cobc -x -Wall compiled the first program of this page with RECORD CONTAINS 60 CHARACTERS on the FD and printed warning: RECORD clause ignored for LINE SEQUENTIAL [-Wadditional], which is why the FD of that program has no clause.
On z/OS, BLOCK CONTAINS 0 lets the system choose an optimal block size when the file is created; without it the compiler assumes the file is not blocked, and even BLOCK CONTAINS 1 RECORD produces fixed blocked records. Setting BLOCK0 as a compiler option applies BLOCK CONTAINS 0 to all QSAM files that have no clause of their own.
OPEN and CLOSE
A file cannot be used until OPEN has succeeded, and every OPEN must be matched by a CLOSE before the file is opened again. The open mode says what the program will do.
| Mode | Allows | Notes |
|---|---|---|
|
|
The file must exist, unless it is |
|
|
Creates the file, or replaces a file that already holds records. On z/OS an existing file with records must be reusable; do not use it on a file whose contents you need |
|
|
Only for direct-access files. Not valid for line-sequential files in Enterprise COBOL; GnuCOBOL 3.2 accepts it |
|
|
For indexed and relative files, "last" means the highest key. Use |
Several files can be opened in one statement, OPEN INPUT MEMBER-FILE BOOK-FILE OUTPUT REJECT-FILE, and the modes may be given in any order. After a successful OPEN the record area is available; before it, moving data to the record is a mistake. OPEN INPUT and OPEN I-O position a file at its start (for an indexed file, at the lowest key), and EXTEND positions it after the last record.
What happens when the file is not there is spelled out by the Language Reference in its availability table.
| Opened as | File available | File not available |
|---|---|---|
|
Normal open |
Open fails, status |
|
Normal open |
Normal open, the first |
|
Normal open |
Open fails, status |
|
Normal open |
The file is created; status |
|
Normal open, file is emptied |
The file is created |
|
Normal open |
Open fails, status |
|
Normal open |
The file is created; status |
The following program tries every case on a small file of rejected transactions, REJECTS, and prints the status after each one. The file is optional for NOTES, which does not exist. The statements that cannot work, such as writing to an input file, are deliberate.
IDENTIFICATION DIVISION.
PROGRAM-ID. OPENMODES.
* OPEN modes, optional files and the statuses they leave behind.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT REJECT-FILE ASSIGN TO "REJECTS"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
SELECT OPTIONAL NOTE-FILE ASSIGN TO "NOTES"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD REJECT-FILE.
01 REJECT-LINE PIC X(30).
FD NOTE-FILE.
01 NOTE-LINE PIC X(30).
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX.
01 WS-COUNT PIC 9(3) VALUE ZERO.
01 WS-EOF PIC X VALUE "N".
88 END-OF-FILE VALUE "Y".
PROCEDURE DIVISION.
0000-MAIN-LINE.
OPEN INPUT REJECT-FILE
DISPLAY "INPUT, file missing : " WS-STATUS
OPEN INPUT NOTE-FILE
DISPLAY "INPUT, OPTIONAL missing : " WS-STATUS
READ NOTE-FILE
AT END DISPLAY "READ of optional file : at end"
END-READ
CLOSE NOTE-FILE
OPEN OUTPUT REJECT-FILE
DISPLAY "OUTPUT creates the file : " WS-STATUS
MOVE "O 102 3 suspended member" TO REJECT-LINE
WRITE REJECT-LINE
CLOSE REJECT-FILE
OPEN EXTEND REJECT-FILE
DISPLAY "EXTEND : " WS-STATUS
MOVE "O 101 9 unknown book" TO REJECT-LINE
WRITE REJECT-LINE
OPEN INPUT REJECT-FILE
DISPLAY "OPEN of an open file : " WS-STATUS
CLOSE REJECT-FILE
CLOSE REJECT-FILE
DISPLAY "CLOSE of a closed file : " WS-STATUS
OPEN INPUT REJECT-FILE
WRITE REJECT-LINE
DISPLAY "WRITE to an INPUT file : " WS-STATUS
PERFORM UNTIL END-OF-FILE
READ REJECT-FILE
AT END SET END-OF-FILE TO TRUE
NOT AT END ADD 1 TO WS-COUNT
END-READ
END-PERFORM
DISPLAY "Rejects on file : " WS-COUNT
CLOSE REJECT-FILE
OPEN OUTPUT REJECT-FILE
READ REJECT-FILE
DISPLAY "READ from an OUTPUT file : " WS-STATUS
CLOSE REJECT-FILE
DELETE FILE REJECT-FILE
DISPLAY "DELETE FILE : " WS-STATUS
OPEN INPUT REJECT-FILE
DISPLAY "INPUT after DELETE FILE : " WS-STATUS
STOP RUN.
INPUT, file missing : 35
INPUT, OPTIONAL missing : 05
READ of optional file : at end
OUTPUT creates the file : 00
EXTEND : 00
OPEN of an open file : 41
CLOSE of a closed file : 42
WRITE to an INPUT file : 48
Rejects on file : 002
READ from an OUTPUT file : 47
DELETE FILE : 00
INPUT after DELETE FILE : 35
The output is the same under -std=ibm. The statuses are the ones in the Language Reference: 35 for an input file that is not there, 05 for an optional file, 41 for opening a file that is already open, 42 for closing one that is not open, 47 for reading a file opened OUTPUT, and 48 for writing a file opened INPUT. The whole list is on File Status Codes.
Two statements in the program need a remark. DELETE FILE is new in COBOL 2023: it removes the external file that the file-name stands for, and GnuCOBOL 3.2 accepts it. The Enterprise COBOL 6.5 Language Reference documents only the record DELETE of indexed and relative files, so a z/OS job removes a data set in JCL or with a utility, not from COBOL. And the OPEN of an open file is an error the program can catch: the Language Reference says the EXCEPTION/ERROR procedure for the file runs.
What happens without FILE STATUS
If a file has neither a FILE STATUS item nor a USE AFTER ERROR declarative, an OPEN failure ends the program. This is a deliberately fragile program: the file NOSUCHBOOKS does not exist and nothing watches the status.
IDENTIFICATION DIVISION.
PROGRAM-ID. NOSTATUS.
* Deliberately fragile: no FILE STATUS, file does not exist.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BOOK-FILE ASSIGN TO "NOSUCHBOOKS"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD BOOK-FILE.
01 BOOK-LINE PIC X(60).
PROCEDURE DIVISION.
OPEN INPUT BOOK-FILE
DISPLAY "not reached"
STOP RUN.
libcob: error: file does not exist (status = 35) for file BOOK-FILE ('NOSUCHBOOKS')
GnuCOBOL printed the run-time message and exit status 1; the line not reached was never displayed. On z/OS the same program fails with a Language Environment condition and an abend or a non-zero return code, depending on the options. The remedy is the one the examples above use: always code FILE STATUS, and test it after OPEN and CLOSE as well as after READ and WRITE.
CLOSE
CLOSE file-name ends the program’s use of the file. Afterwards the record area is no longer available and an OPEN is needed before any other file statement. A successful CLOSE writes any buffered records out (and, for a GnuCOBOL line-sequential file, the final line delimiter). Several files may be named in one statement. The extra phrases belong to z/OS tape and QSAM practice: REEL or UNIT and FOR REMOVAL apply to multi-volume or tape files, WITH NO REWIND to tape, and WITH LOCK prevents the file from being opened again in the same run unit. None of REEL, UNIT or NO REWIND is valid for a VSAM file, and a new program on a disk should use plain CLOSE.
The program must close what it opened, in particular before it ends. Do not rely on the run time to close files at the end of the run: an implicit close hides errors, and an output file that was not closed properly may be missing its last records. STOP RUN after an explicit CLOSE leaves no doubt.
Record locking and DELETE FILE
COBOL 2002 added record locking, and GnuCOBOL implements it: a LOCK MODE IS MANUAL or AUTOMATIC clause in the SELECT, WITH LOCK and WITH KEPT LOCK on READ, and the UNLOCK statement, with SHARING WITH to say who else may open the file. The manual says that record locking for indexed files needs a Berkeley DB build, and that a MinGW build gives up part of file sharing because the operating-system routines it needs do not exist there. The Enterprise COBOL 6.5 Language Reference has no LOCK MODE clause: sharing on z/OS is controlled by the DD statement (DISP=SHR or OLD) and, for VSAM, by the SHAREOPTIONS of the cluster, which are on Indexed Files and VSAM. A program that uses record locks is therefore not portable to z/OS.
Line-sequential files and the dialect line
LINE SEQUENTIAL is the organization of text files, and it is where the two systems differ most. In GnuCOBOL it is the everyday organization: records are lines that end with a line feed, trailing spaces are removed when a record is written (unless the run-time setting COB_LS_FIXED is on), a short line is padded with spaces when it is read, and the file is a plain text file that any editor can open. The manual also lists COB_LS_VALIDATE (check that the data in a record is valid printable text, status 09 on read and 71 on write) and COB_LS_SPLIT (what happens with a record longer than the area), whose defaults the manual gives as "per COBOL 2022". GnuCOBOL 3.2 added OPEN I-O and REWRITE for line-sequential files; in this test OPEN I-O followed by REWRITE of an unchanged record left status 00 both ways.
In Enterprise COBOL, by the Language Reference, a line-sequential file is a z/OS UNIX file whose records end with a delimiter. A record can hold only DISPLAY or DISPLAY-1 data, and a signed zoned item must carry SEPARATE CHARACTER. The clauses LABEL RECORDS, DATA RECORDS, CODE-SET, LINAGE, PADDING CHARACTER, RECORD CONTAINS 0, the second format of RECORD CONTAINS and the I-O phrase of OPEN are not supported for line-sequential files, and REWRITE is not allowed. The newline character X'15' acts as the record delimiter, so it cannot appear in the data. A batch program that exchanges files with other z/OS programs normally uses the plain SEQUENTIAL organization on QSAM data sets instead.
The practical rule for code that has to run in both places is to isolate the file definitions. Keep the FD and the record layout in a copybook, keep the SELECT in the program, and let the build or the job supply the file: DD statements on z/OS, DD_name variables in GnuCOBOL. The compilers and dialects page lists the -std=ibm switches that move GnuCOBOL’s defaults closer to Enterprise COBOL.
The same file on both systems
| Question | Enterprise COBOL 6.5 on z/OS | GnuCOBOL 3.2 |
|---|---|---|
What does |
The ddname |
A key: |
Can the name contain |
No (1 to 8 letters and digits) |
Yes |
Where does the data set name come from? |
The |
The value of the variable, or the literal |
Prefixes |
Defined by the Language Reference |
Interpreted only under the IBM dialect; avoid them |
Variable file name |
Not available: change the |
|
|
z/OS UNIX files only, |
Any text file; |
|
Meaningful (blocks, format) or ignored ( |
Accepted, no effect |
Record locking ( |
Not in the Language Reference |
Supported with a Berkeley DB build |
|
Not documented in 6.5 |
Accepted (COBOL 2023) |
Default organization and access mode |
|
|
References
-
Enterprise COBOL for z/OS 6.5 Language Reference: FILE-CONTROL paragraph, with SELECT clause, ASSIGN clause, ORGANIZATION clause, ACCESS MODE clause and FILE STATUS clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: DATA DIVISION file description entries, with RECORD clause, BLOCK CONTAINS clause, RECORDING MODE clause and LABEL RECORDS clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: OPEN statement and CLOSE statement
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Defining QSAM files and records in COBOL, Establishing record formats and Defining line-sequential files and records in COBOL
-
GnuCOBOL 3.2 manual: compiler configuration (
filename-mapping,assign-clause) and runtime configuration (COB_FILE_PATH,COB_LS_FIXED,COB_VARSEQ_FORMAT) -
GnuCOBOL Programmer’s Guide (the HTML edition is older than 3.2; the 3.2 edition is the PDF on the GnuCOBOL guides page): SELECT statement, ORGANIZATION clauses, record locking, OPEN and CLOSE
-
ISO/IEC 1989:2023, Programming language COBOL (catalogue page; the text is paywalled)