Showcase Article - Writing CL ProgramsIn this rapidly changing world of the AS/400, the only constant is change. In fact, it's not the AS/400 anymore. So with topics like iSeries, Domino, Version 5, Websphere and Java dominating the press, it is easy to loose track of the basic skills needed to survive and flourish in the iSeries world. Here I present a small example of the power of Command Language (CL) programming. This example is small enough to easily follow and yet it uses some of the less obvious features of CL. Beginning programmers as well as system administrators can write these simple programs and learn why experienced programs love the command language of OS/400.
Parkinson's Law of Data is that data expands to fill the space available for storage. My corollary to this law is that no matter how much disk storage you have, you only have 20% left. Here is a simple and powerful tool to help discover where your disk space is going.
This tool consists of two small Command Language (CL) programs. These programs use two features of CL that are somewhat hidden. These are:
This process builds a single database file that summarizes every file on your system. You can use this file as input to a query to list:
The first CL program will generate a list of all user libraries. The second program will read this list and use it to build a composite list of all files on the system.
OS/400 keeps a description of every library and file. You probably know that these descriptions are viewable using the Display Object Description (DSPOBJD) command. By changing the OUTPUT option to *PRINT, you can print the descriptions. But what is less obvious is that by changing the OUTPUT option to *OUTFILE, the command will create a database file as output.
Figure 1 shows the Command Language program to create a file listing all libraries on the system. The operating system keeps a description of each library in the General Purpose Library (QGPL). By using the DSPOBJD command with the *OUTFILE option, OS/400 will build a database file that has a record for each library. The database file is named in the OUTFILE parameter of the DSPOBJD command.
Entering the DSPOBJD command in Figure 1 is a little tricky. Key in the command DSPOBJD and hit F4. Then fill in the values for the object and object type. For the OUTPUT parameter, use *OUTFILE. When you hit enter, OS/400 will see that you are creating an *OUTFILE and will display more keywords. Then you can fill in the OUTFILE keyword.
The program in Figure 1 does a little housekeeping first to delete the list of libraries from the last time this program was run. The MONMSG command tells the program to continue if the file does not already exist. Finally, the DSPOBJD command finds the descriptions of all user libraries on the system and creates a file named ALLLIBS to hold the descriptions.
After running this program, you can examine the records in ALLLIBS. OS/400 has a database format already defined to hold this information. In the file are fields to hold the specific information about the library. Included in these fields are:
Now, all that is needed is a program to read each record in ALLLIBS, find the description of all files in each library and put the descriptions in a single output file. Figure 2 has the CL program to read the data in ALLLIBS and use it to build the file ALLFILES.
In Figure 2, the second line is the Declare File (DCLF) command. This is like a File Specification in RPG. It tells the compiler to look for this file and to use the field names in the file as part of the compiled object. In other words, the CL can reference any field in the file.
The next two lines of the program take care of housekeeping to delete the ALLFILES file. The next executable statement is the Receive File (RCVF) command. This is the instruction to read a record from the file. Since CL allows only one Declare File (DCLF) statement per program, the RCVF command does not need to specify a file name. It reads the only one mentioned in the DCLF command.
When the end of file is reached, the message CPF0864 will be sent. The Monitor Message instruction, MONMSG MSGID(CPF0864) will realize that the end of file has been reached and will go to the label EOJ.
Otherwise, the program will continue with the next statement which is a Display Object Description (DSPOBJD) command. We used this command in the other program to get the descriptions of all libraries. Here, we use it to get a list of all files in the first library. The name of the library is in the field named ODOBNM. So, by using the field name &ODOBNM as the value of the library and using *ALL as the value for the file, the command will get the object descriptions of all files in the first library. By using the *OUTFILE option, we tell OS/400 to write the file descriptions to a file named ALLFILES.
The program then goes back to the statement labeled LOOP. It reads another record from the ALLLIBS file. It gets a list of descriptions of all files from that library and adds them to ALLFILES. It continues until it has added all descriptions of all files.
When the program is done, you have the file, ALLFILES which has the size, description, owner and last date changed of every file on your system. To help manage your disk space you can write queries to find files that should be deleted. You can look for files owned by programmers that no longer work at your company. You can sort the files by last changed date to find files that are no longer in use. You may want to make it a month end procedure to re-create ALLFILES and then print out a summary of file space being used by different systems. In short, you have a tool to help manage disk space.
Figure 1 - This CL program generates a list of all user libraries.
PGM DLTF FILE(YOURLIB/ALLFILES) MONMSG MSGID(CPF2105) DSPOBJD OBJ(*ALLUSR) OBJTYPE(*LIB) OUTPUT(*OUTFILE) + OUTFILE(YOURLIB/ALLLIBS) ENDPGM Figure 2 - This CL program reads the data in ALLLIBS and uses it to build ALLFILES. PGM DCLF FILE(YOURLIB/ALLLIBS) DLTF FILE(YOURLIB/ALLFILES) /* IF FILE DOES NOT EXIST, THAT'S OK */ MONMSG MSGID(CPF2105) LOOP: /* READ A RECORD FROM "ALLLIBS" */ RCVF /* IF END OF FILE IS REACHED, GO TO END */ MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(EOJ)) DSPOBJD OBJ(&ODOBNM/*ALL) OBJTYPE(*FILE) + OUTPUT(*OUTFILE) + OUTFILE(YOURLIB/ALLFILES) OUTMBR(*FIRST + *ADD) /* IF LIBRARY HAS NO FILES, THAT'S OK */ MONMSG MSGID(CPF2123) GOTO LOOP EOJ: