Coder en CL, du bonjour au menu à l'écran
Trois programmes CL : un très simple, un moyen qui lit un fichier et gère ses erreurs, et un menu qui affiche un écran. Écrits en CLLE, compilés par CRTBNDCL, débogués dans STRDBG et testés sur un IBM i 7.5, avec les différences à connaître quand on tombe sur du CLP.
En une phrase
Un programme CL enchaîne des commandes du système (les mêmes
que sur la ligne de commande) entre PGM et ENDPGM, avec des variables
(&NOM), des tests, des boucles et l'interception des messages d'erreur
(MONMSG). C'est le langage qui pilote les travaux, appelle les programmes et lance les
traitements.
Préparer l'atelier
Une bibliothèque JOBICL, un fichier source QCLSRC (membres de type
CLLE) et QDDSSRC pour l'écran. Le fichier ARTICLE est le PF de la
page RPG III, avec les mêmes cinq articles.
CRTLIB LIB(JOBICL) TEXT('job-i.fr : coder en CL')
CRTSRCPF FILE(JOBICL/QCLSRC) RCDLEN(92)
CRTSRCPF FILE(JOBICL/QDDSSRC) RCDLEN(92)
ADDLIBLE JOBICL
CRTPF FILE(JOBICL/ARTICLE) SRCFILE(JOBICL/QDDSSRC)
En CL, les colonnes ne comptent pas : chaque ligne est une commande, et une ligne qui se termine par
+ continue sur la suivante. Dans SEU, F4 sur une ligne ouvre l'invite de la commande,
exactement comme sur la ligne de commande, avec en plus une zone d'étiquette et une de
commentaire.


Programme 1 Très simple
HELLOCL : dire bonjour à l'utilisateur
/* HELLOCL : le premier programme CL */
PGM
DCL VAR(&USER) TYPE(*CHAR) LEN(10)
RTVJOBA USER(&USER)
SNDPGMMSG MSG('Bonjour' *BCAT &USER *TCAT ' !')
ENDPGM
DCL VAR(&USER) TYPE(*CHAR) LEN(10)- Une variable de 10 caractères. Les déclarations viennent toujours en tête, juste après PGM.
RTVJOBA USER(&USER)- Une commande du système qui range le nom de l'utilisateur du travail dans la variable.
MSG('Bonjour' *BCAT &USER *TCAT ' !')*BCATcolle avec un blanc,*TCATcolle en retirant les blancs de fin (ceux de &USER),*CATcolle tel quel.
CRTBNDCL PGM(JOBICL/HELLOCL) SRCFILE(JOBICL/QCLSRC) DBGVIEW(*SOURCE)
CALL HELLOCL

Programme 2 Moyen
ETATSTK : lire un fichier et gérer les erreurs
ETATSTK reçoit un seuil, lit tous les articles, envoie un message pour chacun de ceux dont le stock est sous le seuil, et rend leur nombre.
/* ETATSTK : liste les articles dont le stock est sous un seuil */
/* Paramètres : seuil (*DEC 7 0), nombre trouvé en retour (5 car.) */
PGM PARM(&SEUIL &NBA)
DCL VAR(&SEUIL) TYPE(*DEC) LEN(7 0)
DCL VAR(&NBA) TYPE(*CHAR) LEN(5)
DCL VAR(&NB) TYPE(*DEC) LEN(5 0) VALUE(0)
DCLF FILE(ARTICLE)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERREUR))
/* Le fichier doit exister */
CHKOBJ OBJ(ARTICLE) OBJTYPE(*FILE)
MONMSG MSGID(CPF9801) EXEC(DO)
SNDPGMMSG MSG('Fichier ARTICLE introuvable')
RETURN
ENDDO
/* Lecture de tout le fichier, jusqu'à la fin (CPF0864) */
DOWHILE COND('1')
RCVF
MONMSG MSGID(CPF0864) EXEC(LEAVE)
IF COND(&ARTSTK *LT &SEUIL) THEN(DO)
CHGVAR VAR(&NB) VALUE(&NB + 1)
SNDPGMMSG MSG(&ARTCOD *BCAT %TRIM(&ARTLIB) *CAT +
' : stock' *BCAT %CHAR(&ARTSTK))
ENDDO
ENDDO
CHGVAR VAR(&NBA) VALUE(%CHAR(&NB))
SNDPGMMSG MSG(%CHAR(&NB) *BCAT 'article(s) sous le seuil +
de' *BCAT %CHAR(&SEUIL))
RETURN
/* Toute autre erreur : on la signale à l'appelant */
ERREUR: SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('ETATSTK +
: erreur inattendue, voir l''historique du +
travail') MSGTYPE(*ESCAPE)
ENDPGM
PGM PARM(&SEUIL &NBA)- Les paramètres reçus, déclarés ensuite par DCL.
DCLF FILE(ARTICLE)- Déclare le fichier : ses zones deviennent des variables (
&ARTCOD,&ARTLIB,&ARTSTK). Le fichier doit exister à la compilation. MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERREUR))- Placé juste après les déclarations, ce MONMSG vaut pour tout le programme : toute erreur CPF non traitée ailleurs mène à l'étiquette ERREUR.
CHKOBJsuivi deMONMSG MSGID(CPF9801) EXEC(DO)- Un MONMSG juste après une commande ne vaut que pour elle : ici, l'objet absent.
RCVFetMONMSG MSGID(CPF0864) EXEC(LEAVE)- RCVF lit l'enregistrement suivant ; en fin de fichier, il envoie CPF0864, et LEAVE sort de la boucle DOWHILE.
SNDPGMMSG MSGID(CPF9898) … MSGTYPE(*ESCAPE)- En cas d'erreur, le programme s'arrête en renvoyant un message d'échappement à son appelant, qui peut l'intercepter à son tour.
CRTBNDCL PGM(JOBICL/ETATSTK) SRCFILE(JOBICL/QCLSRC) DBGVIEW(*SOURCE)
Le piège du 15 5
Premier essai depuis la ligne de commande, avec un seuil de 200 :
CALL ETATSTK PARM(200 ' ')

Une constante décimale tapée sur CALL est passée en décimal condensé de 15 chiffres dont 5
décimales, quelle que soit la déclaration du programme appelé. ETATSTK lit les 4 premiers
octets comme un *DEC (7 0) : ce n'est pas un nombre valide, et la comparaison de la
ligne 21 échoue. Le MONMSG global a bien fonctionné : l'erreur est arrivée jusqu'à l'étiquette
ERREUR. Deux remèdes : déclarer le paramètre en LEN(15 5), ou préciser le type sur
l'appel :
CALL ETATSTK PARM((200 (*DEC 7 0)) ' ')

Le problème ne se pose pas entre deux programmes : une variable passée par CALL garde son type et sa longueur. C'est ce que fait le menu du programme 3.
Programme 3 Avec un écran
MENUCL : un menu qui affiche un écran
Un CL sait afficher un écran : il le déclare par DCLF, et SNDRCVF l'affiche puis attend la réponse. L'écran MENUECR propose deux options et une zone de seuil :
A* MENUECR : ecran du programme CL MENUCL
A DSPSIZ(24 80 *DS3)
A CA03(03 'Fin')
A R MENU
A 1 2'MENUCL'
A 1 30'Stock des articles'
A DSPATR(HI)
A 4 4'1. Articles sous un seuil'
A 5 4'2. Afficher le fichier ARTICLE'
A 8 2'Option . . . :'
A OPTION 1A B 8 18VALUES(' ' '1' '2')
A 9 2'Seuil . . . :'
A SEUIL 7Y 0B 9 18EDTCDE(3)
A MSG 60A O 20 2COLOR(WHT)
A 23 2'F3=Fin'
A COLOR(BLU)
/* MENUCL : un menu en CL, avec un écran (MENUECR) */
PGM
DCLF FILE(MENUECR)
DCL VAR(&NBA) TYPE(*CHAR) LEN(5)
DOWHILE COND(*NOT &IN03)
SNDRCVF RCDFMT(MENU)
IF COND(&IN03) THEN(LEAVE)
CHGVAR VAR(&MSG) VALUE(' ')
SELECT
WHEN COND(&OPTION *EQ '1' *AND &SEUIL *EQ 0) +
THEN(CHGVAR VAR(&MSG) VALUE('Indiquez un +
seuil.'))
WHEN COND(&OPTION *EQ '1') THEN(DO)
CALL PGM(ETATSTK) PARM(&SEUIL &NBA)
CHGVAR VAR(&MSG) VALUE(%TRIM(&NBA) *BCAT +
'article(s) sous le seuil de' *BCAT +
%CHAR(&SEUIL) *TCAT '.')
ENDDO
WHEN COND(&OPTION *EQ '2') THEN(DSPPFM +
FILE(ARTICLE))
OTHERWISE CMD(CHGVAR VAR(&MSG) VALUE('Choisissez +
1 ou 2.'))
ENDSELECT
CHGVAR VAR(&OPTION) VALUE(' ')
ENDDO
ENDPGM
DCLF FILE(MENUECR)- Les zones de l'écran deviennent des variables (
&OPTION,&SEUIL,&MSG) et l'indicateur 03 devient&IN03. SNDRCVF RCDFMT(MENU)- Affiche le format MENU et attend que l'utilisateur appuie sur Entrée ou F3.
SELECT…WHEN…OTHERWISE…ENDSELECT- Un choix parmi plusieurs cas : seuil absent, option 1, option 2, autre chose.
CALL PGM(ETATSTK) PARM(&SEUIL &NBA)- Le seuil passe dans une variable
*DEC (7 0): ETATSTK le reçoit exactement. DSPPFM FILE(ARTICLE)- N'importe quelle commande du système peut être lancée depuis le menu.
CRTDSPF FILE(JOBICL/MENUECR) SRCFILE(JOBICL/QDDSSRC)
CRTBNDCL PGM(JOBICL/MENUCL) SRCFILE(JOBICL/QCLSRC) DBGVIEW(*SOURCE)
CALL MENUCL




Déboguer et tester
Un CLLE compilé avec DBGVIEW(*SOURCE) se débogue dans STRDBG, comme le
RPG IV. Ici, un point d'arrêt conditionnel : ne
s'arrêter sur le comptage que pour l'article A0005.
STRDBG PGM(JOBICL/ETATSTK)
BREAK 22 WHEN &ARTCOD = 'A0005'
CALL ETATSTK PARM((200 (*DEC 7 0)) ' ')


EVAL &NB : 1, l'article A0004 déjà compté. EVAL &ARTSTK donne 150.Le test, lui, se fait sur des cas dont on connaît la réponse : seuil 200, A0004 et A0005 attendus ; seuil 0, aucun ; un menu qui refuse une option inconnue et un seuil vide. Toutes ces réponses sont celles des captures de cette page.
CLP ou CLLE ?
Le CLP existe toujours et se rencontre partout dans l'existant (le programme de
test de la page RPG III en est un). Les trois sources de cette
page compilent sans changer une ligne en CLP : nous l'avons fait pour les trois,
par CRTCLPGM. Ce qui change :
| Critère | CLP | CLLE |
|---|---|---|
| Type du membre source | CLP | CLLE |
| Compilation | CRTCLPGM | CRTBNDCL (ou CRTCLMOD puis CRTPGM) |
| Débogueur | STRISDB (ou STRDBG OPMSRC(*YES), avec OPTION(*SRCDBG) ou *LSTDBG) | STRDBG, avec DBGVIEW(*SOURCE) |
| Appeler une procédure liée (CALLPRC) | Non | Oui |
| Source dans l'IFS (SRCSTMF) | Non | Oui |
| Paramètres OPTIMIZE, ACTGRP, INCDIR | Non | Oui |
| DOWHILE, SELECT, %TRIM, %CHAR… (sur 7.5) | Oui | Oui |

Pour un nouveau programme, on écrit du CLLE. Pour un CLP existant, il suffit en général de changer le type du membre en CLLE et de le compiler par CRTBNDCL.
Sources
Les exemples ont été créés et relevés sur un IBM i 7.5 (voir la méthode). Les définitions et les règles viennent de ces sources, consultées le 16 octobre 2026 :
- IBM i 7.5 Programming : CL overview and concepts, IBM (PDF). Programme OPM créé par CRTCLPGM ; vue source par DBGVIEW(*SOURCE) de CRTCLMOD ou CRTBNDCL ; constantes décimales passées en LEN(15 5), et type précisé sur l'appel ; CPF0864 en fin de RCVF ; DOWHILE, SELECT, SUBR et les fonctions intégrées %TRIM et %CHAR.
- Create Bound CL Program (CRTBNDCL), IBM, IBM i 7.5 CL commands. Les paramètres propres au CL ILE : SRCSTMF, DBGVIEW, OPTIMIZE, ACTGRP, INCDIR.
- Create CL Program (CRTCLPGM), IBM, IBM i 7.5 CL commands. La compilation d'un CLP en programme OPM.
- Call Bound Procedure (CALLPRC), IBM, IBM i 7.5 CL commands. CALLPRC ne s'exécute que dans un module CL ILE.
- L'aide en ligne des commandes citées, livrée avec IBM i 7.5 et reprise en français dans les fiches des commandes CL de ce site. Paramètres de CRTRPGPGM, CRTBNDRPG, CRTCLPGM, CRTBNDCL, CALLPRC, STRISDB, STRDBG, STRSEU, WRKMBRPDM, CRTPF, CRTLF et CRTDSPF.
Questions fréquentes
Quelle différence entre CLP et CLLE ?
Un membre CLP se compile par CRTCLPGM en programme OPM, qu'on débogue avec STRISDB ; un membre CLLE se compile par CRTBNDCL en programme ILE, qu'on débogue avec STRDBG. Seul le CLLE peut appeler une procédure liée (CALLPRC) ou être compilé depuis un source de l'IFS (SRCSTMF). Sur IBM i 7.5, DOWHILE, SELECT, %TRIM et %CHAR compilent dans les deux.
Pourquoi CALL PGM(X) PARM(200) donne-t-il MCH1202 ?
Une constante décimale passée par CALL l'est en décimal condensé de longueur 15 5. Si le programme appelé déclare son paramètre autrement, par exemple en *DEC (7 0), il lit une donnée décimale invalide. Précisez le type : PARM((200 (*DEC 7 0))), ou déclarez LEN(15 5).
Comment lire un fichier en CL ?
Déclarez-le par DCLF, puis lisez-le par RCVF dans une boucle ; la fin de fichier envoie CPF0864, que l'on intercepte par MONMSG pour sortir de la boucle.
Un programme CL peut-il afficher un écran ?
Oui : on déclare le fichier écran par DCLF, et SNDRCVF affiche un format puis attend la réponse. Les zones de l'écran deviennent des variables CL, les indicateurs des variables &INxx.