Das Minimum, das Sie vor dem Lesen von COBOL-Quellcode kennen sollten

· · COBOL, Legacy-Technologie, Geschäftssysteme, Wartung, Mainframe

Übergaben, Störungsbehebung, die Wartung eines Fremdherstellerpakets. In solchen Situationen landet eines Tages plötzlich ein Berg COBOL-Quellcode auf dem Tisch.

  • Die Dateinamen enden auf .cbl oder .cpy
  • Die Variablennamen stehen komplett in Großbuchstaben
  • Reihen von 01, 05, 77, 88
  • Etwas wie PIC S9(7)V99 COMP-3 taucht auf – irgendwo zwischen Beschwörungsformel und Buchhaltungssoftware
  • Und dann ist da noch massenhaft COPY, sodass die geöffnete Datei allein nicht einmal das große Ganze zeigt

An diesem Punkt bleiben die meisten Menschen erst einmal ratlos stehen.

Die Landkarte, die man zum Lesen braucht, ist aber gar nicht so groß. COBOL unterscheidet sich zwar von Compiler zu Compiler und von Produkt zu Produkt, doch das Grundgerüst, das man beim Lesen eines bestehenden Business-Systems zuerst greifen sollte, ist ziemlich einheitlich. Dieser Artikel ordnet, mit Blick auf IBM-typisches und allgemein übliches Business-COBOL, das Minimum für Menschen, die plötzlich COBOL-Quellcode lesen müssen.

1. Zuerst das Fazit (in einem Satz)

Ganz grob vorweg, aber praxistauglich formuliert, lautet es so:

  • COBOL ist, bevor es eine Sprache der Logik ist, sehr stark eine Sprache der Datensatzdefinitionen
  • Wer nur die PROCEDURE DIVISION liest, versteht höchstens die Hälfte. Zuerst die DATA DIVISION ansehen
  • PIC ist die Form eines Feldes, USAGE ist die Darstellung, in der es gehalten wird
  • COMP-3 ist packed decimal. Es taucht ständig in der Welt der Beträge und Stückzahlen auf
  • 88 ist keine eigene Variable, sondern ein condition-name, der dem Wert des vorangehenden Feldes einen Namen gibt
  • REDEFINES ist ein Mechanismus, um denselben Speicher in einer anderen Form zu betrachten. Es ist keine Kopie
  • Gibt es ein COPY, ist die gerade geöffnete Quelle noch nicht vollständig. Ohne Blick in die Copybooks sieht man nicht das Ganze
  • Wer PERFORM, IF, EVALUATE, READ, WRITE und CALL verfolgen kann, erfasst den größten Teil des Ablaufs
  • Alter Quellcode liegt im festen Format vor, in dem die Spaltenposition Bedeutung trägt. Die sichtbaren Leerzeichen sind keine bloße Verzierung1

Kurz gesagt: DIVISION, PIC, USAGE, COMP-3, REDEFINES, OCCURS, 88, COPY, PERFORM. Wer diese lesen kann, verirrt sich deutlich seltener.

2. COBOL zuerst als Sprache über die Form von Daten begreifen

Wer mit dem Instinkt von C# oder Java liest, will zuerst den ifs, for-Schleifen und Funktionsaufrufen folgen. Bei COBOL ist es aber schneller, vorher zu klären: „Welche Datensätze empfängt dieses Programm, welche erzeugt es, und welche Puffer hält es?“

Typisches Business-COBOL läuft grob in diesem Ablauf:

  1. Datensätze aus einer Datei oder DB lesen
  2. Sie in WORKING-STORAGE-Felder einlesen
  3. Bedingt verzweigen
  4. Sie in einen anderen Datensatz umpacken
  5. Sie ausschreiben

Mit anderen Worten: Das Layout steht meist vor dem Algorithmus.

Hier ein typisches Grundgerüst als Beispiel.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SAMPLE01.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT SALES-FILE ASSIGN TO ...

       DATA DIVISION.
       FILE SECTION.
       FD  SALES-FILE.
       01  SALES-REC.
           05  SALE-ID       PIC 9(8).
           05  SALE-AMOUNT   PIC S9(7)V99 COMP-3.

       WORKING-STORAGE SECTION.
       01  WS-EOF            PIC X VALUE 'N'.
           88  EOF           VALUE 'Y'.

       PROCEDURE DIVISION.
           PERFORM UNTIL EOF
               READ SALES-FILE
                   AT END
                       SET EOF TO TRUE
                   NOT AT END
                       PERFORM PROCESS-SALE
               END-READ
           END-PERFORM
           STOP RUN.

Beim Lesen dieses Codes sollte man zuerst nicht auf PERFORM schauen, sondern auf den Typ von SALE-AMOUNT und die Bedeutung von EOF. Liest man COBOL in dieser Reihenfolge, wird es plötzlich still.

3. Zuerst die vier DIVISIONs betrachten

COBOL-Quellcode gliedert sich zunächst grob in vier DIVISIONs.

DIVISION Was zuerst zu beachten ist
IDENTIFICATION DIVISION Programmname, alte Kommentare, Herkunft
ENVIRONMENT DIVISION Dateien, externe Ressourcen, Annahmen zur Ein-/Ausgabe
DATA DIVISION Datensatzdefinitionen, Arbeitsbereiche, Parameter
PROCEDURE DIVISION Der eigentliche Verarbeitungsablauf

