DevTools

Cheatsheet COBOL

Linguagem empresarial clássica para sistemas mainframe e processamento batch

Back to languages
COBOL
60 cards found
Categories:
Versions:

Basic


8 cards
Program Structure
       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLO-WORLD.
       AUTHOR. PROGRAMMER.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WS-NAME PIC X(30).

       PROCEDURE DIVISION.
           DISPLAY "Hello World".
           STOP RUN.

A COBOL program has 4 divisions: IDENTIFICATION (name), ENVIRONMENT (configuration), DATA (variables) and PROCEDURE (logic). Each statement ends with a period.

Format and Comments
****** Col 1-6: sequence number (optional)
****** Col 7: indicator (* = comment)
****** Col 8-11: Area A (divisions, sections)
****** Col 12-72: Area B (statements)

*      Traditional comment (column 7)

       *> Modern comment (inline)

       DISPLAY "Code".

* Each statement ends with a period
* Keywords in UPPERCASE (convention)
* Variable names with the WS- prefix

COBOL uses fixed format: column 7 for comments, Area A (8-11) for divisions, Area B (12-72) for statements. *> is the modern inline comment.

Complete Hello World
       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLO.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WS-MESSAGE PIC X(20)
          VALUE "Hello, World!".

       PROCEDURE DIVISION.
           DISPLAY WS-MESSAGE.
           DISPLAY "Goodbye!".
           STOP RUN.

*> cobc -x hello.cob -o hello
*> ./hello

The minimal program: DISPLAY prints on screen and STOP RUN ends. Compile with cobc -x (GnuCOBOL) to create an executable.

The 4 Divisions
* 1. IDENTIFICATION - who the program is
       IDENTIFICATION DIVISION.
       PROGRAM-ID. MY-PROGRAM.

* 2. ENVIRONMENT - configuration and files
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.

* 3. DATA - variables and structures
       DATA DIVISION.
       WORKING-STORAGE SECTION.

* 4. PROCEDURE - the program logic
       PROCEDURE DIVISION.

The 4 divisions are mandatory and in order: IDENTIFICATION, ENVIRONMENT, DATA and PROCEDURE. Only IDENTIFICATION and PROCEDURE are always required.

Compile and Run
# GnuCOBOL:
cobc -x program.cob -o program
./program

# Compile the a module (dynamic):
cobc -m program.cob

# Check syntax only:
cobc -fsyntax-only program.cob

# With debug:
cobc -x -g -debug program.cob

# Run with runtime:
cobcrun program

# List warnings:
cobc -x -Wall program.cob

Use cobc -x for a standalone executable and cobc -m for a module. -fsyntax-only checks without compiling. cobcrun runs modules.

STOP RUN and GOBACK
       PROCEDURE DIVISION.
           DISPLAY "Processing...".

* End main program:
           STOP RUN.

* End subprogram (returns to caller):
           GOBACK.

* STOP RUN with return code:
           MOVE 0 TO RETURN-CODE.
           STOP RUN.

STOP RUN ends a main program. GOBACK returns control to the calling program (in subprograms). RETURN-CODE indicates the status (0 = OK).

DISPLAY and ACCEPT
       PROCEDURE DIVISION.
           DISPLAY "Hello World".
           DISPLAY "Name: " WS-NAME.
           DISPLAY "Value: " WS-VAL
               WITH NO ADVANCING.

           ACCEPT WS-NAME.
           ACCEPT WS-DATE FROM DATE.
           ACCEPT WS-TIME FROM TIME.
           ACCEPT WS-KEY FROM CONSOLE.

DISPLAY writes on screen (can concatenate several items). ACCEPT reads from the keyboard. WITH NO ADVANCING does not move to a new line. FROM DATE/TIME reads from the system.

Paragraphs and Sections
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           PERFORM SETUP.
           PERFORM PROCESS-DATA.
           PERFORM CLEANUP.
           STOP RUN.

       SETUP.
           DISPLAY "Starting...".

       PROCESS-DATA.
           DISPLAY "Processing...".

       CLEANUP.
           DISPLAY "Finishing...".

Paragraphs are named blocks of code (Area A). They are called with PERFORM name. They organize the program into reusable routines.

Data


8 cards
PIC Clauses
       01 WS-FIELDS.
           05 WS-NAME      PIC X(30).
           05 WS-AGE       PIC 9(3).
           05 WS-SALARY    PIC 9(5)V99.
           05 WS-CODE      PIC A(5).
           05 WS-FLAG      PIC X.
           05 WS-NEGATIVE  PIC S9(4).
           05 WS-EDITED    PIC Z(4)9.99.

* X = alphanumeric, 9 = numeric
* A = alphabetic, V = implicit decimal
* S = signed, Z = zero suppressed

The PIC clause defines the type and size. X = text, 9 = number, V = implicit decimal place, S = sign, Z = zero suppressed (formatting).

