88
107 Zum Beispiel Kapitel 2 - Programmbeispiele

Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

  • Upload
    docong

  • View
    214

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 107

ZumBeispiel

Kapitel 2 - Programmbeispiele

Page 2: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E108

Page 3: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 109

Eines der wichtigsten Programme und auch eines der kürzesten. Wir aktivieren denAblaufverfolger und lassen einen Haltepunkt setzen. Am Haltepunkt können wir be-liebige Eingaben durchführen, ohne im Programmablauf weiter fortzuschreiten. Aufdiese Weise lassen sich alle Befehle und Funktionen spielerisch erfahren. Ist bei-spielsweise nicht klar, wie sich eine Funktion äußern wird, kann sie am Haltepunktmit einen SAY-Befehl getippt werden:

SAY CENTER(TRANSLATE(REVERS("albalb")),79)

/* REXX * TRACE1 *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Funktionstest im TRACE-Modus *//* */trace "?R"nopexit

Etwas ähnliches könnten wir uns in der Form basteln, daß in einer ProgrammschleifeTerminaleingaben möglich sind, und diese Eingaben vom Interpreter als Befehleumgesetzt werden:

/* REXX * TRACE2 *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Uebernimmt Terminaleingaben und *//* interpretiert sie als Instruktionen */cleardo forever

say "Befehlstest (ENTER = Ende)"parse external cmdif length(cmd) = 0 then leaveinterpret cmd

endsay "Bye, bye..."exit

TRACE

Testh

ilfen

Page 4: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E110

In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal einkostenloses Bier getrunken, weil kaum jemand glaubte, daß ein Blatt Papier beliebi-ger Größe und Stärke nicht öfter als siebenmal deckungsgleich zusammengefaltetwerden kann, selbst wenn man dazu einen Bogen Seidenpapier in der Größe einesFußballfeldes nehmen würde. Der Biegewiderstand wird spätestens beim achten Malso groß, das ein kantengerades Aufeinanderlegen nicht möglich ist. Programmtech-nisch kann das Zusammenfalten natürlich beliebig oft durchgeführt werden. Wir wol-len über ein Programm vom Anwender bestimmen lassen, wie oft ein Blatt Papierzusammengefaltet werden soll und unterstellen eine Papierstärke von 0,1 mm. Nachdem ersten Falten ergeben sich zwei Lagen, nach dem zweiten Mal 4, dann 8, 16 undso weiter. 16 Lagen zu 0,1 mm ergeben eine Stapelhöhe von 1,6 mm. Das Programmsoll nun abhängig von der Anzahl der Faltungen die Stapelhöhe berechnen. Abhän-gig vom Ergebnis soll die Höhe im Millimetern, Zentimetern, Metern oder Kilometernausgegeben werden. Achtung: Die Wechsel vollziehen sich deutlich schneller, alsman glauben möchte: bei acht Faltungen wechseln wir zu Zentimetern, bei 14 zuMetern und bereits bei 23 zu Kilometern.

/* REXX * FALTEN *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Faltet ein Stueck Papier jeweils in *//* der Mitte und berechnet bei einer *//* Papierstaerke von 0,1 mm die Stapel- *//* hoehe. Die Ausgabe erfolgt abhaengig *//* vom Ergebnis in mm, cm, m oder km. */clearsay "Anzahl Faltungen"pull anzWir unterstellen ganz einfach mal, daß es dem Bediener auf Anhieb gelingt, hier eine integereZahl einzugeben.

h=2 ** anz * 0.1

H enthält die Stapelhöhe in Millimetern.

selectwhen h < 10 then me = "mm"when h < 1000 then do

me = "cm"h = h/10

endwhen h < 1E6 then do

me = "m"

FALTEN

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 5: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 111

h = h/1000endotherwise do

me = "km"h = h/1E6Nachdem mm, cm und m überprüft wurden, kann es sich im OTHERWISE-Zweignur noch um Kilometer handeln.

endendsay "Stapelhoehe nach" anz "Faltungen:" h meexit

Wir bauen uns ein Knobelspiel. Wer kennt es nicht aus seiner Kindheit. Stein - Sche-re - Papier - Brunnen. Mit der Hand wird auf DREI eines der Symbole gezeigt. DieAuflösung des Spieles geschieht nach folgender Regel:

STEIN Gewinnt gegen SCHEREVerliert gegen PAPIER und BRUNNEN

SCHERE Gewinnt gegen PAPIERVerliert gegen STEIN und BRUNNEN

PAPIER Gewinnt gegen BRUNNEN und STEINVerliert gegen SCHERE

BRUNNEN Gewinnt gegen STEIN und SCHEREVerliert gegen PAPIER

Nachdem wir eine MVS-Anlage nur unter großer Mühe dazu bewegen können, ihreHand zum Brunnen zu formen, versuchen wir das Ganze über entsprechende Zah-len. Der Spieler tippt seine Wahl am Terminal ein, der Rechner wählt über einenZufallsgenerator:

/* REXX * KNOBELN ************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Knobelspiel zwischen Computer und *//* Spieler. Entscheidung ueber Abfragen */obj.1="Stein"obj.2="Schere"obj.3="Papier"obj.4="Brunnen"Die Objekttabelle wird gefüllt.

KNOBELN

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 6: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E112

erg.1="unentschieden"erg.2="ich habe gewonnen"erg.3="Du hast gewonnen"Die Ergebnistabelle wird vorbereitet.

do foreverclearDer Bildschirm wird gelöscht.

say center("< Heinzi's kleine Knobelei >",79,'-')say;say;say;saysay center("1 = Stein ",79)say center("2 = Schere ",79)say center("3 = Papier ",79)say center("4 = Brunnen",79)saysay center("E = E N D E",79)say;say;saysay center("Triff Deine Auswahl",79)say;sayEin Auswahlmenü wird ausgegeben

do until s >= 1 & s <= 4 ! s = "E"say "Eingabe?"parse external s

endDie Schleife stellt sicher, daß der Wert in der Variablen S nur eine Zahl zwischen 1 und4 oder ein E sein kann.

if s = "E" then leaveWird vom Anwender ein E eingetippt, wird die äußere Programmschleife verlassen, dasProgramm wird beendet.

s=trunc(s)Generell könnte die Eingabe des Spielers eine Dezimalzahl zwischen 0,? und 3,9999sein. Für den Fall wird die Zahl entsprechend beschnitten. Nur der integere Teil derEingabe wird übernommen.

r=random(1,4)

Der Rechner wählt über die Zufallsroutine eine Zahl zwischen 1 und 4 und hinterlegt siein der Variablen R.

KNOBELN

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 7: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 113

say;say;say "Du hast" obj.s", ich habe" obj.rÜber die Zeiger S und R werden die entsprechenden Spielobjekte des Spielers und desRechners aus der Objekttabelle gemeldet.

selectwhen r=s then e=1Unentschieden

when r=1 thenif s=2 then e=2else e=3

when r=2 thenif s=3 then e=2else e=3

when r=3 thenif s=2 then e=3else e=2

otherwiseif s=3 then e=3else e=2

Wenn Rechner und Spieler nicht die gleiche Zahl, sprich das gleiche Symbol ge-wählt haben, muß die Spielsituation geklärt werden. Wir beginnen, die Rechner-wahl zu ergründen (1 bis 3, anderenfalls kann er nur noch die 4 gewählt haben). Zujeder Wahl gibt es immer eine eindeutige Situation für gewonnen oder verloren.Wenn wir dies bei der Abfragetechnik berücksichtigen, wird die Abfrage sehr simpelund kommt ganz ohne Bool'sche Verknüpfungen aus.

endsay erg.e "- weiter mit ENTER"pullÜber den Inhalt von E wird das entsprechende Ergebnis der Ergebnistabelle ausgege-ben. Der PULL-Befehl bringt das Programm zum Stillstand und wartet auf Datenfreiga-be, da der Stack leer ist. Diese kleine Bremse brauchen wir, weil nach Rückkehr an denSchleifenkopf CLEAR ausgeführt wird (der Bildschirm wird gelöscht, wir könnten unserSpielergebnis nicht lesen).

endsay "Bye, bye..."exit

Manchmal muß man eine Problemstellung auch mal von einer anderen Seite be-trachten. Eine Entscheidung im Programm, und sei sie noch so einfach, braucht Zeit.

KNOBELN

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 8: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E114

Wäre es möglich, das Knobelspiel ohne Entscheidungen zu kodieren, wäre dies si-cher von Vorteil für die Ablaufgeschwindigkeit und die Systembelastung. Wir erzeu-gen im Programmvorlauf eine Entscheidungstabelle, in der alle möglichen Spielsitua-tionen und Ergebnisse enthalten sind. Das braucht natürlich auch Zeit, aber nur ein-mal. Wenn wir davon ausgehen, daß das Spiel mehrere Male nach Spielstart gespieltwerden soll, müssen wir die Tabelle nur einmal erzeugen und können in jedem Durch-gang das Ergebnis unmittelbar, und ohne eine einzige Prüfung aus der Tabelle ent-nehmen.

/* REXX * KNOBELN ************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Knobelspiel zwischen Computer und *//* Spieler. Entscheidung über Tabelle. */obj.1="Stein"obj.2="Schere"obj.3="Papier"obj.4="Brunnen"obj.5="ENDE"obj.6=""obj.7="Triff Deine Auswahl (E für Ende)"erg.1.1="unentschieden"erg.2.2="unentschieden"erg.3.3="unentschieden"erg.4.4="unentschieden"erg.1.2="ich habe gewonnen"erg.1.3="Du hast gewonnen"erg.1.4="Du hast gewonnen"erg.2.1="Du hast gewonnen"erg.2.3="ich habe gewonnen"erg.2.4="Du hast gewonnen"erg.3.1="ich habe gewonnen"erg.3.2="Du hast gewonnen"erg.3.4="ich habe gewonnen"erg.4.1="ich habe gewonnen"erg.4.2="ich habe gewonnen"erg.4.3="Du hast gewonnen"Die Objekttabelle wird um ein Glied erweitert. So können wir die Tabelle zur Menüausgabebenutzen. Die Ergebnistabelle wird aufgebaut. Alle denkbaren Situationen sind beschrieben.Dabei gehen wir davon aus, daß der erste numerische Wert im Namen der Variablen derRechnerwahl, der zweite Wert der Spielerwahl entspricht.

KNOBELN

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 9: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 115

do foreverclearsay center("< Heinzi's kleine Knobelei >",79,'-')do cnt = 0 to 7

say center(obj.cnt,79)endHier wird über die OBJ-Tabelle das Menü erzeugt.

do until s >= 1 & s <= 4parse external sif translate(s) = "E" then exit

endr=random(1,4)say;saysay "Du hast" obj.s", ich habe" obj.r"," erg.r.sDie OBJ-Tabelle liefert die gewählten Spielgegenstände, über die ERGebnistabelle wirddas Spielergebnis direkt abgegriffen.

dmy=time("R")do until time("E") > 2

nopendAuf das Setzen eines Haltepunktes mit Tastatureingabe wird hier verzichtet. Wir errei-chen über TIME(R) das Rücksetzen der Stoppuhr-Funktion. In Schleife wird permanentdie seit dem letzten TIME-RESET aufgelaufene (Elapsed) Zeit überprüft. Sind mehr alszwei Sekunden verstrichen, wird die Schleife verlassen. Das Programm trifft auf CLEAR,löscht den Bildschirm und gibt die Ausgabemaske erneut aus. Der Bediener hat alsozwei Sekunden Zeit, das Spielergebnis zu lesen.

Wir wollen hier auch mal zeigen, was machbar ist. Dieses Verfahren sollte möglichstnicht übertrieben werden, da es sehr CPU-intensiv ist. Steht eines Tages ein Systempro-grammierer mit der Mistgabel in der Hand in Ihrem Büro, haben Sie unter Umständen andieser Stelle etwas falsch gemacht.

endexit

KNOBELN

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 10: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E116

Bevor wir uns mit dem nächsten Thema auseinandersetzen, einige erklärende Wortezur Prüfziffernberechnung nach dem MODULO-11-Verfahren: Was ist das? Ein Re-chenverfahren, das Tippfehler und Zahlendreher in numerischen Werten erkennbarmachen soll. Man trifft es in der Praxis sehr häufig an, weiß es nur nicht. So nutzen esbeispielsweise viele Banken, um ihre Kontonummern "sicherer" zu machen. WennSie sich den Zahlenbandwurm der auch mit diesem Buch belegten ISBN( Internatio-nal Serial Book Number) ansehen, ist die letzte Zahl daraus auch eine Prüfziffer nachdem MODULO-11-Verfahren. Könnte die ISBN in sich nicht auf Plausibilität geprüftwerden, würde wohl kaum eine Buchbestellung über diese Nummer zu einem ver-nünftigen Ergebnis führen. Auch die Banken haben das Problem, daß ein simplerTippfeher in einer Kontonummer möglichst nicht zu einer Falschbuchung führen soll-te. Im Zweifelsfall ist es besser, den falschen Wert nicht zu verarbeiten, als eineFalschverarbeitung mit entsprechenden Stornos und Korrekturen hinnehmen zu müs-sen.

Die Rechenformel am Beispiel der vierstelligen Zahl: 4812

Die hinterste Stelle wird mit 2 multipliziert (4), die vorletzte Stelle mit 3 (3), die dritt-letzte Stelle mit 4 (32), die viertletzte Stelle, in unserem Fall die erste, mit 5 (20).Anders ausgedrückt: Bei einer beliebig langen Zahl wird die hinterste Stelle mit 2multipliziert. Für jede Stelle, die es in der Zahl nach links geht, erhöht sich der Multi-plikator um eins. Die ganzen Multiplikationsergebnisse werden addiert. In unseremFall ergäbe dies eine Summe von (4+3+32+20) 59. Diese Summe wird integer durch11 dividiert. Ergebnis wäre 5 Rest 4. Wichtig ist nun, daß wir mit dem Rest weiterar-beiten müssen. Dieser Rest wird von 11 abgezogen, das Ergebnis ist die Prüfziffer.Theoretisch könnte diese Prüfziffer nun aber auch 10 oder 11 sein (bei Rest 0 oderRest 1). Für den praktischen Einsatz darf die Prüfziffer aber nur ein Byte groß sein.Hier sieht eine Ausnahmeregel vor: Ist der ermittelte Wert größer als 9, wird als Prüf-ziffer 0 angenommen.

Stellen wir uns nun folgende Situation vor: Wir arbeiten als Servicebetrieb für unter-schiedliche Unternehmen. Jedes dieser Unternehmen liefert täglich Daten zur Verar-beitung unter einer von uns vergebenen Firmennummer an. Die von uns vergebenenFirmennummern sind vierstellig und wir berechnen für diese Firmennummer einePrüfziffer nach MODULO 11. Die Prüfziffer ergänzt die von uns vergebene Firmen-nummer, den gebildeten fünfstelligen Wert geben wir unserem Kunden als Kunden-nummer bekannt.

Kundennummer=4812, Prüfziffer wird:

( (2 * 2) + (1 * 3) + (8 * 4) + (4 * 5) ) : 11 = 5 REST 4

11 - rest = 7

PZ

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 11: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 117

Die ermittelte Prüfziffer ist 7, die Kundennummer lautet 48127. Diese Nummer teilenwir unserem Kunden mit. Angenommen er liefert seine Buchungsdaten nun unter derNummer 41827 (Zahlendreher bei der Erfassung), würden wir unter Umständen ohnePrüfverfahren die gelieferten Daten unter einem anderen Unternehmen verbuchen.Stornos, Rückbuchungen und dergleichen sind mit entsprechendem Zeitaufwand un-umgänglich. Besser wäre, wir würden vor Verarbeitung erkennen, daß die Firmen-nummer nicht stimmen kann. Wir gehen also her und führen eine Rückrechnung derFirmennummer durch. Da wir wissen, daß nur die ersten vier Stellen die wirklicheFirmennummer ergeben, prüfen wir:

( (2 * 2) + (8 * 3) + (1 * 4) + (4 * 5)) : 11 = 4 REST 8

11 - REST = 3

3 wäre die rückgerechnete Prüfziffer. Die Kundennummer müßte dementsprechendlauten: 41823, geliefert wurde aber unter 41827. Die gelieferte Kundennummer istfalsch. Wir können zwar nicht erkennen, wie die Kundennummer richtigerweise hei-ßen müßte, werden aber die Daten, die unter falscher Kundennummer geliefert wur-den, nicht verarbeiten und die Situation klären. Damit haben wir eine Fehlbuchungund ein entsprechend aufwendiges Korrekturverfahren umgangen.

Sehen wir uns jetzt ein Programm an, das uns für eine beliebig lange Zahl die Prüfzif-fer nach MODULO-11-Verfahren berechnet:

/* REXX * PZ *****************************************//* Erstellt: 05.94 Wittemann *//* Zweck: berechnet die Pruefziffer einer *//* beliebigen Zahl nach MODULO 11 */cleardo forever

Die äußere Schleife macht das Programm insgesamt wiederholbar. Programmende wirderreicht, wenn der Bediener auf die Aufforderung nach Zahleneingabe nur E drückt.

do until datatype(zahl) = "NUM" ! length(zahl) = 0say 'Zahl eingeben (ENTER = Ende)'pull zahlDie Eingabeschleife wird verlassen, wenn der Bediener eine Zahl eingegeben odernur auf E gedrückt hat.

endif length(zahl) = 0 then leaveWar das Ende der Erfassungsschleife durch E erreicht worden, wird die äußere Pro-grammschleife verlassen, das Programm wird beendet.

PZ

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 12: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E118

zahl = abs(trunc(zahl))summe=0pos=length(zahl)multi=2Für den Fall, daß der Bediener eine Dezimalzahl eingegeben hat, wird sie entsprechendbeschnitten. Übernommen wird nur der integere Teil der Zahl. War der Wert negativ, wirdnur der absolute Wert der Zahl übernommen (ist immer positiv). Ein Summenfeld wird mit0 vorbelegt. Der Positionszeiger POS wird mit Grundwert versorgt (die hinterste Stellevon ZAHL). Ein Multiplikator wird vorbelegt (die erste Multiplikation ist mit 2 durchzufüh-ren).

do length(zahl)stelle_x = substr(zahl,pos,1)Ein einzelnes Byte aus Zahl wird isoliert.

mult_erg = stelle_x * mult

Die ermittelte Stelle wird mit dem Multiplikator malgenommen.

summe = summe + mult_ergDie Multiplikationsergebnisse werden zusammengezählt (beim ersten Mal wird zumersten Multiplikationsergebnis die in SUMME vorbelegte 0 addiert, bei jedem weite-ren Durchlauf wird zum jeweiligen Multiplikationsergebnis der in SUMME enthalte-ne Wert hinzu addiert).

mult = mult + 1Der Multiplikator wird um 1 erhöht.

pos = pos - 1Der Positionszeiger wird um 1 vermindert (beim zweiten Durchlauf wird an das vor-letzte Byte gegriffen, usw.).

endrest = summe // 11

REST wird mit dem Rest der Division 11:SUMME gefüllt.

pz = 11 - restDie Variable PZ wird mit dem Ergebnis aus 11-REST gefüllt und sollte grundsätzlich diePrüfziffer enthalten.

if pz > 9 then pz = 0Der Sonderfall wird geprüft. Ist das ermittelte Ergebnis 10 oder 11, wird als Prüfziffer 0angenommen.

PZ

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 13: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 119

say "Ergebnis mit Pruefziffer:" zahl"-"pzErgebnisausgabe.

endsay "Bye, bye..."exit

Vereinfachen wir den Ablauf. Einige Befehle können zusammengezogen werden. Eineandere Schleifenvariante kommt uns zur Problemlösung weiter entgegen. Gehen wirdavon aus, daß der Bediener das Programm aufruft und die zu berechnende Zahl beiProgrammaufruf mitliefern soll. Das Programm spricht für sich selbst:

/* REXX * PZ *****************************************//* Erstellt: 05.94 Wittemann *//* Zweck: berechnet die Pruefziffer einer *//* beliebigen Zahl nach MODULO 11 */arg zahl .if datatype(zahl) = "CHAR" then do

parse source . . pgm_name .say "Eingabefehler. Aufruf:" pgm_name "zahl"exit

endzahl = abs(trunc(zahl))sum = 0do pos = 1 to length(zahl)

sum=substr(zahl,pos,1)*(length(zahl)+2-pos)+sumendpz=11-a//11if pz > 9 then pz=0say zahl'-'pzEXIT

PZ

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 14: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E120

Eine schöne Übung für Variablenhandling ist ein Lottoprogramm. Viele haben es schonversucht, die meisten davon gehen immer noch mehr oder weniger mürrisch ihrerArbeit nach. Sollten Sie mit dem Programm einen Haupttreffer landen, vergessen Siemich nicht...

Sehen wir uns die Anforderungen an unser Programm an. Wir ziehen flexibel aus 45oder 49 Zahlen. Die Anzahl zu ziehender Zahlen sollte variabel zwischen 6 (müssenes mindestens sein) und 12 (der größtmögliche Tip im Vollsystem) sein. Die Zie-hungszahlen werden über den Zufallsgenerator ermittelt. Ab der zweiten ermitteltenZahl müssen Prüfungen mit allen davor gezogenen Zahlen durchgeführt werden. Wirderkannt, daß eine Ziehung die gleiche Zahl wie eine vorausgegangene Ziehung gelie-fert hat, muß die Ziehung wiederholt werden.

Der Aufruf des Programmes könnte lauten:

%LOTTO 9 aus 45

In diesem Fall sollten 9 Zahlen zwischen 1 und 45 ermittelt werden. Werden keineoder falsche Programmparameter übergeben, wird für die Anzahl zu ziehender Zah-len 6, für den maximalen Wert (zwischen 1 und ?) 49 angenommen.

/* REXX * LOTTO1 *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Zieht 6 bis 12 Zahlen aus 45 oder 49 *//* Doppelte Ziehungen werden durch *//* Pruefroutine verhindert */arg anz temp aus .if temp='TRACE' then trace '?r'if anz < 7 ! anz > 12 then anz = 6if aus >< 45 & aus >< 49 then aus = 49var.=0x=1ANZ und AUS sind die beiden Übergabevariablen, welche die Werte 9 und 45 auf das Bei-spiel bezogen aufnehmen sollen. TEMP ist zum einen nur Platzhalter für eine schönere Schreib-weise beim Programmstart. Zum anderen kann über den zweiten Positionsparameter derAblaufverfolger eingeschaltet werden.

if temp='TRACE' then trace '?r'if anz < 7 ! anz > 12 then anz = 6if aus >< 45 & aus >< 49 then aus = 49var.=0x=1

LOTTO

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 15: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 121

Der Stamm VAR. wird mit 0 vorbelegt. X wird mit einer 0 initialisiert. Über VAR.X werden imProgramm Variable von VAR.1 bis VAR.? (entsprechend der Anzahl zu ziehender Zahlen)gebildet.

do until x > anzZiehungsende ist erreicht, wenn soviele Variable VAR.? gebildet wurden, wie in ANZhinterlegt ist.

var.x=random(1,aus)

Z wird eine Zufallszahl zwischen 1 und dem Maximalwert (45 oder 49).

fehl=0if x > 1 then do y=1 to x /* Prüfen doppelte */

if var.y = var.x then dofehl=1leave

endendSofern Z nicht im ersten Durchlauf gezogen wird (X wäre dann größer 0), muß geprüftwerden, ob die gezogene Zahl Z mit einer der bereits gezogenen Zahlen VAR:? iden-tisch ist. Um dies zu erkennen, wird ein Schalter FEHL auf 0 (NEIN) gesetzt. Anschlie-ßend werden alle bisher gebildeten Variablen VAR.? mit Z verglichen. Gibt es bei einemVergleich Übereinstimmung, wurde Fehler (doppelte Zahl) erkannt. In diesem Fall wirdder Schalter FEHL auf 1 (JA) gesetzt, die Prüfschleife kann verlassen werden.

if fehl=0 then x=x+1Ist die Bedingung erfüllt, wurde in der Prüfschleife auf doppelte Zahlen kein Fehler er-kannt (FEHL enthält immer noch den Wert 0). In diesem Fall kann der Index X um einserhöht werden.

War die Bedingung FEHL=0 nicht zutreffend, wurde der Index für den VariablenstammVAR. nicht erhöht, die Ziehung wird wiederholt.

endHier hätten wir jetzt in VAR.1 bis VAR.? die gezogenen Zahlen liegen. Doppelte Zahlen kön-nen aufgrund des Prüfverfahrens nicht gezogen worden sein. Theoretisch hätten wir die Zah-len dort, wo sie für gut befunden wurden, ans Terminal ausgeben können. Aber, wenn schonkomfortabel, dann richtig. Wir sorgen dafür, daß die ermittelten Zahlen aufsteigend sortiert ineiner Zeile ausgegeben werden.

erg_sort=""Die Variable, in der die Zahlenkolonne fortgeschrieben werden soll, wird mit NULLSTRINGinitialisiert.

LOTTO

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 16: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E122

do x=1 to aus /* Sortierte Zahlenausgabe */do y=1 to anz

if x=var.y then erg_sort = erg_sort var.yend

endDie beiden ineinander verschachtelten Schleifen führen zur Fortschreibung der VariablenERG_SORT. Die äußere Schleife wird AUS (45 oder 49) mal durchlaufen. Dabei wird mitjedem Wert von X (1 bis 45 oder 49) verglichen, ob eine der gezogenen Zahlen mit X identischist. Wenn ja, wird sie in die Zahlenkette in ERG_SORT eingereiht (Ein ziemlich hirnverbrann-tes Verfahren. Wenn das ganze auch nur fünf Zeilen groß ist, gibt es bei 6 Ziehungszahlen 6mal 45 oder 49 Prüfungen. Wir sehen uns zu einem späteren Zeitpunkt mal an, wie eineSortierroutine programmiert werden kann).

clearsay center("Heinzi's kleines Lotto" anz 'aus' aus,79,'-')saysaysaysaysay center(erg_sort,79)saysaysaysaysay center('Viel Erfolg',79,'-')Nachdem alle Ziehungen, Prüfungen und die Sortierung abgeschlossen sind, kann das Er-gebnis mitgeteilt werden.

exit

Relativ aufwendig, das ganze. Letztlich aber eine ganz nette Übung, mit Schleifenund Variablenstämmen in Form von Tabellengebilden zu arbeiten. Versuchen wiruns aber mal zu vergegenwärtigen, wie die Ziehung der Lottozahlen im wirklichenLeben abläuft: Zunächst wird die Ziehungstrommel mit der Anzahl Kugeln gefüllt. DerLottogott bestimmt innerhalb der Trommel die Position der Kugel, die gezogen wer-den soll. Die entsprechende Kugel wird entnommen. Die Anzahl der Kugeln in derTrommel hat sich verringert. Die Kugel, welche die Trommel verlassen hat, kann keinzweites Mal gezogen werden. So einfach ist das. Machen wir es also auch so! Beidieser Gelegenheit sehen wir uns an, wie man auch eine Wortfolge innerhalb einesStrings wie eine Tabelle nutzen kann:

LOTTO

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 17: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 123

/* REXX * LOTTO2 *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Zieht 6 bis 12 Zahlen aus 45 oder 49 *//* Doppelte Ziehungen werden durch *//* Zahlenentnahme verhindert */arg anz temp aus .if temp='TRACE' then trace '?r'if anz < 7 ! anz > 12 then anz = 6if aus >< 45 & aus >< 49 then aus = 49trommel=""kugeln=""do idx=1 to aus

trommel=trommel" "idxenddo idx=1 to anz

rnd=random(1,words(trommel))kugeln=kugeln word(trommel,rnd)trommel=delword(trommel,rnd,1)

endzahlausg=""do x=1 to aus /* Sortierte Zahlenausgabe */

do y=1 to words(kugeln)if x=word(kugeln,y) then

erg_sort=erg_sort word(kugeln,y)end

endsay center(zahlausg,79)saysay center('Viel Erfolg',79,'-')exit

LOTTO

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 18: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E124

Um niemanden zu schockieren, wollen wir uns im nächsten Beispiel nochmal einkeines Spiel anschauen (Vorsicht: irgendwann werden wir Dinge versuchen, die er-schreckende Ähnlichkeit mit Arbeit haben). Interessante Tabellenarbeit liegt in Ma-stermind verborgen.

Zum Spiel: Vier farbige Spielknöpfe werden von einem Spieler verdeckt gelegt. Derzweite Spieler versucht, die Farbkombination zu erraten, indem er ebenfalls vier far-bige Spielsteine auflegt. Spieler 1 teilt nach jedem Versuch von Spieler 2 mit, wievie-le Spielsteine mit richtiger Farbe an der richtigen Position und wieviele Spielsteinemit richtiger Farbe an falscher Position vorkommen. Wir können unter TSO/REXXleider nicht mit farbigen Steinen spielen (mit Dialog Manager kein Problem, sofernFarbterminals vorhanden sind), können das ganze aber mit Zahlen nachvollziehen.Der Rechner ermittelt eine vierstellige Zahl, der Spieler muß sie erraten. Ist das Pro-gramm flexibel ausgelegt, spielt es keine Rolle, ob wir mit drei-, vier- oder fünfstelli-gen Zahlen spielen. Das Programm kommt mit jeder Zahl gleichermaßen zurecht.

/* REXX * MASTER *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Spielt Mastermind gegen einen Spieler *//* Der Spieler hat 10 Loesungsversuche. *//* Bei jedem Versuch wird mitgeteilt: *//* Richtige Zahlen am richtigen Ort *//* Richtige Zahlen am falschen Ort */arg max test .Zwei mögliche Informationen können bei Programmstart übergeben werden.

if test = "T" then trace "?R"Wird im zweiten Parameter T geliefert, wird der Ablaufverfolger aktiviert.

if datatype(max) = "CHAR" then max = 9999Wird im ersten Parameter eine Zahl geliefert, ist sie der Maximalwert, der zum Spielen genutztwerden kann. Anderenfalls wird als Maximum 9999 angenommen.

clearLöschen des Bildschirmes und Spielbeginn.

do foreverDie FOREVER-Schleife macht das Spiel insgesamt wiederholbar.

v=0zahl0=right(random(0,max),length(max),0)

MASTER

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 19: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 125

V ist der Zähler für die Lösungsversuche. ZAHL0 ist die Spielzahl, die vom Rechnervorgelegt wird. Gebildet wird sie über den Zufallsgenerator mit einem Wert zwischen 1und dem Maximalwert (Eingabe bei Programmstart oder 9999). Da bei einem Maximal-wert von 9999 die Spielzahl auch 38 sein könnte, wird sie rechtsbündig mit führendenNullen gefüllt und der Länge der Maximalzahl angepaßt.

do until v >= 10 ! zahl2 = zahl0Hier befinden wir uns in der Schleife, die den Ablauf eines Spieles steuert. Schlei-fenende ist erreicht, wenn der Spieler mit dem 10. Versuch die Vorgabezahl nichterraten hat, oder Zahl des Spielers mit der Vorgabezahl übereinstimmt.

ro=0rz=0zahl1=zahl0

RO wird die Situation "Richtige Zahlen am richtigen Ort", RZ die Situation "RichtigeZahlen am falschen Ort" beschreiben. Die Vorgabezahl wird nach ZAHL1 kopiert.ZAHL1 muß während eines Durchlaufes manipuliert werden. ZAHL0 muß unverän-dert erhalten bleiben.

say v+1'. Eingabe:'pull zahl2do while datatype(zahl2) = char ! zahl2 > max

say 'Eingabe falsch, wiederholen'pull zahl2Wurde vom Spieler eine falsche Eingabe gemacht (nicht numerisch oder grö-ßer als der Maximalwert, wird er in der Schleife festgehalten, bis seine Einga-be dem entspricht, was wir erwarten (eine Zahl zwischen 0 und dem Maximal-wert).

endzahl2=right(zahl2,length(max),0)v=v+1Wir haben in ZAHL2 den ersten Lösungsversuch des Spielers, in V kann erkanntwerden, im wievielten Durchlauf wir uns befinden.

if zahl1=zahl2 then do /* Lösung richtig */select

when v < 2 then e = 'Zufallstreffer'when v < 3 then e = 'Spitzenklasse'when v < 5 then e = 'Super'when v < 8 then e = 'Gut'otherwise e = 'Koennte besser sein'

end

MASTER

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 20: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E126

say e '- im' v'. Versuch. Sie haben gewonnen'Hier wurde erkannt, daß der Spieler das Problem gelöst hat. Abhängig von derAnzahl der Lösungsversuche V wird eine unterschiedliche Meldung ausgege-ben.

endelse do /* Lösung falsch */

Der Lösungsversuch war falsch. Jetzt muß herausgefunden werden, ob be-stimmte Zahlen am richtigen Ort stehen und/oder Zahlen zwar stimmen, aberan der falschen Stelle stehen.

do x=1 to length(zahl1)if substr(zahl2,x,1)=substr(zahl1,x,1) thendo

ro=ro+1zahl1=overlay('a',zahl1,x,1)zahl2=overlay('b',zahl2,x,1)

endendIn der Schleife wird von der ersten bis zur letzten Stelle die jeweilige Position inder Spielzahl (ZAHL1 ist eine Kopie der ursprünglichen Spielzahl ZAHL0) mitder gleichen Position des Lösungsversuches (ZAHL2) verglichen. Stimmendie beiden Werte überein, haben wir die Situation "Richtige Zahl am richtigenOrt" erkannt. Der Zähler RO wird um eins erhöht.

Um "Richtige Zahl am falschen Ort" zu erkennen, muß im Anschluß jede Stelleaus ZAHL1 mit jeder Stelle aus ZAHL2 verglichen werden. Damit hierbei nichtÜbereinstimmungen, die bei "Richtige Zahl am richtigen Ort" bereits erkanntwurden, wahrgenommen werden, wird bei deckungsgleichen Positionen dieentsprechende Stelle in ZAHL1 mit einem "a", in ZAHL2 mit einem "b" ersetzt.

do x=1 to length(zahl1)do y=1 to length(zahl1)

if substr(zahl1,x,1)=substr(zahl2,y,1) ,then do

zahl1=overlay('a',zahl1,x,1)zahl2=overlay('b',zahl2,y,1)rz=rz+1

endend

endNachdem deckungsgleiche Positionen in der oberen Schleife durch unterschied-

MASTER

Sp

eich

erva

riab

len

, Tab

elle

n, S

chle

ifen

Page 21: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 127

liche Buchstaben ausgetauscht wurden, kann jede Stelle von ZAHL1 mit jederStelle von ZAHL2 verglichen werden. Jede gefundene Übereinstimmung er-höht den Zähler RZ (Richtige Zahl am falschen Ort). Auch hier werden dieerkannten "Richtigen Zahlen" in ZAHL1 durch "a", in ZAHL2 durch "b" ersetzt,um bei mehreren gleichen Zahlen, die in ZAHL1 oder ZAHL2 vorkommen, keinfalsches Ergebnis zu erhalten.

if v < 10 then say 'Im' v'.Versuch:' ,'Zahlen am richtigen Ort =>' ,ro 'Zahlen am falschen Ort =>' rz

else say 'Hirni - erst denken dann eingeben'Sofern es nicht der letzte Versuch war, wird dem Spieler mitgeteilt, wievielerichtige Zahlen am richtigen Ort und wieviele richtige Zahlen am falschen Orterkannt wurden. Würde im letzten Durchlauf die Aufgabe nicht gelöst, erhältder Spieler einen entsprechenden Hinweis.

end

Rückkehr an den Schleifenkopf UNTIL, sofern die Aufgabe nicht gelöst wurde, undder Spieler seine zehn Versuche noch nicht ausgeschöpft hat.

endEnde der Schleife für ein Spiel.

do until ant = 'J' ! ant = 'N'say 'Ein neues Spiel (J/N):'pull ant

endif ant = 'N' then leave

endsay "Bye, bye..."exit

MASTER

Sp

eichervariab

len, T

abellen

, Sch

leifen

Page 22: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E128

Ein ganz anderes Problem. Ich habe mich sehr über mein Autoradio mit CD-Wechsergefreut - zunächst mal. Das schlaue Kerlchen kann über einen Zufallsgenerator dasgesamte Wechslermagazin ansteuern und sollte so ein abwechsungsreiches Musik-programm anbieten. Da wir ja nun wissen, daß es in der EDV keine Zufälle gibt, läßtsich natürlich auch ein Zufallsgenerator so steuern, wie wir es für unsere Bedürfnissebrauchen. Als ich per Zufall innerhalb von 45 Minuten dreimal das gleiche Stückhören durfte (gesamt sind etwa 150 Musiktitel verfügbar), war ich doch erstaunt. Wiemeist im Leben passiert so etwas mit einem Musikstück, das man sowieso nicht sogerne hört. Von einem vernünftigen Zufallsgenerator an dieser Stelle würde ich er-warten, daß er zufällig einen Titel wählt und diesen erst dann wieder berücksichtigt,wenn er alle anderen verfügbaren Titel einmal ausgewählt hat. Erst wenn jeder Titeleinmal gespielt wurde, darf der Zufallsgenerator wieder aus allen verfügbaren Stük-ken wählen. Legen wir zugrunde, daß über einen Festwertspeicher die aktuelle Um-gebung der Stücke-Auswahl hinterlegt werden kann. Wenn das Radio ausgeschaltetwird, kann bei erneutem Einschalten wieder dort weitermusiziert werden, wo unter-brochen wurde. Insgesamt möchte ich natürlich der Firma SONY meinen Dank aus-sprechen, für das, was sie im Bereich Unterhaltungselektronik so leistet, aber auchanregen, etwas mehr Intelligenz in die CD-Ansteuerung zu implementieren. So könn-te es in groben Zügen aussehen:

/* REXX * CD *****************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Beispiel fuer Kontrollierte Zufaelle *//* Es werden solange "Zufaellig" Titel *//* aus einer Reihe CD's ausggewaehlt, *//* bis alle Titel aller CD's einmal *//* gespielt sind. Solange Titel noch *//* nicht gehoert wurden, darf ein Titel *//* kein zweites Mal ausgewaehlt werden */clearsay "Zufallsgenerator fuer CD-Steuerung"do until nochmal = "J"

max=6cd.1="DE_BURGH Spaceman Getaway Emotion Borderline"cd.2="DIAMOND Caroline Shilo Play_me Stones Holly"cd.3="FENDRICH Cyrano Papa TuttiFrutti General Macho"cd.4="MAY Wolken Klempner Annabelle Buffet Hilf_mir"cd.5="HAINDLING Depp Baby Hellseher Flipper Samba"cd.6="FENDRICH Wind Regen Nix_is_fix Im_Westen Scene"Der CD-Wechsler ist mit 6 Scheiben gefüllt.

CD

Ko

ntr

olli

erte

Zu

fälle

Page 23: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 129

do while max > 0Die Schleife der CD- und Titelauswahl wird durchlaufen, solange noch mindestensein Titel einer CD nicht gespielt ist.

nr=random(1,max)cd=cd.nrDer Randomizer ermittelt eine CD

ti=random(2,words(cd.nr))

Der Randomizer ermittelt einen Titel auf der CD

say "Gewaehlt:" word(cd.nr,ti) "von" word(cd.nr,ti)CD und Titel werden gespielt.

cd.nr=delword(cd.nr,ti,1)Der gespielte Titel der CD wird ausgetragen.

if words(cd.nr) = 1 then docd.nr=cd.maxmax=max - 1

endWenn alle Titel der ausgewählten CD gespielt wurden, wird die CD bei der Auswahlnicht mehr berücksichtigt (wird CD-3 als erste fertig, tritt CD-6 an deren Platz unddie Anzahl der CD's, aus denen gewählt werden kann, wird um eine vermindert).

endsay "Wiederholung (J/N)pull nochmal

Alle Titel aller CD's wurden gespielt. Wird ein neuer Durchlauf gewünscht, werden alleCD's wieder auf Grundstellung gebracht, eine neue Auswahl beginnt.

endsay "Bye, bye..."exit

CD

Ko

ntro

llierte Zu

fälle

Page 24: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E130

GEHEIM

Ver

arb

eitu

ng

vo

n T

erm

inal

-Au

sgab

en

Immer wieder lustig ist eine Situation, die man heute sehr nobel einen Know-how-Transfer nennt. Man sieht sich in den Programmbibliotheken der Kollegen um undsucht dort nach interessant erscheinenden kleinen Helfern. Wird man fündig, wirdkopiert und der Namen des Programmautors entsprechend verändert. Trifft man da-bei auf übersetzte REXX-Programme, läßt sich leider nicht mehr vorab erkennen,was ein Programm macht. Macht nichts, man kann sich in vielen Fällen an den Pro-grammnamen orientieren. Trifft man auf Programmnamen wie GEHEIM oder VOR-SICHT, üben diese eine schier unwiderstehliche Anziehungskraft aus. Nun ist Adre-nalin ein wichtiges Hormon für den Körper, und wir werden versuchen, unsere liebenKollegen damit ein bißchen zu versorgen:

/* REXX * GEHEIM *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Sorgt fuer die Freisetzung von *//* Adrenalin bei neugierigen Kollegen *//* Optisch wird ein TSO-DELETE-Befehl *//* in Schleife auf alle Dateien des *//* Aufrufers durchgefuehrt. */CLEARsay "Achtung, Sie starten FREE-SPACE, das neue"say "Dienstprogramm der CRG (Computer Revolution Group)"say "Platzoptimierung ist kein Vorrecht des PC's"saysay "Sind Sie sicher, daß Sie das Programm ausführen"say "wollen? (J/N)"do until ant = "J" ! ant = "N"

pull antendif ant = "N" then exitDas Ganze bis hierher ist im grunde nicht nötig, aber wir wollen die Spannung etwas steigern.Die Vorfreude unseres Raubritters ist die schönste Freude. Außerdem dauert sie nicht allzulange.

dmy=OUTTRAP("file.")

Ausgaben, die TSO-Kommandos im Normalfall an das Terminal liefern, werden von OUT-TRAP() abgefangen und Zeile für Zeile genutzt, um den Variablenstamm FILE. zu füllen. Dieerste Terminalausgabe landet in FILE.1, die nächste in FILE.2 und so weiter. Wie viele ausga-bezeilen geliefert wurden (soviele Variablen FILE.? wurden gebildet), steht in der VariablenFILE.0.

LISTCNachdem unsere Ausgabefalle aufgestellt ist, setzen wir den TSO-Befehl LISTC ab, der uns

Page 25: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 131

in dieser Form die Namen aller Dateien des Programmaufrufers liefert. Die erste Zeile derLISTC-Ausgabe enthält dabei einen Hinweis, aus welchem Benutzerkatalog die Dateiinfor-mationen stammen. Die Ausgabezeile 2 und folgende enthalten jeweils einen Dateinamen.

do x=2 to file.trappedsay "ENTRY (A)" file.x "DELETED"In Schleife simulieren wir die Rückmeldung des DELETE-Befehles für jede Datei desAufrufers. An dieser Stelle ist nicht erkennbar, daß die Meldung nur durch den SAY-Befehl erzeugt wird. Optisch gewinnt man den Eindruck, zusehen zu müssen, wie eineDatei nach der anderen gelöscht wird. Könnte man zusehen, würde man sehen, wienach und nach jegliche Farbe aus dem Gesicht des Anwenders weicht. Bisher sind nochalle Aufrufer darauf reingefallen.

end

Wir könnten nun unseren Anwender noch dazu bringen, sich aus dem Kellerfenster zu stür-zen. Das erste, was er nach Programmablauf versuchen wird, ist ein LISTC-Befehl, um zuprüfen, ob seine Dateien wirklich verschwunden sind. Würden wir vorher sein TSO-Benutzer-profil manipulieren (PROFILE NOPREFIX), würden seine Dateien nicht mehr erscheinen. DiePanik wäre perfekt.

say "Danke für den Einsatz von FREE-SPACE-Utility"say "Bye, bye......"EXIT

GEHEIM

Verarb

eitun

g vo

n T

ermin

al-Au

sgab

en

Page 26: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E132

In den nächsten drei Beispielen wollen wir uns, wie versprochen, mit der dynami-schen Erweiterung einer bestehenden Zuweisung auseinandersetzen. Das erste Bei-spiel ergänzt die Zuweisung für SYSEXEC (REXX-Programmaufrufe). Standardmä-ßig wird die bestehende Zuweisung um die Datei userid.MVS.EXEC erweitert. DieDatei wird in der Suchreihenfolge von SYSEXEC vorne eingeschoben (könnte Pro-bleme geben, wenn die verketteten Dateien unterschiedliche Blockung aufweisen).

/* REXX ** ADDEXEC ***********************************//* Erstellt: 05.94 Wittemann *//* Zweck: Dynamische Zuweisung der MVS.EXEC *//* des Aufrufers unter SYSEXEC. *//* Ist noch keine Zuweisung für SYSEXEC *//* erfolgt, wird EXECUTIL ausgeführt. */arg test .if test = 'T' then trace '?r'ddsuch="SYSEXEC"da="MVS.EXEC"Das zu suchende DD-Statement und der Name der zusätzlich zu verkettenden Datei werdenfestgelegt.

if pos(userid(),"XWITT XWIH XHZ9001 UEDT0") > 0 thenda="MVS.EXEC,KURS.EXEC,TW.EXEC"

Entspricht die Benutzerkennung des Aufrufers einer der im Literal angegebenen Benutzer-kennungen, werden mehrere Dateien zusätzlich unter SYSEXEC verknüpft.

x=outtrap(var.)Befehlsausgaben des nachfolgenden LISTA-Befehles werden vom Terminal in einen Varia-blenstamm umgeleitet.

lista stLISTA liefert über den Operanden ST die Informationen aller aktuell zugewiesenen Dateienund DD-Namen.

do x=1 to var.0select

when index(var.x,'DDNAME') > 0 then nopwhen index(var.x,'TERMFI') > 0 then nopotherwise leave

endDas Ausgabeformat des LISTA-Befehles liefert immer in zwei Zeilen die Informationenüber eine Datei, außer bei den ersten Ausgabezeilen (Terminalzuweisungen etc.). Dieseersten Zeilen werden für die Verarbeitung nicht benötigt und werden überlesen.

ADDEXEC

Dyn

amis

che

Zu

wei

sun

gen

Page 27: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 133

endWir befinden uns in der LISTA-Ausgabe bei der ersten zugewiesenen Datei (liegt in VAR.x).

x=x+1dd=word(var.x,1)Da der gesuchte DD-Name eine Zeile tiefer liegt, müssen wir zunächst diese Zeile verarbei-ten. Der Zeiger X wird erhöht, aus der Zeile wird das erste Wort übernommen (hier ist dieseserste Wort noch sicher der DD-Name der darüberliegenden Datei).

cnt=0do ind=x-1 to var.0 by 2

y=ind+1if substr(var.y,3,8) >< " " then dd=word(var.y,1)cnt=cnt+1ddlist.cnt=dd var.ind

Um diese Schleife zu verstehen, muß man sich mit dem Ausgabeformat des LISTA-Befehles auseinandersetzen. Die Information für eine Datei wird immer in zwei aufeinan-derfolgenden Zeilen geliefert. In der oberen Zeile steht ein Dateiname. In der zweitenZeile steht der DD-Name der Datei und deren Disposition. Ist kein DD-Name angegeben,ist die Datei unter dem gleichen DD-Namen verkettet wie die vorausgegangene Datei.Um diese verworrenen Informationen besser verarbeiten zu können (ich verarbeite sol-che Informationen gerne über temporäre Dialog Manager Tabellen weiter), bauen wiruns einen Variablenstamm DDLIST. auf, in dem in jedem Satz der DD-Name und derDateiname nebeneinander stehen.

enddsn_alt=""In der nachfolgenden Schleife wollen wir erreichen, daß alle Dateinamen, die zu unserem DD-Statement gehören, in einer Kette fortgeschreiben werden (zwischen Apostrophen eingeschlos-sen und durch Komma voneinander getrennt). Zur Aufnahme dieser Namenskette bereitenwir die Variable DSN_ALT als Nullstring vor.

do x=1 to cntif word(ddlist.x,1)=ddsuch thendsn_alt=dsn_alt",'"word(ddlist.x,2)"'"

enddsn_neu=da!!dsn_altWir fügen die Dateien der Altzuweisung und die zu ergänzenden Dateien in der VariablenDSN_NEU zusammen. Ob wir dabei die neuen Dateien in der Suchreihenfolge vorne ein-schieben oder hinten anhängen, ist eine Ermessensfrage.

"alloc dd("ddsuch") da("dsn_neu") shr reuse"

ADDEXEC

Dyn

amisch

e Zu

weisu

ng

en

Page 28: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E134

Da in DSN_NEU die Dateien einer eventuell bestehenden Zuweisung enthalten sind, könnenwir im ALLOC den Operanden REUSE nutzen und eine bestehende Zuweisung unter dementsprechenden DD-Namen überschreiben.

if length(dsn_alt)=0 then "executil searchdd(yes)"Grundsätzlich wäre denkbar, daß es für unseren DD-Namen vor Programmstart noch keineZuweisung gab (SYSEXEC wird in vielen Unternehmen noch nicht ausreichend gewürdigt). Indiesem Fall wäre die Zuweisung alleine nicht ausreichend, um aus der zugewiesenen Dateiautomatischen REXX-Programmstart zu erreichen. Wir können diese Situation an der inhalt-lichen Länge von DSN_ALT ablesen. Ist sie 0, gab es keine Altzuweisung. In diesem Fallsorgen wir über EXECUTIL für die vorrangige Durchsuchung der unter SYSEXEC zugewiese-nen Dateien.

exit

Das nächste Beispiel lassen wir für sich selbst sprechen. Im Grunde haben wir dasgleiche Kernproblem bei dynamischer Ergänzung der Dialog Manager Zuweisungen.Wird an dieser Stelle schlecht gearbeitet, gibt es immer lange Gesichter. Versauenwir unsere SYSEXEC-Zuweisung, sind schlimmstenfalls einige Programmaufrufe nichtmehr möglich. Verschlimmbessern wir das DMS-Umfeld, können wir nicht mehr mitPDF arbeiten. Für dynamische Veränderungen im Dialog Manager müssen wir sechsDD-Statements berücksichtigen. Vier davon müssen erweitert, zwei müssen gege-benenfalls umbelegt werden (Die Zuweisungen ISPTABL und ISPFILE dürfen immernur auf eine Datei zeigen). Diese Routine läuft automatisch bei LOGON ab, wenn einService, wie er in den meisten großen Unternehmen von der Systemprogrammierungangeboten wird, genutzt werden kann: In der standardmäßig zugewiesenenSYSPROC-Datei userid.???.CLIST wird ein Mitglied START oder namensgleich mitder Benutzerkennung hinterlegt. Dieses Mitglied wird bei LOGON gesucht. Wird esgefunden, werden die Befehle, die es enthält, ausgeführt. Nun sollten in einer CLIST-Datei auch CLISTen stehen. Die CLIST könnte aber ihrerseits diese REXX-Routine,wie auch das vorausgegangene Beispiel für SYSEXEC-Erweiterung, aufrufen.

/* REXX ** DMSALLOC **********************************//* Erstellt: 05.94 Wittemann *//* Zweck: Erweiterte Zuweisungen fuer DMS *//* ISPPLIB ISPMLIB ISPSLIB und *//* ISPTLIB werden ergaenzt *//* ISPFILE und ISPTABL neu festgelegt *//* ISPTABL wird neu festgelegt */arg test .if test = 'T' then trace '?r'clear

DMSALLOC

Dyn

amis

che

Zu

wei

sun

gen

Page 29: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 135

dd.1="ISPPLIB"da.1="'"userid()".MVS.PANELS','"userid()".TEST.PANELS'"dd.2="ISPSLIB"da.2="'"userid()".TEST.SKELS'"dd.3="ISPMLIB"da.3="'"userid()".TEST.MSGS'"dd.4="ISPTLIB"da.4="'"userid()".TEST.TABLES'"dd.5="ISPTABL"da.5="'"userid()".TEST.TABLES'"dsn_neu.5=da.5dd.6="ISPFILE"da.6="'"userid()".TEST.SKELS'"dsn_neu.6=da.6Die sechs DD-Namen und die darüber zuzuweisenden Dateien werden festgelegt.

dmy=outtrap(var.)lista stDie Output-Falle wird aufgestellt. LISTA liefert sein Ergebnis im Variablenstamm VAR. ab.

do x=1 to var.0select

when index(var.x,'DDNAME') > 0 then nopwhen index(var.x,'TERMFI') > 0 then nopotherwise leave

endDie ersten, für uns unwichtigen Sätze werden überlesen.

endx=x+1dd=word(var.x,1)cnt=0do ind=x-1 to var.0 by 2

y=ind+1if substr(var.y,3,8) >< " " then dd=word(var.y,1)cnt=cnt+1ddlist.cnt=dd var.indDie Tabelle DD-Namen/Dateinamen wird aufgebaut.

enddo i=1 to 4

DMSALLOC

Dyn

amisch

e Zu

weisu

ng

en

Page 30: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E136

dsn_alt.i=""do x=1 to cnt

if word(ddlist.x,1)=dd.i thendsn_alt.i=dsn_alt.i",'"word(ddlist.x,2)"'"

enddsn_neu.i=da.i!!dsn_alt.iFür die ersten vier DD-Statements wird die neue Namenskette der Dateien erzeugt (zuergänzende + bereits zugewiesene Dateien). Für die letzten beiden Statements wird dieAltzuweisung überschrieben, da Tabellen- und File-Tailoring-Ausgabe immer nur auf eineDatei zeigen dürfen.

endtext="erweitert"do i=1 to 6

if i > 4 then text="gesetzt ""alloc dd("dd.i") da("dsn_neu.i") shr reuse"say "DMS-Zuweisung fuer" dd.i text " RC("rc")"Die sechs Zuweisungen werden durchgeführt, die ersten vier davon dynamisch, die letz-ten beiden fest. Erweiterung oder Neuzuweisung wird gemeldet. Der Returncode desALLOC-Befehles wird mitgeteilt.

endexit

In den nächsten Beispielen werden wir uns mit Dateiverarbeitung auseinanderset-

DMSALLOC

Dyn

amis

che

Zu

wei

sun

gen

Page 31: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 137

zen. Wir werden uns einfaches Lesen, Lesen aus mehreren Dateien, Lesen aus undSchreiben in mehrere Dateien in einem Programm, und den Update (Lesen und Schrei-ben in die gleiche Datei in einem Arbeitsgang) ansehen.

Zunächst eine ganz kleine Demonstration für Dateiausgabe auf Bildschirm (vergleich-bar mit TYPE datei unter DOS - Verzeihung).

/* REXX **********************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Zeigt Inhalt einer Datei am Terminal */arg datei .if length(datei) = 0 then call fehler

Der Anwender könnte bereits bei Programmstart den Namen der Datei angeben, deren Inhalter sehen will. Tut er das nicht, wird eine Fehlermeldung ausgegeben.

dmy=msg(off)"alloc dd(list) da("datei") shr reuse"if rc > 0 then call fehlerLiefert der ALLOC-Befehl nicht RC 0 (Datei exklusiv belegt, nicht vorhanden oder ähnliches),wird ebenfalls eine Fehlermeldung ausgegeben und das Programm beendet. Diese Form derAnwendung unserer Variablen DATEI bedingt, daß im Falle vollqualifizierter Dateinamen derAnwender die Apostrophe um den Dateinamen herum selbst tippen muß. Sie wären dannBestandteil der Variablen.

"execio * diskr list (finis"Die Datei wird komplett in den Stack gelesen.

do queued()parse pull satzsay satzDer Reihe nach werden die Sätze aus dem Stack entnommen und am Terminal ange-zeigt.

end"free dd(list)say "Bye, bye..."Der DD-Name LIST wird aus der Zuweisungstabelle entfernt, das Programm wird beendet.

exitfehler:say "Datei nicht angegeben, oder Zuweisungsfehler"exit

LIST

Dateiverarb

eitun

g

Page 32: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E138

Nutzen wir das Verfahren, um die vorbereiteten Inhalte eines bestimmten PO-Mem-bers an beliebige TSO-Benutzer als Nachrichten zu versenden.

/* REXX * SEND ***************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Liest den Inhalt eines PO-Members *//* und uebermittelt die Saetze zeilen- *//* weise an einen oder mehrere TSO-User */arg member empfBei Programmstart sind das zu versendende Member und der oder die Empfänger der Nach-richt anzugeben. wird mehr als ein TSO-Benutzer (durch Blank getrennt) angegeben, liegenalle Benutzernamen in der Variablen EMPF durch Blank getrennt.

dummy=msg(off)

Rückmeldungen aus TSO-Befehlen werden unterdrückt.

if length(empf)=0 then signal fehlerWurde kein Empfänger angegeben, wird das Label FEHLER angesprungen

if length(member)=0 then member='STDMSG'Wurde kein Nachrichten-Mitglied angegeben, wird standardmäßig der Inhalt des MitgliedesSTDMSG verschickt.

"alloc dd(msg) da(msgs.data("member")) shr reuse"if rc > 0 then signal fehlerWar die Zuweisung des Nachrichten-Mitgliedes nicht möglich (vermutlich Schreibfehler imMembernamen - Member nicht vorhanden), wird das Label FEHLER angesprungen.

"execio * diskr msg (finis"Das Meldungs-Member wird im Stack abgelegt.

"free dd(msg)"Der DD-Name MSG wird aus der Zuweisungstabelle entfernt.

do queued()parse pull msgmsg=left(msg,71)"se '"msg"' user("empf") logon"Zeile für Zeile des Meldungs-Members wird aus dem Stack entnommen und an den oderdie Empfänger verschickt, sofern sie im LOGON sind.

end

SEND

Dat

eive

rarb

eitu

ng

, Les

en

Page 33: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 139

say;say "SEND an" empf "ausgefuehrt.."exitfehler:

"free dd(msg)"say "Zuweisungsfehler, oder Empfänger nicht angegebenObwohl ich es am liebsten verschweigen würde, es ist dennoch möglich: Durch SIGNALläßt sich in REXX der GOTO-Befehl anderer Sprachen auch erzwingen. Wird ein Fehlererkannt, aufgrund dessen eine weitere Verarbeitung nicht mehr sinnvoll erscheint, kannman an einen Punkt zur Fehlerbehandlung (oder auch zur regülären Endeverarbeitungdes Programmes) springen. Eine Rückkehr von dort ins Programm wird dringend abge-raten. Würde eine weitere SIGNAL-Anweisung von hier aus in eine beliebige Schleifeinnerhalb des Programmes zurückkehren, endet ein derartiger Versuch immer im gren-zenlosen Chaos. Wer SIGNAL als GOTO mißbraucht, hat verdient, was er bekommt.

exit

Lesen aus einer Datei in den Stackbereich mit wiederholter Verarbeitung.

/* REXX * LIST ***************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Suchen wahlfreier Begriffe in einer *//* beliebigen Datei */arg trace .if trace="T" then trace '?r'else msg("OFF")CLEARsay 'Datei vollqualifiziert (ENTER = Ende)'do forever

parse upper external dateiif datei='' then exitif sysdsn("'"datei"'") >< OK then

msg='Datei nicht vorhanden'else do

listdsi("'"datei"'")select

when sysdsorg="PO" & index(datei,'(')=0 thenmsg='Datei hat PO-Format, Membername fehlt'

when sysdsorg >< "PO" & sysdsorg >< "PS" then

SUCH

Dateiverarb

eitun

g, L

esen

Page 34: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E140

msg='Dateiformat' sysdsorg 'nicht moeglich'otherwise leave

endendsay msg ', bitte nochmal'

endHier geht es für uns los. Es ist sichergestellt, daß wir in der Variablen DATEI einen Dateina-men empfangen haben, der für uns brauchbar ist.

"qstack"if queued() > 0 ! rc > 1 then do

"newstack"endSofern der Stack nicht leer ist, oder wir uns nicht im originären Stack befinden (in beidenFällen wäre unser Programm als Unterprogramm aufgerufen worden), erzeugen wir für unse-re Verarbeitung einen neuen Stack, den wir beliebig manipulieren und bei Programmendewieder löschen können.

"alloc dd(such) da('"datei"') shr reuse""execio * diskr such (finis""free dd(such)"Die vom Anwender ausgewählte Datei wird zugewiesen, in den Stack gelesen und wiederfreigegeben. Die Datei liegt im Stack zur Verarbeitung bereit.

do foreverÜber die Schleife wird die Suche beliebig wiederholbar. Es können beliebig oft Begriffeaus der eingelesenen Datei gesucht werden.

say 'Suchbegriff angeben:'parse upper external suchHier ist sehr wichtig, daß über PARSE EXTERNAL von der Tastatur gelesen wird. DaEXECIO Sätze im Stack abgelegt hat, hätte ein Lesen über PULL fatale Folgen:

Ein Satz aus dem Stack würde entnommen werden. Lesen von der Tastatur würde nichtstattfinden. Die restlichen Sätze im Stack würden mit dem entnommenen Satz vergli-chen und wieder im Stack abgelegt werden. Eine Übereinstimmung wäre vermutlich nieder Fall. Nach einem derartigen Durchlauf der Schleife wäre ein Satz weniger im Stack,als EXECIO dort abgeliefert hat (wegen des PULL-Befehles für den Suchbegriff). DieFOREVER-Schleife würde ohne sichtbares Ergebnis einmal weniger durchlaufen wer-den, als ursprünglich Sätze im Stack lagen. Dann wäre der Stack leer und wir könntenmit PULL von der Tastatur lesen. Hier würde unser Bediener erstmals registrieren, daßdas Programm mit ihm arbeiten will. Suchen kann er jetzt endlich, aber finden kann ernichts. Da der Stack leer ist, wird die Schleife DO QUEUED() (gleichzusetzen mit mach

SUCH

Dat

eive

rarb

eitu

ng

, Les

en

Page 35: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 141

es 0 mal) übersprungen und der Bediener sofort wieder aufgefordert, seinen Suchbegriffeinzugeben.

if length(such) = 0 then leaveWurde vom Anwender nur E gedrückt, wird die Schleife verlassen.

c_found='0'

Ein Zähler für gefundene Sätze wird mit 0 vorbelegt.

clearmsg='Suche nach' such 'in' dateisay center(msg,79,'*')sayWir erzeugen einen leeren Schirm mit einer Überschrift.

do c_satz = 1 to queued()QUEUED() liefert uns die Anzahl der Datensätze im Stack (die von EXECIO imStack hinterlegt wurden). Grundsätzlich könnten wir unsere Verarbeitung auch überdie Schleife DO QUEUED() steuern. Lassen wir einen Zähler mitlaufen, können wiraber mitteilen, in welchem Datensatz jeweils Übereinstimmung festgestellt wurde.

parse pull satzEin Satz wird aus dem Stack entnommen. Groß-/Kleinschreibung bleibt erhalten.Wir wollen den Satz so zeigen können, wie er in der Datei liegt.

if pos(such,translate(satz)) > 0 then doHier muß man sich grundsätzlich überlegen, was das Programm können soll.Wir gehen davon aus, daß zur Prüfung auf gefunden Groß-/Kleinschreibungnicht berücksichtigt werden soll. Der Inhalt von SUCH ist sicher großgeschrie-ben (PARSE UPPER..). Der Datensatz soll aber nicht verändert werden. Fürdie Prüfung betrachten wir den Datensatz so, als wäre er großgeschrieben,ohne ihn zu verändern (TRANSLATE()). Liefert POS einen Wert größer 0 (diePosition, an der SUCH in SATZ vorkommt), war die Suche erfolgreich.

say "("right(c_satz,3,"0")")" satzc_found=c_found+1Der Satz wird ausgegeben. Ein Zähler für gefundene Sätze wird erhöht.

endqueue satzEgal, ob gefunden oder nicht gefunden, der Satz wird wieder in den Stack einge-stellt. Auf diese Weise sieht der Stack inhaltlich nach der Schleife QUEUED() ge-nauso aus wie vorher. Die Datei liegt komplett im Stack für den nächsten Suchvor-

SUCH

Dateiverarb

eitun

g, L

esen

Page 36: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E142

gang bereit, ohne ein weiteres Mal eingelesen werden zu müssen.

endsayif c_found=0 then msg='Suchbegriff nicht gefunden'else msg='Listende, gefundene Saetze:' c_foundsay center(msg,79,'*')Eine Meldung wird ausgegeben (Suche negativ oder ? gefundene Sätze).

end

Hier landen wir, wenn der Anwender auf die Frage nach einem Suchbegriff nur E gedrückthat. Wir können hier unser Programm nicht einfach beenden, da ansonsten der gefüllte Stackbei Programmende zum TSO hin ausgeschüttet werden würde, und TSO jeden Satz im Stackals Befehl interpretieren würde (dies würde eine Reihe Fehlermeldungen erzeugen).

"delstack"Sofern wir uns nicht im originären Stack befinden, wird der Stack mitsamt seinen Daten ent-fernt, anderenfalls wird er inhaltlich gelöscht. Aufgrund der Prüfung am Programmanfang unddes eventuell ausgeführten Befehles NEWSTACK können wir uns das leisten und trotzdemsicherstellen, daß nicht ungewollt Daten verloren gehen.

say "Bye, bye..."exit

Lesen aus einer Datei in einen Variablenstamm. Um nicht immer das Gleiche zusehen, hier eine Variante, die uns erlaubt, bei der Anzeige größerer Dateien rück-wärts zu blättern. Dies ist nur möglich, weil die Datei im Programm als Variablen-stamm vorliegt, innerhalb dessen beliebig positioniert werden kann. EXECIO selbsterlaubt nur eine starre, sequentielle Verarbeitung. Das heißt: lesen wir den 7. Satzund wollen zurück zum 5., muß die Datei geschlossen, erneut geöffnet und vorgele-sen werden.

/* REXX * SHOW ***************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Zeigt den Inhalt einer Datei seiten- *//* weise am Terminal und erlaubt dem *//* Anwender, in der Datei vorwaerts *//* oder rueckwaerts zu blaettern */arg datei test .

SHOW

Dat

eive

rarb

eitu

ng

, Les

en

Page 37: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 143

if test = "T" then trace "?R"if length(datei) = 0 then do

say "Dateiname muss bei Programmstart angegeben werden"exit

enddo forever

if sysdsn(datei) >< 'OK' thenmsg = 'Datei' datei 'nicht verfuegbar'

else dox=listdsi(datei)select

when sysdsorg='PO' & index(datei,'(')=0 thenmsg='Dateiname als Parameter erforderlich'

when sysdsorg >< "PO" & sysdsorg >< "PS" thenmsg='Dateiorganisation wird nicht unterstuetzt'

otherwise leaveend

endsay msgexit

endErreichen wir diese Stelle des Programmes, liegt in der Variablen DATEI ein für uns brauch-barer Wert. Auch hier gehen wir mal davon aus, daß bei vollqualifizierten Namensangabender Anwender die Apostrophe liefern muß.

"alloc dd(list) da("datei") shr reuse""execio * diskr list (stem satz. finis""free dd(list)"Die gewünschte Datei wird zugewiesen und in einem Variablenstamm hinterlegt. Der benutz-te DD-Name wird wieder freigegeben.

x=1X wird ein Positionszeiger im Stamm SATZ., über den gesteuert wird, welche maximal 18Sätze des Stammes am Terminal gezeigt werden sollen. Da wir zunächst mal mit dem erstenSatz (SATZ.1) beginnen wollen, wird X mit 1 initialisiert.

do foreverclearsay center('< 'datei' >',79,'-');sayZu Beginn einer Bildschirmseite wird eine Titelzeile mit den Dateinamen und eine an-schließende Leerzeile ausgegeben.

SHOW

Dateiverarb

eitun

g, L

esen

Page 38: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E144

do y=x to x+17say right(y,4,0)'..'left(satz.y,73,)if y >= satz.0 then leaveEs werden maximal 18 Datensätze (beginnend mit SATZ.X) ausgegeben. Wird da-bei vor dem 18. Durchlauf der Schleife auf Dateiende getroffen (SATZ.0 wird über-schritten), wird die Schleife vorzeitig verlassen. Die Ausgabezeile wird einen vier-stelligen Hinweis auf die Zeilennummer innerhalb der Datei und die ersten 73 Bytedes Datensatzes enthalten (wird als Übersicht reichen).

endsay;say 'F<oreward> B<ack> E<nd>'do until ant = 'F' ! ant = 'B' ! ant = 'E'

pull antendDer Anwender kann mit seiner Eingabe entscheiden, ob er weiter nach unten oder nachoben blättern will, oder aufhören möchte.

selectwhen ant='F' then x=y-1

Beim Blättern Richtung Dateiende wird der Index für die zu zeigende Datei auf denletzten gezeigten Datensatz gesetzt. Bei erneuter Terminalausgabe wird der unter-ste Datensatz zum obersten.

when ant='B' then dox=y-35if x < 1 then x=1Beim Zurückblättern wird der oberste gezeigte Datensatz zum untersten. Wirdbei Rückpositionierung vor den ersten Satz positioniert, wird als oberster Da-tensatz der erste angezeigt.

endotherwise leaveIm NEIN-Zweig der Abfrage angelangt, wissen wir, daß der Anwender sich für Auf-hören entschieden hat und verlassen die äußere Programmschleife.

endendsay "Bye, bye..."exit

SHOW

Dat

eive

rarb

eitu

ng

, Les

en

Page 39: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 145

Das nächste Programm zeigt uns, was viele Menschen im heute biblischen Alterjenseits der 35 damals im Kindesalter als Zeitvertreib kannten (es soll tatsächlich ingrauer Vorzeit nicht in jedem Kinderzimmer ein PC gestanden haben). Heute lebtdieser Zeitvertreib in TV-Sendungen wie Glücksrad wieder auf: WORTE RATEN. Wirholen uns dazu die zu ratenden Worte aus einer Datei. Dies hat den Vorzug, daß dieWortauswahl beliebig erweitert werden kann, ohne am Programm rumfummeln zumüssen. Aus den gelesenen Worten wird jeweils eines per Zufallsgenerator ausge-wählt (entspricht letztendlich dem wahlfreien Zugriff auf eine Datei). Ein ausgewähl-tes Wort soll innerhalb des Spieles kein zweites Mal benutzt werden. Das gesuchteWort wird vorgelegt, wobei an Stelle der Buchstaben für den Spieler nur Punkte zusehen sind. Der Anwender kann einen Buchstaben eingeben, und es wird ihm ge-zeigt, ob und an welchen Positionen im Wort der Buchstabe vorkommt. Daß ein Buch-stabe vom Anwender mehr als einmal benutzt wird, wird vom Programm ausgeschlos-sen. Welche Buchstaben noch zum Raten zur Verfügung stehen, wird gezeigt (jederbenutzte Buchstabe wird aus der Reihe der noch zur Verfügung stehenden Buchsta-ben ausgeschlossen). Meint der Anwender, das Wort erraten zu haben, kann er dasLösungswort auch vollständig eintippen.

/* REXX * WORTE **************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Der Rechner liest Worte aus einer *//* Datei und waehlt sie ueber Zufalls- *//* generator aus. Der Spieler muß das *//* Wort erraten. */arg test .if abbrev(test,'T') then trace '?r'"alloc fi(worte) da('xwitt.mvs.data(worte)') shr reuse""execio * diskr worte (stem wort. finis"do forever

Die äußere Schleife wird je Wort durchlaufen, das der EXECIO-Befehl im Variablen-stamm WORT. hinterlegt hat

x=1abc='A B C D E F G H I J K L M N O P Q R S T U V W X Y Z'

X wird der Durchlaufzähler für die Rateversuche eines Wortes. ABC wird mit allen Buch-staben gefüllt. Die Variable ABC zeigt dem Spieler innerhalb der Schleife je Wort, welcheBuchstaben ihm noch zur Verfügung stehen.

if wort.0=0 then leaveGibt es keine Worte mehr für das Spiel (Datei war leer, oder alle Worte wurden bereitsaufgebraucht), wird die äußere Schleife verlassen.

WORTE

Dateiverarb

eitun

g, L

esen

Page 40: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E146

i=random(1,wort.0)Der Zufallsgenerator liefert eine Zahl zwischen 1 und der Anzahl verfügbarer Worte. DieVariable I wird uns innerhalb des Stammes WORTE. unser Spielwort liefern (WORT.I).

wort1=space(translate(wort.i),0)wort2=copies('.',length(wort1))Das ermittelte Spielwort wird in die Variable WORT1 übernommen. Dabei wird es aufGroßschreibung konvertiert und eventuell darin enthaltene Blanks werden entfernt.WORT2 wird mit so vielen Punkten gefüllt, als WORT1 Buchstaben hat. WORT1 brau-chen wir im Hintergrund, um zu kontrollieren, ob der Spieler das gesuchte Wort erratenhat. In WORT2 baut sich die Lösung des Spielers auf (jeder positive Rateversuch wird inWORT2 ergänzt).

do until wort2 = wort1

Die UNTIL-Schleife wird durchlaufen, bis das gesuchte Wort gefunden ist (oder derSpieler das Programm vorzeitig beendet).

clearsay center("< Heinzi's lustiges Worteraten >",79,'-')say;say;saysay center(wort2,79)say;say;saysay 'Geben Sie einen Buchstaben, oder das

Loesungswort ein (*=Ende)'say abcsay;saysay x'. Versuch';saypull antNeben Überschrift und einigen Leer- und Hinweiszeilen sieht der Spieler eine ge-punktete Zeile, bei der jeder Punkt einem Buchstaben des gesuchten Wortes ent-spricht. Außerdem wird ihm eine Buchstabenreihe gezeigt, in der der Spieler able-sen kann, welche Buchstaben für den Rateversuch zur Verfügung stehen. Das Pro-gramm wartet auf die Eingabe des Spielers.

selectwhen ant='*' then do

say "Das gesuchte Wort war" wort1say "Schaem Dich...exitWir haben vorzeitiges Ende erreicht, der Spieler will nicht mehr (wiedereinen fertig gemacht, haha).

end

WORTE

Dat

eive

rarb

eitu

ng

, Les

en

Page 41: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 147

when ant < 'A' ! ant > 'Z' then dosay 'Nur ein Buchstabe oder die Loesung'say 'weiter mit <ENTER>'pulliterate

endwhen x > 1 & length(ant) = 1 & ,

index(abc,ant) = 0 then dosay 'Buchstabe nicht mehr moeglich'say 'weiter mit <ENTER>'pulliterate

endDie zweite und dritte WHEN-Klausel erkennt Falscheingaben. Entweder wur-de Nullstring, Zahl oder Sonderzeichen empfangen (2. WHEN), oder ein Buch-stabe gewählt, der nicht mehr verfügbar war (3. WHEN).

otherwise if length(ant) = 1 then call checkIm NEIN-Zweig wissen wir, daß der Spieler entweder einen Lösungsversuch inForm des ganzen Wortes gewagt hat, oder ein Buchstabe empfangen wurde.Im zweiten Fall rufen wir eine Subroutine zur Prüfung des gelieferten Buchsta-bens auf.

endif ant=wort1 ! wort2=wort1 then do

Entweder hat der Spieler mit dem Eingegebenen Wort das gesuchte Wort er-raten, oder die gebildete Buchstabenkette (aus den jeweiligen buchstabewei-sen Versuchen) ist mit dem gesuchten Wort identisch.

say 'Gut!'say 'Loesung im' x'. Versuch'loesung=''do x=1 to length(wort1)

loesung=loesung!!substr(wort1,x,1)' 'endsay;say 'Loesung:' loesung;say;saysay 'weiter mit <ENTER>'pullleaveNach Ausgabe des Lösungswortes wird ein Haltepunkt gesetzt, damit der Spielerden ausgegebenen Text lesen kann. Nach E verläßt das Programm die Ra-

WORTE

Dateiverarb

eitun

g, L

esen

Page 42: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E148

teschleife und kehrt in die äußere Schleife zur Auswahl des nächsten Worteszurück. Da am dortigen Schleifenkopf CLEAR durchgeführt wird, könnte derSpieler ohne den Halt den Text nicht lesen.

endx=x+1Das gesuchte Wort ist noch nicht gefunden. Der Durchlaufzähler wird erhöht.

end /* until wort1 = wort2 */

Nächste Runde. Wir brauchen ein neues Wort. Damit der Zufallsgenerator nicht wiederdas gleiche Wort liefern kann, wird das benutzte Wort mit dem letzten verfügbaren Wortüberschrieben. Die Anzahl verfügbarer Worte wird um eins vermindert.

max=wort.0wort.i=wort.maxwort.0=wort.0-1Auf ein neues.

endsay 'Bye, bye...'exit/* Beginn SUBROUTINE CHECK **************************/check:

Hier haben wir zu prüfen, ob der vom Spieler getippte Buchstabe in unserem gesuchtenWort enthalten ist. Wenn ja, wird WORT2 (der aktuelle Stand der Lösung) entsprechendaktualisiert. Darüber hinaus müssen wir aus der Kette der möglichen Buchstaben denletzten getippten Buchstaben des Spielers entfernen.

do y=1 to length(wort1)if substr(wort1,y,1) = ant then

wort2 = overlay(ant,wort2,y,1)endDie Schleife aktualisiert WORT2

if index(abc,ant) > 0 thenabc= overlay('-',abc,index(abc,ant),1)

Der zuletzt getippte Buchstabe wird aus der Kette verfügbarer Buchstaben entfernt.

return/* Ende SUBROUTINE CHECK ****************************/

WORTE

Dat

eive

rarb

eitu

ng

, Les

en

Page 43: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 149

Bisher alles Kinderkram, machen wir mal was vernünftiges. Legen Sie Papier undSchere bereit. Wir basteln uns ein Mischprogramm. Zwei Datenbestände zu einemdritten zusammenzumischen erfordert bestimmte Voraussetzungen: Beide Dateienmüssen gleich strukturiert und nach dem Mischkriterium aufsteigend sortiert vorlie-gen. Die beiden Eingaben werden parallel zueinander verarbeitet. Je ein Satz ausbeiden Dateien wird verglichen. Der kleinere Datensatz (der Satz, dessen Mischkrite-rium das kleinere ist) wird in die Ergebnisdatei geschrieben. Aus der Datei, derenSatz geschrieben wurde, wird der nächste Datensatz gelesen. Ist eine der beidenDateien zu Ende, werden die Restsätze der anderen Datei noch in die Ergebnisdateiüberführt. Das Programm sollte möglichst so angesetzt werden, daß es eine Gleich-behandlung aller Datensätze bis zum Schluß erlaubt, gleichgültig, welche der beidenEingabedateien zuerst auf Dateiende läuft (alles andere hat nichts mit Mischen zutun, sondern wäre besser in die Rubrik "Schütteln" einzureihen).

Gehen wir nun für unser Beispiel von einigen Dingen aus: Die beiden Eingaben sinddie Mitglieder MISCH1 und MISCH2 der Datei XWITT.MVS.DATA. Das Ergebnis wirdim Mitglied MISCHERG der gleichen Datei hinterlegt. Die Eingaben liegen nach Arti-kelnummern sortiert aufsteigend vor (Stelle 1 bis 4 des Datensatzes). Die Artikelnum-mer ist das Mischkriterium.

Die folgende Lösung wurde nach rein logischen Gesichtspunkten kodiert und wäre indieser Form nahezu unverändert in jede andere Programmiersprache übertragbar.Sorry..., aber wie man es mit allen Vorzügen von REXX anders machen kann, zeigtdann das erweiterte zweite Beispiel.

/* REXX * MISCH **************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Mischt zwei Eingaben zu einer neuen *//* Ausgabe zusammen. Mischkriterium ist *//* eine Artikelnummer in Stelle 1 bis 4 *//* der Eingabedateien. */parse upper arg trace .if trace="T" then trace '?r'else msg(off)"free dd(m1 m2 m3)""alloc dd(m1) da(kurs.data132(misch1)) shr reuse""alloc dd(m2) da(kurs.data132(misch2)) shr reuse""alloc dd(m3) da(kurs.data132(mische)) old"Die Zuweisung der beiden Eingaben und der Ausgabe ist erfolgt. Der FREE-Befehl am An-fang müßte nicht sein, ist aber hilfreich, wenn das Programm angelaufen ist, die Zuweisungenerfolgt sind und wegen Abbruches der FREE-Befehl am Ende des Programmes nicht ausge-führt werden konnte.

MISCH

Dateiverarb

eitun

g, L

esen u

nd

Sch

reiben

Page 44: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E150

s_eof_m1 = 0s_eof_m2 = 0s_les_m1 = 1s_les_m2 = 1Wir brauchen vier Schalter, die Dateiende der Eingaben erkennen lassen (S_EOF_M?, 0=NEIN)und steuern, ob von einer Datei gelesen werden soll (S_LES_M?, 1=JA)

do foreverDie Schleife wird durchlaufen, bis beide Dateien zu Ende sind (Bei Dateiende wird derentsprechende Endeschalter auf 1 gesetzt. Haben beide Schalter den Inhalt 1 und wer-den sie direkt aneinandergereiht, ergibt das 11 - Schleifenende ist erreicht. Die Prüfungerfolgt in der Schleife, nachdem das Lesen erfolgt ist).

if s_eof_m1 = 0 then do

Ist der EOF-Schalter von MISCH1 inaktiv (beim ersten Durchlauf muß er das durchdie Vorbelegung mit 0 sein), wird ein Datensatz aus MISCH1 gelesen (Bei wieder-holtem Lesen in Schleife darf die Datei nach dem Lesen nicht geschlossen werden.Ansonsten würde sie beinm nächsten Lesen wieder geöffnet, und immer nur dererste Satz gelesen werden).

"execio 1 diskr m1"if rc = 2 then do

Wurde EOF beim Lesen von MISCH1 erkannt (RC = 2, RC 0 wird gesetzt,wenn der Lesebefehl einen Satz geliefert hat), wird der Schalter für EOF-Er-kennung von MISCH1 auf aktiv gesetzt. Damit die späteren Vergleiche ohneRücksicht darauf, welche der beiden Dateien zuerst fertig war, immer gleich-bleibend durchgeführt werden können, wird die Hilfsvariable, die zum Artikel-nummernvergleich genutzt wird, auf HIGH VALUE gesetzt (FFhex, alle Bitseines Byte zeigen auf 1. Dieser Wert ist größer als jede vorstellbare Zahl, die ineinem Byte abgebildet werden kann - LOW VALUE = 00hex).

s_eof_m1 = 1art_m1 = "ff"x

endelse do

Wurde kein EOF erkannt, hat EXECIO einen Datensatz von MISCH1 in denStack gelegt.

parse pull satz_m1art_m1=left(satz_m1,4)s_les_m1 = 0Der Satz wird aus dem Stack entnommen und in der Variablen SATZ_M1 hin-terlegt. In die Hilfsvariable zum Artikelnummernvergleich werden die erstenvier Byte des Satzes kopiert. Der Leseschalter für MISCH1 wird auf inaktiv (0 =

Dat

eive

rarb

eitu

ng

, Les

en u

nd

Sch

reib

en

MISCH

Page 45: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 151

NEIN) gesetzt, da erst in der Mehrfachabfrage entschieden werden kann, auswelcher der beiden Dateien nachzulesen ist.

endendif s_eof_m2 = 0 then do

"execio 1 diskr m2"if rc = 2 then do

s_eof_m2 = 1art_m2 = "ff"x

endelse do

parse pull satz_m2art_m1=left(satz_m2,4)s_les_m1 = 0

endendFür MISCH2 wird genauso verfahren wie für MISCH1. Die Details können wir uns hiersparen.

if s_eof_m1!!s_eof_m2 = 11 then leave

Beide EOF-Schalter sind aktiv. Die Verarbeitung ist fertig.

selectWir treffen die Entscheidung über den weiteren Verlauf.

when art_m1 < art_m2 then doqueue satz_m1s_les_m1 = 1Die kleinere Artikelnummer liegt im MISCH1-Satz. Dementsprechend wird derMISCH1-Satz in den Stack gelegt (zum Schreiben vorbereitet) und der Schal-ter, der das Lesen von MISCH1 steuert, wird aktiviert.

endwhen art_m1 > art_m2 then do

queue satz_m2s_les_m2 = 1Die kleinere Artikelnummer liegt im MISCH2-Satz. Dementsprechend wird derMISCH2-Satz in den Stack gelegt (zum Schreiben vorbereitet), und der Schal-ter, der das Lesen von MISCH2 steuert, wird aktiviert.

end

Dateiverarb

eitun

g, L

esen u

nd

Sch

reiben

MISCH

Page 46: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E152

otherwise doqueue satz_m1queue satz_m2s_les_m1 = 1s_les_m2 = 1Die beiden Artikelnummern sind gleich. Es werden beide Sätze in den Stackgelegt, die Schalter für das Nachlesen beider Dateien werden aktiviert.

endendIm Stack liegt zum Schreiben vorbereitet: Der Satz aus MISCH1, oder der Satz ausMISCH2, oder beide, sofern ihre Artikelnummern gleich waren.

"execio" queued() "diskw m3"

Alles, was im Stack liegt (1 oder 2 Sätze) wird in die Ausgabedatei geschrieben. DieDatei darf hier nicht geschlossen werden, da sie sonst beim nächsten Durchlauf wiedergeöffnet , und die darin liegenden Sätze überschrieben werden würden.

endHierhin kommen wir, wenn die Verarbeitungsschleife wegen der Bedingung "in beiden Datei-en EOF erkannt" verlassen wird (IF S_EOF_M1!!S_EOF_M2 = 11 THEN LEAVE).

"execio 0 diskr m1 (finis""execio 0 diskr m2 (finis""execio 0 diskw m3 (finis"Alle drei Dateien werden geschlossen (Anzahl zu verarbeitender Sätze ist 0, wichtig für uns istnur FINIS).

"free dd(m1 m2 m3)"Die Einträge in der Zuweisungstabelle unter M1, M2 und M3 werden entfernt.

exit

MISCH

Dat

eive

rarb

eitu

ng

, Les

en u

nd

Sch

reib

en

Page 47: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 153

Jetzt legen wir noch eins drauf: Werden gleiche Artikelnummern in beiden Dateienerkannt, darf nur der Datensatz aus MISCH1 in die Ergebnisddatei geschrieben wer-den. Der Satz aus MISCH2 muß übersprungen werden. Ein Fehlerprotokoll muß er-zeugt und im Mitglied MISCHF der gleichen Datei hinterlegt werden. Traten keinegleichen Artikelnummern auf, sollte auch dies in der Protokolldatei erkennbar sein.

Gleichzeitig ändern wir unser Programm ein wenig ab und nutzen bei der Kodierungdie Vorzüge von REXX, was den Ablauf wesentlich vereinfacht. Die beiden Eingabe-dateien hinterlegen wir in Variablenstämmen, das Protokoll bereiten wir in einemVariablenstamm vor, die Ergebnisdatei wird im Stack zum Schreiben vorbereitet.

/* REXX * MISCH **************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Mischt zwei Eingaben zu einer neuen *//* Ausgabe zusammen. Mischkriterium ist *//* eine Artikelnummer in Stelle 1 bis 4 *//* der Eingabedateien. Gleiche Artikel- *//* nummern werden in einer Protokoll- *//* datei mitgeschrieben */parse upper arg trace .if trace="T" then trace '?r'else msg(off)"free fi(m1 m2 m3 m4)""alloc fi(m1) da(mvs.data(misch1)) shr reuse""alloc fi(m2) da(mvs.data(misch2)) shr reuse""alloc fi(m3) da(mvs.data(mische)) old""alloc dd(m4) da(mvs.data(mischf)) old"

Die vier Datenbestände werden zugewiesen.

"execio * diskr m1 (stem m1. finis)""execio * diskr m2 (stem m2. finis)"MISCH1 und MISCH2 werden in die Variablenstämme M1. und M2. überführt.

neu=m1.0+1m1.neu="ff"xneu=m2.0+1m2.neu="ff"xAn beide Stämme wird ein Satz FFhex angehängt. Dies erlaubt uns, die EOF-Prüfung zuvernachlässigen. Ist ein Stamm zu Ende verarbeitet, trifft er auf den FF-Satz (wird in COBOLbeispielsweise HIGH VALUE genannt - alle Bits eines Byte sind aktiv). FFhex ist größer alsjede denkbare Artikelnummer (die größte darstellbare Zahl in einem Byte wäre die 9 - F9hex),

MISCH

Dateiverarb

eitun

g, L

esen u

nd

Sch

reiben

Page 48: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E154

sodaß der noch zu verarbeitende Stamm immer die kleinere Artikelnummer enthalten wird.Zeigen beide Stämme auf ihren FF-Satz, ist Programmende erreicht.

x=1y=1z=0Die Zeiger für MISCH1 (X) und MISCH2 (Y) werden auf 1 gesetzt und zeigen auf den jeweilsersten Satz des Stammes. Der Zeiger für den Protokoll-Stamm wird mit 0 vorbelegt, da nochnicht sicher ist, ob ein Protokollsatz (zwei gleiche Artikelnummern in beiden Stämmen gefun-den) zu erzeugen ist.

do foreverselect

when left(m1.x,4) < left(m2,y,4) then doqueue m1.xx=x+1

War der Satz des MISCH1-Stammes kleiner, wird er in den Stack gelegt undder Zeiger des MISCH1-Stammes wird erhöht.

endwhen art_m1 > art_m2 then do

queue satz_m2s_les_m2 = 1War der Satz des MISCH2-Stammes kleiner, wird er in den Stack gelegt undder Zeiger des MISCH2-Stammes wird erhöht.

endotherwise do

Hier wissen wir zunächst mal, daß beide Sätze gleich sind. Entweder wegengleicher Artikelnummern, oder weil beide Stämme auf ihren FF-Satz zeigen.

if m1.x = "ff"x then leaveZeigt einer der beiden Stämme auf seinen FF-Satz, muß es der andere zwangs-läufig auch tun. Wir hätten Programmende erreicht und können die Schleifeverlassen.

queue m1.xz=z+1m3.z="Satz doppelt (M1:" x "M2:" y "M2-Satz folgt"z=z+1m3.z=m2.yx=x+1

MISCH

Dat

eive

rarb

eitu

ng

, Les

en u

nd

Sch

reib

en

Page 49: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 155

y=x+1Wäre Gleichheit wegen übereinstimmender Artikelnummern gewesen, mußVerarbeitung stattfinden: Der MISCH1-Satz wird in den Stack gelegt. Zwei Sät-ze für den Protokollstamm werden gebildet. Der erste davon enthält einen Feh-lerhinweis, welcher Satz aus MISCH1 mit welchem Satz aus MISCH2 überein-stimmt. Der zweite Satz enthält den doppelten MISCH2-Satz. Anschließendwerden die Zeiger beider Eingabestämme erhöht. Auf diese Weise wird, wasdie Ergebnisdatei angeht, der Satz aus MISCH2 überlesen.

endend

endqueueWir hinterlegen Pseudo-EOF im Stack.

if z = 0 then m3.1 = "Keine doppelten Sätze erkannt"

Steht der Zeiger Z immer noch auf 0, wurden nie doppelte Artikelnummern erkannt. Ein ent-sprechender Satz für die Protokolldatei wird erzeugt.

"execio * diskw m3 (stem m3. finis""execio * diskw m4 (finis"Die Protokolldatei wird geschrieben, Eingabe ist der Stamm E3.. Die Ergebnisdatei wird ge-schrieben, Eingabe ist der Stack.

exit

MISCH

Dateiverarb

eitun

g, L

esen u

nd

Sch

reiben

Page 50: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E156

Wenden wir uns dem letzten zu, was uns die Dateiverarbeitung anzubieten hat: demUPDATE. Unter UPDATE verstehen wir nicht, eine Datei komplett in den Stack zulesen, dort einzelne Sätze zu verändern und anschließend den gesamten Stack wie-der zu schreiben und dabei die alte Datei zu überschreiben. Vielmehr geht es darum,eine Datei satzweise zu lesen, zu erkennen, ob der gelesene Satz verändert werdensoll, die Änderung durchzuführen und den Satz in seiner geänderten Form dorthin indie Datei zurückzubringen, von wo er gelesen wurde.

Das folgende Beispiel zeigt ein Programm, das in einer Artikeldatei nach bestimmtenSätzen sucht und für diese Sätze eine Preisaktualisierung durchführt. Wir unterstel-len, daß die Datensätze in den ersten sechs Stellen eine Artikelnummer enthalten.Die Änderung könnte über eine gezielte Artikelnummer oder einen Teil davon (Einga-be 01 soll alle Sätze aktualisieren, bei denen die Artikelnummer mit 01 beginnt) erfol-gen. Der Preis steht rechtsbündig zwischen den Stellen 56 und 65.

/* REXX * PREISUPD ***********************************//* Erstellt: 05.94 Wittemann *//* Zweck: Rechnet einen prozentualen Auf- oder *//* Abschlag für angegebene Artikel- *//* nummern. Gesucht wird nach voll- *//* ständigen Artikelnummern oder Teilen *//* (linksbuendig) davon. Wird ein zu *//* aendernder Satz gefunden, wird er *//* geaendert in die Datei zurueckge- *//* schrieben.arg test .if test = 'T' then trace '?r'dmy=msg(off)"free dd(upd)""alloc dd(upd) da(mvs.data(preise)) old"clearsay center('< Preis-Aenderungsdienst >',79,'-')say;say;saysay "Artikelnummer eingeben (mindestens 2 Stellen)"do until datatype(artnum) = 'NUM' & length(agru) <= 2

say "(numerisch)"parse external artnum

Die Schleife wird durchlaufen, bis ein mindestens zweistellig numerischer Wert inARTNUM empfangen wird.

end

PREISUPD

Dat

eive

rarb

eitu

ng

, Up

dat

e

Page 51: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 157

artnum=trunc(artnum)Sofern ARTNUM ein Dezimalbruch ist, werden die Kommastellen abgeschnitten.

say;saysay "Preisaenderung in %"do until datatype(aend)='NUM'

say "(z.B.: 5.7 oder -3)"parse external aendWir übernehmen jeden beliebigen positiven oder negativen numerischen Wert (ein posi-tiver Wert führt zu einem prozentualen Aufschlag, ein negativer Wert sorgt für eine pro-zentuale Reduzierung des ursprünglichen Betrages).

endergebnis="Kein Satz zu Nummer" artnum "gefunden."satzupd=0

Zwei Variable werden vorbelegt. ERGEBNIS wird in dieser Form ausgegeben, wenn kein zuverändernder Satz gefunden wird. SATZUPD enthält bei Programmende die Anzahl der ge-änderten Sätze.

"execio 1 diskru upd"Hier mal eine andere Variante. Es wird zunächst ein Satz vorgelesen. War das Lesen erfolg-reich, wird RC 0 ausgegeben und die WHILE-Schleife wird betreten. Anderenfalls wird dieSchleife übersprungen (RC war 2, Eingabedatei war leer).

do while RC = 0Wir haben einen Datensatz empfangen und entnehmen ihn aus dem Stack.

parse pull satzAls nächstes müssen wir erkennen, ob der empfangene Satz verändert werden mußoder nicht.

if left(satz,length(artnum)) = artnum then dopreisneu=substr(satz,56,10) * ((100 + aend) / 100)satz=left(satz,55)!!right(format(preisneu,,2),10)queue satz"execio 1 diskw upd"satzupd=satzupd + 1

Hier wird die Aktualisierung durchgeführt. Zunächst wird der alte Preis aus demDatensatz über SUBSTR() isoliert und entsprechend multipliziert. Das Rechener-gebnis wird in der Variablen PREISNEU hinterlegt. Der Datensatz wird neu aufge-baut. Die ersten 55 Stellen werden unverändert übernommen. Daran angefügt wirdrechtsbündig auf 10 Stellen der neue Preis (formatiert auf 2 Dezimalstellen unter

PREISUPD

Dateiverarb

eitun

g, U

pd

ate

Page 52: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E158

Berücksichtigung der Regeln des arithmetischen Rundens). Der geänderte Daten-satz wird zurück in den Stack gestellt und an die Stelle der Datei zurückgeschrie-ben, von welcher der letzte Datensatz gelesen wurde. Der Zähler für geänderteSätze wird erhöht.

end"execio 1 diskru upd"In der Schleife erfolgt ein Nachlesen. Anschließend wird an den Schleifenkopf zurückge-kehrt und die WHILE-Bedingung wird aufs neue überprüft. Wurde erneut ein Satz gele-sen, wird die Schleife wieder betreten. Führte das Lesen zum Erkennen des EOF-Ereig-nisses, wird RC 2 ausgegeben, die Schleife wird verlassen.

end"execio 0 diskw upd (finis""free dd(upd)"

Die Datei wird geschlossen, der symbolische Eintrag UPD wird aus der Zuweisungstabelleentfernt.

if satzupd > 0 thenergebnis="Durchgefuehrte Aenderungen:" satzupd

Sofern wenigstens eine Änderung durchgeführt wurde, wird der ERGEBNIS-Test geändert.

say ergebnisexit

Das zweite Update-Beispiel soll undokumentiert auf Sie wirken. Versuchen Sie selbstnachzuvollziehen, was in diesem Programm vor sich geht.

/* REXX * TELUPD *************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Fuehrt Aenderungen im Telefonver- *//* zeichnis durch. Gesucht wird nach *//* Namen oder Namensteilen. Geaendert *//* werden kann die Telefonnummer. */"alloc dd(upd) da(mvs.data(telefon2)) shr reuse"clearsay center( ' Aenderung Telefonverzeichnis ',79,'*'say;saysay 'Suchbegriff eingeben'parse upper external such

TELUPD

Dat

eive

rarb

eitu

ng

, Up

dat

e

Page 53: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 159

say 'Neue Telefonnummer'parse external aendc_found=0c_change=0do forever

"execio 1 diskru upd"if rc=2 then leaveparse pull telefonx=translate(left(telefon,79))if index(x,such) > 0 then do

gefunden=gefunden+1clearsay center(' Aenderung Telefonverzeichnis ',79,'*')say;saysay telefonsay copies(' ',index(x,such)-1)copies('-',length(such))say copies(' ',index(x,such)-1)aendsaysay 'Aenderung durchfuehren (J/N)'do until ant = 'J' ! ant = 'N'

pull antendif ant = 'J' then do

neu1=overlay(aend,telefon,index(x,such))if pos(such,x) <= 51 then

blank=copies(' ',51-pos(such,x)-length(aend))else

blank=copies(' ',28-pos(such,x),length(such))neu2=overlay(aend!!blank,telefon,pos(such,x))say 'Alt:' telefonsay 'U =>' neu1say 'R =>' neu2say 'U=Ueberlagern oder R=ueberlagern undRestfeld loeschen'do until ant = 'R' ! ant = 'U'

pull antendif ant = 'U' then neu=neu1

TELUPD

Dateiverarb

eitun

g, U

pd

ate

Page 54: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E160

else neu=neu2say 'Aenderung in Datei eintragen (J/N)'do until ant = 'J' ! ant = 'N'

pull antendif ant = 'J' then do

queue neu"execio 1 diskw upd"say 'geaendert'copies(' ',40)'<ENTER>'parse externalgeaendert=geaendert+1

endendelse say 'keine Aenderung'

endend"execio 0 diskw upd (finis"saysay center('Gefundene Datensaetze.:' gefunden,79)say center('Geaenderte Datensaetze:' geaendert,79)saysay center(' Ende der Verarbeitung ',79,'*')exitD

atei

vera

rbei

tun

g, U

pd

ate

TELUPD

Page 55: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 161

Im folgenden sehen wir uns ein paar Beispiele für Job-Control-Verarbeitung mit REXXan. Im ersten Beispiel werden die JCL-Sätze für einen Druckauftrag mit IEBGENERim Stack hinterlegt und zur Ausführung an den INTERNAL READER übergeben.

/* REXX * JCLPRT1 ************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Hinterlegt JCL-Saetze im Stack und *//* fuehrt SUBMIT-Befehl aus. */CLEARarg test .if abbrev(test,'T',1) then trace('?r')Über einen Startparameter kann der Ablaufverfolger aktiviert werden, sofern der Parametermit einem T beginnt.

else dmy=msg("OFF")

Wurde der Ablaufverfolger nicht aktiviert, werden die Meldungen der TSO-Kommandos unter-drückt. MSG() ist eine erweiterte TSO-Funktion. Damit der Interpreter nicht "Funktion außer-halb Befehl - RC -3" reklamiert, machen wir REXX-seitig eine Variablenzuweisung daraus.

say 'Abrechnungsnummer (JOB-ACCOUNT) angeben:'pull actnumDie Abrechnungsnummer wird abgefragt und in der Variablen ACTNUM abgelegt. Eine Prü-fung der Variablen macht nicht viel Sinn. Für eine vernünftige Prüfung müßten wir Zugriff aufeine systemnahe Datei haben, in der alle gültigen Account-Nummern und Mitarbeiter ver-zeichnet sind. Dieser Zugriff wird uns sicher verweigert werden. Im täglichen Leben würdenwir die Accountnummer nicht abfragen, sondern über einen Dialog Manager Service selbstermitteln.

do foreverDie äußere Schleife erlaubt die mehrfache Ausführung des Programmes. Beliebig vieleDateien können gedruckt werden. Die Abrechnungsnummer brauchen wir dazu nur ein-mal.

say 'Druckdatei vollqualifiziert (ENTER = Ende)'do forever

pull dateiif length(datei) = 0 then exitif sysdsn("'"datei"'")=OK then do

dmy=listdsi("'"datei"'")select

when sysdsorg >< "PS" & sysdsorg >< "PO" ,then msg = "Dateiorganisation falsch"

when sysdsorg="PO" & right(datei,1) >< ")" ,

JCLPRT1

Job

-Co

ntro

l-Verarb

eitun

g

Page 56: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E162

then msg = "Memberangabe fehlt"when syslrecl > 132 then

msg = "Satzlaenge zu groß"otherwise leaveHier haben wir eine Stelle erreicht, an der wir wissen: Die Datei ist verfüg-bar. Ihre Satzlänge überschreitet 132 Byte nicht. Ihr Format entspricht POoder PS. Sofern es eine PO-Datei ist, wurde auch ein Mitgliedsname an-gegeben. Jetzt können wir die Schleife für Dateinamensangabe verlas-sen und im Programm fortfahren.

endendelse msg='Datei nicht verfuegbar'say msg", Eingabe wiederholen."Irgend etwas spricht gegen eine Verarbeitung. Der Grund dafür wurde, wo er zwei-felsfrei erkannt worden ist, in der Variablen MSG hinterlegt. Wir geben den Hinweisaus, und der Bediener kann einen neuen Eingabeversuch starten.

endsay 'Ausgabeklasse angeben (A, R oder X - ENTER=X)'pull oklif okl >< 'A' & okl >< 'R' then okl='X'Die Druckausgabeklasse wird abgefragt. Wir gehen davon aus, daß jedem Mitarbeiterdie Ausgabeklassen A, R und X gestattet sind. Zudem wird in vielen Unternehmen überdie Klasse A Druckausgabe außerhalb des Rechenzentrums, auf von JES gesteuertenAbteilungsdruckern ermöglicht. Falsche Eingaben haben Klasse X zur Folge.

if okl = "A" then dosay "JES-Local-Drucker (ENTER = SPOOL)"pull rmtif length(rmt) > 0 then okl = okl",DEST="rmtHat sich der Anwender für Klasse A entschieden, fragen wir, ob er Ausgabe aufeinem Abteilungsdrucker wünscht. In diesem Fall hätte er den Namen des Druckersanzugeben. Der Inhalt der Variablen OKL wird in diesem Fall um die Angabe",DEST=druckername" ergänzt und braucht in der JCL nicht weiter berücksichtigtzu werden. Wird kein Druckername angegeben, erfolgt die Ausgabe auf RZ-Druk-ker in der Ausgabeklasse A. Den Namen des Druckers zu prüfen, ist im allgemei-nen nicht möglich (siehe Accountnummer).

endsay 'Druckzeilen je Seite (40 bis 72, ENTER = 60):'pull zeilen

JCLPRT1

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 57: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 163

if zeilen < 40 ! zeilen > 72 then zeilen=60Normalerweise werden von allen Dienstprogrammen bei Druckausgabe maximal 60 Druck-zeilen je Seite ausgegeben, dann erfolgt Seitenwechsel. Wir wollen unserem Anwenderhier etwas mehr Service bieten und erlauben ihm, selbst zu bestimmen, wieviele Zeilenje Seite er will. Über- oder unterschreitet er aber die erlaubte Bandbreite, wird der Stan-dardwert 60 angenommen (dies wäre auch dann der Fall, wenn der Bediener nur Edrückt).

say 'Anzahl Exemplare (1 - 99, ENTER = 1):'pull anz_ausgif anz_ausg < 1 ! anz_ausg > 99 then anz_ausg = 1Sofern unser Anwender mehr als ein Druckexemplar wünscht, kann er hier seine Einga-be machen. E oder Falscheingabe hat eine Ausgabe zur Folge.

"newstack"

Für den Fall, daß das Programm von einem anderen Programm aufgerufen wurde unddieses Programm bereits Daten im Stack liegen hat, die später noch gebraucht werden(diese Daten würden sehr wahrscheinlich auch unsere JCL-Verarbeitung zum Scheiternbringen), erzeugen wir einen neuen Stackbereich, den wir nach Ausführung des SUB-MIT-Befehles wieder eliminieren können.

name=userid()!!substr(time(),8,1)queue "//"name" JOB ("actnum"),NOTIFY="userid()queue "/*JOBPARM LINECT="zeilenAuf diese Weise steuern wir den Seitenwechsel. Diese Anweisung geht unmittelbar anJES2.

queue "//DRUCK EXEC PGM=IEBGENER"queue "//SYSUT1 DD DSN="datei",DISP=SHR"queue "//SYSUT2 DD SYSOUT="okl",COPIES="anz_ausgqueue "//SYSIN DD DUMMY"queue "//SYSPRINT DD DUMMY"queue "XX"Der letzte Eintrag in den Stack ist das Endekriterium für den SUBMIT-Befehl (EOF-Ereig-nis beim Auslesen des Stack).

"submit * end(XX)"say 'Job' name 'gestartet um' substr(time(),1,5)"delstack"Die Übergabe der JCL ist erledigt. Der erzeugte Stack ist wieder entfernt. Das Programmkehrt zum Kopf der äußeren Schleife zurück. Ein neues Spiel, ein neues Glück.

endexit

JCLPRT1

Job

-Co

ntro

l-Verarb

eitun

g

Page 58: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E164

Sehen wir uns das gleiche Beispiel an, bei dem die JCL in einem Variablenstammvorbereitet wird. Der Variablenstamm wird in eine Datei geschrieben. Der Benutzerkann bei Programmende entscheiden, ob die JCL sofort ausgeführt und die Dateiwieder gelöscht werden soll, oder ob keine sofortige JCL-Ausführung gewünscht istund die Datei erhalten bleibt. Für dieses Beispiel führen wir keine so aufwendigePlausibilitätsprüfung durch. Es geht uns hier nur ums Prinzip:

/* REXX * JCLPRT2 ************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Schreibt JCL-Saetze in eine Datei *//* und fuehrt, sofern gewollt, den *//* SUBMIT-Befehl auf diese Datei aus. */arg trace .if trace="T" then trace '?r'dmy=msg(off)CLEARsay 'Abrechnungsnummer (fuer Job-Account)'parse external actnumsay 'Druckdatei angeben (ENTER = Ende)'parse upper external dateiif datei='' then exitWird hier nur E gedrückt, wird das Programm beendet. Jede andere Form der Eingabe wirdhingenommen und als Dateiname in die Job Control eingebaut. Macht der Anwender hierFehler, wird dies zum späteren Abbruch der Job Control führen.

say 'Ausgabeklasse angeben (A, R oder X)'parse upper external oklif okl >< 'A' & okl >< 'R' then okl = xif okl = "A" then do

say "JES-Local-Drucker (ENTER = SPOOL)"pull rmtif length(rmt) > 0 then okl = okl",DEST="rmt

endsay 'Druckzeilen je Seite (40 bis 72):'parse external zeilenif zeilen < 40 ! zeilen > 72 then zeilen=60say 'Anzahl Ausgaben (bis 99, ENTER = 1)"parse external anz_ausgif anz_ausg < 1 ! anz_ausg > 99 then anz_ausg = 1

JCLPRT2

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 59: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 165

jname=userid()!!substr(time(),8,1)x.1="//"jname" JOB ("actnum"),NOTIFY="userid()Die JOB-Anweisung müssen wir in beiden Fällen erzeugen. Zwar würde prinzipiell eine Stan-dard-Jobanweisung beim SUBMIT erzeugt werden, sofern keine kodiert wurde. In unseremFall würde aber die JES2-Steueranweisung "verlorengehen".

x.2="/*JOBPARM LINECT="zeilenx.3="//DRUCK EXEC PGM=IEBGENER"x.4="//SYSUT1 DD DSN="datei",DISP=SHR"x.5="//SYSUT2 DD SYSOUT="klasse",COPIES="anz_ausgx.6="//SYSIN DD DUMMY"x.7="//SYSPRINT DD DUMMY"Die Job Control ist im Variablenstamm X. vorbereitet.

if sysdsn(sub.druck)="OK" then "del sub.druck""free fi(jcl) attr(dcb)""attr dcb blksize(4000) lrecl(80) recfm(f b) dsorg(ps)""alloc dd(jcl) da(sub.druck) new unit(sysda) block(1)using(dcb)"Sofern die Datei, welche die Job Control aufnehmen soll, aus einer früheren Verarbeitungexistiert, wird sie gelöscht, um im ALLOC-Befehl wieder erstellt werden zu können.

"execio * diskw jcl (stem x. finis)"say "S (fuer SUBMIT und Loeschen), ENTER (kein SUBMIT)"parse upper antwif antw = "S" then do

"submit sub.druck""del sub.druck"say 'Job' name 'gestartet um' substr(time(),1,5)Hat sich der Anwender für sofortige Verarbeitung entschieden, wird der Inhalt der Dateian den INTERNAL READER des JES übergeben. Die Datei wird anschließend gelöscht.

Hat der Anwender dagegen nicht mit s auf die Frage geantwortet (was immer er sonstgetippt hat, ist egal), bleibt die Datei erhalten und es gibt zunächst keine weitere Verar-beitung. Die erzeugte Datei könnte zu einem späteren Zeitpunkt noch per SUBMIT-Be-fehl an das JES übergeben oder editiert werden.

endexit

Soweit ganz einfach. Bauen wir eine JCL-Verarbeitung, bei der die Ausgabe nicht

JCLPRT2

Job

-Co

ntro

l-Verarb

eitun

g

Page 60: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E166

über SPOOL, sondern auf Datei laufen soll und nehmen wir zur Demonstration denSort des MVS, ICEMAN. Die Problemstellungen vorweg. Die Sort-Ausgabe könntetheoretisch vorhanden sein oder nicht. Abhängig davon müßten wir die Datei-Dispo-sition auf der JCL-Seite steuern. Kein Problem, aber: eine bereits vorhandene Aus-gabedatei würde, was Dateiaufbau und -größe angeht, sehr wahrscheinlich nicht ver-nünftig zu dem Format passen, das wir als Sorteingabe nutzen. Sinnvoll ist also,dafür zu sorgen, daß die Sortausgabedatei neu erstellt werden kann. Die Datei-Kenn-größen könnten in diesem Fall von der Eingabedatei übernommen werden. Die Sor-tierkriterien sollten ebenfalls flexibel sein. Dem Anwender sollte innerhalb gewisserGrenzen überlassen werden, wieviele Sortierfelder er bestimmen will. Eines ist Pflicht,bis zu vier sind möglich. Dabei können wir nicht davon ausgehen, daß der Anwenderdie Details der Syntax einer SORT FIELD-Anweisung wissen muß.

/* REXX * JCLSORT ************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Hinterlegt JCL-Saetze im Stack und *//* fuehrt SUBMIT-Befehl aus. *//* Die Eingabedatei ist variabel, die *//* Ausgabedatei beginnt mit der USERID *//* des Anwenders, fuehrt im zweiten *//* Namensteil SORTAUS, der Rest ist *//* wahlfrei. *//* Bis zu vier Sortfelder koennen *//* definiert werden. */parse upper arg trace .if trace="T" then trace '?r'else msg("OFF")name_ok='ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890$§#.()'CLEARsay 'Sort-Eingabedatei vollqualifiziert (ENTER=Ende)'do forever

parse upper external sort_einif length(sort_ein)=0 then exitdatei=sort_eincall namcheck

Hier mal was anderes: eine Subroutine prüft vorab, ob der Name der SORT-Eingabeda-tei aus gültigen Zeichen besteht (siehe in der Subroutine).

JCLSORT

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 61: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 167

if fehl = 0 then doif sysdsn("'"sort_ein"'") >< 'OK' then do

msg='Datei oder Member nicht verfuegbar'fehl=1Wir haben erkannt, daß die Eingabedatei nicht zur Verfügung steht.

endelse do

listdsi("'"sort_ein"'")fehl=1select

when right(sort_ein,1) >< ")" & ,sysdsorg='PO' , then

msg='Mitgliedsangabe fehlt'when sysdsorg >< "PS" & sysdsorg >< "PO" then

msg='Falsche Dateiorganisation"otherwise fehl=0

endend

endif fehl=0 then leavesay msg ', bitte nochmal'

endsay 'Letzten Qualifier der Ausgabedatei'say userid()'.SORTAUS.???????? angeben'parse upper external sort_aus .name_ok=substr(name_ok,1,length(name_ok)-2)datei=sort_auscall namcheckif fehl=1 ! length(sort_aus) = 0 ! ,

datatype(left(aus,1),u)=0 ! length(sort_aus) > 8 ,then dosay 'Name formal falsch, STANDARD wird angenommen'aus='STANDARD'

endHier ist der Name der Ausgabedatei bekannt. Sie beginnt in jedem Fall mit userid.SORTAUS.Der letzte Namensteil wurde vom Anwender gebildet. Hat der Anwender nur E gedrückt,falsche Zeichen für den Namen genutzt, oder ist der letzte Qualifier länger als acht Byte, wirdals letzter Qualifier STANDARD eingesetzt.

JCLSORT

Job

-Co

ntro

l-Verarb

eitun

g

Page 62: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E168

if sysdsn('sortaus.'sort_aus)='OK' then"del sortaus."aus

Sofern die Ausgabedatei aus einem früheren Sortlauf noch vorhanden wäre, wird sie ge-löscht. Dies gibt uns die Möglichkeit, die Datei im JCL-Lauf in jedem Fall neu anzulegen unddie Kenngrößen der Ausgabedatei von der Eingabedatei zu übernehmen.

x=1folge=''Es folgt die Schleife, die uns die Sortierkriterien liefert. Einmal muß die Schleife zwingenddurchlaufen werden. Maximal fünf Durchläufe sind möglich. Ab dem zweiten Durchlauf kannder Anwender aus der Schleife vorzeitig aussteigen.

do forever /* abholen der Sortierkriterien */say 'Beginn Sortierfeld' x ' (1 bis' syslrecl')'if x > 1 then say '(ENTER = Ende Kriterienabfrage)'

Im ersten Durchlauf wird der Bediener nach dem Beginn des Sortierfeldes gefragt (Stelle1 bis Satzlänge). Ab dem zweiten Durchlauf erhält er einen Hinweis, daß er an dieserStelle die Abfrageschleife nach den Sortkriterien verlassen kann, sofern er keine weite-ren Sortierfelder mehr anzugeben hat.

do foreverparse external von .select

when length(von)=0 & x > 1 then dokrit='ENDE'leaveWurde nach dem ersten Durchlauf nur E gedrückt, wird der SchalterKRIT auf ENDE gesetzt und die aktive Schleife (Abfrage Sortierfeld-Be-ginnposition) wird verlassen.

endwhen datatype(von) ^= 'NUM' then

msg='Eingabe nicht numerisch'when von < 1 ! von > syslrecl then

msg='Beginnposition falsch'otherwise leave

endendif krit='ENDE' then leaveDiese Bedingung kann frühestens beim zweiten Durchlauf erfüllt werden. Wird keineweitere Sortierfeldangabe vom Anwender mehr gewünscht, wird die aktuelle Schleife(Abfrage der Sortierfelder) verlassen.

JCLSORT

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 63: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 169

if von=syslrecl then dosay 'Beginn=Satzende, Laenge wird auf 1 gesetzt'lang=1

endelse do

max=syslrecl-von+1say 'Laenge des' x'. Sortierfeldes, max.' maxparse external lang .if lang < 1 ! lang > max then do

say 'Falsche Laengenangabe, 1 wird angenommen'lang=1

endendHier haben wir die Sortierfeldlänge. War Feldbeginn auf Satzende oder in der Längenan-gabe ein Fehler (E durch den Anwender oder Feld überschreitet Satzlänge), wird dieLänge des Sortierfeldes auf ein Byte festgelegt.

do until richt = 'A' ! richt = 'D'say 'Sortierrichtung: A=Aufsteigend, D=Absteigend'parse upper external richt .

endDer Anwender entscheidet die Sortierrichtung.

s_folge=s_folge','von','lang',CH,'richtDie empfangenen Werte eines Durchlaufes werden in der Variablen S_FOLGE fortge-schrieben. Als Datenformat wird dabei immer CH (Character) angenommen. Viele An-wender sind überfordert, würden sie nach Datenformaten wie character, packed oderunpacked decimal gefragt werden.

x=x+1Der Durchlaufzähler wird erhöht.

if x > 4 then leaveWar dies bereits der vierte Durchlauf, wird die Schleife verlassen.

endWir haben im Grunde alle Informationen zusammengetragen, die wir brauchen. Einige Infor-mationen müssen aber noch umgewandelt werden. LISTDSI liefert bei Abfrage der Werte fürdas Einrichten CYLINDER, TRACKS oder BLOCKS. Wir müssen in der JCL aber CYL, TRKoder die Blocklänge angeben, wenn wir für die Ausgabedatei gleiches erreichen wollen.

JCLSORT

Job

-Co

ntro

l-Verarb

eitun

g

Page 64: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E170

selectwhen sysunits='CYLINDER' then unit='CYL'when sysunits='TRACK' then unit='TRK'otherwise unit=sysblksize

endpri=sysprimarysec=syssecondsDie Werte der Erst- und Zweitzuweisung werden mehrfach benötigt und hier nur der Faulheitwegen in kürzer benannte Variablen übertragen.

s_folge=space(substr(s_folge,2,length(s_folge)),0)Zum einen müssen wir das führende Komma von S_FOLGE abschneiden. Zum anderen ent-fernen wir alle Blanks aus der Sortierfolge (hätte der Bediener beispielsweise bei der Sortier-richtung ein führendes Blank mitgetippt, wäre es mit übernommen worden).

queue "//SORT EXEC PGM=ICEMAN"queue "//SORTIN DD DSN="ein",DISP=SHR"queue "//SORTOUT DD DSN="userid()'.SORTAUS.'aus","queue "// DISP=(NEW,CATLG,DELETE),"queue "// UNIT=SYSDA,VOL=SER="sysvolume","queue "// SPACE=("unit",("pri","sec"))"do cnt=1 to 4

queue "//SORTWK0"cnt "DD UNIT=SYSDA,"queue "// SPACE=("unit",("pri","sec"))"

endqueue "//SYSOUT DD SYSOUT=*"queue "//SYSIN DD *"queue " SORT FIELDS=("folge")"queue "XX"Die JCL ist im Stack hinterlegt. Das Endekriterium für den SUBMIT-Befehl wurde als letztes inden Stack geschrieben.

jc=substr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",random(1,26),1)Das JOB-CHARACTER-Byte wird per Zufallsgenerator ausgewählt.

"submit * end(XX) jobchar("jc")"Die Übergabe der JCL-Sätze an den INTERNAL READER des Spools ist erfolgt. Programm-ende.

exit/* SUBROUTINE NAMCHECK * Prueft DSN auf Sonderzeichen */

JCLSORT

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 65: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 171

namcheck:Die Routine prüft, ob in der Variablen DATEI ausschließlich Zeichen vorkommen, die inder Variablen NAME_OK hinterlegt sind. Wenn nein, wird die Variable FEHL auf 1 ge-setzt, anderenfalls bleibt sie mit 0 gefüllt.

fehl=0do x=1 to length(datei)

if pos(substr(datei,x,1),name_ok)=0 then domsg='Nur A-Z 0-9 $ # ( ) und .'fehl=1leave

endend

return

Das nachfolgende Beispiel wird nicht mehr weiter dokumentiert. Wird der Name einerPO-Datei eingegeben, werden möglich:

● Komprimierung der Datei

● Anpassung des Directories (größer oder kleiner)

● Verkleinerung der Primär- und Sekundärzuweisung

● Vergrößerung der Primär- und Sekundärzuweisung

Das Beispiel soll für sich selbst sprechen:

/* REXX * JCLCOMPR ***********************************//* Erstellt: 05.94 Wittemann *//* Zweck: PO-Reorganisation ohne Beigeschmack */arg trace .if trace="T" then trace '?r'else dmy=msg("OFF")erlaubt='ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890$§#.'CLEARsay center("< Reorganisieren von PO-Dateien >",79,"-")say;saydo forever

say 'PO-Datei ohne userid angeben <ENTER> Ende:'

JCLCOMPR

Job

-Co

ntro

l-Verarb

eitun

g

Page 66: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E172

do foreverparse upper external dateiif datei='' then exitfehl=0do x=1 to length(datei)

if index(erlaubt,substr(datei,x,1))=0 then dofehl=1leave

endendselect

when index(datei,'(') > 0 thenmsg='Keine Mitgliedsangaben erlaubt'

when fehl=1 thenmsg='Nur A-Z, 0-9, $, §, # und . erlaubt'

when sysdsn("'"userid()'.'datei"'") ^= OK thenmsg='Datei nicht vorhanden'

otherwise leaveendsay msg ', bitte nochmal'

endlistdsi("'"userid()'.'datei"'" directory)if sysdsorg ^= PO then

msg='Datei hat kein PO-Format, bitte neu'else do

selectwhen sysunits='CYLINDER' then do

unit='CYL'zuord='Zylinder'

endwhen sysunits='TRACK' then do

unit='TRK'zuord='Spuren'

endotherwise do

unit=sysblksizezuord='Blocks je' sysblksize

endend

JCLCOMPR

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 67: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 173

freesize=(16-sysextents)*syssecondsmaxsize=sysalloc+freesizeif freesize / (maxsize / 100) > 60 then do

option1=2x=2auswahl.0='AB'auswahl.1='A <==> Datei verkleinern'auswahl.2='B <==> Leichen entfernen'

endelse do

option1=1auswahl.0='ABC'auswahl.1='A <==> Datei vergroessern'auswahl.2='B <==> Leichen entfernen'auswahl.3='C <==> Directory erweitern'x=3

endclearsay center('Aktuelle Kenngroessen',79,'-')saysay 'Datei-INFO fuer.......:' sysdsnamesay 'Aktuelle Zuordnung....:' sysalloc zuordsay 'Moegliche Erweiterung.:' freesize zuordsay 'Zugeordnete Extents...:' sysextentssaysay 'Directory-Auslastung'say 'Vorhandene DIR-Blocks.:' sysadirblksay 'Davon belegt..........:' sysudirblksay 'Noch frei.............:' ,

sysadirblk-sysudirblksay 'Platz fuer Member-Eintraege.:' ,

((sysadirblk-sysudirblk) * 5.5) % 1saymsg='Funktionsauswahl (ENTER = Ende)'say center(msg,79,'-')say

JCLCOMPR

Job

-Co

ntro

l-Verarb

eitun

g

Page 68: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E174

do cnt=1 to xsay center(auswahl.cnt,79)

endsaydo forever

parse upper external option2 .if option2 = '' then exitif length(option2)=1 & ,index(auswahl.0,option2) > 0 then leavesay 'Falsche Auswahl - bitte neu:'

endcnt=1x.cnt="//POREORG EXEC PGM=IEBCOPY"if option2='B' then do

input=dateiaus='alt'

endelse do

input='D'substr(date('s'),3,6)'.T',input=input!!space(translate(time(),' ',':'),0)"rename" datei inputaus='neu'

endcnt=cnt+1x.cnt='//SYSUT1 DDDSN='userid()'.'input',DISP=SHR'cnt=cnt+1x.cnt='//SYSUT2 DD DSN='userid()'.'datei','if aus='alt' then do

cnt=cnt+1x.cnt='// DISP=OLD'

endelse do

cnt=cnt+1x.cnt='// DISP=(NEW,CATLG,DELETE),'cnt=cnt+1x.cnt='// UNIT=SYSDA,VOL=SER='sysvolume','if option2 = 'A' then do

JCLCOMPR

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 69: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 175

if option1=1 then doif sysprimary=1 then neuprim=2else neuprim=(sysprimary*1.5) % 1if sysseconds=1 then neusec=2else neusec=(sysseconds*1.5) % 1dir=sysadirblk * 2

endelse do

if sysprimary=1 then neuprim=1else neuprim=sysprimary % 1.5if sysseconds=1 then neusec=1else neusec=sysseconds % 1.5dir=sysadirblk

endendelse dir=sysadirblk * 2cnt=cnt+1x.cnt='// SPACE=('unit',('neuprim','neusec','dir'))'

endcnt=cnt+1x.cnt='//SYSUT3 DD UNIT=SYSDA,'cnt=cnt+1x.cnt='// SPACE=('unit',('sysprimary','sysseconds'))'cnt=cnt+1x.cnt='//SYSUT4 DD UNIT=SYSDA,'cnt=cnt+1x.cnt='// SPACE=('unit',('sysprimary','sysseconds'))'cnt=cnt+1x.cnt='//SYSPRINT DD SYSOUT=*'cnt=cnt+1x.cnt='//SYSIN DD *'cnt=cnt+1x.cnt=' COPY INDD=SYSUT1,OUTDD=SYSUT2'if aus='neu' then do

cnt=cnt+1x.cnt='//ALTDEL EXEC PGM=IEFBR14,'cnt=cnt+1x.cnt='// COND=(0,NE,POREORG)'

JCLCOMPR

Job

-Co

ntro

l-Verarb

eitun

g

Page 70: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E176

cnt=cnt+1x.cnt='//DATEI1 DD DSN='userid()'.'input','cnt=cnt+1x.cnt='// DISP=(OLD,DELETE)'

end/******************************************//* Erzeugen Temporaerdatei für JCL-SUBMIT */if sysdsn(sub.reorg)=OK then "del sub.reorg""free fi(jcl) attr(dcb)""attr dcb blksize(4000) lrecl(80) recfm(f b)dsorg(ps)""alloc fi(jcl) da(sub.reorg) new unit(sysda)block(1) using(dcb)""execio * diskw jcl (stem x. finis""submit sub.reorg""del sub.reorg"say 'Job' name 'ausgefuehrt um', left(time(),5)

endendexit

Wichtig für die Lesbarkeit eines Programmes ist neben einer vernünftigen Logik auchdie Schreibweise. Konsequentes Ein- und Ausrücken der Befehle gehört ebenso dazu,wie auf die Unart zu verzichten, mehrere unterschiedliche Befehle in eine Zeile zukodieren. Wird ein Programm spaltengerade mit allen seinen Befehlen ab Spalte 1kodiert, ist es sehr schwierig, logische Abhängigkeiten zu erkennen, ein fehlendesoder überflüssiges END zu finden und den Verlauf des Programmes am Terminal zuverfolgen (ich meine hier natürlich nicht im Ablauf unter TRACE). Werden völlig un-motivierte Ein- und Ausrückungen gemacht, wird das Ganze noch verrückter.

Im Anschluß deshalb ein Versuch, die unsaubere Schreibweise, sowie zu viele oderzu wenige END von einem REXX-Programm überprüfen zu lassen. Das Ergebniswird in eine sequentielle Datei geschrieben und (wir nehmen uns einfach mal einekleine Anleihe beim Dialog Manager) durch Browse gezeigt.

Daß das Strukturierprogramm keinen lauffähigen Quellencode erzeugt, ist Absicht(sonst würde sich niemand mehr Mühe geben, saubere Programme zu schreiben).Dieses Manko (sofern man es so sieht) kann aber mit ein paar kleinen Änderungenbehoben werden.

JCLCOMPR

Job

-Co

ntr

ol-

Ver

arb

eitu

ng

Page 71: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 177

/* REXX * RXSTRU ************************************//* Erstellt: 05.93 Mattern *//* Geaendert: 05.94 Wittemann *//* Zweck: Strukturiert REXX-Code, rueckt ein *//* und aus *//* Aufruf: TSO %STRU parm1 parm2 *//* Parameter1: Dateiname des Quelltextes, der unter- *//* sucht werden soll: vollqualifiziert *//* Parameter2: Anzahl Stellen, um die eingerueckt *//* werden soll (Standard 3 Stellen) */arg source ein_aus .if ein_aus < 2 ! ein_aus > 4 then ein_aus = 3Bei Programmstart können zwei Parameter mitgegeben werden. Der erste ist der Name derBibliothek und des Programmes, das überprüft werden soll (XWITT.MVS.EXEC(PROG1)),der zweite wäre die Anzahl Stellen, die bei Ein- und Ausrückungen zu berücksichtigen sind(Standard = 3; 2 oder 4 wären noch möglich).

pref="!"copies(" ",ein_aus-1)Ein Vorspann für das Einrücken wird erzeugt.

"clear"if source = "?" then do

do z=1 while left(strip(sourceline(z)),2)="/*"say left(sourceline(z),79)

endexitWurde bei Programmaufruf als Parameter ein Fragezeichen(?) übergeben, gibt das Pro-gramm seinen Programmkopf aus (und damit eine kleine Online-Hilfe) und beendet sich.

endsay center(" REXX-Quelltext strukturieren ",79,"*")do until sysdsn("'"source"'") = "OK" & right(datei,1)=")" say "dateiname(programm) vollqualifiziert eingeben" pull source

Sofern als erster Parameter kein Quellprogramm mit Bibliotheksname angegeben wur-de, oder die Quelle nicht verfügbar ist, wird in der Schleife eine Bedienereingabe zurAngabe der PO-Datei und des entsprechenden Members möglich.

end"alloc dd(source) da('"source"') shr reuse"

RXSTRU

tzliches u

nd

Interessan

tes

Page 72: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E178

if rc > 0 then dosay "Alloc verunglueckt mit RC="rcexitFunktioniert der Zuweisungsbefehl nicht, kann die Quelle nicht gelesen werden. In die-sem Fall ist eine weitere Verarbeitung sinnlos, das Programm wird nach Ausgabe einerNachricht beendet.

end"newstack""execio * diskr source (finis"Der Quellencode wird in den neuen Stackbereich gelesen.

z = 0

Z wird der Index, über den der Variablenstamm QT. (QuellText) aufgebaut werden soll.

do queued()Alle Datensätze im Stack werden verarbeitet.

parse pull satzsatz = strip(satz,"B")pos = pos(";"satz,")do while pos > 0 & pos >< lastpos(";",satz)

z = z+1qt.z = substr(satz,1,pos)satz = substr(satz,pos+1,length(satz)-pos)pos = pos(";"satz,")Enthält ein Satz ein Semikolon(;), hinter dem noch Daten folgen, gehen wir davonaus, daß es sich hierbei um mehrere Befehle in einer Zeile handelt. In diesem Fallwird der Satz aus dem Stack aufgesplittet. Es werden mehrere Variablen innerhalbdes Stammes QT. gebildet (eine je Befehl).

endz = z+1qt.z = satz

endDer Variablenstamm QT. wird nun interpretiert. Dabei werden bei Erkennen von DO, SELECToder END die entsprechenden Ein- und Ausrückungen durchgeführt. Das Ergebnis wird imVariablenstamm QTN. hinterlegt.

qt.0 = zspalte = 0anz_ein = 0

RXSTRU

tzlic

hes

un

d In

tere

ssan

tes

Page 73: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 179

anz_aus = 0QT.0 enthält die Anzahl gebildeter Variabler im Stamm QT.. SPALTE ist der Bezugspunkt fürdas Ein-/Ausrücken. ANZ_EIN und ANZ_AUS zählen die jeweilige Anzahl der Ein- und Aus-rückungen (Einrücken bei DO oder SELECT, Ausrücken bei END).

qtn.1 = "REXX" source "von" userid() date() time()Erster Eintrag im neuen Stamm ist eine Hinweiszeile mit Programm, Anwender, Datum undUhrzeit.

do z = 2 to qt.0+1qt.such = qt.zpos_cmta = pos("/*",qt.such)pos_cmte = pos("*/",qt.such)

Für die Untersuchung des Quellencodes bilden wir eine Hilfsvariable QT.SUCH.POS_CMTA und POS_CMTE enthalten die Bytepositionen eines Kommentarbeginn- und-endezeichens (/* und */) innerhalb QT.SUCH, oder 0, sofern das jeweilige Zeichen nichtin QT.SUCH enthalten war.

if pos_cmta > 0 then doqt.such = left(qt.z,pos_cmta-1)

endWurde ein Kommentarbeginn gefunden, wird zur weiteren Bearbeitung nur der Teil desQuelltextes berücksichtigt, der vor dem Kommentarzeichen liegt.

selectwhen pos("DO",translate(qt.such)) > 0 then do

qtn.z = copies(pref,spalte/ein_aus)!!qt.zspalte = spalte + ein_ausanz_ein = anz_ein + 1

endwhen pos("SELECT",translate(qt.such)) > 0 then do

qtn.z = copies(pref,spalte/ein_aus)!!qt.zspalte = spalte + ein_ausanz_ein = anz_ein + 1

endwhen pos("END",translate(qt.such)) > 0 then do

if spalte >= ein_aus then dospalte = spalte - ein_aus

endqtn.z = copies(pref,spalte/ein_aus)!!qt.zanz_aus = anz_aus + 1

end

RXSTRU

tzliches u

nd

Interessan

tes

Page 74: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E180

otherwise qtn.z=copies(pref,spalte/ein_aus)!!qt.zendDas SELECT-Gebilde erkennt die entsprechenden Schlüsselworte. Bei DO und SEL-ECT erfolgt eine Einrückung. Bei END eine Ausrückung. Wird keines der Schlüsselwortein der Zeile erkannt, wird die Zeile entsprechend der letzten Ein-/Ausrückunge ein- be-ziehungsweise ausgerückt.

endz = qt.0 + 1qtn.z = ">>>>>>>>>"Anzahl DOs/SELECTs.:" anz_einz = z + 1qtn.z = ">>>>>>>>>"Anzahl ENDs........:" anz_ausZwei Hinweiszeilen werden an das Ende des Variablenstammes QTN. angehängt. Ausgege-ben werden die gezählten DO- und SELECT-Befehle, sowie die Anzahl erkannter END-State-ments. Weichen die beiden Werte voneinander ab, liegt ein Fehler vor, der aufgrund der Ein-/Ausrückungen relativ einfach zu finden sein sollte.

"del rexx.stru""alloc dd(aus) da(rexx.stru) new unit(sysda) space(1)tracks dsorg(ps) lrecl(80) blksize(80) recfm(v)"Die Datei, in der das Ergebnis hinterlegt wird, wird im ALLOC-Befehl neu erstellt. Falls sie auseiner früheren Verarbeitung noch existieren würde, wird sie im darüberliegenden DEÖ-Kom-mando gelöscht.

"execio * diskw aus (stem qtn. finis"Das Ergebnis wird in die Datei geschrieben.

"free dd(source aus)"Die DD-Namen SOURCE und AUS werden freigegeben.

if sysvar(sysispf) = "ACTIVE" thenaddress ispexec "edit dataset('"userid()".rexx.stru"')"

elsecall "LIST" "'"userid()".rexx.stru'"

Sofern die Aktion aus einer ISPF/PDF-Session heraus gestartet wurde, wird der PDF-Editaufgerufen, um die Ergebnisdatei zu zeigen. Anderenfalls bemühen wir unser LIST-Programm,das innerhalb dieses Kapitels als DEMO für Dateiverarbeitung beschrieben ist.

exit

RXSTRU

tzlic

hes

un

d In

tere

ssan

tes

Page 75: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 181

Wie versprochen, möchte ich nun an dieser Stelle zeigen, wie eine kleine Sortier-Routine aussehen kann. Grundsätzlich wollen wir die Problemlösung als UDF (UserDefined Function) anlegen, um sie in jedem beliebigen Programm einsetzen zu kön-nen. Die Funktion übernimmt eine Wortkette und gibt sie aufsteigend sortiert wiederzurück. Selbstverständlich könnten wir die Funktion über einen weiteren Parameterveranlassen, ein absteigendes Ergebnis zu liefern, aber ein bißchen Kreatives möch-te ich gerne für Sie übriglassen (Bescheidenheit war immer meine Stärke). Es bedarfnur ein paar kleiner Veränderungen, viel Spaß dabei.

/* REXX * SORT ***************************************//* Erstellt: 11.93 Rieder *//* Geaendert: 05.94 Wittemann *//* Zweck: Uebernimmt eine Wortkette und gibt *//* sie aufsteigend sortiert zurueck */arg parmDie Wortkette, welche die Funktion übernehmen soll, wird der Variablen PARM zugewiesen.Wird keine Information übergeben, ist PARM Nullstring. Die WHILE-Schleife wird in diesemFall übergangen. Die Funktion liefert Nullstring zurück.

do while length(parm) > 0done=0do pos = 1 to words(parm) -1

idx1 = word(parm,pos)idx2 = word(parm,pos+1)if idx2 > idx1 then do

parm = subword(parm,1,pos-1) IDX2 IDX1 ,subword(parm,pos+2)

done=1end

endif done=0 then leave

end

Die innere Schleife schiebt beim ersten Durchlauf den größten Wert nach ganz hinten, beimzweiten Durchlauf den zweitgrößten Wert an die vorletzte Position, und so weiter. Zu Beginnjeder Verarbeitung der inneren Schleife wird die Variable DONE auf 0 gesetzt. Solange inner-halb der inneren Schleife mindestens einmal zwei Werte gegeneinander ausgetauscht wer-den, wird die Variable DONE auf 1 gesetzt. Wurde die innere Schleife einmal abgearbeitet,ohne daß die Reihenfolge der einzelnen Werte zu beanstanden gewesen wäre, liegen dieWerte in der richtigen Reihenfolge. In diesem Fall ist DONE nach Ende der inneren Schleifeimmer noch auf 0, Programmende wird erreicht.

return parm

SORT

tzliches u

nd

Interessan

tes

Page 76: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E182

Sehen wir uns das gleiche Beispiel leicht geändert an:

/* REXX * SORT ***************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Uebernimmt eine Wortkette und gibt *//* sie aufsteigend sortiert zurueck */arg parmend=words(parm)do x=1 to end parm.x=word(parm,x)endtmp=end+1parm.tmp="ff"xWir bauen mit der empfangenen Wortkette einen Variablenstamm auf. Das erste Wort derKette liegt in PARM.1, etc.. Hinter die letzte gebildete Variable wird HIGH-Value angehängt(alle Bits stehen auf 1). Auf diese Weise haben wir kein Problem, wenn zum Vergleich hinterdie letzte Variable gegriffen wird.

do while end > 0done=0do x1 = 1 to endx2=x1+1

if parm.x1 > parm.x2 then dohelp = parm.x1parm.x1 = parm.x2parm.x2 = help

Wird erkannt, daß zwei Werte nicht in der richtigen Reihenfolge liegen, werdensie gegeneinander getauscht. Dazu brauchen wir eine Hilfsvariable.

done=1end

endif done = 0 then do

parm=""do x=1 to end parm = parm parm.xendDie zurückzugebende Wortkette wird in der Variablen PARM wieder aufgebaut.

leaveend

SORT

tzlic

hes

un

d In

tere

ssan

tes

Page 77: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 183

endreturn strip(parm)Zunächst erscheint die zweite Variante viel aufwendiger als die erste. Dies ist bedingt richtig.Natürlich ist es mehr Arbeit, die gelieferte Wortfolge in einen Variablenstamm umzubauenund nach erfolgter Sortierung wieder zurück zu verwandeln. Der Vorteil liegt aber darin, daßinnerhalb der Verarbeitungsschleife mit Compound-Variablen wesentlich schneller gearbeitetwerden kann als mit den WORD-Funktionen im ersten Beispiel. Diesen Vorteil registriert manallerdings erst, wenn die Länge der angelieferten Wortkette über ein bestimmtes Maß hinaus-geht. Werden in der Wortkette unter fünf Werte geliefert, kann das zweite Beispiel sogar mehrZeit brauchen als das erste.

Deutlich optimiert wird die Sort-Variante auch Bubble-Sort genannt. Hier haben wirnicht schier endlose Durchläufe, immer in der gleichen Richtung, bis in einem Durch-lauf keine Änderung mehr zu machen war. Wir durchlaufen die Wortkette einmal vonlinks nach rechts. Stellen wir beim Vergleich zweier Werte fest, daß der rechte Wertkleiner dem linken ist, drehen wir die beiden Werte und halten fest, wieweit wir aufdem Weg nach rechts bereits waren. Anschließend gehen wir mit dem ausgetausch-ten Wert sofort nach links und schieben ihn so weit in Richtung Anfang der Wortket-te, wie dies nötig ist. Anschließend können wir unsere Verarbeitung nach rechts ander unterbrochenen Stelle fortsetzen.

/* REXX * BSORT **************************************//* Erstellt: 05.94 Wittemann *//* Zweck: Uebernimmt eine Wortkette und gibt *//* sie aufsteigend sortiert zurueck */arg parmparm.0="00"xVor die erste Variable des zu bildenden Stammes hängen wir LOW-VALUE (alle Bits des Bytezeigen auf 0). Diese Variable wird in jedem Fall kleiner sein als der erste übernommene Wert.

end=words(parm)do x=1 to end

parm.x=word(parm,x)endtmp=end+1parm.tmp="ff"xHinter die letzte gebildete Variable des Stammes hängen wir HIGH-VALUE (alle Bits des Bytezeigen auf 1). Diese Variable wird größer des größten gelieferten Wortes der Wortkette PARMsein. Wir können somit für die Vergleiche vor die erste und hinter die letzte für uns relevanteVariable greifen, ohne Probleme im Ablauf zu bekommen (Mit den WORD-Funktionen ist dies

BSORT

tzliches u

nd

Interessan

tes

Page 78: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E184

unter MVS auch möglich, in REXX unter DOS würden wir aber einen Syntaxfehler erzeugen).Der Vorteil des Variablenstammes wird sich auch hier um so mehr verdeutlichen, je länger diezu sortierende Wortkette ist.

do x1 = 1 to words(parm) -1x2=x1+1if parm.x1 > parm.x2 then do

help = parm.x1parm.x1 = parm.x2parm.x2 = helpdo y1 = x1 to 1 by -1

y2=y1-1if parm.y1 >= parm.y2 then leavehelp = parm.y1parm.y1 = parm.y2parm.y2 = help

endend

endparm=""do x=1 to end

parm = parm parm.xendreturn strip(parm)Auch dieses Beispiel mag bei erster Betrachtung einen eher komplizierten Eindruck vermit-teln, als das erste. Interessant ist aber das Zeitverhalten der drei unterschiedlichen Variantenim Ablauf. Um keine störenden Nebenwirkungen zu haben, wieder ein Testergebnis auf ei-nem PC 80486/50 MHz. Sortiert wurde jeweils eine Kette aus 100 Zahlen, die über einenZufallsgenerator zusammengebaut wurde. Die unterschiedlichen Ergebnisse beruhen auf demAufbau der Kette (Je nachdem, wieviele Umstellungen für die richtige Sortierung nötig waren,wird, je weniger oft dies der Fall ist, die erste Lösung zeitlich etwas profitieren):

Durchläufe Zeitbedarf in SekundenVariante 1 Variante 2 Variante 3

1. Durchlauf 24,40 13,95 5,492. Durchlauf 25,54 12,63 5,333. Durchlauf 27,02 13,40 5,664. Durchlauf 27,29 13,78 5,395. Durchlauf 24,33 11,92 5,55

BSORT

tzlic

hes

un

d In

tere

ssan

tes

Page 79: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 185

Abschließend im Beispiele-Kapitel einige kleine Helferlein für all jene, die sich immerwieder mit Datumsvariablen in jeder denkbaren Form herumschlagen müssen. Viel-fach muß ein Datum auf formale Richtigkeit überprüft werden. Praktisch wäre oft, mitDatumsfeldern rechnen zu können (Eine beliebige Anzahl Tage aufrechnen oder ab-ziehen und das Ergebnis sollte wieder ein gültiges Datum sein). Die Wochentageeines bestimmten Datums wären oft auch nicht schlecht. Und zur Krönung: Die Er-mittlung der beweglichen Feiertage (Alle kirchlichen beweglichen Feiertage sind ab-hängig von Ostern. Ostern wiederum ist abhängig vom ersten Vollmond im Frühling.Wie man den berechnet hat schon früher ein kluger Mann entdeckt. Wir wollen hiernur die Formel umsetzen).

Die Problemlösungen sind als UDFs (benutzerdefinierte Funktionen) geschrieben undmüssen lediglich innerhalb der SYSPROC/SYSEXEC-Verkettung gefunden werdenkönnen.

Die Funktionen werden nicht mehr im Detail kommentiert.

/* REXX * DATECNT ************************************//* Erstellt: 04.93 Rieder/Bosshardt/Keller *//* Zweck: Tagedifferenz-Berechnung *//* Uebernimmt Datum im Format dd.mm.jjjj *//* oder tt.mm.jj und die Anzahl der *//* Tage (+ oder -). *//* Wird das Jahr zweistellig geliefert, *//* wird vom 19. Jahrhundert ausgegangen, *//* anderenfalls wird 20 vor die Jahres- *//* zahl gesetzt. *//* Die Ausgabe des errechneten Datums *//* erfolgt im Format tt.mm.jjjj *//* dd.mm.jjjj */arg datum,tageselect

when length(datum)=8 then doif #jjjj<=80 then #jh=20else #jh=19#jjjj=#jh!!right(datum,2)

endwhen length(datum)><8 then #jjjj=right(datum,4)

DATECNT

tzliches u

nd

Interessan

tes

Page 80: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E186

otherwise nopend#tage_m="31 28 31 30 31 30 31 31 30 31 30 31"#tage_j="365"if #jjjj//4 = 0 then do

#tage_m=overlay('9',#tage_m,5,1)#tage_j="366"

end#ttt=0do idx=1 to substr(datum,4,2)-1

#ttt=#ttt + word(#tage_m,idx)end#ttt=right(#ttt + left(datum,2),3,'0')#tt1=#ttt + tageselect

when #tt1 > #tage_j then do /* ins naechste Jahr */do while #tt1 > #tage_j

if #jjjj // 4 =0 then #tage_j=366else #tage_j=365#jjjj=#jjjj+1#tt1=#tt1-#tage_j

endendwhen #tt1 < 1 then do /* ins letzte Jahr */

do while #tt1 < 1#jjjj=#jjjj-1if #jjjj // 4 = 0 then #tage_j=366else #tage_j=365#tt1=#tt1+#tage_j

endendotherwise nop

endNEUDAT=#jjjj!!right(#tt1,3,'0')neudat=dj2dn(NEUDAT)return NEUDAT

DATECNT

tzlic

hes

un

d In

tere

ssan

tes

Page 81: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 187

/* REXX * DATEDIFF ***********************************//* Erstellt: 02.93 Rieder/Bosshardt/Keller *//* Zweck: Ermittelt die Anzahl der Tage, die *//* zwischen zwei Datums liegen. *//* Variabeluebergabe an die Funktion: *//* datum1,datum2 im Format tt.mm.jjjj, *//* wobei die Jahreszahlen nicht unter *//* 1582 liegen duerfen. *//* Ausgabe der Funktion: Anzahl Tage */arg datum.1, datum.2do i = 1 to 2

parse value datum.i with tt.i '.' mm.i '.' jjjj.iif length(jjjj.i)=2 then do

if jjjj.i<=80 then jh=20else jh=19jjjj.i=jh!!jjjj.i

endselect

when mm.i > 2 then dof.i=365 * jjjj.i + tt.i + 31 * (mm.i - 1)f.i=f.i - trunc(.4 * mm.i + 2.3) + (jjjj.i % 4)f.i=f.i - trunc(.75 * (jjjj.i % 100 + 1))

endotherwise do

f.i=365*jjjj.i+tt.i+31*(mm.i-1)+trunc((jjjj.i-1)/4)f.i=f.i-trunc(.75*(jjjj.i-1)%100+1)

endend

endreturn (f.2-f.1)

DATEDIFF

tzliches u

nd

Interessan

tes

Page 82: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E188

/* REXX * DATEOSO ************************************//* Erstellt: 04.93 Wittemann *//* Zweck: Ostersonntagberechnung *//* Wird das Jahr zweistellig und kleiner *//* 81 geliefert, wird 20 als Jahrhundert *//* gesetzt. Ist das Jahr zweistellig *//* größer/gleich 80, wird als das Jahr *//* mit 19 erweitert. *//* Eingabe: jj oder jjjj *//* Ausgabe: tt.mm.jjjj (Ostersonntag) *//* */arg jahrselect

when length(jahr)=2 then doif jahr<=80 then #jh=20else #jh=19jahr=#jh!!jahr

endotherwise nop

enda=jahr // 19b=jahr // 4c=jahr // 7d=((19 * a + 24) // 30)e=((2 * b + 4 * c + 6 * d + 5) // 7)f=22 + d + eif f = 57 then f =50if f = 56 & d = 28 & e = 6 & a > 10 then f = 49if f <= 31 then monat = "03"else do

f = f-31monat = "04"

endif length(jahr)=2 then jahr=#jh!!jahrreturn right(f,2,'0')'.'monat'.'jahr

DATEOSO

tzlic

hes

un

d In

tere

ssan

tes

Page 83: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 189

/* REXX * DATETRUE ***********************************//* Erstellt: 04.93 Wittemann *//* Zweck: Prueft DATUM auf Korrektheit und *//* liefert die moeglichen Werte: *//* *//* OK (Datum richtig) *//* Falsches Eingabeformat, .... *//* Tag falsch *//* Monat falsch *//* */arg checkdatz=0msg.0="OK"msg.1="Falsches Eingabeformat, richtig: tt.mm.jj"msg.2="Tag falsch"msg.3="Monat falsch"if substr(checkdat,3,1)!!substr(checkdat,6,1) >< '..'then

z=1else do

parse value checkdat with tag '.' mon '.' jahrselect

when length(checkdat) >< 8 & ,length(checkdat) ><10 then z=1when datatype(tag!!mon!!jahr) = 'CHAR' then z=1when tag < 1 ! tag > 31 then z=2when mon < 1 ! mon > 12 then z=3when pos(MON,"04 06 09 11") > 0 & tag > 30 then z=2when mon = 2 thenif tag > 29 ! jahr // 4 > 0 & TAG > 28 then z=2otherwise nop

endendreturn msg.z

DATETRUE

tzliches u

nd

Interessan

tes

Page 84: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E190

/* REXX * DJ2DN **************************************//* Erstellt: 04.93 Rieder *//* Zweck: Umwandlung von Julianischem Datum *//* ins normale Format. *//* *//* Eingabe: jjttt Ausgabe: tt.mm.jj *//* Eingabe: jjjjttt Ausgabe: tt.mm.jjjj *//* */arg dateif length(date)>5 then do

jh=left(date,2)date=right(date,5)

endelse jh=""mon='31 28 31 30 31 30 31 31 30 31 30 31'if substr(date,1,2) // 4 = 0 then mon=overlay('9',MON,5,1)ttt=substr(date,3,3)cnt=0do cnt=1 while ttt > word(mon,cnt)

ttt=ttt-word(mon,cnt)endneudat = right(ttt,2,'0')'.'!!right(cnt,2,'0')'.'if length(jh)=2 then neudat = neudat!!jhneudat = neudat!!left(date,2)return neudat

DJ2DN

tzlic

hes

un

d In

tere

ssan

tes

Page 85: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 191

/* REXX * DOW ****************************************//* Erstellt: 04.93 Rieder *//* Geaendert: 05.94 Wittemann/* Zweck: Liefert den deutschen Wochentag nach *//* Gregorianischer Datumsrechnung *//* (365.2425 Tage). *//* Eingabe: tt.mm.jjjj, wobei jjjj nicht *//* unter 1582 liegen darf. *//* Rueckmeldung: Montag, Dienstag etc. *//* Bei Aufruf in der Form DOW(datum,"N") *//* wird ein numerischer Wert geliefert *//* 0=Montag, 1=dienstag... */arg datum, formif form="N" then pos=2else pos=1parse value datum with tt '.' mm '.' jjjjselect

when length(datum)=8 then do#jjjj=right(datum,2)if #jjjj<=80 then jh=20else jh=19jjjj=jh!!#jjjj

endotherwise nop

endselect

when mm > 2 then dof=365*jjjj+tt+31*(mm-1)-trunc(.4*mm+2.3)+(jjjj%4)f=f-trunc(.75*(jjjj%100+1))

endotherwise do

f=365*jjjj+tt+31*(mm-1)+(jjjj-1)%4f=f-trunc(.75*((jjjj-1)%100)+1)

endendw=f+(-f%7)*7tag="Samstag 5 Sonntag 6 Montag 0 Dienstag 1"tag=tag "Mittwoch 2 Donnerstag 3 Freitag 4"return word(tag,w*2+pos)

DOW

tzliches u

nd

Interessan

tes

Page 86: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E192

Da die Funktion sicher extern aufgerufen wird, müssen wir hier für eine sinnvolleAnwendung das Ergebnis in den SHARED-Variablenpool des Dialog Managers schrei-ben. Das aufrufende Programm kann es von dort wieder lesen. Diese Version ist eine"Sonderanfertigung Schweiz" und enthält für den Bereich Zürich auch die Feiertage"Knabenschießen" und "Sechseläuten".

/* REXX * FTAGE **************************************//* Erstellt: 04.93 Keller/Wittemann *//* Zweck: Berechnung der festen und beweglichen *//* Feiertage des gewuenschten Jahres. *//* *//* In 'FTJ' stehen alle Jahre, die *//* bereits berechnet wurden, in 'FTD' *//* die Feiertagsdaten aller berechneten *//* Jahre und in'FTG' die Namen der *//* Feiertage. *//* Falls ein Jahr schon vorhanden ist, *//* wird es nicht mehr berechnet. *//* Wenn kein Jahr angegeben, wird das *//* aktuelle Jahr angenommen. *//* *//* Bemerkung: *//* Bei Uebergabe von 'jj' wird geprueft, *//* ob das Jahr groesser 80 ist. *//* Wenn ja, wird 19xx angenommen, sonst *//* gilt 20xx. *//* *//* Ausgabe: *//* alle Feiertage im Format tt.mm *//* */arg #jahrselect

when length(#jahr)=2 then doif #jahr<=92 then #jh=20else #jh=19#jahr=#jh!!#jahr

endotherwise nop

end

FTAGE

tzlic

hes

un

d In

tere

ssan

tes

Page 87: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

Kapitel 2 - Programmbeispiele 193

osodate=dateoso(#jahr)ftd=' 01.01. 02.01. 'ftd=ftd' 'left(datecnt(osodate,-2),6)ftd=ftd' 'left(osodate,6)ftd=ftd' 'left(datecnt(osodate,+1),6)/* Sechselaeuten */do z=15 to 21

y=z'.04.'#jahrif word(dow(y),1)='Montag' then leave

endselect when y = datecnt(osodate,1) theny=left(datecnt(y,7),6) when y = datecnt(osodate,-6) then y=left(datecnt(y,-7),6) otherwise y=left(y,6)endftd=ftd' 'yftd=ftd' 01.05. 'left(datecnt(osodate,39),6)ftd=ftd' 'left(datecnt(osodate,49),6)ftd=ftd' 'left(datecnt(osodate,50),6)do x=1 to 7

y='0'!!x'.09.'#jahrif word(dow(y),1)='Sonntag' then do

boyshot=datecnt(y,8)leave

endendftd=ftd' 01.08. 'left(boyshot,6)ftd=ftd' 25.12. 26.12. 'ftg='Neujahr Berchtoldstag Karfreitag Ostersonntag 'ftg=ftg'Ostermontag Sechselaeuten 1.Mai Auffahrt 'ftg=ftg'Pfingstsonntag Pfingstmontag 1.August Knaben-schiessen 'ftg=ftg'1.Weihnachtstag 2.Weihnachtstag 'address ispexec "vput (ftd,ftg) shared"exit

FTAGE

tzliches u

nd

Interessan

tes

Page 88: Zum Beispiel - spgmbh.com · 110 REXX unter MVS-TSO/E In jungen Jahren, so um die Zeit der Brotvermehrung, habe ich des öfteren mal ein kostenloses Bier getrunken, weil kaum jemand

REXX unter MVS-TSO/E194

/* REXX * WOY ****************************************//* Erstellt: 04.93 Rieder *//* Zweck: Errechnet die Woche, in der das *//* Datum liegt, das der Funktion ueber- *//* geben wird. *//* Eingabe: tt.mm.jjjj *//* Ausgabe: wwjj (jj evtl Vorjahr) *//* *//* ACHTUNG: *//* Wenn die 1. Woche im Jahr am 4. JAN *//* beginnt, zaehlen die Tage vom 1.-3. *//* noch zur letzten Woche im alten Jahr. *//* */arg date/* BERECHNUNG DES THEOR. WOCHE-1-BEGINNES ***********/do forever

jjjj = left(substr(date,7,4),7,'0')if subword(dow(dj2dn(jjjj+1)),2) > 3 then

dayone=jjjj+1+(8-(subword(dow(dj2dn(jjjj+1)),2)))else

dayone=(jjjj+1)-(subword(dow(dj2dn(jjjj+1)),2)-1)if dn2dj(date) >= dayone then leaveelse date = "31.12."!!left(jjjj-1,4)

end/* WOCHENBERECHNUNG *********************************/ww = ((dn2dj(date) - dayone)%7)+1jj = substr(date,7,4)say jj right(ww,2,'0')return

WOY

tzlic

hes

un

d In

tere

ssan

tes