Besonders wichtig sind diese Bereiche.

  • FILE SECTION Enthält die Datensatzdefinitionen für Ein-/Ausgabedateien
  • WORKING-STORAGE SECTION Enthält alltägliche Variablen, Flags, Zähler, Arbeitspuffer
  • LOCAL-STORAGE SECTION Kann Bereiche enthalten, die bei jedem Aufruf neu initialisiert werden
  • LINKAGE SECTION Kann von außen übergebene Argumente enthalten – die Empfangsstelle eines Unterprogramms

Sieht man eine LINKAGE SECTION und PROCEDURE DIVISION USING ..., ist es sehr wahrscheinlich, dass das Programm nicht in sich abgeschlossen ist, sondern mit von außen übergebenen Daten arbeitet.

4. Vor dem Aussehen des festen Formats nicht erschrecken

In altem COBOL trägt die Spaltenposition selbst in einer Quellcodezeile Bedeutung. Wer das nicht weiß, wird nie herausfinden, „warum links dieser merkwürdige Rand ist“.1

Im festen Format gilt grob:

  • Spalten 1–6: laufende Nummer
  • Spalte 7: Indikator
  • Spalten 8–11: Area A
  • Spalten 12–72: Area B

Spalte 7 ist besonders wichtig.

  • * oder /: Kommentarzeile
  • -: Fortsetzungszeile
  • D: debugging line
  • *>: ein Kommentar, der auch mitten in der Zeile stehen kann

Den Zusammenhang zwischen Spalte und Inhalt sieht man mit einer Skala am besten. Die erste Zeile zeigt die Zehnerstelle, die zweite die Einerstelle der Skala.

         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
SSSSSSIAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB........
  • S = laufende Nummer (Spalten 1–6)
  • I = Indikator (Spalte 7)
  • A = Area A (Spalten 8–11)
  • B = Area B (Spalten 12–72)
  • . = ab Spalte 73. Wird je nach Compiler als Kennzeichnungsfeld genutzt, hat aber keinen Einfluss auf die Bedeutung des Programms

Auf echten Quellcode angewendet, sieht das so aus.

000100* Diese Zeile ist ein Kommentar, weil Spalte 7 ein * ist
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. SAMPLE01.
000400 DATA DIVISION.
000500 WORKING-STORAGE SECTION.
000600 01  WS-ORDER.
000700     05  WS-ORDER-ID    PIC 9(8).
000800     05  WS-LONG-NAME   PIC X(30) VALUE 'ABCDEFGHIJKLMNOPQRST
000900-    'UVWXYZ0123'.

Beim Lesen sind vier Punkte wichtig.

  • Spalten 1–6 sind die laufende Nummer. Im Beispiel oben ist das die 000100-artige Nummerierung, und sie hat keinen Einfluss auf das Programmverhalten. Manchmal ist sie auch leer.
  • Spalte 7 ist der Indikator. * bedeutet Kommentar, - bedeutet Fortsetzungszeile. Im Beispiel oben ist das die Zeile 000900, die die Fortsetzung des Zeichenkettenliterals aus der vorherigen Zeile ist.
  • Spalten 8–11 sind Area A. DIVISION, SECTION, Paragraphennamen, FD sowie die Stufennummern 01 und 77 beginnen hier. Im Beispiel oben beginnen IDENTIFICATION DIVISION. und 01 WS-ORDER. in Area A.
  • Spalten 12–72 sind Area B. Gewöhnliche Anweisungen und untergeordnete Stufen wie 05 werden hier geschrieben. Im Beispiel oben beginnt 05 WS-ORDER-ID in Area B.

Die Leerzeichen hier sind keine „Formatierung“ im modernen Sinn, sondern teilweise Syntax. Eine Tab-Umwandlung im Editor, ein Linksbündig-Ausrichten oder ein achtloses Copy-Paste macht die Datei ohne Weiteres kaputt. Bei alten Quellen sollten Sie zuerst infrage stellen, ob die Datei fixed format oder free format ist. Wendet man auf fixed-format-Quellcode moderne Auto-Formatierung an, brechen die Grenzen zwischen Area A und Area B, und die Kompilierung schlägt fehl.

5. Das Minimum der DATA DIVISION

5.1 Stufennummern

COBOL-Datendefinitionen bilden ihre Hierarchie nicht über Einrückung, sondern über Stufennummern.2

       01  WS-ORDER.
           05  WS-ORDER-ID    PIC 9(8).
           05  WS-AMOUNT      PIC S9(7)V99 COMP-3.
           05  WS-STATUS      PIC X.
               88  WS-OK      VALUE '0'.
               88  WS-ERROR   VALUE '9'.

       77  WS-COUNT           PIC 9(4).

Diese Mindestpunkte reichen völlig aus.

  • 01: die zusammenhängende oberste Datensatzebene, eine Gruppe
  • 0249: die darunterliegenden Ebenen
  • 77: ein eigenständiges Elementarfeld
  • 88: condition-name. Gibt dem Wert des vorangehenden Feldes einen Namen3
  • 66: für RENAMES. Kommt nicht häufig vor, existiert aber

Wichtig ist, 88 nicht als eigene bool-Variable zu betrachten. Es gibt kein eigenes Feld namens WS-OK; vielmehr lässt sich WS-STATUS unter dem Namen WS-OK lesen, wenn es den Wert '0' hat – so ungefähr fühlt sich das an.

Ein weiterer wichtiger Punkt: Nicht die Leerzeichen, sondern die Stufennummern bestimmen die Hierarchie. Die sichtbare Einrückung ist eine nützliche Orientierung, aber letztlich sollten Sie 01 / 05 / 10 / 88 vertrauen.2

5.2 PICTURE

PIC gibt die Form eines Feldes an. Am häufigsten sieht man das hier.

Notation Grobe Bedeutung
X Zeichen
9 Ziffer
S vorzeichenbehaftet
V Dezimalpunkt existiert nur logisch
X(10) 10 Zeichen
9(5) 5-stellige Zahl
S9(7)V99 vorzeichenbehaftet, 7 Ganzzahlstellen + 2 Nachkommastellen

Zum Beispiel:

  • PIC X(10) → 10 Zeichen
  • PIC 9(5)V99 → 5 Ganzzahlstellen + 2 Nachkommastellen
  • PIC S9(7)V99 → vorzeichenbehaftet, 7 Ganzzahlstellen + 2 Nachkommastellen

Besonders wichtig ist hier V. V besitzt kein tatsächliches .-Zeichen. PIC 9(5)V99 wird als „Zahl mit 2 Nachkommastellen“ behandelt, aber in den Daten steht kein Punktzeichen. Wer eine Datei oder einen Dump als „die sichtbare Zeichenkette“ interpretiert, stolpert daher fast immer.

5.3 USAGE / DISPLAY / COMP / COMP-3

Wenn PIC die Form ist, dann ist USAGE die Darstellung, in der das Feld gehalten wird. Schon dieses Minimum reicht, um sehr viel lesen zu können.45

Notation Grobe Bedeutung Worauf beim Lesen zu achten ist
DISPLAY externe Dezimalzahl, als Zeichen sichtbar Auf dem Mainframe wird dabei manchmal EBCDIC vorausgesetzt6
COMP / BINARY binär Die sichtbare Stellenzahl und die interne Darstellung sind zwei verschiedene Dinge
COMP-3 / PACKED-DECIMAL packed decimal Als Zeichen gelesen sieht es kaputt aus

Zum Beispiel:

       01  WS-AMOUNT-DISP   PIC S9(7)V99.
       01  WS-AMOUNT-BIN    PIC S9(7) COMP.
       01  WS-AMOUNT-PACK   PIC S9(7)V99 COMP-3.

Alle drei sind „Zahlen“, aber sie halten ihren Inhalt unterschiedlich.

In der Praxis zahlt sich am meisten der Reflex aus, den man beim Anblick von COMP-3 entwickelt.

  • Das ist packed decimal
  • Vermutlich ein Betrag, ein Steuerbetrag, eine Stückzahl oder ein Satz
  • Als Text betrachtet sieht es zwangsläufig kaputt aus
  • Wer es im CSV- oder UTF-8-Modus betrachtet, erlebt einen Unfall

Diese Grundhaltung sorgt dafür, dass man beim Anblick von Dumps und Binärdateien nicht unnötig in Panik gerät.

Wie COMP-3 tatsächlich als Bytefolge aussieht

Das hier lohnt sich, einmal von Hand nachzuvollziehen – danach ändert sich der Blick darauf dauerhaft.

Die Regeln von packed decimal sind nur zwei.45

  1. In ein Byte werden 2 Dezimalziffern gepackt
  2. Nur das letzte Byte ganz rechts enthält die niederwertigste Ziffer und das Vorzeichen

Das Vorzeichen wird als 4-Bit-Wert dargestellt, wobei C für positiv, D für negativ und F für vorzeichenlos steht.

Zur Überprüfung dient ein Beispiel aus dem IBM-Handbuch.4

Definition Wert Bytefolge
PIC S9(4) PACKED-DECIMAL +1234 01 23 4C
PIC S9(4) PACKED-DECIMAL -1234 01 23 4D
PIC 9(4) PACKED-DECIMAL 1234 01 23 4F

1234 hat 4 Stellen, weshalb durch Regel 2 vorn eine freie halbe Stelle entsteht. Das ist die führende 0.

Nach demselben Prinzip lässt sich das in diesem Artikel schon mehrfach erwähnte PIC S9(7)V99 COMP-3 nachvollziehen.

  • S9(7)V99 bedeutet 7 Ganzzahlstellen + 2 Nachkommastellen = 9 Stellen
  • V gibt nur die Position des Dezimalpunkts an, verbraucht also kein einziges Byte
  • Packt man 9 Stellen zu je 2 Stellen pro Byte, ergeben sich 4 Bytes, für die restliche Stelle und das Vorzeichen 1 weiteres Byte. Insgesamt 5 Bytes

Bei einem Wert von +12345.67 ergibt sich, auf 9 Stellen aufgefüllt, 001234567, also:

Wert         : +12345.67
9-stellig    : 0 0 1 2 3 4 5 6 7  und Vorzeichen
Bytefolge    : 00 12 34 56 7C
                              ^ Vorzeichen C = positiv

Bei einem negativen Wert -12345.67 ändert sich nur das letzte Byte zu 7D.

Bytefolge : 00 12 34 56 7D

Die eigentliche Frage lautet: Wie sieht das aus, wenn man es als Text öffnet? Bildet man 00 12 34 56 7C gewaltsam Byte für Byte auf ASCII ab, ergibt sich:

  • 00 ist NUL, 12 ist ein Steuerzeichen – beide lassen sich gar nicht als Zeichen darstellen
  • 34 ist 4, 56 ist V, 7C ist |

Auf dem Bildschirm erscheint also so etwas wie „einige nicht darstellbare Zeichen, gefolgt von 4V|“. Die Folge 12345.67 taucht nirgendwo auf.

Das ist das eigentliche Wesen von „sieht aus wie Mojibake, ist aber nicht kaputt“. Stoßen Sie beim Öffnen eines Dumps auf eine unverständliche Bytefolge, sollten Sie zuerst vermuten, ob die USAGE dieses Feldes vielleicht COMP-3 ist.

Als Ergänzung noch eine Faustregel, um von der Stellenzahl auf die Byteanzahl zu kommen. Die Byteanzahl ergibt sich aus „die Anzahl der 9en durch 2 teilen, den Nachkommaanteil abrunden und 1 addieren“.

Anzahl der 9en in der PICTURE Byteanzahl bei COMP-3
1 1
2–3 2
4–5 3
6–7 4
8–9 5
10–11 6
12–13 7

Dass bei derselben Byteanzahl jeweils zwei Stellenzahlen nebeneinanderstehen, liegt daran, dass nur bei einer geraden Anzahl von 9en vorn eine freie halbe Stelle entsteht. Deshalb wird S9(4) zu den 3 Bytes 01 23 4C, und auch S9(5) kommt mit denselben 3 Bytes aus.

Beim Abgleich des Layouts mit einer externen Datei verschiebt sich ohne diese Tabelle alles um jeweils ein Byte.

Noch eine kleine Ergänzung: DISPLAY bedeutet nicht zwangsläufig eine ASCII-Zeichenkette. Auf z/OS-Systemen wird EBCDIC vorausgesetzt, deshalb kann selbst wenn Ziffern wie Zeichen aussehen, der Bytewert von ASCII '0''9' abweichen.6

5.4 REDEFINES / OCCURS / COPY / FILLER

Diese vier sind beim Lesen die typischen Stolperstellen.

REDEFINES

REDEFINES ist ein Mechanismus, um denselben Bereich in einer anderen Form zu betrachten. Es ist keine Kopie.7

       01  REC-BUF.
           05  REC-TYPE      PIC X.
           05  REC-DATA      PIC X(99).

       01  HEADER-REC REDEFINES REC-BUF.
           05  HDR-TYPE      PIC X.
           05  HDR-DATE      PIC 9(8).
           05  FILLER        PIC X(91).

Das kommt dem Gefühl einer union aus der C-Familie nahe. Es taucht oft in der Form auf: „Ein und dieselben 100 Bytes werden je nach Datensatztyp unterschiedlich gelesen.“

OCCURS

OCCURS ist ein Array. In COBOL nennt man das gern table.

       05  WS-ITEM OCCURS 12 TIMES.
           10  WS-PRICE    PIC 9(5).

Taucht zusätzlich OCCURS DEPENDING ON auf, handelt es sich um eine variable Tabelle. In diesem Fall kann sich das auch auf die Position nachfolgender Felder auswirken, sodass man mit einer Denkweise für feste Längen leicht den Boden unter den Füßen verliert.8

COPY

COPY ist ein Include zur Compile-Zeit. Das heißt, die gerade geöffnete Quelle ist möglicherweise noch nicht die vollständige Form.9

       COPY CUSTOMER-REC.
       COPY ERROR-MAP.

Es ist ziemlich üblich, dass Datensatzdefinitionen, gemeinsame Flags, Host-Variablen für SQL und externe Schnittstellen in Copybooks stecken.

Ist der Code wegen vieler COPY schwer lesbar, ist es schneller zu prüfen, ob man an die expandierte Quelle oder ein Compiler-Listing herankommt. IBM Enterprise COBOL bietet dafür auch MDECK, eine Option, mit der die Eingabequelle nach der Bibliotheksverarbeitung ausgegeben wird.10

FILLER

FILLER ist ein namenloses Feld. Das heißt aber nicht „ungenutzt, also bedeutungslos“.

  • reservierter Bereich
  • eine Kompatibilitätslücke für eine alte Spezifikation
  • Auffüllung zum Angleichen der Datensatzlänge
  • Freiraum für REDEFINES

All das kommt ganz normal vor.

FILLER hat nur keinen Namen – als Byteanzahl existiert es trotzdem. Vergisst man das, verschiebt sich der Abgleich mit einer externen Datei Byte für Byte.

6. Das Minimum der PROCEDURE DIVISION

Ist die DATA DIVISION die Landkarte, ist die PROCEDURE DIVISION die Route, die man zurücklegt.

6.1 PERFORM

PERFORM ist die grundlegende Steuerübergabe in COBOL. Grob gesagt bedeutet es: eine Verarbeitung aufrufen und zurückkehren.11

Am häufigsten sieht man diese Form.

       PERFORM INIT-PROC
       PERFORM UNTIL EOF
           PERFORM READ-PROC
           IF NOT EOF
               PERFORM EDIT-PROC
               PERFORM WRITE-PROC
           END-IF
       END-PERFORM

Bei PERFORM gibt es im Wesentlichen zwei Varianten.

  • out-of-line PERFORM, das einen Paragraphen oder eine Section benennt
  • inline PERFORM ... END-PERFORM, das den Block direkt an Ort und Stelle schreibt

In älterem Code begegnet einem außerdem regelmäßig die Bereichsangabe PERFORM A-100 THRU A-199. Das ist praktisch, aber wird nachträglich ein Paragraph in der Mitte eingefügt, gerät er leicht ungewollt mit in den Bereich. Beim Lesen sollte man deshalb genau hinschauen, wo der Bereich endet.

6.2 IF / EVALUATE / Scope

Für bedingte Verzweigungen ist IF das Grundwerkzeug. EVALUATE als eine Art switch/case zu betrachten, trifft es größtenteils.

Worauf man achten muss, ist wie ein Scope endet.12

Code mit expliziten Terminatoren wie

  • END-IF
  • END-PERFORM
  • END-READ

lässt sich noch gut lesen.

Das Problem ist alter Code. In COBOL wirkt der . als impliziter scope terminator und schließt auf einen Schlag alle noch offenen Anweisungen.12

Das bedeutet, ein einziger Punkt entscheidet:

  • wie weit ein IF reicht
  • wie weit ein PERFORM reicht
  • wo das nächste sentence beginnt

Außerdem ist NEXT SENTENCE nicht dasselbe wie CONTINUE. NEXT SENTENCE springt hinter den nächsten Punkt, weshalb sich das Sprungziel je nach Position des folgenden . verschiebt.12

Beim Lesen von altem COBOL fährt man gut damit, nicht auf das Zeilenende, sondern auf den Punkt zu achten.

6.3 READ / WRITE / CALL

In Business-COBOL kommen vor allem diese vor.

  • READ
  • WRITE
  • REWRITE
  • START
  • CALL

Besonders READ ... AT END ... ist der Klassiker.

       READ IN-FILE
           AT END
               SET EOF TO TRUE
           NOT AT END
               PERFORM PROCESS-REC
       END-READ

Gibt es ein CALL 'SUBPGM' USING ..., springt die Ausführung in ein anderes Programm. In dem Fall lohnt sich ein Blick in die LINKAGE SECTION und die PROCEDURE DIVISION USING der Gegenseite – die Form der Übergabe wird dadurch weitgehend sichtbar.

7. Was außerhalb von COBOL liegt

COBOL ist häufig eine Welt, die sich nicht allein im Quellcode erschöpft.

  • Dateidefinitionen
  • die Ausführungsumgebung
  • DB-Verbindungen
  • der Transaktionskontext
  • Job-Steuerung

liegen außerhalb.

Zumindest diese Punkte machen das Lesen deutlich leichter.

Dateien und FILE STATUS

Die FILE-CONTROL der ENVIRONMENT DIVISION und die FILE SECTION / FD der DATA DIVISION liest man als Paar.13

       SELECT IN-FILE ASSIGN TO ...
           FILE STATUS IS WS-FS.

       FD  IN-FILE.
       01  IN-REC.
           05 ...

Gibt es ein FILE STATUS, erhält es nach jeder E/A den Ergebniscode. Beim Lesen von dateibezogenen Störungen oder der EOF-Erkennung kommt man ohne diesen Blick nicht weiter.14

EXEC SQL

Taucht das auf, handelt es sich um eingebettetes SQL.

       EXEC SQL
           SELECT ...
       END-EXEC.

In diesem Fall ist COBOL nur „das Gefäß für Host-Variablen“, während die eigentlichen Auswahlbedingungen und Aktualisierungsziele auf der SQL-Seite liegen. Der schnellste Weg ist daher, den Inhalt von EXEC SQL wie gewöhnliches SQL zu lesen.

EXEC CICS

Taucht das auf, befindet man sich im CICS-Transaktionskontext.15

       EXEC CICS
           RECEIVE MAP(...)
       END-EXEC.

Ab diesem Moment ist es kein reines Batch-Lesen mehr. Man muss es zusammen mit dem externen Kontext lesen: Bildschirmmasken, Transaktionen, Antwortcodes, COMMAREA und Ähnliches.

JCL und Ausführungsdefinitionen

Im Mainframe-Batch ist es nicht ungewöhnlich, dass welches Dataset tatsächlich zugewiesen wird und in welcher Reihenfolge Jobs ablaufen, außerhalb des COBOL-Quellcodes liegt. Sieht man nur den Quellcode und findet nicht heraus, „wo diese Datei liegt“, liegt das oft nicht am Code, sondern schlicht daran, dass der betrachtete Ausschnitt noch nicht groß genug ist.

Unterschiede zwischen Compilern

Dieser Artikel ist mit Blick auf IBM-Systeme geschrieben, aber in der Praxis begegnet man auch Micro Focus oder COBOL unter Linux/Windows. Das Grundgerüst ist gemeinsam, deshalb ändert sich die Lesart nicht, aber die Stellen, an denen „dieselbe Schreibweise, aber ein anderes Ergebnis“ auftritt, sind bekannt – wer sie vorher kennt, vermeidet Fehler.

