| The API Corner: Reading a Message File |
|
|
|
| Programming - APIs | |||||
| Written by Bruce Vining | |||||
| Wednesday, 16 March 2011 00:00 | |||||
|
Retrieve, display, and generally work with messages your way.
In the last article, "Reducing the Use of Compile-Time Arrays," we looked at using the Retrieve Message API QMHRTVM to randomly access the first-level message text associated with a message description. In this article, we'll look at an optional parameter of the QMHRTVM API. This parameter, Retrieve option, enables us to sequentially read messages from a message file in addition to random access to messages. We'll use this capability to write a sample program that finds all messages, within a specified message file, that contain one or more specific words (or more accurately, a sequence of letters) within the first-level text of the message.
The actual "find" function will be implemented in the next article due to space reasons. This article will provide a basic message description work-with program, one that we will then enhance next month. Today's program will accept a qualified message file name and then display the messages found within the message file. From this display, you can change one or more message descriptions, display the details of one or more message descriptions, and/or remove one or more message descriptions. The APIs that we will be using initially are these:
The provided sample program is functional though hardly complete. For instance, there is no checking for when the subfile displaying the found message descriptions is full (more than 9,999 message descriptions within a message file), a message file not found condition results in an escape message being sent rather than a recoverable error message being shown to the user, etc. The sample program is, however, sufficient to demonstrate the use of various APIs.
To run the Find Messages (FNDMSGS) program, we will create a command also called FNDMSGS. Using the message file TEXTMSGS first created in the article "Still Using Compile-Time Arrays?," run the following four Add Message Description commands.
ADDMSGD MSGID(CMD0001) MSGF(TEXTMSGS) MSG('Find Messages') ADDMSGD MSGID(CMD0002) MSGF(TEXTMSGS) MSG('Message file') ADDMSGD MSGID(CMD0003) MSGF(TEXTMSGS) MSG('Library') ADDMSGD MSGID(DSP5001) MSGF(TEXTMSGS) MSG('*** Message Removed ***')
The first three message descriptions are used as prompt text in creating the FNDMSGS command. The fourth message, DSP5001, is used by the FNDMSGS command processing program to indicate that the user has removed a message from the currently displayed message file.
This is the source for the FNDMSGS command:
CMD PROMPT(CMD0001) 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)
To create the FNDMSGS command, you can use this command:
CRTCMD CMD(FNDMSGS) PGM(FNDMSGS) PMTFILE(TEXTMSGS)
The FNDMSGS program uses a *DSPF, named FNDMSGSDF, to provide a work-with interface to the user. The source for the *DSPF follows:
CA03(03 'Exit') CF05(05 'Refresh') R MSG_SFL SFL OPTION 1 B 9 3VALUES(' ' '2' '4' '5') 40 DSPATR(PC) MSGID 7 O 9 6 MSGTXT 60 O 9 14 R MSG_CTL SFLCTL(MSG_SFL) OVERLAY SFLPAG(12) SFLSIZ(13) 31 SFLDSP N32 SFLDSPCTL 32 SFLCLR 35 SFLEND N35 PAGEDOWN(41) SFL_DSPPAG 4S 0H SFLRCDNBR 1 26'Work with Found Messages' COLOR(WHT) 3 2'Message file:' MSG_FILE 10 B 3 18 3 33'Library:' MSG_LIB 10 B 3 44 5 2'Type options, press Enter.' COLOR(BLU) 6 4'2=Change' COLOR(BLU) 6 15'4=Delete' COLOR(BLU) 6 26'5=Display message' COLOR(BLU) 8 2'Opt' COLOR(WHT) 8 6'MsgID' COLOR(WHT) 8 14'Message text' COLOR(WHT) R MSG_KEYS 23 2'F3=Exit' COLOR(BLU) 23 15'F5=Refresh' COLOR(BLU)
To create the *DSPF, use this command:
CRTDSPF FILE(FNDMSGSDF) SRCFILE(QDDSSRC)
The command processing program, FNDMSGS, is shown below. Note that the referenced binding directory, MSGSPTBD, was created in the previous article "Reducing the Use of Compile-Time Arrays."
h dftactgrp(*no) bnddir('MSGSPTBD')
fFndMsgsDF cf e workstn sfile(Msg_Sfl :MsgSflRRN1)
dFndMsgs pr d QualMsgF_in 20a const
dFndMsgs pi 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)
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,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
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
/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; MsgID = RcvVar.API.QMHMID; MsgTxt = %subst(RcvVar :(RcvVar.API.QMHOMRtn + 1) :RcvVar.API.QMHLMRtn04);
MsgSflRRN1 += 1; write Msg_Sfl; *in40 = *off;
RtvMsgAPI(RcvVar :%size(RcvVar) :'RTVM0300' :MsgID :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);
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
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
The program can be created using the following command:
CRTBNDRPG PGM(FNDMSGS)
To run the program, you can enter a command such as this:
FNDMSGS
This command, which defaults to the qualified message file name *LIBL/TEXTMSGS per the command definition source, allows you to work with the message descriptions found in the specified message file.
The program, upon entry, runs the SetUp procedure. The SetUp procedure does the following:
Before seeing what happens when SetUp returns to the mainline, let's review the processing of the procedures called by SetUp. The GetNewMsgF procedure is run whenever a new message file is to be used. This could be when the program is initially called or when the user changes the message file name and/or library name on the display record format MSG_CTL. The GetNewMsgF procedure does the following:
The GetNxtMsgs procedure performs these actions:
Having reviewed what happened when the SetUp procedure runs, SetUp now returns to the mainline of the FNDMSGS program. The mainline logic is a DO WHILE loop exited with command key 3. Within the DOW is a SELECT group used to determine what processing should be done.
If the user changes the message file name, the message file library name, or both, then the GetNewMsgF procedure is used to clear the current subfile and load a new first subfile page using the newly specified qualified message file name.
If command key 5 (indicator 5) is used, then the GetNewMsgF procedure is used to clear the current subfile and reload a new first subfile page using the current qualified message file name.
If PageDown (indicator 41) is used, then the GetNxtMsgs procedure is used to load the next subfile page of the current qualified message file name.
If none of the previous three conditions occurred, and there are message descriptions loaded in the subfile (TopSflRRN1 > 0), the program then sets indicator UserAction to indicate that no user action has (yet) been detected and enters a DOW to determine if the user has selected to change, remove, or display any of the message descriptions currently loaded in the subfile.
If the user selected option 2 (change), the GetCurMsg procedure is used to re-access the message description for the message identified by the MsgID variable of the read subfile record, and the RunCmd procedure is used to run the CHGMSGD command. The CHGMSGD command is prompted due to the leading question mark (?), rendering the user unable to change the MSGID or MSGF keyword values (the "?*" prompt controls). The first-level text of the message to be changed is included in the prompt, which I find a lot more useful than the default value of *SAME. The RunCmd procedure returns an indicator setting, which is stored in variable Run. If variable Run is set to on after the RunCmd procedure returns, then the program again uses the GetCurMsg procedure, this time to get the (presumably) changed first-level text of the message. This text will later be used to update the current subfile record to reflect the changed message text. As the user has performed some action (even if the user then canceled the prompted request), indicator UserAction is set to on.
The RunCmd procedure uses the Process Command (QCAPCMD) API, discussed in previous columns starting with "Running CL Commands from RPG," to run the command specified by its caller. After running the command, RunCmd checks to see if an error was returned and, if there was, if the error message is CPF6801 (Command prompting ended when user pressed &1). This error can be returned if the CL command (such as the CHGMSGD command) is prompted and the user then uses F3 or F12 to end the command prompt without actually running the command. If F3 was used, RunCmd sets on indicator 3 and returns an "off" indicator with the RETURN operation. If F12 was used, RunCmd simply returns an "off" indicator with the RETURN operation. If any error was returned by the API other than CPF6801, RunCmd resends the escape message using the SndEscape procedure. If no error was returned by the API, then RunCmd returns an "on" indicator with the RETURN operation.
If the user selected option 4 (remove) the RunCmd procedure is used to run the RMVMSGD command (without prompting). If the command runs successfully, then the text of message DSP5001 ("*** Message Removed ***"), added earlier in this column, is retrieved using the RtvMsgTxt procedure defined in the previous article "Reducing the Use of Compile-Time Arrays." This text will be used to later update the current subfile record text. As with the change option, indicator UserAction is also set on to indicate that the user has performed a function.
If the user selected option 5 (display), the RunCmd procedure is used to run the DSPMSGD command, and again the UserAction indicator is set to on.
After running the selected option, a check is made for indicator 3 being on. This is done as the user could have used F3 to exit from the command prompt for CHGMSGD. If indicator 3 is on, the DOW processing of the subfile is exited. If indicator 3 is off, the current subfile record is updated by setting the option value to blanks and writing the current text associated with the message (previously set by the processing associated with either option 2 or option 4). The next changed subfile record is then read and the DOW rerun.
After all changed subfile records have been processed, a check is made to determine if any user action was detected while processing the subfile. If not (variable UserAction is still off), then the outer DOW loop (conditioned by indicator 3) is exited and the program ends. Otherwise, the program redisplays the subfile and waits for the next user request.
That's our basic work-with message descriptions application. Next month, we'll add the ability to find and display only those messages where one or more words are found in the first-level text of the message description. And needless to say, we'll be using an API to help us in this task.
In the meantime, if you have any API questions, send them to me at bvining@brucevining.com. 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, | |||||
View all articles by this author
|
|||||
| Last Updated on Monday, 14 March 2011 13:25 |








You must be logged in to view or make comments on this article