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,

BLOG COMMENTS POWERED BY DISQUS