Worauf zu achten ist z/OS-Systeme (IBM Enterprise COBOL) Open-Systeme (Micro Focus, Linux-/Windows-Version usw.)
Zeichenkodierung EBCDIC wird vorausgesetzt6 ASCII wird vorausgesetzt6
Referenzformat fixed format ist traditionell der Standard. free format ist ebenfalls wählbar1 verfügt über beides, fixed und free, und welches Standard ist, hängt von der Build-Konfiguration ab1
Suche nach Copybooks Bibliotheksangabe Suchpfadangabe über Compiler-Optionen
Dialekt Es gibt eine Option, um umzuschalten, an welchem Compiler man sich orientiert

Besonders wirksam ist die Zeichenkodierung. Selbst bei identischem PIC X(10) erhält man, wenn eine unter z/OS geschriebene Datei unverändert unter Windows gelesen wird, sogar bei Ziffern abweichende Bytewerte. Ein Großteil von „nach der Übertragung war alles kaputt“ liegt genau hieran – ein anderes Problem als bei COMP-3.

Auch bei der internen Darstellung von Zahlen gibt es eine Stelle, an der sich Unterschiede häufig zeigen. Bei IBM Enterprise COBOL werden BINARY / COMP-4 auf die in der PICTURE angegebene Stellenzahl abgeschnitten, während COMP-5 den Wert bis zur nativen Kapazität einer 2-, 4- oder 8-Byte-Binärzahl behält, und auch das Abschneiden erfolgt entsprechend der Binärgröße.16 Das heißt, PIC S9(4) COMP und PIC S9(4) COMP-5 sehen zwar gleich aus, unterscheiden sich aber in der Obergrenze der zulässigen Werte. Taucht COMP-5 in Code auf, der binäre Werte unverändert mit anderen Systemen austauscht, sollten Sie davon ausgehen, dass es absichtlich so geschrieben wurde.

Der kürzeste Ablauf, um dies mit der eigenen Umgebung abzugleichen, sieht so aus.

  1. Zuerst die Build-Definition öffnen (Makefile, JCL, Projekteinstellungen). So erfährt man, mit welchem Compiler und welchen Optionen kompiliert wird, noch bevor man den Quellcode selbst liest.
  2. Das Referenzformat (fixed / free) festlegen. Irrt man sich hier und formatiert dann im Editor, geht die Datei kaputt.
  3. Die Zeichenkodierung festlegen. Ob EBCDIC oder ASCII, ändert die Art, wie man einen Dump liest.
  4. Felder vom Typ COMP markieren. Genau hier zeigen sich fast alle Unterschiede zwischen Compilern.

8. Die sichere Lesereihenfolge

Muss man plötzlich COBOL lesen, ist folgende Reihenfolge sicher.

  1. Alle COPY durchgehen Copybooks öffnen, wenn möglich. Wenn nicht, nach Listing oder expandierter Quelle suchen
  2. Die Datensatzdefinitionen der 01-Ebene erfassen Die obersten Ebenen von FILE SECTION, WORKING-STORAGE und LINKAGE SECTION auflisten
  3. PIC und USAGE lesen Beträge, Datumsangaben, Stückzahlen, Codes, Flags identifizieren
  4. Nach READ / WRITE / REWRITE / CALL / EXEC SQL / EXEC CICS suchen Zuerst Ein-/Ausgabe und externe Grenzen erfassen
  5. Nur dem ersten Hauptpfad folgen Der PERFORM-Kette vom Anfang der PROCEDURE DIVISION an nachgehen
  6. 88 und Status-Felder betrachten Die Bedeutung von EOF, Erfolg/Fehler und Typcodes wird dadurch leichter lesbar
  7. REDEFINES / OCCURS DEPENDING ON / COMP-3 markieren Sie wirken sich später garantiert aus, deshalb vorab als Gefahrenstellen kennzeichnen
  8. Bei Dateien den FILE STATUS betrachten Das verringert Fehllesungen bei E/A-Fehlern erheblich

Bei dieser Reihenfolge muss man nicht sofort den gesamten Text im Detail lesen. Bei COBOL ist es deutlich entspannter, nicht von Anfang an 100 % verstehen zu wollen, sondern zuerst die drei Punkte Datensätze, externe Grenzen, Hauptpfad zu fassen und erst danach in die Details zu gehen.

8.1 Übung: Diese Datensatzdefinition lesen

Nur die Erklärung der Lesart festigt sich nicht von selbst, deshalb hier eine kleine Datensatzdefinition zum Üben. Finden Sie erst selbst die Antworten, bevor Sie unten nachsehen.

       01  CUST-REC.
           05  CUST-ID          PIC X(8).
           05  CUST-NAME        PIC X(20).
           05  CUST-KBN         PIC X.
               88  CUST-NORMAL  VALUE '0'.
               88  CUST-VIP     VALUE '1'.
           05  CUST-BALANCE     PIC S9(7)V99 COMP-3.
           05  CUST-HIST OCCURS 3 TIMES.
               10  HIST-DATE    PIC 9(8).
               10  HIST-AMOUNT  PIC S9(5)V99 COMP-3.
           05  FILLER           PIC X(4).

Aufgaben

  1. Wie viele Bytes umfasst CUST-REC insgesamt?
  2. Ab welchem Byte, vom Anfang des Datensatzes gezählt, beginnt CUST-BALANCE?
  3. Ab welchem Byte beginnt das HIST-AMOUNT des 2. Eintrags?
  4. Welcher condition-name wird wahr, wenn CUST-KBN den Wert '1' enthält?
  5. Öffnet man diese Datei in einem Texteditor, welches Feld sieht kaputt aus?
  6. Nennen Sie 2 Dinge, die sich allein aus dieser Definition nicht erschließen.

