15
Fri, Nov
2 New Articles

The CL Corner: Using Command Parameter Lists, Elements, and Conditional Prompting

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

More functionality extends the USEDIRPGM command.

 

In last month's article, "Isn't Recursion Great?," we saw how a CL command can recursively invoke itself in order to provide nested processing—in the case of USEDIRPGM, nested processing of imbedded IFS directories. Today, we will expand on last month's program with more-specific handling of the stream files (*STMF) and directories (*DIR) that are processed by the DIR3 program, which is the command processing program (CPP) for the USEDIRPGM command.

 

We will be adding two parameters to the USEDIRPGM command.

 

The first parameter, STMFOPTS or Stream file options, will be a simple list parameter where the user can specify a maximum of two options related to the processing of stream files. The two special values supported are these:

 

  • *DSP—Displays information about the stream file. *DSP will be the default value and will display the same type of information as the earlier version of the USEDIRPGM command.
  • *RMV—Removes any stream files that have not been accessed within the number of days indicated by the DAYS parameter of the USEDIRPGM command

 

The second parameter, SUBTREEIND or Subtree indentation, will be a two-element parameter where the user can specify what indentation should be used when displaying imbedded directory information. The first element is the initial indentation to be used in messages related to the directory specified by the DIR parameter. The second element is the amount of indentation to be used in messages related to any imbedded directories that are encountered when running the USEDIRPGM command. The default value for both elements is *NONE, which is compatible with the earlier versions of the USEDIRPGM command. The SUBTREEIND parameter will only be prompted when the SUBTREE parameter value is *ALL, indicating that imbedded directories are to be processed (or, of course, if you use F9, all parameters, when prompting the USEDIRPGM command). Note that though SUBTREEIND is prompted only when the SUBTREE value is *ALL, the keyword parameter values are "active" regardless of the SUBTREE value. This active state is due to the current implementation of the DIR3 CPP; another implementation could just as easily ignore the SUBTREEIND values when SUBTREE is not *ALL.

 

Using these new parameters, as in 'USEDIRPGM DIR('/myplaydir') STMFOPTS(*DSP *RMV) SUBTREEIND(*NONE 3)', might provide a display such as this:

 

*STMF file1.txt recently accessed

Starting imbedded directory: MyImbeddedDir

   *STMF file2.txt recently accessed

   *STMF file2a.txt                      

   Last change: Sat May 14 09:09:13 2011

   Last access: Sat May 14 09:09:06 2011

   file2a.txt removed

   Starting imbedded directory: AnotherDir    

      *STMF deepfile.txt recently accessed

   Ended imbedded directory: AnotherDir                        

Ended imbedded directory: MyImbeddedDir

*STMF file1a.txt recently accessed

 

In keeping with how the USEDIRPGM command is currently defined, we'll first add several message descriptions to the USERMSGF message file to describe our new parameters. Add these message descriptions using the following commands.

 

ADDMSGD MSGID(DIR0005) MSGF(USERMSGF) MSG('Stream file options')

ADDMSGD MSGID(DIR0006) MSGF(USERMSGF) MSG('Subtree indentation')

ADDMSGD MSGID(DIR0007) MSGF(USERMSGF) MSG('Initial indentation')

ADDMSGD MSGID(DIR0008) MSGF(USERMSGF) MSG('Level indentation')  

 