VALUE (initial values)
       01 WS-COUNTER PIC 9(4) VALUE 0.
       01 WS-NAME PIC X(20) VALUE "Anonymous".
       01 WS-PI PIC 9V9(6) VALUE 3.141593.
       01 WS-FLAG PIC X VALUE "N".
       01 WS-MAX PIC 9(3) VALUE 999.

* Constants (level 01 with VALUE):
       01 WS-VAT-RATE PIC 9V99 VALUE 0.23.

* Without VALUE, fields start with spaces/zeros

VALUE sets the initial value of the variable. Without VALUE, alphanumeric fields start with spaces and numeric ones with zeros. Useful for constants and defaults.

MOVE and SET
       MOVE "Anna" TO WS-NAME.
       MOVE 42 TO WS-AGE.
       MOVE 1500.50 TO WS-SALARY.
       MOVE ZERO TO WS-COUNTER.
       MOVE SPACES TO WS-NAME.
       MOVE ALL "X" TO WS-LINE.
       MOVE HIGH-VALUE TO WS-FIELD.

       SET WS-INDEX TO 1.
       SET WS-INDEX UP BY 1.
       SET WS-INDEX DOWN BY 1.

MOVE assigns values (destination last). Special figures: ZERO, SPACES, ALL "x", HIGH-VALUE. SET is used for table indexes.

REDEFINES
       01 WS-DATA.
           05 WS-FULL-DATE PIC X(8).
           05 WS-DATE-PARTS
              REDEFINES WS-FULL-DATE.
               10 WS-YEAR PIC 9(4).
               10 WS-MONTH PIC 9(2).
               10 WS-DAY  PIC 9(2).

       PROCEDURE DIVISION.
           MOVE "20240315" TO WS-FULL-DATE.
           DISPLAY WS-YEAR "/" WS-MONTH "/" WS-DAY.
           *> Shows: 2024/03/15

REDEFINES lets you view the same memory with another structure. Here an 8-char string is reinterpreted the year/month/day. It takes no extra space.

Levels and Groups
       01 WS-CLIENT.
           05 WS-NAME      PIC X(30).
           05 WS-ADDRESS.
               10 WS-STREET PIC X(40).
               10 WS-CITY   PIC X(20).
               10 WS-ZIP     PIC X(8).
           05 WS-PHONE     PIC X(15).

* Level 01 = main record
* Level 05-49 = subfields (hierarchy)
* Level 77 = independent variable
* Level 88 = condition (boolean)

Levels create hierarchy: 01 is the main group, 05-49 are nested subfields. 77 declares standalone variables. 88 defines named conditions.

Edited Fields
       01 WS-VALUE PIC 9(6)V99 VALUE 1234.56.
       01 WS-EDITED PIC Z(5)9.99.
       01 WS-CURRENCY PIC ZZZ.ZZ9,99.
       01 WS-SIGN PIC +Z(5)9.

       PROCEDURE DIVISION.
           MOVE WS-VALUE TO WS-EDITED.
           DISPLAY WS-EDITED.  *> " 1234.56"
           MOVE WS-VALUE TO WS-CURRENCY.
           DISPLAY WS-CURRENCY.    *> "  1.234,56"

Edited fields format the output: Z suppresses leading zeros, . and , insert separators, + shows the sign. Only for DISPLAY, not for calculations.

Level 88 (conditions)
       01 WS-STATUS PIC X.
           88 WS-ACTIVE    VALUE "A".
           88 WS-INACTIVE  VALUE "I".
           88 WS-PENDING   VALUE "P".

       01 WS-DAY PIC 9(2).
           88 WS-WEEKEND VALUES 6, 7.
           88 WS-WEEKDAY VALUES 1 THRU 5.

       PROCEDURE DIVISION.
           IF WS-ACTIVE
               DISPLAY "Active client".
           SET WS-ACTIVE TO TRUE.

The level 88 creates named conditions tied to values. IF WS-ACTIVE tests whether WS-STATUS = "A". You can use multiple VALUES and THRU (range). SET ... TO TRUE activates.

Level 77 and RENAMES
* Level 77 - independent variable:
       77 WS-COUNTER PIC 9(4) VALUE 0.
       77 WS-FLAG PIC X VALUE "N".

* RENAMES - rename a group:
       01 WS-PERSON.
           05 WS-NAME PIC X(30).
           05 WS-AGE PIC 9(3).
       66 WS-INFO RENAMES WS-NAME
           THRU WS-AGE.

* 66 groups existing fields under a new name

Level 77 declares independent variables (no group). RENAMES (level 66) gives an alternative name to a set of existing fields.

Flow Control


8 cards
IF/ELSE
       IF WS-AGE >= 18
           DISPLAY "Adult"
       ELSE
           DISPLAY "Minor"
       END-IF.

       IF WS-GRADE >= 14
           DISPLAY "Passed"
       ELSE IF WS-GRADE >= 10
           DISPLAY "Resit"
       ELSE
           DISPLAY "Failed"
       END-IF.

* Operators: =  <>  <  >  <=  >=
* Logical: AND, OR, NOT

IF/ELSE/END-IF is the basic conditional. Nest with ELSE IF. Operators: = (equal), <> (not equal), <, >, <=, >=. Combine with AND, OR, NOT.

EVALUATE (case)
       EVALUATE WS-DAY
           WHEN 1
               DISPLAY "Monday"
           WHEN 2 THRU 5
               DISPLAY "Weekday"
           WHEN 6 7
               DISPLAY "Weekend"
           WHEN OTHER
               DISPLAY "Invalid"
       END-EVALUATE.

* WHEN 6 7 = multiple values (OR)
* WHEN 2 THRU 5 = range
* WHEN OTHER = default

EVALUATE is the "switch/case". WHEN tests each case, THRU defines ranges, values separated by a space are OR. WHEN OTHER is the default.

PERFORM VARYING (for)
* for (i = 1; i <= 10; i++):
       PERFORM VARYING WS-I FROM 1 BY 1
           UNTIL WS-I > 10
           DISPLAY WS-I
       END-PERFORM.

* Decreasing:
       PERFORM VARYING WS-I FROM 10 BY -1
           UNTIL WS-I < 1
           DISPLAY WS-I
       END-PERFORM.

* Step 2:
       PERFORM VARYING WS-I FROM 0 BY 2
           UNTIL WS-I > 20
           DISPLAY WS-I
       END-PERFORM.

PERFORM VARYING is the "for" loop: FROM (start), BY (step), UNTIL (stop condition). The step can be negative or greater than 1.

EVALUATE TRUE (if/elseif)
       EVALUATE TRUE
           WHEN WS-AGE >= 65
               DISPLAY "Senior"
           WHEN WS-AGE >= 18
               DISPLAY "Adult"
           WHEN WS-AGE >= 12
               DISPLAY "Teenager"
           WHEN OTHER
               DISPLAY "Child"
       END-EVALUATE.

* Equivalent to an IF/ELSE-IF chain
* More readable for multiple conditions

EVALUATE TRUE evaluates boolean expressions in each WHEN. It is the clean alternative to long IF/ELSE-IF chains. The first true WHEN runs.

PERFORM UNTIL (while)
* while (answer <> "Y"):
       PERFORM UNTIL WS-ANSWER = "Y"
           DISPLAY "Continue? (Y/N)"
           ACCEPT WS-ANSWER
       END-PERFORM.

* Test at the end (do-while):
       PERFORM WITH TEST AFTER
           UNTIL WS-COUNTER > 5
           ADD 1 TO WS-COUNTER
           DISPLAY WS-COUNTER
       END-PERFORM.

PERFORM UNTIL is the "while" loop (test at the start). WITH TEST AFTER moves the test to the end (runs at least once, like do-while).

PERFORM paragraphs
       PROCEDURE DIVISION.
           PERFORM START-PROGRAM.
           PERFORM PROCESS-DATA.
           PERFORM END-PROGRAM.
           STOP RUN.

       START-PROGRAM.
           DISPLAY "Starting...".
           OPEN INPUT CLIENTS-FILE.

       PROCESS-DATA.
           PERFORM READ-RECORD
               UNTIL WS-EOF = "Y".

       END-PROGRAM.
           CLOSE CLIENTS-FILE.

PERFORM paragraph-name runs a routine and returns automatically. It is the classic way to organize COBOL into subroutines (before CALL subprograms).

PERFORM TIMES
* Repeat 5 times:
       PERFORM 5 TIMES
           DISPLAY "Hi"
       END-PERFORM.

* With a variable:
       PERFORM WS-TIMES TIMES
           ADD WS-VALUE TO WS-TOTAL
       END-PERFORM.

* Call a paragraph N times:
       PERFORM SHOW-LINE 3 TIMES.

PERFORM n TIMES repeats a block a fixed number of times. It accepts a numeric variable the the counter. The simplet form of loop.

Nested IF and Conditions
       IF WS-HAS-STOCK
           IF WS-PRICE <= WS-LIMIT
               PERFORM BUY-ITEM
           ELSE
               DISPLAY "Too expensive"
           END-IF
       ELSE
           DISPLAY "Out of stock"
       END-IF.

* Compound conditions:
       IF WS-AGE >= 18 AND WS-HAS-LICENSE
           PERFORM RENT-CAR.

       IF WS-VIP OR WS-SENIORITY > 10
           PERFORM GIVE-DISCOUNT.

Nest IF with clear indentation and close each one with END-IF. Combine conditions with AND/OR. Use the implicit precedence (AND before OR).

Operations


7 cards
Arithmetic
       ADD 10 TO WS-TOTAL.
       ADD A B GIVING WS-SUM.
       SUBTRACT 5 FROM WS-BALANCE.
       MULTIPLY WS-PRICE BY WS-QTY
           GIVING WS-TOTAL.
       DIVIDE WS-TOTAL BY WS-QTY
           GIVING WS-AVERAGE
           REMAINDER WS-REMAINDER.

* ADD WS-A TO WS-B  =>  WS-B += WS-A
* GIVING stores the result without altering operands

Arithmetic verbs: ADD, SUBTRACT, MULTIPLY, DIVIDE. TO/FROM modify the operand; GIVING stores in a separate field. REMAINDER gives the remainder.

INSPECT and reference
* Replace characters:
       INSPECT WS-TEXT
           REPLACING ALL "a" BY "o".

* Count characters:
       INSPECT WS-TEXT
           TALLYING WS-CNT FOR ALL "e".

* Reference modification (substring):
       MOVE WS-NAME(1:5) TO WS-PART.
       MOVE "2024" TO WS-DATE(1:4).

* WS-NAME(1:5) = 5 chars from pos 1
* Reference with a variable:
       MOVE WS-NAME(WS-POS:3) TO WS-TMP.

INSPECT replaces (REPLACING) or counts (TALLYING) characters. Reference modification field(pos:len) extracts or writes substrings. Very useful for parsing.

COMPUTE (expressions)
       COMPUTE WS-AREA = 3.14 * WS-R ** 2.

       COMPUTE WS-INTEREST ROUNDED =
           WS-CAPITAL * WS-RATE / 100.

       COMPUTE WS-AVERAGE =
           (WS-GRADE1 + WS-GRADE2) / 2.

       COMPUTE WS-RESULT = WS-A * WS-B
           + WS-C - WS-D / WS-E.

* ** = power, ROUNDED = round
* ON SIZE ERROR handles overflow
       COMPUTE WS-X = WS-Y * WS-Z
           ON SIZE ERROR DISPLAY "Overflow".

COMPUTE accepts full expressions with + - * / ** and parentheses. ROUNDED rounds the result. ON SIZE ERROR handles overflow.

Intrinsic Functions
* Mathematical:
       COMPUTE WS-R = FUNCTION SQRT(16).
       COMPUTE WS-R = FUNCTION ABS(-5).
       COMPUTE WS-R = FUNCTION MAX(A B C).
       COMPUTE WS-R = FUNCTION MIN(A B C).

* Strings:
       MOVE FUNCTION UPPER-CASE(WS-S) TO WS-R.
       MOVE FUNCTION LOWER-CASE(WS-S) TO WS-R.
       MOVE FUNCTION TRIM(WS-S) TO WS-R.
       MOVE FUNCTION REVERSE(WS-S) TO WS-R.
       MOVE FUNCTION LENGTH(WS-S) TO WS-N.

FUNCTION gives access to built-in functions: SQRT, ABS, MAX, MIN (math) and UPPER-CASE, LOWER-CASE, TRIM, REVERSE, LENGTH (strings). Always prefix with FUNCTION.

STRING (concatenate)
       STRING WS-NAME DELIMITED BY SPACE
           " - " DELIMITED BY SIZE
           WS-CITY DELIMITED BY SPACE
           INTO WS-RESULT.

* DELIMITED BY SPACE - up to the 1st space
* DELIMITED BY SIZE - whole field

* With pointer (write position):
       STRING WS-A DELIMITED BY SIZE
           INTO WS-B
           WITH POINTER WS-POS.

* ON OVERFLOW handles lack of space

STRING concatenates several fields into a destination. DELIMITED BY SPACE cuts at trailing spaces, BY SIZE uses the whole field. WITH POINTER controls the position.

Comparison and Classes
* Numeric conditions:
       IF WS-VALUE IS NUMERIC
       IF WS-VALUE IS ZERO
       IF WS-VALUE IS POSITIVE
       IF WS-VALUE IS NEGATIVE

* Class conditions:
       IF WS-FIELD IS ALPHABETIC
       IF WS-FIELD IS ALPHANUMERIC

* Condition-name tests (level 88):
       IF WS-FLAG IS TRUE
       IF WS-FLAG IS FALSE

* Combined:
       IF WS-A > 0 AND WS-B < 100
       IF NOT WS-ERROR

Class tests: IS NUMERIC, IS ALPHABETIC, IS ALPHANUMERIC. Value tests: IS ZERO, IS POSITIVE, IS NEGATIVE. Level 88 condition tests: IS TRUE/FALSE.

UNSTRING (split)
* Split by delimiter:
       UNSTRING WS-DATE DELIMITED BY "/"
           INTO WS-DAY WS-MONTH WS-YEAR.

* Multiple delimiters:
       UNSTRING WS-LINE DELIMITED BY ","
           OR ";" INTO WS-C1 WS-C2 WS-C3.

* With counters:
       UNSTRING WS-PHRASE DELIMITED BY " "
           INTO WS-W1 WS-W2 WS-W3
           TALLYING IN WS-NUM-WORDS.

* WS-DATE = "15/03/2024"
* => WS-DAY=15, WS-MONTH=03, WS-YEAR=2024

UNSTRING splits a string by delimiter into several fields. It accepts multiple delimiters with OR. TALLYING IN counts how many segments were extracted.

files


8 cards
Define Files
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CLIENTS
               ASSIGN TO "clients.dat"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.

       DATA DIVISION.
       FILE SECTION.
       FD CLIENTS.
       01 REC-CLIENT.
           05 REC-NAME    PIC X(30).
           05 REC-AGE     PIC 9(3).
           05 REC-BALANCE PIC 9(6)V99.

Declare it in FILE-CONTROL (SELECT/ASSIGN) and define the record in FD (FILE SECTION). FILE STATUS stores the result code of each operation.

Indexed File
       SELECT CLIENTS
           ASSIGN TO "clients.idx"
           ORGANIZATION IS INDEXED
           ACCESS MODE IS DYNAMIC
           RECORD KEY IS REC-CODE
           ALTERNATE KEY IS REC-NAME
               WITH DUPLICATES.

* Read by key:
       MOVE 123 TO REC-CODE.
       READ CLIENTS
           KEY IS REC-CODE
           INVALID KEY DISPLAY "Not found"
       END-READ.

ORGANIZATION INDEXED allows direct access by key. RECORD KEY is the primary key, ALTERNATE KEY are secondary keys. ACCESS MODE DYNAMIC allows sequential + direct.

Sequential Read
       PROCEDURE DIVISION.
           OPEN INPUT CLIENTS.
           IF WS-STATUS NOT = "00"
               DISPLAY "Error: " WS-STATUS
               STOP RUN
           END-IF.

           PERFORM UNTIL WS-EOF = "Y"
               READ CLIENTS
                   AT END
                       MOVE "Y" TO WS-EOF
                   NOT AT END
                       DISPLAY REC-NAME
               END-READ
           END-PERFORM.
           CLOSE CLIENTS.

OPEN INPUT opens for reading. The READ ... AT END loop processes record by record until the end. CLOSE releases the file. Classic batch processing pattern.

START and Sequential Read
* Position at the start (key >= 100):
       MOVE 100 TO REC-CODE.
       START CLIENTS
           KEY IS >= REC-CODE
           INVALID KEY DISPLAY "No records".

* Read sequentially from there:
       PERFORM UNTIL WS-EOF = "Y"
           READ CLIENTS NEXT
               AT END MOVE "Y" TO WS-EOF
               NOT AT END DISPLAY REC-CODE
           END-READ
       END-PERFORM.

START positions the indexed file at a key (with =, >, >=, <). Then READ NEXT goes sequentially from there. Ideal for range reports.

Write
* Create/overwrite:
       OPEN OUTPUT CLIENTS.
       MOVE "Anna" TO REC-NAME.
       MOVE 30 TO REC-AGE.
       WRITE REC-CLIENT.
       CLOSE CLIENTS.

* Append to the end:
       OPEN EXTEND CLIENTS.
       WRITE REC-CLIENT.
       CLOSE CLIENTS.

* WRITE with check:
       WRITE REC-CLIENT
           INVALID KEY DISPLAY "Write error".

OPEN OUTPUT creates/overwrites. OPEN EXTEND appends to the end. WRITE writes the FD record. INVALID KEY detects write errors.

DELETE
       OPEN I-O CLIENTS.

* Position at the record:
       MOVE 123 TO REC-CODE.
       READ CLIENTS
           KEY IS REC-CODE
           INVALID KEY DISPLAY "Does not exist"
       END-READ.

* Delete:
       DELETE CLIENTS RECORD
           INVALID KEY DISPLAY "Delete error".

       CLOSE CLIENTS.

* Only works on indexed/relative files

DELETE file RECORD removes the current record. Requires the file open in I-O and a previously read record. Only works on indexed or relative organizations.

Update (I-O)
       OPEN I-O CLIENTS.

* Read the record to modify:
       MOVE 123 TO REC-CODE.
       READ CLIENTS
           KEY IS REC-CODE
           INVALID KEY DISPLAY "Does not exist"
       END-READ.

* Modify and rewrite:
       MOVE 999.99 TO REC-BALANCE.
       REWRITE REC-CLIENT
           INVALID KEY DISPLAY "Error".

       CLOSE CLIENTS.

OPEN I-O opens for reading and writing. Read the record, modify the fields and use REWRITE to save the changes in place. Requires an indexed or relative file.

FILE STATUS
       01 WS-STATUS PIC XX.

* Common codes:
* "00" - Success
* "02" - Success (duplicate on alternate key)
* "10" - End of file
* "23" - Record not found
* "35" - File does not exist
* "46" - Invalid read (sequential)
* "9x" - System error

       IF WS-STATUS = "00"
           DISPLAY "OK"
       ELSE
           DISPLAY "Error: " WS-STATUS
       END-IF.

The FILE STATUS (2 chars) stores the result of each file operation. "00" = success, "10" = EOF, "23" = not found, "35" = nonexistent file. Always check after OPEN/READ/WRITE.

Advanced


7 cards
COPY and COPYBOOK
* COPYBOOK file (common.cpy):
       01 WS-COMMON-DATA.
           05 WS-USER      PIC X(20).
           05 WS-SYS-DATE  PIC 9(8).
           05 WS-SYS-TIME  PIC 9(6).

* In the program:
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY "common.cpy".

* With REPLACING (rename prefix):
       COPY "common.cpy"
           REPLACING WS- BY CLI-.

COPY inserts a copybook (shared code). REPLACING renames prefixes to avoid conflicts. Essential for sharing structures between programs.

SORT and MERGE
* Sort a file:
       SORT WORK-FILE
           ON ASCENDING KEY REC-CODE
           USING INPUT-FILE
           GIVING OUTPUT-FILE.

* With INPUT/OUTPUT procedures:
       SORT WORK-FILE
           ON DESCENDING KEY REC-BALANCE
           INPUT PROCEDURE IS READ-DATA
           OUTPUT PROCEDURE IS WRITE-DATA.

* MERGE joins sorted files:
       MERGE WORK-FILE
           ON ASCENDING KEY REC-CODE
           USING FILE-A FILE-B
           GIVING FILE-C.

SORT sorts a file by key (ASCENDING/DESCENDING). MERGE joins already sorted files. USING/GIVING define input and output. Native COBOL sorting.

Subprograms (CALL)
* Calling program:
       CALL "SUBPROG" USING
           WS-PARAM1
           WS-PARAM2
           WS-RESULT.

* Subprogram (SUBPROG.COB):
       IDENTIFICATION DIVISION.
       PROGRAM-ID. SUBPROG.
       DATA DIVISION.
       LINKAGE SECTION.
       01 LS-PARAM1  PIC X(10).
       01 LS-PARAM2  PIC 9(5).
       01 LS-RESULT  PIC 9(8).
       PROCEDURE DIVISION USING
           LS-PARAM1 LS-PARAM2 LS-RESULT.
           COMPUTE LS-RESULT = LS-PARAM2 * 2.
           GOBACK.

CALL "name" USING calls a subprogram passing parameters. The subprogram declares them in the LINKAGE SECTION and receives them in PROCEDURE DIVISION USING. It ends with GOBACK.

Error Handling
* FILE STATUS:
       IF WS-STATUS = "00"
           DISPLAY "Success"
       ELSE IF WS-STATUS = "10"
           DISPLAY "End of file"
       ELSE IF WS-STATUS = "23"
           DISPLAY "Record does not exist"
       ELSE
           DISPLAY "Error: " WS-STATUS
       END-IF.

* USE declaratives (global handler):
       DECLARATIVES.
       ERROR-HANDLING SECTION.
           USE AFTER STANDARD ERROR
               PROCEDURE ON CLIENTS.
           DISPLAY "File error".
       END DECLARATIVES.

Check the FILE STATUS after each operation. DECLARATIVES defines global error handlers that run automatically when a file error occurs.

BY VALUE/REFERENCE Parameters
* By reference (default - can modify):
       CALL "SUB" USING WS-DATUM.

* By value (copy - does not modify):
       CALL "SUB" USING BY VALUE WS-NUM.

* In the subprogram (BY VALUE):
       PROCEDURE DIVISION USING
           BY VALUE LS-NUM.

* BY CONTENT (read-only copy):
       CALL "SUB" USING BY CONTENT WS-TEXT.

* Return a value:
       CALL "SUB" USING WS-X
           RETURNING WS-RESULT.

By default parameters are BY REFERENCE (the subprogram can modify them). BY VALUE passes a copy. RETURNING returns a value like a function.

Pointers and ADDRESS
       01 WS-POINTER USAGE POINTER.
       01 WS-DATA PIC X(100).

* Get the address:
       SET WS-POINTER TO ADDRESS OF WS-DATA.

* Allocate dynamic memory:
       ALLOCATE 100 CHARACTERS
           RETURNING WS-POINTER.

* Free:
       FREE WS-POINTER.

* SET POINTER to a position:
       SET WS-POINTER UP BY 10.

* Compare pointers:
       IF WS-POINTER = NULL
           DISPLAY "Out of memory".

USAGE POINTER declares pointers. ADDRESS OF gets the address of a field. ALLOCATE/FREE manage dynamic memory. Useful for variable-length structures.

INITIALIZE
       01 WS-RECORD.
           05 WS-NAME   PIC X(20).
           05 WS-AGE    PIC 9(3).
           05 WS-BALANCE PIC 9(6)V99.

* Clear everything (spaces + zeros):
       INITIALIZE WS-RECORD.

* Only alphanumeric fields:
       INITIALIZE WS-RECORD
           REPLACING ALPHANUMERIC BY SPACES.

* With a specific value:
       INITIALIZE WS-RECORD
           REPLACING NUMERIC DATA BY ZERO.

* Without INITIALIZE, fields keep garbage

INITIALIZE clears a group: alphanumerics to spaces, numerics to zeros. REPLACING lets you specify the values. Avoids memory "garbage" in reused records.

Tables


7 cards
OCCURS (arrays)
       01 WS-TABLE.
           05 WS-ITEM OCCURS 10 TIMES
               INDEXED BY WS-IDX.
               10 WS-CODE PIC 9(4).
               10 WS-NAME PIC X(20).

* Traverse:
       PERFORM VARYING WS-IDX FROM 1 BY 1
           UNTIL WS-IDX > 10
           DISPLAY WS-NAME(WS-IDX)
       END-PERFORM.

* Access by index:
       MOVE 5 TO WS-IDX.
       DISPLAY WS-CODE(WS-IDX).

OCCURS n TIMES creates an array with n elements. INDEXED BY declares an index. Access with field(index). Indexes start at 1.

SET UP / DOWN
       01 WS-TAB.
           05 WS-ELEM OCCURS 10 TIMES
               INDEXED BY WS-IDX
               PIC X(10).

* Increment/decrement the index:
       SET WS-IDX TO 1.
       SET WS-IDX UP BY 1.      *> WS-IDX = 2
       SET WS-IDX DOWN BY 1.    *> WS-IDX = 1

* Traverse backwards:
       SET WS-IDX TO 10.
       PERFORM VARYING WS-IDX FROM 10 BY -1
           UNTIL WS-IDX < 1
           DISPLAY WS-ELEM(WS-IDX)
       END-PERFORM.

SET index UP BY n increments, DOWN BY n decrements. Indexes (INDEXED BY) can only be modified with SET, not with MOVE/COMPUTE.

SEARCH (linear search)
       SET WS-IDX TO 1.
       SEARCH WS-ITEM
           AT END
               DISPLAY "Not found"
           WHEN WS-CODE(WS-IDX) = WS-TARGET
               DISPLAY "Found: " WS-NAME(WS-IDX)
       END-SEARCH.

* SEARCH scans from the current index
* Stops at the first true WHEN
* AT END runs if there is no match

SEARCH does a linear search on the table. Set the index first (SET ... TO 1). WHEN tests each element, AT END runs if not found.

OCCURS DEPENDING ON
       01 WS-TABLE.
           05 WS-COUNT PIC 9(3).
           05 WS-ITEM OCCURS 1 TO 100 TIMES
               DEPENDING ON WS-COUNT
               PIC X(20).

* Define how many elements to use:
       MOVE 50 TO WS-COUNT.

* Now WS-ITEM has 50 valid elements
       PERFORM VARYING WS-I FROM 1 BY 1
           UNTIL WS-I > WS-COUNT
           DISPLAY WS-ITEM(WS-I)
       END-PERFORM.

OCCURS DEPENDING ON creates variable-length tables. The counter field (WS-COUNT) defines how many elements are active. Saves memory at runtime.

SEARCH ALL (binary)
* The table MUST be sorted by the key:
       SEARCH ALL WS-ITEM
           AT END
               DISPLAY "Not found"
           WHEN WS-CODE(WS-IDX) = WS-TARGET
               DISPLAY "Found: " WS-NAME(WS-IDX)
       END-SEARCH.

* SEARCH ALL = binary search (O(log n))
* Faster than SEARCH for large tables
* Requires ASCENDING/DESCENDING KEY on OCCURS

SEARCH ALL uses binary search (much faster). The table must be sorted. Declare ASCENDING KEY on the OCCURS. Ideal for large tables.

Fill a Table
       PERFORM VARYING WS-I FROM 1 BY 1
           UNTIL WS-I > 10
           DISPLAY "Value " WS-I ":"
           ACCEPT WS-VALUE(WS-I)
       END-PERFORM.

* Or read from a file:
       MOVE 0 TO WS-COUNT.
       PERFORM UNTIL WS-EOF = "Y" OR
           WS-COUNT >= 100
           READ DATA-FILE AT END MOVE "Y" TO WS-EOF
               NOT AT END
                   ADD 1 TO WS-COUNT
                   MOVE REC-FIELD TO
                       WS-ITEM(WS-COUNT)
           END-READ
       END-PERFORM.

Fill tables with PERFORM VARYING + ACCEPT or by reading from a file record by record. Always control the maximum limit so you do not exceed the OCCURS.

Multidimensional Table
       01 WS-MATRIX.
           05 WS-ROW OCCURS 3 TIMES.
               10 WS-COLUMN OCCURS 4 TIMES
                   PIC 9(4).

* Access [row][column]:
       MOVE 100 TO WS-MATRIX(2, 3).
       DISPLAY WS-MATRIX(1, 1).

* Traverse everything:
       PERFORM VARYING WS-R FROM 1 BY 1
           UNTIL WS-R > 3
           PERFORM VARYING WS-C FROM 1 BY 1
               UNTIL WS-C > 4
               DISPLAY WS-MATRIX(WS-R, WS-C)
           END-PERFORM
       END-PERFORM.

Nest OCCURS to create matrices. Access with (row, column). Traverse with nested PERFORM VARYING (one per dimension).

Tips and Good Practices


7 cards
Naming Conventions
* Prefixes by type:
       01 WS-COUNTER PIC 9(4).       *> Working Storage
       01 REC-CLIENT PIC X(30).      *> FD record
       01 LS-PARAM PIC 9(5).         *> Linkage Section
       01 FD-DATA PIC X(10).         *> File Description

* Descriptive names with hyphens:
       01 WS-TOTAL-SALES PIC 9(8)V99.
       01 WS-CLIENT-NAME PIC X(40).

* Level 88 with condition names:
       88 WS-VALID-RECORD VALUE "Y".

* Maximum 30 characters per name

Use prefixes: WS- (working storage), REC- (records), LS- (linkage). Descriptive names with hyphens. Level 88 with names that read like conditions.

Modern COBOL (2002+)
* Inline comments:
       *> This is a modern comment

* Real booleans:
       01 WS-OK PIC 1 VALUE 0.
       SET WS-OK TO TRUE.

* Intrinsic functions:
       COMPUTE WS-R = FUNCTION SQRT(16).

* Reference modification:
       MOVE WS-S(1:3) TO WS-T.

* SELF (stateful programs):
       IDENTIFICATION DIVISION.
       PROGRAM-ID. COUNTER IS RECURSIVE.

* GnuCOBOL supports most standards
* COBOL 85, 2002, 2014 and 2023

Modern COBOL (2002+) added *> comments, booleans, intrinsic functions and reference modification. GnuCOBOL supports the 85, 2002, 2014 and 2023 standards.

Clean Program Structure
       PROCEDURE DIVISION.
       0000-MAIN.
           PERFORM 1000-INITIALIZE.
           PERFORM 2000-PROCESS
               UNTIL WS-EOF = "Y".
           PERFORM 9000-FINALIZE.
           STOP RUN.

       1000-INITIALIZE.
           OPEN INPUT CLIENTS.
           MOVE 0 TO WS-COUNTER.

       2000-PROCESS.
           READ CLIENTS
               AT END MOVE "Y" TO WS-EOF
           END-READ.

       9000-FINALIZE.
           CLOSE CLIENTS.
           DISPLAY "Total: " WS-COUNTER.

Number the paragraphs (1000-, 2000-, 9000-) for organization and easy insertion. MAIN calls INITIALIZE, PROCESS and FINALIZE. Classic maintenance pattern.

GnuCOBOL
# Install:
# Ubuntu: sudo apt install gnucobol
# Windows: via MSYS2 or binaries

# Compile and run:
cobc -x program.cob -o program
./program

# Tools:
cobc --version        *> version
cobc --list-intrinsics *> available functions
cobcrun --runtime-conf *> configuration

# Free format (no fixed columns):
cobc -x -free program.cbl

# Optimization:
cobc -x -O2 program.cob

GnuCOBOL is the most widely used open-source compiler. -free allows free format (no fixed columns). -O2 optimizes. --list-intrinsics shows the available functions.

Debug with DISPLAY
* Show values during execution:
       DISPLAY "WS-COUNTER = " WS-COUNTER.
       DISPLAY "STATUS = " WS-STATUS.
       DISPLAY "RECORD: " REC-CLIENT.

* With UPON to separate:
       DISPLAY "DEBUG" UPON SYSERR.

* GnuCOBOL - execution trace:
*> cobc -x -debug -ftraceall program.cob
*> COB_SET_TRACE=1 ./program

* ACCEPT to pause:
       DISPLAY "Press ENTER to continue".
       ACCEPT WS-KEY.

DISPLAY is the main debugging tool. Show variables at key points. GnuCOBOL supports -ftraceall for a full trace. ACCEPT pauses execution.

Program Template
       IDENTIFICATION DIVISION.
       PROGRAM-ID. TEMPLATE.
       AUTHOR. YOUR-NAME.
       DATE-WRITTEN. 2024-01-15.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.

       DATA DIVISION.
       FILE SECTION.
       WORKING-STORAGE SECTION.
       01 WS-COUNTER PIC 9(4) VALUE 0.

       PROCEDURE DIVISION.
       0000-MAIN.
           PERFORM 1000-START.
           PERFORM 2000-PROCESS.
           PERFORM 9000-END.
           STOP RUN.
       1000-START.
       2000-PROCESS.
       9000-END.

A standard template with a header (AUTHOR, DATE-WRITTEN), empty divisions and numbered paragraphs. Copy and adapt it for new programs — it saves time and keeps consistency.

Common Mistakes
* 1. Forgetting the period:
       DISPLAY "Hi"       *> ERROR!
       DISPLAY "Hi".      *> correct

* 2. MOVE in the wrong order:
       MOVE WS-A TO WS-B.  *> WS-B receives WS-A

* 3. PIC 9 with a decimal value without V:
       01 WS-X PIC 99.
       MOVE 3.14 TO WS-X.  *> becomes 31 (truncated!)
       01 WS-Y PIC 9V99.   *> correct: 3.14

* 4. Comparing alphanumeric with numeric:
       IF WS-NAME = 100   *> type ERROR
       IF WS-NAME = "100" *> correct

* 5. Index outside the OCCURS

Frequent mistakes: missing period, MOVE order reversed, PIC without V for decimals (truncates!), comparing different types, index outside the table.