Antworten

  1. 74 Bytes. Die Aufschlüsselung sieht so aus.

    Feld Berechnung Byteanzahl
    CUST-ID X(8) 8
    CUST-NAME X(20) 20
    CUST-KBN X 1
    CUST-BALANCE COMP-3 mit 9 Stellen 5
    CUST-HIST (8 + 4) × 3-mal 36
    FILLER X(4) 4
    Summe   74

    Die 88-Ebene ist ein condition-name, verbraucht also keine Bytes. Genau das mitzuzählen ist ein häufiger Fehler. FILLER hat nur keinen Namen – die 4 Bytes existieren durchaus.

  2. Ab Byte 30. Davor liegen 8 + 20 + 1 = 29 Bytes, also beginnt es unmittelbar danach.

  3. Ab Byte 55. CUST-HIST beginnt bei Byte 35, ein Eintrag ist 12 Bytes lang, also liegt der 1. Eintrag bei Byte 35–46, der 2. bei Byte 47–58. Davon sind die ersten 8 Bytes HIST-DATE, also beginnt HIST-AMOUNT bei Byte 55.

  4. CUST-VIP. Es gibt kein eigenes Feld namens CUST-VIP; man kann CUST-KBN nur unter diesem Namen lesen, wenn es '1' enthält.

  5. CUST-BALANCE und HIST-AMOUNT. Beide sind COMP-3, deshalb ergeben sie als Zeichen gelesen eine bedeutungslose Folge. HIST-DATE ist PIC 9(8) mit DISPLAY, also in einer ASCII-Umgebung als Ziffern lesbar, etwa als 20260317. In einer EBCDIC-Umgebung weichen die Bytewerte jedoch von ASCII ab, auch wenn es wie Ziffern aussieht.

  6. Zum Beispiel Folgendes.

    • Ob diese Definition selbst über COPY eingebunden ist. Ohne Blick in das Copybook lässt sich nicht sagen, ob es sich um die tatsächlich verwendete Version handelt.
    • Ob die Zeichenkodierung der Datei EBCDIC oder ASCII ist. Das ändert, wie PIC X und PIC 9 DISPLAY aussehen.
    • Ob im Betrieb auch andere Werte als '0' und '1' in CUST-KBN vorkommen. Es sind nur zwei condition-names definiert, das garantiert aber nicht, dass keine anderen Werte auftreten.
    • Die Eigenschaften der Datei selbst (Satzlänge, ob variabel lang, FILE STATUS). Das lässt sich nur aus ENVIRONMENT DIVISION und FD erschließen.

Wer bei Aufgabe 1 oder 3 danebenlag, sollte zur Tabelle mit Stellenzahl und Byteanzahl in 5.3 zurückkehren. Lesefehler in COBOL beginnen meistens genau hier.

9. Häufige Stolperstellen

Zum Schluss eine Zusammenfassung der Stellen, an denen Einsteiger mit hoher Wahrscheinlichkeit hängen bleiben.

REDEFINES für „eine andere Variable“ halten

Falsch. Es ist derselbe Bereich, nur in anderer Form gelesen. Ändert man die eine Seite, ändert sich auch die Ansicht der anderen.7

88 für ein „eigenständiges bool“ halten

Falsch. Es ist nur ein Name, der dem Wert des vorangehenden Feldes gegeben wird. SET WS-OK TO TRUE trägt im Hintergrund den entsprechenden Wert in das zugrunde liegende Feld ein.3

COPY ignorieren und nur den Haupttext lesen

Die geöffnete Datei ist noch nicht einmal die halbe Wahrheit. Dass Felddefinitionen, gemeinsame Flags und Host-Variablen komplett außerhalb liegen, ist normal.9

MOVE für eine einfache Zuweisung halten

MOVE ist kein bloßes memcpy. Je nach Typ des Zielfeldes können Umwandlung, Stellenausrichtung, Nullauffüllung, Abschneiden sowie editing/de-editing dazukommen.17

Die Wirkung des . unterschätzen

Der . in COBOL ist schwerer, als man denkt. In altem Code ohne explizite Terminatoren führt ein Fehleinschätzen von wie weit dieser Punkt schließt zu einer falschen Lesart des Kontrollflusses.12

packed decimal oder EBCDIC für „Mojibake“ halten

Nicht zwangsläufig kaputt. Sehr oft war es von Anfang an gar keine Zeichenkette oder einfach nicht ASCII.46

Annehmen, dass hinter OCCURS DEPENDING ON feste Positionen folgen

Bei einer variablen Tabelle können sich die Positionen der nachfolgenden Felder je nach Wert verschieben. Wer mit einer Denkweise für feste Längen liest, verschiebt alle Offset-Berechnungen.8

10. Schnellreferenz: Was zuerst zu bedenken ist

Gefundenes Wort Was zuerst zu bedenken ist
01 Oberste Ebene eines Datensatzes oder einer Gruppe. Von hier aus das Gesamtbild erfassen
88 Name für eine Bedeutung eines Flags oder Statuscodes. Der Schlüssel zum Lesen von Verzweigungen
PIC X(...) Zeichenfeld
PIC 9(...) / S9(...)V... numerisches Feld. Stellenzahl und Dezimalposition prüfen
COMP binär
COMP-3 packed decimal. Wahrscheinlich ein Betrag oder eine Stückzahl
REDEFINES derselbe Bereich, anders interpretiert
OCCURS Array/table
OCCURS DEPENDING ON variable Länge. Auch auf nachfolgende Positionen achten
FILLER kein Name, aber vorhandene Länge
COPY ohne Copybook sieht man nicht die vollständige Form
PERFORM das Grundgerüst des Hauptpfads
READ / WRITE / REWRITE Datei-E/A
EXEC SQL DB-Verarbeitung
EXEC CICS Transaktionsverarbeitung
FILE STATUS E/A-Ergebniscode

11. Zusammenfassung

COBOL ist nicht deshalb schwierig, weil es alt ist. Es ist nur so, dass Datendefinitionen, externe Dateien und der Ausführungskontext eng miteinander verflochten sind, wodurch der erste Zugang schwer zu erkennen ist.

Das Minimum zum Lesen noch einmal zusammengefasst:

  • Mit den DIVISIONs die Landkarte erfassen
  • Zuerst die DATA DIVISION lesen
  • Mit PIC und USAGE die Form der Felder lesen
  • COMP-3, REDEFINES, OCCURS, 88, COPY markieren
  • PERFORM, READ, WRITE, CALL verfolgen
  • Mit FILE STATUS, EXEC SQL, EXEC CICS die externen Grenzen erfassen
  • Die Wirkung des . nicht auf die leichte Schulter nehmen

Ist das einmal sichtbar, wird COBOL von „geheimnisvoller uralter Magie“ zu „einer Sprache für Datensatzverarbeitung“. Legacy-Technologie ist nicht deshalb beängstigend, weil der Name alt klingt – es ist nur so, dass ein falsch gewählter erster Maßstab die Sache plötzlich unverständlich macht. Stimmt der Maßstab der Landkarte, liest sie sich erstaunlich normal.

12. Quellen

Die wichtigsten im Text verwendeten Referenzen. Die hochgestellten Nummern im Text verlinken direkt auf diese Liste, und über den Pfeil am Ende jedes Eintrags gelangen Sie zur ursprünglichen Stelle zurück.

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Wo sollte man beim Lesen von COBOL-Quellcode anfangen?
Wenn Sie nur die PROCEDURE DIVISION lesen, verstehen Sie höchstens die Hälfte. COBOL ist, bevor es eine Sprache der Logik ist, sehr stark eine Sprache der Datensatzdefinitionen – deshalb sehen Sie sich zuerst die DATA DIVISION an. Der sichere Leseablauf lautet: alle COPY-Anweisungen durchgehen und die Copybooks prüfen, die Datensatzdefinitionen der 01-Ebene auflisten, die Form der Felder anhand von PIC und USAGE lesen, mit READ, WRITE, CALL, EXEC SQL und EXEC CICS suchen, um Ein-/Ausgabe und externe Grenzen zu erfassen, und erst danach der PERFORM-Kette am Anfang der PROCEDURE DIVISION folgen, um nur den Hauptpfad zu verfolgen.
Was bedeutet PIC S9(7)V99 COMP-3?
PIC gibt die Form eines Feldes an, USAGE gibt an, in welcher Darstellung es gespeichert wird. S9(7)V99 ist eine vorzeichenbehaftete Zahl mit 7 Ganzzahlstellen und 2 Nachkommastellen, aber V ist nur ein logisches Dezimaltrennzeichen – in den Daten steht kein tatsächliches Punktzeichen. COMP-3 ist packed decimal und taucht häufig bei Beträgen, Steuerbeträgen, Stückzahlen und Ratenfeldern auf. Als Text betrachtet sieht es zwangsläufig kaputt aus, deshalb geht es schief, wenn man einen Dump im CSV- oder UTF-8-Modus liest.
Wie sollte man 88-Ebenen und REDEFINES in COBOL verstehen?
88 ist keine eigenständige bool-Variable, sondern ein condition-name, der einem Wert des vorangehenden Feldes einen Namen gibt. SET WS-OK TO TRUE trägt im Hintergrund den entsprechenden Wert in das zugrunde liegende Feld ein. REDEFINES ist ein Mechanismus, um denselben Speicherbereich in einer anderen Form zu betrachten – keine Kopie, sondern eher vergleichbar mit einer union aus der C-Familie. Ändert man die eine Seite, ändert sich auch die Ansicht der anderen, weshalb dieses Muster häufig verwendet wird, um innerhalb eines Bereichs verschiedene Datensatztypen zu unterscheiden.
Was tun, wenn zu viele COPY-Anweisungen den Überblick verhindern?
COPY ist ein Include zur Compile-Zeit, daher ist die gerade geöffnete Quelle möglicherweise noch nicht die vollständige Form. Es ist durchaus üblich, dass Datensatzdefinitionen, gemeinsame Flags, Host-Variablen für SQL und externe Schnittstellen in Copybooks stecken. Ist der Code so schwer lesbar, prüfen Sie am schnellsten, ob Sie an die expandierte Quelle oder ein Compiler-Listing herankommen. IBM Enterprise COBOL bietet dafür auch die Option MDECK, mit der die Eingabequelle nach der Bibliotheksverarbeitung ausgegeben wird.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog