Producing a Printed Report

COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x Β· βœ“ verified by execution on 2026-08-16

Producing a printed report is one of the most common tasks in a mainframe batch job, and the fiddly part is not the data β€” it is the paper. Every page needs a heading, every page has a fixed number of usable lines, and something has to notice when the current page is full. Doing that by hand means a line counter threaded through your entire program, incremented in a dozen places and wrong in one of them. COBOL has a built-in answer: the LINAGE clause.

The LINAGE clause allows COBOL to automatically format a sequential file into pages by specifying the logical page size. It also defines margins and footing areas to trigger page breaks.

The LINAGE Clause

When you add the LINAGE clause to a File Description (FD), you define the structure of a page. You can specify the total lines, a top margin, a bottom margin, and a footing area.

LINES AT TOP (Margin) Page Body FOOTING Area LINES AT BOTTOM (Margin) LINAGE
A logical page defined by the LINAGE clause. The top and bottom margins are skipped, and the footing area triggers the END-OF-PAGE condition.

Here is a basic example setting up a page with 20 lines, a footing starting at line 18, and margins at the top and bottom. The LINES AT TOP and LINES AT BOTTOM phrases define blank margins that COBOL automatically skips when advancing to a new page or reaching the end of the page body.

cobol βœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. LINAGE-BASIC.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REPORT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  REPORT-FILE
           LINAGE IS 20 LINES
           WITH FOOTING AT 18
           LINES AT TOP 2
           LINES AT BOTTOM 3.
       01  REPORT-RECORD PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-LINE PIC X(80) VALUE "This is a test line.".
       PROCEDURE DIVISION.
           OPEN OUTPUT REPORT-FILE
           WRITE REPORT-RECORD FROM WS-LINE
           WRITE REPORT-RECORD FROM WS-LINE
           WRITE REPORT-RECORD FROM WS-LINE AFTER ADVANCING PAGE
           CLOSE REPORT-FILE
           STOP RUN.
Output
 

The LINAGE-COUNTER Register

When the LINAGE clause is specified, COBOL maintains a special register called LINAGE-COUNTER that tracks the current line number on the page. You do not need to define this in WORKING-STORAGEβ€”COBOL manages it automatically.

cobol βœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. LINAGE-COUNTER-TEST.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REPORT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  REPORT-FILE
           LINAGE IS 5 LINES
           WITH FOOTING AT 4.
       01  REPORT-RECORD PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-LINE PIC X(80) VALUE "Test.".
       PROCEDURE DIVISION.
           OPEN OUTPUT REPORT-FILE
           WRITE REPORT-RECORD FROM WS-LINE
           DISPLAY LINAGE-COUNTER
           WRITE REPORT-RECORD FROM WS-LINE
           DISPLAY LINAGE-COUNTER
           CLOSE REPORT-FILE
           STOP RUN.
Output
0000000002
0000000003

Using WRITE ... AFTER ADVANCING PAGE automatically spaces to the top of the next page and resets LINAGE-COUNTER to 1.

cobol βœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ADV-PAGE-TEST.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REPORT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  REPORT-FILE
           LINAGE IS 5 LINES.
       01  REPORT-RECORD PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-LINE PIC X(80) VALUE "Page test".
       PROCEDURE DIVISION.
           OPEN OUTPUT REPORT-FILE
           WRITE REPORT-RECORD FROM WS-LINE
           DISPLAY LINAGE-COUNTER
           WRITE REPORT-RECORD FROM WS-LINE AFTER ADVANCING PAGE
           DISPLAY LINAGE-COUNTER
           CLOSE REPORT-FILE
           STOP RUN.
Output
0000000002
0000000001

Catching the End of the Page

The WITH FOOTING AT phrase defines a footing area at the bottom of the page. Writing into this area triggers the END-OF-PAGE (or EOP) condition, allowing you to print footers or trigger a new page.

Predict the Output

A common misconception is that END-OF-PAGE only triggers exactly when LINAGE-COUNTER reaches the maximum lines of the page. In reality, it triggers on any WRITE that advances the counter into the footing area or occurs within the footing area.

Read this code carefully. At what line count will the END-OF-PAGE message first appear?

Predict the output cobol

Read the code. What exactly will it print? Commit to an answer before you look.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. P.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL. SELECT F ASSIGN "r" ORGANIZATION LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION. FD F LINAGE 5 WITH FOOTING 4. 01 R PIC X.
       WORKING-STORAGE SECTION. 01 C PIC 9 VALUE 1.
       PROCEDURE DIVISION. OPEN OUTPUT F.
           PERFORM 5 TIMES
             WRITE R FROM "X" AT END-OF-PAGE DISPLAY "EOP " C END-WRITE
             ADD 1 TO C
           END-PERFORM. CLOSE F. STOP RUN.
Output
EOP 3
EOP 4
EOP 5

Because the footing starts at line 4, writing the third line advances the counter into the footing, triggering the condition. Lines 4 and 5 also occur within the footing, so they trigger it as well.

Printing Page Headings

When formatting a report you will frequently print a page heading at the top of each new page. ADVANCING PAGE handles the form feed for you.

cobol βœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PAGE-HEADING.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REPORT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  REPORT-FILE
           LINAGE IS 20 LINES.
       01  REPORT-RECORD PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-HEADING PIC X(80) VALUE "*** MONTHLY SALES REPORT ***".
       01  WS-DETAIL  PIC X(80) VALUE "Item 1: $100.00".
       PROCEDURE DIVISION.
           OPEN OUTPUT REPORT-FILE
           WRITE REPORT-RECORD FROM WS-HEADING AFTER ADVANCING PAGE
           WRITE REPORT-RECORD FROM WS-DETAIL AFTER ADVANCING 2 LINES
           CLOSE REPORT-FILE
           STOP RUN.
Output
 

You can combine END-OF-PAGE with ADVANCING PAGE to automatically print headers on each new page when the page becomes full.

cobol βœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FOOTING-HEADER.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REPORT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  REPORT-FILE
           LINAGE IS 5 LINES
           WITH FOOTING AT 5.
       01  REPORT-RECORD PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-HEADING PIC X(80) VALUE "*** NEW PAGE ***".
       01  WS-DETAIL  PIC X(80) VALUE "Detail line".
       PROCEDURE DIVISION.
           OPEN OUTPUT REPORT-FILE
           PERFORM 5 TIMES
               WRITE REPORT-RECORD FROM WS-DETAIL
                   AT END-OF-PAGE
                       WRITE REPORT-RECORD FROM WS-HEADING 
                             AFTER ADVANCING PAGE
               END-WRITE
           END-PERFORM
           CLOSE REPORT-FILE
           STOP RUN.
Output
 

Edge Cases

Check yourself

How does COBOL know when to automatically generate the LINAGE-COUNTER special register?

Reveal answer

It is generated automatically whenever you add the LINAGE clause to a file description entry β€” If you use the LINAGE clause, COBOL automatically creates and maintains the LINAGE-COUNTER special register for you. LINAGE is part of standard sequential file I/O and does not require the Report Writer module.

When does the END-OF-PAGE condition trigger during a WRITE?

Reveal answer

Any time a WRITE occurs within or advances into the footing area β€” END-OF-PAGE triggers on any WRITE that advances the counter into the footing area (defined by WITH FOOTING AT) or occurs within the footing area.

What does WRITE ... AFTER ADVANCING PAGE actually do to the file?

Reveal answer

It sends a form-feed character (or blank lines) to start a new logical page and resets LINAGE-COUNTER β€” ADVANCING PAGE sends a form-feed character (or pads with blank lines) to start a new logical page and resets LINAGE-COUNTER.

Can you use the LINAGE clause in a standard sequential file without using the Report Writer module?

Reveal answer

Yes, LINAGE is part of standard sequential file I/O β€” LINAGE is part of standard sequential file I/O and does not require the Report Writer module.

Challenges

Challenge 1 +15 XP

Fix the FD so that the report has 30 lines per page, a footing starting at line 25, a top margin of 3 lines, and a bottom margin of 2 lines.

cobol βœ“ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real β€” it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 β€” expects ""
Need a hint? (βˆ’25% XP)

Use LINAGE IS ... WITH FOOTING AT ... LINES AT TOP ... LINES AT BOTTOM ...

Show solution (0 XP)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CHALLENGE.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT RPT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  RPT-FILE
           LINAGE IS 30 LINES
           WITH FOOTING AT 25
           LINES AT TOP 3
           LINES AT BOTTOM 2.
       01  RPT-REC PIC X(80).
       PROCEDURE DIVISION.
           STOP RUN.

🐞 Bug Hunt +15 XP

Complete the WRITE statement to display 'FOOTING REACHED' when the end-of-page condition occurs. There is a bug in the starter codeβ€”fix it so it correctly detects the footing.

This code runs. It just does the wrong thing. Read it, find the defect, fix it β€” the tests below decide when you are right.

cobol βœ“ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real β€” it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 β€” expects "FOOTING REACHED\nFOOTING REACHED\n"
Need a hint? (βˆ’25% XP)

Use AT END-OF-PAGE instead of AT END-OF-FILE to detect when the LINAGE-COUNTER enters the footing area.

Show solution (0 XP)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CHALLENGE.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT RPT-FILE ASSIGN TO "report.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  RPT-FILE
           LINAGE IS 5 LINES
           WITH FOOTING AT 4.
       01  RPT-REC PIC X(80).
       WORKING-STORAGE SECTION.
       01  WS-LINE PIC X(80) VALUE "Line".
       PROCEDURE DIVISION.
           OPEN OUTPUT RPT-FILE
           PERFORM 4 TIMES
               WRITE RPT-REC FROM WS-LINE
                   AT END-OF-PAGE
                       DISPLAY "FOOTING REACHED"
               END-WRITE
           END-PERFORM
           CLOSE RPT-FILE
           STOP RUN.