Aller au contenu
Coder sur IBM i · 4 / 4 · CL

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.

3programmes, du simple au menu
15 5le format d'une constante décimale passée par CALL
2compilateurs : CRTBNDCL et CRTCLPGM
0ligne à changer pour compiler ces sources en 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.

SEU sur le membre CLLE ETATSTK : PGM PARM, les DCL, DCLF, MONMSG global, CHKOBJ et son MONMSG
ETATSTK dans SEU.
Invite de la commande CHKOBJ ouverte par F4 dans SEU : Label, Objet ARTICLE, Type d'objet *FILE, Commentaire
F4 sur CHKOBJ : l'invite de la commande, avec Label et 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 ' !')
*BCAT colle avec un blanc, *TCAT colle en retirant les blancs de fin (ceux de &USER), *CAT colle tel quel.
CRTBNDCL PGM(JOBICL/HELLOCL) SRCFILE(JOBICL/QCLSRC) DBGVIEW(*SOURCE)
CALL HELLOCL
Écran Entrée de commandes : après CALL HELLOCL, le message Bonjour SAKADM !
SNDPGMMSG sans destinataire envoie le message à l'appelant : ici, l'écran d'entrée de commandes.

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.
CHKOBJ suivi de MONMSG MSGID(CPF9801) EXEC(DO)
Un MONMSG juste après une commande ne vaut que pour elle : ici, l'objet absent.
RCVF et MONMSG 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 ' ')
Écran Entrée de commandes : CALL ETATSTK PARM(200 ' ') donne Erreur dans une donnée décimale, MCH1202 non intercepté par ETATSTK, puis ETATSTK : erreur inattendue
MCH1202 « Erreur dans une donnée décimale », puis le message d'ETATSTK : « erreur inattendue ».

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)) ' ')
Écran Entrée de commandes : CALL ETATSTK PARM((200 (*DEC 7 0)) ' ') liste A0004 Clou 50 mm stock 0 et A0005 Boulon M8 x 60 stock 150, puis 2 article(s) sous le seuil de 200
Avec le type précisé : A0004 et A0005, « 2 article(s) sous le seuil de 200 ».

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
Écran MENUCL : option 1, seuil 200, message 2 article(s) sous le seuil de 200.
Option 1, seuil 200 : ETATSTK rend 2.
Écran MENUCL : option 1 sans seuil, message Indiquez un seuil.
Option 1 sans seuil : le premier WHEN.
Écran MENUCL : option 9 refusée, message Valeur de zone entrée incorrecte en bas de l'écran
Option 9 : refusée par VALUES dans les DDS, avant même le CL.
Écran Membre de fichier physique : DSPPFM du fichier ARTICLE de JOBICL, cinq articles
Option 2 : DSPPFM. Le stock, en décimal condensé, ne se lit pas en clair.

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)) ' ')
STRDBG arrêté à la ligne 22, CHGVAR VAR(&NB) VALUE(&NB + 1), dans la boucle DOWHILE d'ETATSTK
Arrêt à la ligne 22, seulement quand &ARTCOD vaut A0005.
STRDBG : EVAL &NB affiche &NB = 00001. en bas de l'écran
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èreCLPCLLE
Type du membre sourceCLPCLLE
CompilationCRTCLPGMCRTBNDCL (ou CRTCLMOD puis CRTPGM)
DébogueurSTRISDB (ou STRDBG OPMSRC(*YES), avec OPTION(*SRCDBG) ou *LSTDBG)STRDBG, avec DBGVIEW(*SOURCE)
Appeler une procédure liée (CALLPRC)NonOui
Source dans l'IFS (SRCSTMF)NonOui
Paramètres OPTIMIZE, ACTGRP, INCDIRNonOui
DOWHILE, SELECT, %TRIM, %CHAR… (sur 7.5)OuiOui
STRISDB sur HELLOCLP, version CLP de HELLOCL : source numéroté de 100 en 100, arrêt à l'instruction 600
La version CLP de HELLOCL dans STRISDB, en pas à pas.

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 :

  1. 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.
  2. Create Bound CL Program (CRTBNDCL), IBM, IBM i 7.5 CL commands. Les paramètres propres au CL ILE : SRCSTMF, DBGVIEW, OPTIMIZE, ACTGRP, INCDIR.
  3. Create CL Program (CRTCLPGM), IBM, IBM i 7.5 CL commands. La compilation d'un CLP en programme OPM.
  4. Call Bound Procedure (CALLPRC), IBM, IBM i 7.5 CL commands. CALLPRC ne s'exécute que dans un module CL ILE.
  5. 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.