Il CL (Control Language) è il linguaggio in cui sono scritti i comandi del sistema, ed è anche un linguaggio di programmazione vero, con variabili, condizioni, cicli e gestione degli errori.
Nella pratica è quello che tiene insieme tutto il resto: prepara l'ambiente, chiama i programmi, gestisce le eccezioni, sottomette il lavoro. Se in un applicativo qualcosa gira di notte, quasi certamente c'è un CL che lo lancia.
Un CL minimo
PGM PARM(&AZIENDA)
DCL VAR(&AZIENDA) TYPE(*CHAR) LEN(3)
DCL VAR(&DATA) TYPE(*CHAR) LEN(8)
DCL VAR(&MSG) TYPE(*CHAR) LEN(132)
RTVSYSVAL SYSVAL(QDATE) RTNVAR(&DATA)
OVRDBF FILE(ORDINI) TOFILE(DATILIB/ORDINI&AZIENDA)
CALL PGM(PRODLIB/CHIUSURA) PARM(&AZIENDA)
MONMSG MSGID(CPF0000) EXEC(DO)
CHGVAR VAR(&MSG) VALUE('Chiusura fallita per azienda' *BCAT &AZIENDA)
SNDPGMMSG MSG(&MSG) TOMSGQ(QSYSOPR) MSGTYPE(*INFO)
DLTOVR FILE(ORDINI)
RETURN
ENDDO
DLTOVR FILE(ORDINI)
ENDPGM
Ci sono dentro quasi tutte le cose che un CL fa davvero: ricevere parametri, leggere lo stato del sistema, ridirigere un file, chiamare un programma, accorgersi che è andato male e dirlo a qualcuno.
MONMSG, che è il cuore di tutto
Un comando che fallisce manda un messaggio di eccezione. MONMSG è l'unico modo per
accorgersene: senza, il CL si ferma con un errore che, in batch, nessuno vede fino al
giorno dopo.
Si usa in due modi, e la differenza conta:
CALL PGM(PRODLIB/CHIUSURA)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERRORE))
Messo subito dopo un comando, vale per quel comando soltanto.
PGM
MONMSG MSGID(CPF0000 MCH0000) EXEC(GOTO CMDLBL(ERRORE))
...
Messo all'inizio del programma, subito dopo le dichiarazioni (o dopo PGM, se non ce
ne sono), vale per tutti i comandi: è il MONMSG a livello di programma. Lì EXEC può
contenere solo un GOTO a un'etichetta, e un MONMSG sul singolo comando ha la precedenza
su quello generale.
Avvertenza
MONMSG MSGID(CPF0000) cattura qualunque messaggio della famiglia CPF, e
il modo in cui viene usato più spesso — MONMSG MSGID(CPF0000) da solo, senza EXEC —
significa "ignora ogni errore e vai avanti". A volte è ciò che si vuole, per esempio dopo
un DLTF su un file che potrebbe non esistere. Molto più spesso è un modo per nascondere
i guasti: il programma continua su dati che non ci sono, e l'errore compare tre passi più
avanti, dove non ha più senso.
Un MONMSG senza EXEC andrebbe scritto solo sul singolo comando di cui si è deciso che
l'errore è accettabile, mai su tutto il programma.
Le variabili
Cominciano con &, hanno un tipo dichiarato e non c'è conversione implicita fra tipi.
DCL VAR(&CONTATORE) TYPE(*DEC) LEN(5 0) VALUE(0)
DCL VAR(&NOME) TYPE(*CHAR) LEN(10)
DCL VAR(&TROVATO) TYPE(*LGL) VALUE('0')
CHGVAR VAR(&CONTATORE) VALUE(&CONTATORE + 1)
CHGVAR VAR(&NOME) VALUE('CLIENTE' *BCAT &CODICE)
*CAT concatena, *BCAT concatena inserendo uno spazio, *TCAT toglie prima gli spazi in
coda. Sono tre operatori diversi e sbagliarli produce nomi con spazi in mezzo, che è una
classe intera di errori "l'oggetto non esiste" su oggetti che esistono.
Leggere lo stato del sistema
I comandi che cominciano con RTV restituiscono un'informazione in una variabile invece di
mostrarla a schermo. Sono quelli che rendono un CL capace di adattarsi:
RTVJOBA JOB(&NOMEJOB) USER(&UTENTE) NBR(&NUMERO) CURLIB(&LIBCORR)
RTVSYSVAL SYSVAL(QDATE) RTNVAR(&DATA)
RTVOBJD OBJ(PRODLIB/ORDINI) OBJTYPE(*FILE) RTNLIB(&LIBTROVATA)
Consiglio
per sapere se un oggetto esiste c'è CHKOBJ, e lo stesso vale per
RTVOBJD: tutti e due mandano CPF9801 se l'oggetto non c'è e CPF9802 se mancano i
diritti. Conviene monitorare quei due messaggi per nome, non CPF0000: così «non esiste»
e «non hai i diritti» restano due casi diversi.
CHKOBJ OBJ(PRODLIB/ORDINI) OBJTYPE(*FILE)
MONMSG MSGID(CPF9801) EXEC(GOTO CMDLBL(MANCA))
Leggere un file
Il CL può leggere un file, ma un record alla volta e in sola lettura:
DCLF FILE(PRODLIB/PARAMETRI)
LEGGI:
RCVF
MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(FINE))
...
GOTO CMDLBL(LEGGI)
FINE:
CPF0864 è la fine del file, e va monitorata sempre: è il modo in cui il ciclo si chiude.
Non è un linguaggio per elaborare dati, e usarlo per farlo è una scelta che si paga in prestazioni e in leggibilità. Per leggere un parametro di configurazione va benissimo; per scorrere un archivio, no.
OVRDBF, l'ignoto potente
Le sovrascritture di file sono uno degli strumenti più usati e meno spiegati:
OVRDBF FILE(ORDINI) TOFILE(COLLAUDO/ORDINI)
OVRDBF FILE(ORDINI) MBR(GENNAIO)
OVRDBF FILE(REPORT) SHARE(*YES)
Un programma compilato per leggere ORDINI legge quello che la sovrascrittura gli mette
davanti: un file in un'altra libreria, un altro membro, con altri parametri di apertura.
È il modo in cui lo stesso programma serve dodici mesi di archivio, o gira su collaudo
senza essere ricompilato.
Avvertenza
una sovrascrittura ha un ambito, il parametro OVRSCOPE. Il valore
di serie, *ACTGRPDFN, dipende dal gruppo di attivazione del programma che la fa: nel
gruppo predefinito vale per il suo livello di chiamata e per quelli sotto; in un gruppo
con nome vale per tutto il gruppo, che resta nel job dopo il ritorno (capitolo su
ILE). Con OVRSCOPE(*JOB) vale per tutto il job. Nei due ultimi casi un OVRDBF
dimenticato fa leggere il file sbagliato a quello che viene dopo: le sovrascritture si
cancellano sempre, anche sulla strada dell'errore, ed è il motivo per cui nell'esempio in
cima al capitolo DLTOVR compare due volte.
Come si compila
CRTBNDCL PGM(PRODLIB/CHIUSURA) SRCFILE(PRODLIB/QCLSRC) SRCMBR(CHIUSURA)
CRTBNDCL produce un programma ILE, ed è la forma da usare oggi; come CRTBNDRPG, di serie
lo crea con DFTACTGRP(*YES), nel gruppo di attivazione predefinito. CRTCLPGM produce un
programma del vecchio modello: si incontra sul codice storico e non c'è motivo di usarlo
per il codice nuovo.
Commenti
Nessun commento ancora. Sii il primo a commentare!
Devi avere un account per commentare. Accedi · Registrati