06
Wed, Nov
7 New Articles

The API Corner: Monocasing Character Data

APIs
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Easily find text strings, regardless of case.

 

In the last article, "Reading a Message File," we used the Retrieve Message API QMHRTVM to sequentially read the messages of a message file and display them using a subfile. In this article, we'll implement a find capability that will allow us to quickly find all messages containing one or more character strings (words).

 

As a character string such as "library" may be stored in a message description as "Library," "library," or even "LIBRARY" and a good find function should find all instances, we'll also monocase the message description text prior to scanning it for the character strings of interest. To perform this monocasing, we'll use the Convert Case API documented here. The Convert Case API is available as both a program (QLGCNVCS) and as a procedure export (QlgConvertCase) from a service program. In the following sample program, we'll use the QlgConvertCase procedure.

 

The previous article created the command FNDMSGS, which defined the optional parameter MSGF (message file). The MSGF keyword was used to specify the qualified message file that was to be used, with a default of *LIBL/TEXTMSGS. Today, we'll add the required parameter STRING. The STRING keyword allows you to specify from one to three character strings that must be found in the first-level text of any message in the message file in order for the message to be displayed using the FNDMSGS command.

 

To add prompt text for the STRING parameter, run the following Add Message Description command.  

 

ADDMSGD MSGID(CMD0004) MSGF(TEXTMSGS) MSG('Find string')

 

The message description CMD0004 will be added to the TEXTMSGS message file with the first-level text of 'Find string'.

 

To add the new STRING parameter to the FNDMSGS command, add a new PARM command to the command source used last month. The STRING parameter should be defined before the MSGF parameter as it is a required parameter. Below is the entire command definition for FNDMSGS with the STRING parameter being defined with the first PARM command shown.

 

            CMD        PROMPT(CMD0001)

            PARM       KWD(STRING) TYPE(*CHAR) LEN(20) MIN(1) + 

                         MAX(3) PROMPT(CMD0004)         

            PARM       KWD(MSGF) TYPE(QUALNAME) PROMPT(CMD0002) 

QUALNAME:   QUAL       TYPE(*NAME) LEN(10) DFT(TEXTMSGS)        

            QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL) +  

                         (*CURLIB)) PROMPT(CMD0003)             

 

STRING is a required parameter as specified by MIN(1), and up to three character strings can be entered as indicated by MAX(3). Each character string can be up to 20 bytes in length due to the specification of LEN(20), and the text for the STRING parameter, when prompting the FNDMSGS command, comes from message description CMD0004.

 

To create the updated FNDMSGS command, run the following Create Command command.

 

CRTCMD CMD(FNDMSGS) PGM(FNDMSGS) ALLOW(*INTERACT *IPGM) PMTFILE(TEXTMSGS)

 

The updated FNDMSGS command processing program (CPP) source is shown below. As a reading aid, the changed portions of the program are shown in bold.

 

h dftactgrp(*no) bnddir('MSGSPTBD')                                

                                                                   

fFndMsgsDF cf   e             workstn sfile(Msg_Sfl :MsgSflRRN1)   

                                                                   

dFndMsgs          pr                                               

d NbrOfStrings                   5i 0                              

d QualMsgF_in                   20a   const                        

                                                                    

dFndMsgs          pi                                               

d NbrOfStrings                   5i 0                              

d QualMsgF_in                   20a   const                        

                                                                    

dGetNewMsgF       pr                                               

                                                                   

dGetNxtMsgs       pr                                               

d NbrMsgsToGet                   5u 0 const                        

                                                                  

dGetCurMsg        pr                                              

d MsgID                          7a   const                       

                                                                   

dRtvMsgTxt        pr                                              

dMsgRcv                      65535a   options(*varsize)           

dLenMsgRcv                      10i 0 const                        

dMsgID                           7a   const                       

                                                                  

dRunCmd           pr              n                               

d Cmd                          512a   const                       

                                                                  

dSndEscape        pr                                              

                                                                  

dSetUp            pr                                               

                                                                  

dRtvMsgAPI        pr                  extpgm('QMHRTVM')           

d MsgInfo                    65535a   options(*varsize)           

d LenMsgInfo                    10i 0 const                       

d Format                         8a   const                       

d MsgID                          7a   const                       

d QualMsgF                      20a   const                        

d RplDta                     65535a   const                       

d LenRplDta                     10i 0 const                       

d RplOpt                        10a   const                       

d RtnFmtOpt                     10a   const                       

d ErrCde                              likeds(QUSEC)               

d RtvOpt                        10a   const options(*nopass)      

d ToCCSID                       10i 0 const options(*nopass)      

d RplDtaCCSID                   10i 0 const options(*nopass)      

                                                                  

dSndMsg           pr                  extpgm('QSYS/QMHSNDPM')     

d MsgID                          7    const                       

d QualMsgF                      20    const                       

d MsgDta                     65535    const options(*varsize)       

d LenMsgDta                     10i 0 const                         

d MsgType                       10    const                          

d CallStackEntry             65535    const options(*varsize)       

d CallStackCntr                 10i 0 const                         

d MsgKey                         4                                  

d QUSEC                               likeds(QUSEC)                 

d LenCSE                        10i 0 const options(*nopass)        

d CSEQual                       20    const options(*nopass)        

d DSPWaitTime                   10i 0 const options(*nopass)        

d CSEType                       10    const options(*nopass)        

d CCSID                         10i 0 const options(*nopass)        

                                                                    

dMonoCase         pr                  extproc('QlgConvertCase')     

d CtlBlk                     65535a   const options(*varsize)       

d SourceString               65535a   const options(*varsize)       

d OutputString               65535a   options(*varsize)             

d LenSrcString                  10i 0 const                      

d QUSEC                               likeds(QUSEC)              

                                                                 

dProcessCmd       pr                  extpgm('QCAPCMD')          

d SourceCmd                  65535a   const options(*varsize)    

d LenSrcCmd                     10i 0 const                      

d CtlBlk                     65535a   const options(*varsize)    

d LenCtlBlk                     10i 0 const                      

d Format                         8a   const                      

d ChgCmd                         1a   options(*varsize)          

d LenAvlChgCmd                  10i 0 const                      

d LenRtnChgCmd                  10i 0                             

d QUSEC                               likeds(QUSEC)              

                                                                 

dRtvObjD          pr                  extpgm('QUSROBJD')         

d ObjInfo                    65535a   options(*varsize)          

d LenObjInfo                    10i 0 const                      

d Format                         8a   const                          

d QualObjN                      20a   const                          

d ObjType                       10a   const                          

d QUSEC                               likeds(QUSEC) options(*nopass) 

d ASPCtl                     65535a   const options(*nopass)         

                                                                      

 /copy qsysinc/qrpglesrc,qlg                                         

 /copy qsysinc/qrpglesrc,qmhrtvm                                     

 /copy qsysinc/qrpglesrc,qcapcmd                                     

 /copy qsysinc/qrpglesrc,qusrobjd                                     

 /copy qsysinc/qrpglesrc,qusec                                       

                                                                     

dRcvVar           ds                  qualified                      

d API                                 likeds(QMHM030000)             

d MsgDta                      4096a                                  

                                                                     

dObjInfo          ds                  qualified                       

d API                                 likeds(QUSD0100)            

                                                                  

dQualMsgF         ds                                              

d Msg_File                      10                                 

d Msg_Lib                       10                                

                                                                  

dErrCde           ds                  qualified                   

d Common                              likeds(QUSEC)               

d MsgTxt                       512                                

                                                                  

dStringPtr        s               *                               

dString           s             20    based(StringPtr)            

dMsgDtaUpr        s           4096                                

dFound            s               n                               

dMsgSflRRN1       s              4  0                              

dTopSflRRN1       s              4  0                             

dSizeOfSFLPage    s              5u 0 inz(12)                     

dTopMsgID         s              7a                               

dRun              s               n                                

dUserAction       s               n                               

dPrvMsg_File      s                   like(Msg_File)              

dPrvMsg_Lib       s                   like(Msg_Lib)               

                                                                   

dNotUsedChr       s              1                                

dNotUsedInt       s             10i 0                             

                                                                  

 /free                                                             

                                                                  

  SetUp();                                                        

                                                                   

  dow (not *in03);                                                

      select;                                                     

         when ((Msg_File <> PrvMsg_File) or                       

               (Msg_Lib <> PrvMsg_Lib));                           

              GetNewMsgF();                                           

                                                                      

         when *in05;                                                  

              GetNewMsgF();                                           

                                                                      

         when *in41;                                                  

              GetNxtMsgs(SizeOfSFLPage);                               

                                                                      

         when TopSflRRN1 > 0;                                         

              UserAction = *off;                                      

              readc Msg_Sfl;                                          

              dow (not %eof(FndMsgsDF));                              

                  select;                                             

                     when Option = '2';                                

                          GetCurMsg(MsgID);                           

                          Run = RunCmd('?CHGMSGD ?*MSGID(' + MsgID +  

                                       ') ?*MSGF(' + %trimr(Msg_Lib) +

                                       '/' + %trimr(Msg_File) +      

                                       ') MSG(''' +                  

                                       %subst(RcvVar                 

                                         :(RcvVar.API.QMHOMRtn + 1)  

                                         :RcvVar.API.QMHLMRtn04) +   

                                       ''')');                       

                          if Run;                                    

                             GetCurMsg(MsgID);                       

                             MsgTxt = %subst(RcvVar                  

                                         :(RcvVar.API.QMHOMRtn + 1)  

                                         :RcvVar.API.QMHLMRtn04);    

                          endif;                                     

                          UserAction = *on;                          

                                                                     

                     when Option = '4';                               

                          Run = RunCmd('RMVMSGD MSGID(' + MsgID +    

                                       ') MSGF(' + %trimr(Msg_Lib) + 

                                       '/' + %trimr(Msg_File) + ')');

                          if Run;                                    

                             RtvMsgTxt(MsgTxt :%size(MsgTxt)         

                                       :'DSP5001');                  

                          endif;                                      

                          UserAction = *on;                          

                                                                     

                     when Option = '5';                              

                          Run = RunCmd('DSPMSGD RANGE(' + MsgID +    

                                       ') MSGF(' + %trimr(Msg_Lib) + 

                                       '/' + %trimr(Msg_File) + ')');

                          UserAction = *on;                           

                  endsl;                                             

                                                                     

                  if *in03;                                          

                     leave;                                          

                  endif;                                             

                                                                 

                  Option = *blanks;                              

                  update Msg_Sfl;                                

                  readc Msg_Sfl;                                 

              enddo;                                             

                                                                  

              if (not UserAction);                               

                 leave;                                          

              endif;                                             

                                                                  

         other;                                                  

              // Nothing left to do                              

      endsl;                                                     

                                                                  

      write Msg_Keys;                                            

      *in31 = (TopSflRRN1 > 0);                                  

      exfmt Msg_Ctl;                                             

  enddo;                                                       

                                                              

  *inlr = *on;                                                

  return;                                                     

                                                               

 /end-free                                                    

                                                              

pGetNxtMsgs       b                                           

dGetNxtMsgs       pi                                           

d NbrMsgsToGet                   5u 0 const                   

                                                              

dmyX              s              5u 0                         

dmyY              s              5u 0                         

                                                              

 /free                                                        

                                                              

  if TopMsgID <> RcvVar.API.QMHMID;                           

     // Need to refresh the first message to retrieve               

     GetCurMsg(TopMsgID);                                           

  endif;                                                            

                                                                     

  *in40 = *on;                                                      

  MsgSflRRN1 = TopSflRRN1;                                          

                                                                     

  for myX = 1 to (NbrMsgsToGet + 1);                                

      MonoCase(QLGIDRCB00                                           

               :%subst(RcvVar :(RcvVar.API.QMHOMRtn + 1)            

                       :RcvVar.API.QMHLMRtn04)                      

               :MsgDtaUpr :RcvVar.API.QMHLMRTN04 :QUSEC);           

      Found = *on;                                                  

      for myY = 1 to NbrOfStrings;                                  

          StringPtr = %addr(NbrOfStrings) + %size(NbrOfStrings) +   

                       ((myY - 1) * %size(String));                 

          if (%scan(%trimr(String)                                  

                    :%subst(MsgDtaUpr :1 :RcvVar.API.QMHLMRTN04))) = 0;

             Found = *off;                                            

             leave;                                                   

          endif;                                                      

      endfor;                                                          

                                                                      

      select;                                                         

         when not Found;                                               

              myX -= 1;                                               

         when myX > NbrMsgsToGet;                                     

              leave;                                                  

         other;                                                        

              MsgID = RcvVar.API.QMHMID;                              

              MsgTxt = %subst(RcvVar :(RcvVar.API.QMHOMRtn + 1)       

                              :RcvVar.API.QMHLMRtn04);                 

                                                                      

              MsgSflRRN1 += 1;                                        

              write Msg_Sfl;                                      

              *in40 = *off;                                        

      endsl;                                                      

                                                                  

      RtvMsgAPI(RcvVar :%size(RcvVar) :'RTVM0300'                 

                :RcvVar.API.QMHMID :QualMsgF                      

                :' ' :0 :'*NO' :'*NO' :QUSEC                      

                :'*NEXT' :0 :0);                                  

                                                                   

      if RcvVar.API.QMHBAVL09 = 0;                                

         // End of message descriptions if bytes avail = 0        

         *in35 = *on;                                             

         leave;                                                    

      else;                                                       

         TopMsgID = RcvVar.API.QMHMID;                            

      endif;                                                      

                                                                   

  endfor;                                                 

                                                          

  TopSflRRN1 = MsgSflRRN1;                                

  Sfl_DspPag = MsgSflRRN1;                                 

                                                          

 /end-free                                                

                                                          

pGetNxtMsgs       e                                        

 *                                                        

pGetNewMsgF       b                                       

dGetNewMsgF       pi                                      

                                                          

 /free                                                     

                                                          

  *in32 = *on;                                            

  write Msg_Ctl;                                          

  *in32 = *off;                                            

  MsgSflRRN1 = 0;                                        

  TopSflRRN1 = 0;                                        

  *in35 = *off;                                          

                                                          

  RtvMsgAPI(RcvVar :%size(RcvVar) :'RTVM0300'            

            :' ' :QualMsgF                               

            :' ' :0 :'*NO' :'*NO' :ErrCde                

            :'*FIRST' :0 :0);                             

                                                         

  select;                                                

     when ErrCde.Common.QUSBAvl > 0;                     

          SndEscape();                                   

                                                          

     when RcvVar.API.QMHBAVL09 = 0;                      

          // No messages in message file                 

          *in35 = *on;                                   

                                                          

     other;                                                   

          TopMsgID = RcvVar.API.QMHMID;                       

          GetNxtMsgs(SizeOfSFLPage);                          

  endsl;                                                       

                                                              

  if %subst(Msg_Lib :1 :1) = '*';                             

     // Resolve special values like *LIBL and *CURLIB         

     RtvObjD(ObjInfo :%size(ObjInfo) :'OBJD0100'              

             :QualMsgF :'*MSGF' :QUSEC);                      

     Msg_Lib = ObjInfo.API.QUSRL01;                           

  endif;                                                      

                                                               

  PrvMsg_File = Msg_File;                                     

  PrvMsg_Lib = Msg_Lib;                                       

                                                              

 /end-free                                                     

                                                              

pGetNewMsgF       e                                            

 *                                                             

pGetCurMsg        b                                             

dGetCurMsg        pi                                           

d MsgID                          7a   const                    

                                                               

 /free                                                          

                                                               

  RtvMsgAPI(RcvVar :%size(RcvVar) :'RTVM0300'                  

            :MsgID :QualMsgF                                   

            :' ' :0 :'*NO' :'*NO' :QUSEC                       

            :'*MSGID' :0 :0);                                  

                                                               

 /end-free                                                     

                                                                

pGetCurMsg        e                                            

 *                                                             

pRunCmd           b                                            

dRunCmd           pi              n                            

d Cmd                          512a   const                    

                                                               

 /free                                                         

                                                                

  ProcessCmd(Cmd :%len(%trimr(Cmd))                            

             :QCAP0100 :%size(QCAP0100) :'CPOP0100'            

             :NotUsedChr :0 :NotUsedInt :ErrCde);               

                                                               

  if ErrCde.Common.QUSBAvl > 0;                                

     select;                                                   

        when ErrCde.Common.QUSEI = 'CPF6801';                   

             if %subst(ErrCde.MsgTxt :1 :3) = 'F3 ';           

                *in03 = *on;                                   

                return *off;                                   

             else;                                              

                return *off;                                 

             endif;                                          

        other;                                               

             SndEscape();                                     

     endsl;                                                  

  else;                                                      

     return *on;                                             

  endif;                                                      

                                                             

 /end-free                                                   

                                                             

pRunCmd           e                                           

 *                                                           

pSetUp            b                                          

dSetUp            pi                                         

                                                             

 /free                                                       

                                                                 

  QUSBPrv = 0;                                                   

  ErrCde.Common.QUSBPrv = %size(ErrCde);                          

                                                                 

  // Process command API control structure                       

  QCAP0100 = *loval;              // init API ctls to nulls      

  QCACMDPT = 0;                   // Run command                 

  QCABCSDH = '0';                 // Ignore DBCS                 

  QCAPA = '2';                    // Prompt if  controls found   

  QCACMDSS = '0';                 // Use i OS syntax             

                                                                  

  // Convert case API control structure                          

  QLGIDRCB00 = *loval;            // init API ctls to nulls      

  QLGTOR02 = 1;                   // CCSID based casing          

  QLGIDOID00 = 0;                 // Use job CCSID               

  QLGCR00 = 0;                    // Convert to uppercase        

                                                                 

  QualMsgF = QualMsgF_in;                               

                                                         

  GetNewMsgF();                                         

                                                        

  write Msg_Keys;                                       

  *in31 = (TopSflRRN1 > 0);                              

  exfmt Msg_Ctl;                                        

                                                        

 /end-free                                              

                                                        

pSetUp            e                                     

 *                                                      

pSndEscape        b                                     

dSndEscape        pi                                    

                                                         

dLenMsgDta        s             10i 0                   

dMsgKey           s              4a                     

                                                                   

 /free                                                              

                                                                   

  if ErrCde.Common.QUSBAVL < 16;                                   

     LenMsgDta = 0;                                                

  else;                                                             

     LenMsgDta = ErrCde.Common.QUSBAVL - %size(ErrCde.Common);     

  endif;                                                           

                                                                   

  SndMsg(ErrCde.Common.QUSEI :'QCPFMSG   *LIBL'                    

         :ErrCde.MsgTxt :LenMsgDta                                 

         :'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);                 

                                                                    

 /end-free                                                         

                                                                   

pSndEscape        e                                                

 

As we have added a new parameter to the FNDMSGS command definition, we need to change the prototype and procedure interface of the CPP. In the sample program, this is done by adding the NbrOfStrings parameter definition to the FNDMSGS pr and pi specifications.

 

The STRING parameter of the FNDMSGS command is defined as a simple list—a list of from one to three values where each value is of the same type. This type of list, when passed to a CPP written in a high-level language such as RPG, is formatted as a 2-byte binary value representing the number of values passed to the program, followed immediately by the specified number of passed parameter values. In the case of the STRING character value inputs, each value will be left-adjusted and blank-padded to a length of 20 bytes.

 

The STRING parameter can be defined several ways within the RPG application program. One common approach is to define the parameter as a data structure containing a 2-byte binary value followed by a 60-byte character string (a maximum of three values, each 20 bytes in size). The program can then access the string values using substring operations against the 60-byte subfield within the bounds of the 2-byte binary value representing the number of STRING values passed to the program. A second approach is to define the parameter as a data structure containing a 2-byte binary value followed by an array of three 20-byte elements. The program can then access the string values using array indexing within the bounds of the 2-byte binary value representing the number of STRING values passed to the program. A third approach, and the one taken in the sample program, is to define only the 2-byte binary value representing the number of STRING values passed to the program. The FNDMSGS program, as you will see shortly, then accesses the STRING values using a pointer and a based variable.

 

Using a pointer approach eliminates the need for the RPG program to explicitly code a character string of 60 bytes, as in the first approach, or an array of three elements, as in the second solution. If users of the FNDMSGS command were to request an enhancement next year to allow up to five search values, you would only need to change the command definition if the search values are accessed using a pointer approach. With the other solutions, you would also need to change the RPG program, making the character string 100 bytes in length when using the substring approach, or changing the DIM(3) declaration to DIM(5) when using the array approach.

 

The next two changes to the RPG program are related to the use of the Convert Case API. This API is used to uppercase the first-level text of retrieved messages so that the program can easily find all messages containing the text specified by the FNDMSGS STRING parameter (the i operating system command analyzer will by default uppercase the STRING values if you do not quote the searched-for values, which the FNDMSGS program is implicitly assuming you will not do). First, the program provides a prototype, MonoCase, defined as a procedure call to the QlgConvertCase API with five parameters. The first parameter is a control block passed to the API and specifies the type of conversion to be performed. The second parameter of the Convert Case API is the textual data to be monocased, the third parameter is used to receive the monocased data from the API, and the fourth parameter specifies the length of the textual data to be monocased. The last parameter is the standard API error code data structure. Having prototyped the Convert Case API, the program also uses the /copy directive to include the QLG source member of QSYSINC/QRPGLESRC. This member provides a data structure, QLGIDRCB00, which defines the Convert Case control block parameter. The FNDMSGS program initializes this structure in the SetUp procedure.

 

Related to the search for messages containing the user-specified STRING values, several variables are also declared. StringPtr and String are used to access the STRING values, MsgDtaUpr to hold the uppercased first-level text of retrieved messages (a separate variable is used for the uppercased message text so that the subfile will continue to display the message text in mixed case), and Found is an indicator to represent whether or not all STRING values were found in the retrieved first-level text of a message.

 

The functional changes to the program can be found in the GetNxtMsgs procedure. As with last month's version of the GetNxtMsgs procedure, the intent of the routine is to load the next subfile page of messages and determine whether or not the end of the subfile messages has been reached. The actual processing has changed quite a bit, though, as the program is now conditionally writing messages to the subfile based on the message text containing all of the specified STRING values.

 

As determining whether or not to set SFLEND is dependent on there being another message containing all of the STRING values (as opposed to just any "next" message existing), the main FOR loop of GetNxtMsgs is changed from running NbrMsgsToGet to (NbrMsgToGet + 1).

 

Upon entering the FOR group, the message currently being processed is converted to uppercase using the Convert Case API (named MonoCase within the program). The first parameter passed to the API is the Request control block structure QLGIDRCB00. The Convert Case API provides three ways to monocase a text string with the Request control block structure controlling which method is to be used. One option, and the one used by the sample program, is to specify the CCSID encoding of the textual data. The other options are to name a table object to use or to provide a user-defined conversion table directly. When a CCSID-based conversion is specified, you indicate what CCSID to use and whether to uppercase or lowercase the data. In the SetUp procedure, the Request control block is formatted for a CCSID-based conversion (QLGTOR02 is set to 1) to uppercase (QLGCR00 is set to 0) using the job CCSID (QLGIDOI00 is set to 0). The Retrieve Message API, due to our taking the default for the CCSID to convert to parameter, is converting the retrieved message text to the job CCSID.

 

While the SetUp procedure has only four statements related to properly initializing the QLGIDRCB00 control block (not counting the comment) for uppercasing by job CCSID, two of the four are technically not needed. Both the CCSID to use (QLGIDOID00) and the conversion request (QLGCR00) subfields of QLGIDRCB00 are defined as integer values, so when SetUp set QLGIDRCB00 to *loval, SetUp also implicitly set these two variables to 0. The redundant setting of QLGIDOID00 to 0 (use job CCSID) and of QLGCR00 to 0 (uppercase) is done in SetUp solely as an aid in seeing what the program is doing.

 

The remaining three parameters passed to the Convert Case API are the text to be uppercased, the variable to receive the uppercased data, and the length of the data to uppercase, respectively. The substring operation used to identify the text to uppercase is the same as last month's substring operation to move the message text to the subfile field MsgTxt. In case you're wondering, the variables used for the second and third parameters (the input and output parameters of the API) can identify the same variable, so FNDMSGS could uppercase the message text in place. As mentioned earlier in this article, though, a separate output variable is used as I prefer to show the first-level message text in mixed case (and, as written, FNDMSGS doesn't load the first-level message text until after Convert Case uppercases the text and the STRING values are found in the message text).

 

At this point (if not earlier…), some readers may be wondering why the FNDMSGS program is using the Convert Case API rather than RPG's translate (XLATE or %xlate) support. The translate function can certainly be used for the monocasing of textual data, but it has a limitation that may not be immediately obvious—namely, that the characters specified in the From and To strings passed to xlate are stored in the CCSID of the module, and the module source will only be in one CCSID. If you are confident that all textual data your programs need to monocase will be in only one CCSID and you can clearly identify the characters that need to be monocased (for instance, many lowercase to uppercase xlate-based From and To strings seem to forget about names containing â, ä, à, á, ã, and ä, just to mention a few variants of "a" that are all valid in CCSID 37), then the translate function may be sufficient for your needs. As using the Convert Case API is quite simple, and the API provides support if you ever do need to work with multiple CCSIDs or an expanded set of characters, I recommend using the API across the board for textual monocasing. Having uppercased the message text, the program sets indicator Found to *on (providing a default that the message does contain all user-specified STRING values) and then searches the current message for each specified value.

 

A FOR group, conditioned by the number of values to search for, is used to scan the message text. As soon as any searched-for value is not found, indicator Found is set to *off and the FOR loop exited. You might note that this Found indicator is not really necessary. You could remove all references to the Found indicator and still accomplish this search/validation function. This indicator is included solely for ease of reading. The test for 'not Found', located later in the program, can be accomplished simply by checking if myY (the control variable of the FOR loop) is less than or equal to NbrOfStrings. The only time myY will be greater than NbrOfStrings is when the FOR loop was successfully run for all STRING values (that is, indicator Found would have still been "on").

 

As mentioned earlier in this article, a based variable is used to identify the STRING values to be searched for. The based variable is String, and the basing pointer is StringPtr. The location of each String value is calculated by taking the address of the NbrOfStrings parameter (the 2-byte binary value passed as a parameter to the FNDMSGS program), adding the size of the NbrOfStrings field (2 bytes), and then adding the offset from this location to the string value to be used. The first passed STRING value is found immediately after the NbrOfStrings field (that is, an offset of 0), the second STRING value 20 bytes after the first value (or an offset of 20), and so on. To calculate the appropriate offset, the program takes the FOR control value (variable myY), subtracts 1, and then multiplies this value by the size of each string (20 bytes). StringPtr is then pointing to the correct user-specified String value.

 

The program now uses the %scan built-in to scan for the String value within the uppercased message text (MsgDtaUpr) for the length of the message text. If %scan returns 0, the String value was not found, indicator Found is set to *off, and the FOR group is left. Otherwise, the FOR group is re-run using the next user-specified String value.

 

Upon exiting the FOR group, either due to a String value not being found or all String values being found, a SELECT is entered. If a String value was not found, the outer FOR loop control value (myX) is decremented as the message is not being written to the subfile, the next message from the message file is retrieved, and the outer FOR group re-run. If the outer FOR loop control value (myX) indicates that the subfile page is full and that the next message to be written to the subfile has been found (myX > NbrMsgsToGet), then the outer FOR group is left and the subfile page containing the most recent set of messages will be shown. Otherwise, a message has been found that contains all of the user-specified String values, the message is written to the subfile, the next message from the message file is retrieved, and the outer FOR group re-run. The actual writing of the message to the subfile is the same as was done last month.

 

You can compile the FNDMSGS program using this command:

 

CRTBNDRPG PGM(FNDMSGS)

 

Run the program using the FNDMSGS command. Prompt FNDMSGS, and search for strings such as "library" in message file QCPFMSG. As that returns quite a few messages, try the command again using string values of "library," "file," and "not." Still quite a few matches, so change the MAX(3) of the FNDMSGS command PARM definition for STRING to MAX(5), re-create the FNDMSGS command using the CRTCMD command provided at the start of this article (note that you do not need to recompile the FNDMSGS program), and try "library," "file," "not," and "authorized." As you can see, the FNDMSGS program works as-is with the larger number of user-specified values.

 

Hopefully, you will find FNDMSGS to be a useful utility and will add the Convert Case API to your developer's toolkit. Convert Case is an API that can be utilized when you need to uppercase or lowercase text in order to simplify sorting or searching. For FNDMSGS, it's useful when searching message text, but it can be applied just as easily to customer names and other textual information. Next month, we'll look at additional APIs that are available to you.

 

In the meantime, if you have any API questions, send them to me at This email address is being protected from spambots. You need JavaScript enabled to view it.. I'll see what I can do about answering your burning questions in future columns.   

as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7, V6R1

Bruce Vining

Bruce Vining is president and co-founder of Bruce Vining Services, LLC, a firm providing contract programming and consulting services to the System i community. He began his career in 1979 as an IBM Systems Engineer in St. Louis, Missouri, and then transferred to Rochester, Minnesota, in 1985, where he continues to reside. From 1992 until leaving IBM in 2007, Bruce was a member of the System Design Control Group responsible for OS/400 and i5/OS areas such as System APIs, Globalization, and Software Serviceability. He is also the designer of Control Language for Files (CLF).A frequent speaker and writer, Bruce can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it.. 


MC Press books written by Bruce Vining available now on the MC Press Bookstore.

IBM System i APIs at Work IBM System i APIs at Work
Leverage the power of APIs with this definitive resource.
List Price $89.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: