Midrange News for the IBM i Community


Posted by: Bob Cozzi
Rogue Programmer
Cozzi Productions, Inc.
Chicagoland
The CASE for CL - Part 2
has no ratings.
Published: 28 May 2014
Revised: 03 Apr 2016 - 421 days ago
Last viewed on: 28 May 2017 (2932 views) 

Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.

Using C and APIs to convert upper lower case in CL Published by: Bob Cozzi on 28 May 2014 view comments(2)

Converting CASE in CL - 2

By Bob Cozzi

Last year I showed you how to use the QlgConvertCase API within CL to convert a variable to all upper or all lower case letters. While using an API within CL programs is interesting, it isn't something the vast majority of CL programmers enjoy.

Following that article I introduced a CLCONVERT command in my appTools product. This command converts between upper/lower case, but also supports CCSID conversion (for example, EBCDIC to ASCII). While speaking with a lot of our users, I discovered that the simple ability to convert to all upper case is important to CL programmers and the other stuff like CCSID or ASCII conversion isn't widely interesting.

The issue comes from user-entered data or parameters passed to the CL program. Then in CL comparing CL variables to literals or other variables is always case-sensitive. For example:

IF (&OUTQ *EQ 'QPRINT') THEN(DO)
  /* What if &OUTQ contains 'qprint'? The above IF test, fails. */

What we need is the ability to

turn this: qprint

into this: QPRINT

So I decide to create a stand-alone program with a CL command interface that allows CL programmers to convert between upper/lower case without the need to understand the QlgConvertCase API. As is the case for most of my appTools, this new CLCASE command is written, not in good old RPG IV, but in ILE C. Don't worry, you don't have to know C to compile it or use it. You only have to have the C compiler installed on your IBM i system. If  you have the RPG compiler installed, then by definition you also have the C and C++ compilers installed.

CLCASE (Convert Case in CL) Command

The CLCASE command allows you to convert the contents of a CL variable to all lower or all upper case letters. It is only allowed within a CL program.

CLCASE VAR( &target ) VALUE( data or CL variable to be converted ) XLATE( *TOUPPER | *TOLOWER )

The VAR parameter must be a CL variable of TYPE(*CHAR). Its length may be 1 to 5000 bytes. It receives the converted text.

The VALUE parameter accepts a literal, a character-string expression or a *CHAR CL variable. The text in this parameter is converted to upper or lower and the converted text is returned to the CL variable specified on the VAR parameter.

The XLATE parameter controls whether to convert to all lower or all upper case letters.

Below is the CMD source code for the CLCASE command. Note that since this command may only be used within a CL program, the ALLOW parameter of the CRTCMD must be ALLOW(*IPGM *BPGM *IMOD *BMOD). I've included this parameter on the CMD statement within the command source, but if you're on V5R4 this capability isn't there yet, so it has to be specified when the command is compiled. 

 CLCASE:     CMD        PROMPT('Convert CLVar Upper/Lower Case') +
                          ALLOW(*IPGM *BPGM *IMOD *BMOD)

             PARM       KWD(VAR) TYPE(*CHAR) LEN(1) RTNVAL(*YES) +
                          MIN(1) VARY(*YES *INT2) CHOICE('CL var +
                          TYPE(*CHAR)') PROMPT('CL var for +
                          converted data')

             PARM       KWD(VALUE) TYPE(*CHAR) LEN(5000) MIN(1) +
                          EXPR(*YES) VARY(*YES *INT2) INLPMTLEN(32) +
                          PROMPT('Data to convert')

             PARM       KWD(XLATE) TYPE(*LGL) LEN(1) RSTD(*YES) +
                          DFT(*TOUPPER) SPCVAL((*TOUPPER '0') +
                          (*TOLOWER '1') (*UPPER '0') (*LOWER '1')) +
                          PROMPT('Translation target')

CLCASE C Source Member

The CLCASE Command Processing Program is also named CLCASE and is an ILE C program; SEU Type: C

To compile this source, save it into a source member in QCSRC in a library, then use PDM option 14 to compile it. Make sure the SEU Source Type is 'C' so that PDM selects the proper compiler.

I've made the program independent of any of any commercial tools, such as appTools,, so it should work fine as is, on any release of IBM i--probably as far back as you can go.

    // © Copyright 2014 by Cozzi Productions, Inc. All right reserved.
   // Visit Bob Cozzi's websites:  midrangeNews.com for free discussion 
   // forums on RPG IV and IBM i and cozTools.com for information on his offerings. 
   // Right use is hereby granted for non-commercial or in-house applications using IBM i operating systems only.
   // Include this notice with the source code.
#include 
#include 
#include  
#include 
#include "qlgcase.h"
#include "qusec.h"
#include "micptcom.mih"
#include "cpybytes.mih"

#define TO_UPPER 0
#define TO_LOWER 1

#ifndef min
#define min(a,b)  (((a) < (b)) ? (a) : (b))
#endif

typedef _Packed struct tagVARCHAR_T
{
   unsigned short length;
   char           data[1];
} VARCHAR_T;


void main(int argc, char *argv[])
{
  VARCHAR_T* outData;
  VARCHAR_T* inData;
  char*      toLower;

  Qus_EC_t   apiError;
  Qlg_CCSID_ReqCtlBlk_T frcb;
  int        len = 0;

  outData = (VARCHAR_T*) argv[1];
  inData  = (VARCHAR_T*) argv[2];
  toLower = argv[3];
  frcb.Type_of_Request = 1;
  frcb.CCSID_of_Input_Data = 0;
  memset(frcb.Reserved,'\0',10);

  if (toLower == NULL)
    frcb.Case_Request = 0;
  else
    frcb.Case_Request = (*toLower == '1') ? TO_LOWER : TO_UPPER;

  len = min(inData->length,outData->length);

  memset(&apiError,'\0',sizeof(apiError));
  apiError.Bytes_Provided = sizeof(apiError);

  QlgConvertCase( (char*) &frcb, inData->data,
                  outData->data, len ,
                  (char*) &apiError);
}  // endpgm

 

Why C and Instead of RPG IV?

I first saw the C language sometime in the 1980s and first learned and used it in the early 1990s. Unlike RPG, the C language is like holding a knife with no handle. Only those not of the faint of heart should work with it. In recent years as I've built the "Cozzi Tools" (xTools, cozTools, and appTools) library and now Query File, I've learned that access to low-level functions and APIs is almost always easier with C than with RPG. After all, IBM doesn't even bother to provide RPG Prototypes for APIs, but they do provide all the prototypes in C syntax. They also just announced that they are going to provide correct API data structures in RPG IV syntax starting with IBM i v7r2. These structures have been correct in the C language version since the beginning. In addition, RPG excels at two things: (1) Record I/O is integrated into the language and its support for embedded SQL (RPG is better at it than C) and (2) RPG is very safe when moving data from one place to another (copying or converting fields) But for everything else, C is at least as good as RPG and in more cases than not, it is better at it. So while RPG continues to attempt to become a completely free-format language, C has always been free format so there's none of those "we gotta do it this goofy way because we made design decisions 30 years ago that we don't want to let go" frustrations. So more often than not, I choose C or C++ for non-end-user application development for IBM i and for virtually all my CL commands enhancements.

CL Program to Illustrate the CLCASE

This CL program runs the CLCASE command and sends a message with the before and after images of the &NAME and &OUTPUT variables.

  CVTCASE:    PGM      /* Example Convert Case in CL by Bob Cozzi  */

             DCL        VAR(&NAME) TYPE(*CHAR) LEN(50) +
                          VALUE('Cozzi test to convert to all UPPER case.')
             DCL        VAR(&OUTPUT) TYPE(*CHAR) LEN(50)
             MONMSG     MSGID(CPF0000)

 CONVERT:    CLCASE     VAR(&OUTPUT) VALUE(&NAME) XLATE(*TOUPPER)

             SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Name =' +
                          *BCAT &NAME) MSGTYPE(*COMP)
             SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Upper=' +
                          *BCAT &OUTPUT) MSGTYPE(*COMP)
 ENDPGM:     ENDPGM 

This CL program converts the text in the CL variable &NAME to all upper case and stores the converted text in the &OUTPUT variable. The program writes this information to the joblog for you to review. The conversion is performed on the line labeled "CONVERT:" using the CLCASE command featured in this article.

If you're like me, you enjoy having F1=Help actually work when prompting a command. I don't know what IBM is thinking with some of its newer commands, by not included helptext. But in any event, I've included the UIM help text for CLCASE below. Use PDM option 14 to compile it and then identify it on the Panel Group ID parameter on the CRTCMD command.

:pnlgrp.
.************************************************************************
.*  Help for command CLCASE
.************************************************************************
:help name='CLCASE'.
Convert CL Variable Case - Help
:p.The Convert Letter Case (CLCASE) command converts the contents
of a CL variable to all UPPER or all lower case letters.
The original text (specifed on the VALUE parameter) is not modified.
CLCASE has been deprecated in Cozzi appTools by the CLCONVERT command.
:p.:hp2.Restrictions::ehp2.
:ul.
:li.
The command is only allowed within a CL program.
:eul.
:ehelp.
.*******************************************
.*   Help for parameter VAR
.*******************************************
:help name='CLCASE/VAR'.
CL variable for converted text (VAR) - Help
:xh3.CL variable for converted text (VAR)
:p.Specify a CL variable of TYPE(*CHAR) to receive the
converted text. This can be the same variable as specified
on the VALUE parameter or it may be another CL variable.
:p.This is a required parameter.
:ehelp.
.*******************************************
.*   Help for parameter VALUE
.*******************************************
:help name='CLCASE/VALUE'.
Text value to convert (VALUE) - Help
:xh3.Text value to convert (VALUE)
:p.Specify the original text to be converted.
:p.This is a required parameter.
:parml.
:pt.:pv.character-value:epv.
:pd.
Specify a CL variable of TYPE(*CHAR) or a literal or
expression value (an expression value must result in text).
:eparml.
:ehelp.
.*******************************************
.*   Help for parameter CASE
.*******************************************
:help name='CLCASE/CASE'.
Convert to (CASE) - Help
:xh3.Convert to (CASE)
:p.Specify the resulting text case.
:parml.
:pt.:pk def.*TOUPPER or *UPPER:epk.
:pd.
The text specified on the VALUE parameter is converted to all
UPPER CASE and stored in the CL variable specified on the VAR
parameter.
:pt.:pk.*TOLOWER or *LOWER:epk.
:pd.
The text specified on the VALUE parameter is converted to all
lower case and stored in the CL variable specified on the VAR
parameter.
:eparml.
:ehelp.
.**************************************************
.*
.* Examples for CLCASE
.*
.**************************************************
:help name='CLCASE/COMMAND/EXAMPLES'.
Examples for CLCASE - Help
:xh3.Examples for CLCASE
:p.:hp2.Example 1: Convert to Upper Case:ehp2.
:xmp.
CLCASE  VAR(&NAME) VALUE(&USER)
:exmp.
:p.By default the *TOUPPER option is selected, therefore
this command will convert the contents of &USER to uppercase
and store the results in the &NAME CL variable.
.*
:p.:hp2.Example 2: Conver to Lower Case:ehp2.
:xmp.
DCL VAR(&NAME) TYPE(*CHAR) LEN(40)
CHGVAR VAR(&NAME) VALUE('COZZI APPTOOLS ARE COOL!')
CLCASE  VAR(&NAME) VALUE(&NAME) CASE(*TOLOWER)
  /* &NAME = 'cozzi apptools are cool!'   */
:exmp.
:p.In this example, the text in &NAME is convert to lower case
and is stored in &NAME.
.* Describe a more complex invocation of the command.
:ehelp.
:epnlgrp.

 


Bob Cozzi is author of Cozzi appTools a collection of modern tools for RPG and CL programms that provide capability much more efficiently than any other methods. His recent project is Query File the easiest to use SQL for non-SQL users that can be used to create Reports, Listings and PDF files without writing any code. Query File queries the local system with ease, and a remote system nearly as easy. Check out Query File for free at http://www.cozTools.com/qryfile

 

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS

(Sign in to Post a Comment)
Posted by: Taylordh0210
Premium member *
Comment on: The CASE for CL - Part 2
Posted: 2 years 5 months 27 days 21 hours 55 minutes ago

  if (toLower == NULL)

CZM0045: Undeclared identified NULL. 

 

Attempted compile from RDI 9.

 

Posted by: bobcozzi
Site Admin ****
Chicagoland
Comment on: The CASE for CL - Part 2
Posted: 2 years 5 months 13 days 22 hours 26 minutes ago
Edited: Sun, 03 Apr, 2016 at 09:56:27 (421 days ago)

I use the C++ compiler for the C source. Change the SEU Type to CPP and try it again, or simply define NULL similar to what the headers have it defined when __cplusplus__ is defined.