To support these new command parameters, we need to change the USEDIRPGM command definition. The new command definition, with the changes shown in bold, is this:

 

             CMD        PROMPT(DIR0001)                              

             PARM       KWD(DIR) TYPE(*PNAME) LEN(1024) DFT(*CURDIR) +

                          SPCVAL((*CURDIR '.')) PROMPT(DIR0002)      

             PARM       KWD(STMFOPTS) TYPE(*CHAR) LEN(10) RSTD(*YES) +

                          DFT(*DSP) VALUES(*DSP *RMV) MIN(0) MAX(2) +

                          PROMPT(DIR0005)                            

             PARM       KWD(DAYS) TYPE(*UINT4) DFT(3) PROMPT(DIR0003)

             PARM       KWD(SUBTREE) TYPE(*CHAR) LEN(10) RSTD(*YES) +

                          DFT(*ALL) VALUES(*ALL *NONE) PROMPT(DIR0004)

             PARM       KWD(SUBTREEIND) TYPE(INDENTVALS) +           

                          PMTCTL(SUBTREEALL) PROMPT(DIR0006)         

 INDENTVALS: ELEM       TYPE(*UINT2) DFT(*NONE) SPCVAL((*NONE 0)) +  

                          PROMPT(DIR0007)                            

             ELEM       TYPE(*UINT2) DFT(*NONE) SPCVAL((*NONE 0)) +  

                          PROMPT(DIR0008)                            

 SUBTREEALL: PMTCTL     CTL(SUBTREE) COND((*EQ *ALL))                

 

The first new parameter, STMFOPTS, is a simple list of options. An earlier article, "Trim Multiple Leading Characters with TRMLFTCHR," introduced how to process a simple list. Basically, the USEDIRPGM CPP will be passed the STMFOPTF parameter as a 2-byte integer value indicating the number of options specified by the user, and immediately following this integer value will be that number of Char(10) option values.

 

The second new parameter, SUBTREEIND, has a few characteristics we have not seen in previous articles. The TYPE keyword specifies the label INDENTVALS rather than a predefined type such as *CHAR, *DEC, or *LGL. Within the USEDIRPGM command definition, INDENTVALS is further defined as having two elements. The first element is an unsigned 2-byte integer value representing the initial indentation to be used when displaying directory-related information. The default value for this element is *NONE, which is passed to the CPP as the value 0. The second element is also an unsigned 2-byte integer value. This value represents the amount of indentation to be used when displaying nested directory-related information. The default value for this element is also *NONE and is passed to the CPP as the value 0. When using elements, the parameter value actually passed to the CPP is a 2-byte integer value indicating the number of elements passed followed immediately by that number of element values. So, for SUBTREEIND, the parameter value passed to the CPP will be six bytes in length: the number of elements passed to the program, the initial indentation value, and the level indentation value.

 

The SUBTREEIND parameter definition also specifies the PMTCTL, or prompt control, keyword. This keyword identifies the label SUBTREEALL, which, like INDENTVALS, is found later within the USEDIRPGM command definition source. The PMTCTL keyword can be used to control when the SUBTREEIND parameter is prompted. The SUBTREEALL statement indicates that the SUBTREEIND keyword is to be prompted only when the controlling (CTL) USEDIRPGM parameter SUBTREE value meets the condition (COND) of being equal to *ALL.

 

With that brief introduction to the USEDIRPGM command changes, let's create the USEDIRPGM command using the following command.

 

CRTCMD CMD(USEDIRPGM) PGM(DIR3) PMTFILE(USERMSGF)

 

The CPP for USEDIRPGM, DIR3, is shown below with the changes from last month shown in bold.

 

Pgm        Parm(&Dir_In &NbrStmFOpt &Days &SubTree &SubTreeInd)       

Dcl        Var(&Dir_In)     Type(*Char) Len(1024)                     

Dcl        Var(&NbrStmFOpt) Type(*Int)  Len(2)                        

Dcl        Var(&Days)       Type(*UInt)                               

Dcl        Var(&SubTree)    Type(*Char) Len(10)                       

Dcl        Var(&SubTreeInd) Type(*Char) Len(6)                        

                                                                      

Dcl        Var(&NbrSubTInd) Type(*Int)  Stg(*Defined) +               

             Len(2) DefVar(&SubTreeInd 1)                             

Dcl        Var(&InlIndent)  Type(*UInt) Stg(*Defined) +               

             Len(2) DefVar(&SubTreeInd 3)  /* Initial indentation */  

Dcl        Var(&SubIndent)  Type(*UInt) Stg(*Defined) +               

             Len(2) DefVar(&SubTreeInd 5)  /* Subsequent indentation */

                                                                      

Dcl        Var(&InlPath)    Type(*Char) Len(1025)                     

