Wednesday, February 28, 2018

Creating your own commands, part 1

creating your own ibm i commands part 1

We have been able to create our own commands, I believe, since the AS400 was first launched. Why would I want to create my own commands?

I create them just for ease of use. In my opinion it is easier to remember:

ITTOOLS/ITTOOLS

Than this:

CALL ITTOOLS/ITT001

For a command I just need two things:

  1. A command object (type = *CMD)
  2. A program object that is called by the command. This can be written in any IBM i programming language.

I can have a "validity checking program" too to validate the information input into the command, but it is optional.

I have spend hours having spirited discussions with some programmers when they ask: "Where is the display file?" There is no display file, the user interface for the command is generated when it is compiled.

Let me start with the simplest command, one that requires no parameters, it just calls the "command processing program". The source member for this command, source type CMD, contains just one line:

01  CMD PROMPT('Start IT Tools')

All commands require the CMD statement. The prompt text is what is displayed when your prompt the command with the F4 key.

This command is going to display the main menu, ITTMENU1, from the IT tools library, ITTOOLS. The CL program that is called is simple:

01  PGM

02  ADDLIBLE LIB(ITTOOLS) POSITION(*LAST)
03  MONMSG MSGID(CPF0000)

04  GO MENU(ITTOOLS/ITTMENU1)

05  RMVLIBLE LIB(ITTOOLS)

06  ENDPGM

The Create Command command, CRTCMD, is used to compile the command. If you are using PDM you can just put "14" next to the command's member and press Enter.

                 Create Command (CRTCMD)

Type choices, press Enter.

Command  . . . . . . . . . . . . > ITTOOLS   
  Library  . . . . . . . . . . . >   ITTOOLS   
Program to process command . . . > ITT0001   
  Library  . . . . . . . . . . . >   ITTOOLS   
Source file  . . . . . . . . . . > TOOLSRC   
  Library  . . . . . . . . . . . >   ITTOOLS   
Source member  . . . . . . . . .   *CMD      
Threadsafe . . . . . . . . . . .   *NO  

I changed the "program to process command" from the command's default, ITTOOLS, to the name of the program I want to be called, ITT0001. I have chosen to hard code the library names. By doing this I do not need to have the library ITTOOLS in my library list when I execute this command.

I don't need any parameters for this command I just type:

ITTOOLS/ITTOOLS

All I want to happen is the menu ITTMENU1 to be displayed.

The create command does not validate if the program the command will call exists. Be careful to make sure to enter the correct name of the program, or you are going to get unexpected results.

My next example is a command with just one parameter, the name of a library.

01  CMD PROMPT('Do something with a library')

02  PARM KWD(LIBRARY) TYPE(*NAME) LEN(10) +
           MIN(1) PROMPT('Library')

PARM is used to define the command's parameters.

KWD is the keyword the command's parameter will be known as.

TYPE as this parameter is going to be the name of something I have made it a *NAME. I explain below what happens if I had defined it as *CHAR.

LEN is self explanatory, this parameter can only be a maximum of ten characters.

MIN and a minimum of one character.

PROMPT is the text for the parameter.

When I compiled this command I left the library names as *LIBL.

                 Create Command (CRTCMD)

Type choices, press Enter.

Command  . . . . . . . . . . . . > TESTCMD   
  Library  . . . . . . . . . . . >   MYLIB     
Program to process command . . . > TESTPGM   
  Library  . . . . . . . . . . . >   *LIBL     
Source file  . . . . . . . . . . > MYSRC     
  Library  . . . . . . . . . . . >   *LIBL     
Source member  . . . . . . . . .   *CMD      
Threadsafe . . . . . . . . . . .   *NO  

Having successfully compiled when I prompt my command I see.

                            Library (TESTCMD)

Type choices, press Enter.

Library  . . . . . . . . . . . .                 Name

If I had define this parameter as *CHAR rather than Name being displayed to the right of the parameter field the words 'Character value' would have displayed instead. In my opinion that is not as obvious to the user what should be entered.

If I press F11 to display the keywords for the command it now looks like.

                            Library (TESTCMD)

Type choices, press Enter.

Library  . . . . . . . . . . . . LIBRARY                  

How about I make this a little more complicated. In this example I have a file and library, I want to library to "belong" to the file.

01        CMD PROMPT('File & library')

02        PARM KWD(FILE) TYPE(Q1) MIN(1) PROMPT('File')

03  Q1:   QUAL TYPE(*NAME) LEN(10) MIN(1)               

04        QUAL TYPE(*NAME) LEN(10) DFT(*LIBL) +
                 SPCVAL((*LIBL)) PROMPT('Library')

Line 2: Rather than have *NAME I have used a name in the TYPE. This name refers to a "Qualifier definition". I can use the same qualifier name in more than one PARM.

Line 3: The Qualifier definition starts with the label that is its name, in this case Q1. All I have to do here is just the minimum: type, length, and minimum length. All the following QUAL are included in Q1 until there is another Qualifier definition with a label, or the end of source member.

Line 4: This QUAL is used to define the parameter for the library, that "belongs" to the file name. Here I have given a default, DFT, and as it is a special value (it starts with an asterisk) I have to include it in a list of special values, SPCVAL.

After compiling the command when I prompt it I see that the library "belongs" to the file name.

                   File & library (TESTCMD)

Type choices, press Enter.                                  

File . . . . . . . . . . . . . .                Name
  Library  . . . . . . . . . . .     *LIBL      Name, *LIBL

I did not want to show what the called program looks like for the previous example, library only, as it was too simple. This time I do want to show how the command passes the library and file name.

01  PGM PARM(&FILELIB)

02  DCL VAR(&FILELIB) TYPE(*CHAR) LEN(20)

03  ENDPGM

The called program has a parameter variable for each of the command's parameters. In this case the file and library names were contained in one parameter, therefore, they are passed as one 20 long character variable. If I put a debug statement in this CL program I can see that the file and library are in the one variable, the file starting at the first position and ten long, the library starting at the eleventh position and ten long.

&FILELIB = 'FILE1     MYLIB     '

The called program could just as well be in RPG.

01  **free
02  ctl-opt main(Main) ;

03  dcl-pr Main extpgm('TESTPGM') ;
04    *n char(20) ;
05  end-pr ;

06  dcl-proc Main ;
07    dcl-pi *n ;
08      FileLib char(20) ;
09    end-pi ;

10    return ;
11  end-proc ;

In this RPG program I used a Main procedure with a procedure interface, rather than a fixed format parameter list.

We have all seen those commands with a list of values next to them, well this is how I do it.

01        CMD PROMPT('File & library')

02        PARM KWD(SPACE) TYPE(Q1) MIN(1) PROMPT('File')

03  Q1:   QUAL TYPE(*NAME) LEN(10) MIN(1)               

04        QUAL TYPE(*NAME) LEN(10) DFT(*LIBL) +
                 SPCVAL((*LIBL)) PROMPT('Library')
                         SPCVAL((*LIBL)) PROMPT('Library')

05        PARM KWD(NUMBER) TYPE(*DEC) LEN(1) RSTD(*YES) +
                 DFT(1) VALUES(1 3 5 7 9) MIN(0) +
                 PROMPT('Number')

Lines 1 – 4: Are the same as the previous example.

Line 5: The parameter is numeric, TYPE(*DEC), with a length of 1. The allowed values are contained in the values, I also need to restrict the values to those in the list, RSTD(*YES). As I have given a default, the minimum length is zero, as if no value is given for this parameter the default is used.

After compiling the command now look like this:

                           File & library (TESTCMD)

Type choices, press Enter.

File . . . . . . . . . . . . . . > NOT_A_FILE    Name
  Library  . . . . . . . . . . . >   QTEMP       Name, *LIBL
Number . . . . . . . . . . . . .   1             1, 3, 5, 7, 9

When I press F4 on the "Number" parameter, I can see the list of allowed values. This did not require any additional programming.

Specify Value for Parameter NUMBER

Type choice, press Enter.


 Number . . . . . . . . . . . . .   1  

    1
    3
    5
    7
    9

The values in the command validates like the values keyword does in DDS. If, for example, I enter "6" I receive the following message at the bottom of my screen:

'6         ' not valid for parameter NUMBER.

This simple validation required no additional programming.

 

To include everything I want to discuss about creating command would make this post too long, therefore, the rest of what I want to discuss is in the part two.

 

This article was written for IBM i 7.3, and should work for earlier releases too.

7 comments:

  1. Great post about commands. I think commands are one of the most underutilized features of the OS.

    You might cover this in the next post but a great feature of commands being able to manage the library list is the Product Library (PRDLIB) parameter on the Create Command command (or on the command definition (CMD) command in the source file). If a library is specified for that parameter, then that library is automatically added to the library list when the command starts execution and is automatically removed when the execution is finished, so manually adding and removing a library in the command processing program becomes unnecessary.

    One correction about the MIN parameter on the PARM command is that it is the minimum number of values required to be entered for that parameter rather than the minimum value length.

    ReplyDelete
  2. Simon, you could write a book on what you can do with commands. Have made good use of these for many years. Like the consistency provided. One thing I wish you could do would be to specify the CPP on the CMD instruction (you can with the VCP) as you can only have one CPP per CMD and it would be self-documenting.

    ReplyDelete
    Replies
    1. You can put in a RFE and the post the link here. I image many would vote for it as would I. https://www.ibm.com/developerworks/rfe/execute?use_case=submitRfe&lnk=hm

      -Matt

      Delete
    2. Does anyone have a clue as to why IBM omitted the CPP from the CMD statement? It seems like a no-brainer to me.

      Delete
  3. Like this post.

    Command is an interesting and useful interface for validation the input and control the access with authority, exit program. Worth to use it to simplify the work.

    ReplyDelete
  4. I'm using commands in a very simple way, eg. sp = wrksplf *current. Now I wanted to make a command in order to replace eg. wrkjobscde bsd150*. But my command doesn't accept a generic name : he makes wrkjobscde 'bsd150*' of it. Is there a way to work with generic names in the keywords

    ReplyDelete
    Replies
    1. Try changing the TYPE parmeter from *NAME to *GENERIC

      Delete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.