Dcl        Var(&Dir_Ptr)    Type(*Ptr)                                

                                                               

Dcl        Var(&StmFOptPtr) Type(*Ptr)                          

Dcl        Var(&StmFOpt)    Type(*Char) Stg(*Based) +          

             Len(10) BasPtr(&StmFOptPtr)                       

                                                               

Dcl        Var(&StmF_Dsp)   Type(*Lgl)                          

Dcl        Var(&StmF_Rmv)   Type(*Lgl)                         

                                                               

Dcl        Var(&DirEnt_Ptr) Type(*Ptr)                         

Dcl        Var(&DirEnt)     Type(*Char) Len(696) +             

             Stg(*Based) BasPtr(&DirEnt_Ptr)                   

Dcl        Var(&LenOfName)  Type(*UInt) Stg(*Defined) +        

             DefVar(&DirEnt 53)                                

Dcl        Var(&Name)       Type(*Char) Len(640) +             

             Stg(*Defined) DefVar(&DirEnt 57)                  

                                                                      

Dcl        Var(&FileInfo)   Type(*Char) Len(128)                      

Dcl        Var(&LstAccess)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 25)       /* Last open   */

Dcl        Var(&LstDtaChg)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 29)       /* Last changed*/

Dcl        Var(&ObjTyp)     Type(*Char) Len(10) +                     

             Stg(*Defined) DefVar(&FileInfo 49)       /* Object type */

                                                                      

Dcl        Var(&DatTim_Ptr) Type(*Ptr)                                

Dcl        Var(&DatTim)     Type(*Char) Len(24) +                     

             Stg(*Based) BasPtr(&DatTim_Ptr)                          

                                                                      

Dcl        Var(&SecsInDay)  Type(*Int)  Value(86400)                  

Dcl        Var(&TimeFilter) Type(*Int)                                

                                                                      

Dcl        Var(&Path)       Type(*Char) Len(10000)                    

Dcl        Var(&MsgTxt)     Type(*Char) Len(300)          

Dcl        Var(&Status)     Type(*Int)                    

Dcl        Var(&Counter)    Type(*Int)  Len(2)            

Dcl        Var(&Str)        Type(*Int)                     

Dcl        Var(&StrIndent)  Type(*Int)                    

Dcl        Var(&Len)        Type(*Int)                    

Dcl        Var(&DaysC)      Type(*Char) Len(10)           

Dcl        Var(&StrIndentC) Type(*Char) Len(5)            

Dcl        Var(&SubIndentC) Type(*Char) Len(5)            

Dcl        Var(&Cmd)        Type(*Char) Len(11000)        

Dcl        Var(&LenCmd)     Type(*Dec)  Len(15 5) +        

             Value(11000)                                 

Dcl        Var(&Null)       Type(*Char) Len(1) +          

             Value(x'00')                                 

Dcl        Var(&Null_Ptr)   Type(*Ptr)                    

                                                          

ChgVar     Var(&InlPath) Value(&Dir_In)                    

ChgVar     Var(&Path) Value(&InlPath *TCat &Null)          

CallPrc    Prc('opendir') Parm(&Path) RtnVal(&Dir_Ptr)     

If         Cond(&Dir_Ptr = &Null_Ptr) Then(Do)             

           SndPgmMsg Msg('Directory not found') +          

             ToPgmQ(*Ext)                                  

           Return                                          

           EndDo                                           

                                                            

CallPrc    Prc('time') Parm((*Omit)) RtnVal(&TimeFilter)   

ChgVar     Var(&TimeFilter) +                              

             Value(&TimeFilter - (&Days * &SecsInDay))     

                                                           

ChgVar     Var(&StmFOptPtr) Value(%Addr(&NbrStmFOpt))      

ChgVar     Var(%ofs(&StmFOptPtr)) +                        

             Value(%ofs(&StmFOptPtr) + 2)                  

                                                           

DoFor      Var(&Counter) From(1) To(&NbrStmFOpt)              

           Select                                             

              When Cond(&StmFOpt = '*DSP') +                  

                   Then(ChgVar Var(&StmF_Dsp) Value('1'))     

              When Cond(&StmFOpt = '*RMV') +                  

                   Then(ChgVar Var(&StmF_Rmv) Value('1'))     

           EndSelect                                          

           ChgVar Var(%ofs(&StmFOptPtr)) +                    

                    Value(%ofs(&StmFOptPtr) + 10)             

           EndDo                                              

                                                              

ChgVar     Var(&Str) Value(&InlIndent + 1)                    

ChgVar     Var(&Len) Value(300 - &InlIndent)                  

                                                              

CallPrc    Prc('readdir') +                                   

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)      

                                                               

DoWhile    Cond(&DirEnt_Ptr *NE &Null_Ptr)                         

                                                                   

           If Cond(%sst(&Name 1 1) *NE '.') Then(Do)               

              ChgVar Var(&Path) Value(&InlPath *TCat '/' *TCat +   

                         %sst(&Name 1 &LenOfName) *TCat &Null)     

              CallPrc Prc('stat') +                                

                Parm(&Path &FileInfo) RtnVal(&Status)               

                                                                   

              If Cond(&Status = 0) Then(Do)                        

                                                                   

                 Select                                             

                    When Cond((&ObjTyp = '*DIR') *And +            

                              (&SubTree = '*ALL')) Then(Do)        

                         ChgVar Var(%sst(&MsgTxt &Str &Len)) +     

                           Value('Starting imbedded directory: ' + 

                                 *Cat %sst(&Name 1 &LenOfName))    

                         SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)       

                                                                     

                         ChgVar Var(&Path) Value(&InlPath *TCat +   

                           '/' *TCat +                              

                           %sst(&Name 1 &LenOfName))                

                                                                     

                         ChgVar Var(&DaysC) Value(&Days)           

                         ChgVar Var(&StrIndent) +                  

                           Value(&InlIndent + &SubIndent)          

                         ChgVar Var(&StrIndentC) Value(&StrIndent) 

                         ChgVar Var(&SubIndentC) Value(&SubIndent) 

                         ChgVar Var(&Cmd) Value( +                 

                           'UseDirPgm Dir(''' *Cat +               

                           &Path *TCat +                           

                           ''') Days(' *Cat &DaysC *Cat +          

                           ') SubTree(' *Cat &SubTree *TCat +      

                           ') SubTreeInd(' *Cat +                  

                           &StrIndentC *Cat ' ' *Cat +             

                           &SubIndentC *Cat ') StmFOpts(')         

                                                                   

                         ChgVar Var(&StmFOptPtr) +                 

                           Value(%Addr(&NbrStmFOpt))               

                         ChgVar Var(%ofs(&StmFOptPtr)) +           

                           Value(%ofs(&StmFOptPtr) + 2)             

                         DoFor Var(&Counter) From(1) To(&NbrStmFOpt)

                               ChgVar Var(&Cmd) Value( +            

                                 &Cmd *TCat ' ' *Cat &StmFOpt)      

                               ChgVar Var(%ofs(&StmFOptPtr)) +      

                                 Value(%ofs(&StmFOptPtr) + 10)      

                               EndDo                                

                         ChgVar Var(&Cmd) +                         

                           Value(&Cmd *TCat ')')                    

                         Call Pgm(QCmdExc) Parm(&Cmd &LenCmd)     

                         ChgVar Var(%sst(&MsgTxt &Str &Len)) +    

                           Value('Ended imbedded directory: ' +   

                                 *Cat %sst(&Name 1 &LenOfName))   

                         SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)      

                         EndDo                                    

                                                                   

                    When Cond((&ObjTyp = '*STMF') *And +          

                              (&LstAccess < &TimeFilter)) +        

                         Then(Do)                                  

                              If Cond(&StmF_Dsp) +                 

                                 Then(CallSubr Subr(DspStmF))      

                              If Cond(&StmF_Rmv) +                 

                                 Then(CallSubr Subr(RmvStmF))      

                              EndDo                                

                                                                   

                    When Cond(&ObjTyp = '*STMF') Then(Do)          

                         ChgVar Var(%sst(&MsgTxt &Str &Len)) +     

                           Value(&ObjTyp *TCat ' ' *Cat +          

                                 %sst(&Name 1 &LenOfName) *TCat +  

                                 ' recently accessed')             

                         SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)       

                         EndDo                                     

                                                                   

                    Otherwise Cmd(Do)                              

                         ChgVar Var(%sst(&MsgTxt &Str &Len)) +    

                           Value(&ObjTyp *TCat ' ' *Cat +         

                                 %sst(&Name 1 &LenOfName) *TCat + 

                                 ' not processed')                

                         SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)      

                         EndDo                                    

                 EndSelect                                        

                 EndDo                                            

                                                                   

              Else Cmd(Do)                                        

                   ChgVar Var(%sst(&MsgTxt &Str &Len)) +          

                     Value('** ERROR **' *Cat +                    

                     %sst(&Name 1 &LenOfName))                    

                   SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)            

                   EndDo                                          

                                                                   

              EndDo                                               

                                                               

           CallPrc Prc('readdir') +                            

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)       

           EndDo                                               

                                                               

CallPrc    Prc('closedir') Parm((&Dir_Ptr *ByVal))             

                                                                

Subr       Subr(DspStmf)                                       

           ChgVar Var(%sst(&MsgTxt &Str &Len)) +               

                    Value(&ObjTyp *TCat ' ' *Cat +             

                          %sst(&Name 1 &LenOfName))            

           SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)                 

                                                               

           CallPrc Prc('ctime') +                              

                     Parm(&LstDtaChg) RtnVal(&DatTim_Ptr)      

           ChgVar Var(%sst(&MsgTxt &Str &Len)) +               

                    Value('Last change: ' *Cat &DatTim)        

           SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)                 

                                                                

           CallPrc Prc('ctime') +                              

                     Parm(&LstAccess) RtnVal(&DatTim_Ptr)      

           ChgVar Var(%sst(&MsgTxt &Str &Len)) +               

                    Value('Last access: ' *Cat &DatTim)        

           SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)                 

EndSubr                                                        

                                                               

Subr       Subr(RmvStmf)                                       

           ChgVar Var(&Path) Value(&InlPath *TCat +            

                              '/' *TCat +                      

                              %sst(&Name 1 &LenOfName))        

           RmvLnk ObjLnk(&Path)                                

           MonMsg MsgID(CPF0000) Exec(Do)                      

                  ChgVar Var(%sst(&MsgTxt &Str &Len)) +        

                    Value('Failure removing ' *Cat +            

                    %sst(&Name 1 &LenOfName))               

                  SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)       

                  ChgVar Var(%sst(&MsgTxt &Str &Len)) +     

                    Value('See job log for details')        

                  SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)       

                  RtnSubr                                   

                  EndDo                                     

           ChgVar Var(%sst(&MsgTxt &Str &Len)) +            

                  Value(%sst(&Name 1 &LenOfName) *TCat +    

                    ' removed')                             

           SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)              

EndSubr                                                     

EndPgm                                                       

 

As with last month's article, you can create the DIR3 program on V5R4 using the two following commands.

 

CRTCLMOD MODULE(DIR3)

CRTPGM PGM(DIR3) BNDDIR(QC2LE)

 

If your system is V6R1 or later, you can also use the single command shown below.

 

CRTBNDCL PGM(DIR3)

 

The first changes seen in DIR3 are related to the new keywords of the USEDIRPGM command. They are the addition of the parameters &NbrStmFOpt and &SubTreeInd on the PGM command along with their associated DCLs.

 

In terms of the DIR3 DCL of the STMFOPTS parameter, the program could take several approaches. One approach would be to define the parameter as a 22-byte character string, where the length of 22 represents the 2-byte integer count of the number of options actually specified followed by two (the maximum number supported) 10-byte character values, and then using CLs %sst or *Defined storage capabilities to access the actual option values specified by the user. Alternatively, and as shown in the modified DIR3 program, the program could define only the leading 2-byte integer value (&NbrStmFOpt) and then use CL pointer support to access the actual options specified. I prefer this second approach as it eliminates the need to maintain the DCL length of the parameter if the STMFOPTS MAX ever changes to a value larger than 2. The pointer used to access the stream file options is &StmFOptPtr, with the associated *Based Char(10) variable (&StmFOpt). Both of these variables are DCLed later in the program.

 

For the new SUBTREEIND parameter, DIR3 directly declares the structure &SubTreeInd as six bytes in length with *Defined subfields of &NbrSubTInd, &InlIndent, and &SubIndent to represent the number of elements passed to the program, the initial indentation value, and the level indentation value, respectively.

 

Several other new variables are DCLed by the DIR3 program. These variables will be explained as we discuss the new processing introduced into the CPP.

 

After verifying that the directory specified by the user exists and setting the time filter value, DIR3 processes the stream file options specified by the user.

 

To access the first option specified by the user, DIR3 sets the pointer variable &StmFOptPtr to the address of &NbrStmFOpt and adds two to skip over the 2-byte integer &NbrStmFOpt value. Having established addressability to the first option specified, DIR3 then enters a DOFOR group to process the options. This method of accessing the options list is slightly different than that used in the article "Trim Multiple Leading Characters with TRMLFTCHR," where the first element was declared as part of the parameter being passed to the TRMLFTCHR CPP. This difference in implementation is introduced solely to demonstrate that you have flexibility in terms of addressing the list values. Feel free to go with either approach (or others) within your CL applications.

 

As the STMFOPTS values specified by the user could be in any order, and I prefer to minimize the number of times the program needs to process the list when working with the stream files encountered in various user directories, a DOFOR group is used to set logical variables (indicators) to represent those options that are in effect. If the STMFOPT *DSP option has been selected, logical variable &StmF_Dsp is set to "on." If STMFOPT *RMV has been specified, logical variable &StmF_Rmv is set to "on." These logical variables are then used to control subsequent stream-file processing.

 

Having set the indicators related to stream-file processing, DIR3 now determines what amount of indentation should be used for displayed messages related to directory processing. In past versions of the CPP, messages were displayed using the variable &MsgTxt, as with the following two commands.

 

ChgVar Var(&MsgTxt) Value(&ObjTyp *TCat ' ' *Cat +           

                          %sst(&Name 1 &LenOfName) *TCat +   

                          ' recently accessed')              

SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)        

 

To support user-specified message indentation, all instances of 'ChgVar Var(&MsgTxt) Value(…' within DIR3 have been changed to only modify a substring of &MsgTxt, as in the following example.

 

ChgVar Var(%sst(&MsgTxt &Str &Len)) +     

         Value(&ObjTyp *TCat ' ' *Cat +          

               %sst(&Name 1 &LenOfName) *TCat +  

               ' recently accessed')             

 

With this change, the program is now setting the variable &MsgTxt value starting at a location defined by the variable &Str and for a length of &Len. The value of &MsgTxt at byte locations prior to &Str will be blanks, providing the user with visible indentation. The variable &Str is set to the value (&InlIndent + 1) as the indentation amount is specified using a base of 0 and the %sst built-in uses a base of 1. The variable &Len is set to the value (300 - &InlIndent) as the size of &MsgTxt is 300 and the value of &InlIndent is reducing, by that amount, the available storage of &MsgTxt that can be used for displaying information to the user.

 

The next change is related to the processing of imbedded directories. In last month's DIR3 program, we simply recursively ran the USEDIRPGM command when encountering a subdirectory and SUBTREE(*ALL) is specified. With the introduction of a variable-length list parameter (STMFOPTS), it gets a bit more involved in order to recursively use USEDIRPGM for imbedded directory processing.

 

There are several ways that DIR3 can invoke itself recursively. One approach, which is similar to what was used last month, would be to invoke USEDIRPGM through a SELECT list based on the options in effect—that is, using an approach such as this:

 

Select                                       

   When Cond(&StmF_Dsp *And &Stmf_Rmv) +     

          Then(UseDirPgm … StmFOpts(*Dsp *Rmv) …)

   When Cond(&StmF_Dsp) +                    

          Then(UseDirPgm … StmFOpts(*Dsp) …)

I've seen this solution at various companies, and it certainly works, but it can be a real pain as the number of option values grows.

 

A second approach, again similar to last month's, would be to always pass the maximum number of options, as with this code:

UseDirPgm … StmFOpts(&Opt1 &Opt2) …

 

This solution is one that I've also seen many places. Variables &Opt1 and &Opt2 are defined as Char(10) variables and set to the option values passed to the DIR3 program in the STMFOPTS parameter. If only one option is passed, then &Opt2 is set to either the same value as &Opt1, or &Opt2 is set to blanks (which would also require changing the USEDIRPGM command definition for STMFOPTS to allow a blank special value). As with the first approach, this solution requires maintenance when the number of options allowed changes.

 

Yet a third approach would be to bypass the USEDIRPGM command and directly call the DIR3 program. For the STMFOPTS parameter, the current DIR3 instance would just pass the &StmFOpts parameter passed to it, as is, to the called DIR3 instance. You would, though, need to pass modified versions of the parameters corresponding to the DIR, SUBTREEIND, etc. parameters. This approach can work quite well as long as you are calling a user program that happens to also be a CPP, but you can encounter problems if you are trying to call the CPP of an IBM-provided command.

 

A fourth approach, and the one used by the sample program, is to dynamically construct the USEDIRPGM command string and then run the command using an API such as Process commands (QCAPCMD), Execute command (QCMDEXC), or Execute a command (system). These APIs (discussed in the past in my "API Corner" column starting with the article "Do I Really Need to Call a CL Program to Perform This Function?) allow you to set a character variable to a command string and then run the command. In "real life," I would use the QCAPCMD API for the reasons cited in the referenced articles, but for simplicity this month's DIR3 program utilizes the QCMDEXC API.

 

DIR3 uses this fourth approach as it represents a very general-purpose solution that does not require maintenance as the number of supported options in the list changes and provides the ability to be used with any command that your application might need to run (well, any command that you're authorized to run anyway).

 

The variable &Cmd is used to store the dynamically constructed USEDIRPGM command to run and is set to the appropriate command string value using several CHGVAR commands. As CHGVAR does not like mixing numeric and character variables within the same VALUE parameter, DIR3 first copies numeric variables such as &Days, and new calculated numeric values such as initial indention, to character variables (&DaysC, &StrIndentC, etc). A rather lengthy CHGVAR command is then used to set &Cmd to all of the USEDIRPGM parameters with the exception of STMFOPTS. DIR3 constructs the STMFOPTS values within a DOFOR group similar to that used when initially setting variable &StmF_Dsp and&StmF_Rmv. Having set the full USEDIRPGM command string within the &Cmd variable, DIR3 then runs the command using the QCMDEXC API. The second parameter of the QCMDEXC API, &LenCmd, is the maximum size of the command string found in the first parameter (the &Cmd variable). DIR3 could calculate the actual length of the USEDIRPGM command string, but as QCMDEXC is tolerant of trailing blanks in the command string, why bother?

 

The next change to DIR3 is related to the processing of stream files that have not been accessed within the last &Days days. Last month, DIR3 unconditionally called subroutine DspStmF when such a stream file was encountered. This month's DIR3 program conditionally calls the DspStmF subroutine when logical variable &StmF_Dsp is also "on." In addition, the new subroutine RmvStmF is called if logical variable &StmF_Rmv is "on."

 

The RmvStmF subroutine, the last addition to the DIR3 CPP, is quite straightforward. The subroutine formats the &Path variable in the same manner used in past articles for the USEDIRPGM command and then runs the Remove link (RMVLNK) command. Depending on whether or not the RMVLNK command is successful, an appropriate message is then sent to the user.

More CL Questions?

Wondering how to accomplish a function in CL? Send your CL-related questions to me at This email address is being protected from spambots. You need JavaScript enabled to view it..

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: