In "Reading IFS Directories in Cobol," (April 19, 2007, article ID 54504 ), I demonstrated how to use a series of Unix-type APIs from a Cobol program to read IFS directories. The program's user interface showed all the stream files of one particular IFS directory, but omitted all subdirectory entries. The program was not able to deal with tree structures in an easy, natural way. Recursion seems to be the natural solution to this problem because, in the words of Niklaus Wirth, "Recursive algorithms are particularly appropriate when the underlying problem or the data to be treated are defined in recursive terms." In this article, I present a recursive version of last week's program that demonstrates how to walk through an IFS directory tree.
In business programming we rarely have to deal with recursion. If fact, I haven't worked with recursion since the old good days when I was a computer science student. However, there is no reason to avoid recursion if, as professor Wirth says, the data itself is defined in recursive terms, as is the case with tree structures. A program is recursive if it contains an explicit or implicit reference to itself.
The following example from ILE Cobol Programmer's Guide (SC09-2540-05) illustrates how to write a recursive program in Cobol:
ID DIVISION. PROGRAM-ID. FACTORIAL RECURSIVE. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SOURCE-COMPUTER. IBM-ISERIES. OBJECT-COMPUTER. IBM-ISERIES. DATA DIVISION. WORKING-STORAGE SECTION. 01 numb PIC 9(4) VALUE 5. 01 fact PIC 9(8) VALUE 0. LOCAL-STORAGE SECTION. 01 num PIC 9(4). PROCEDURE DIVISION. MOVE numb TO num. IF numb = 0 MOVE 1 TO FACT ELSE SUBTRACT 1 FROM numb CALL "FACTORIAL" MULTIPLY num BY fact END-IF. DISPLAY num "! = " fact. GOBACK.
Here are a few notes on the preceding code sample:
Because this program is an adaptation of the one in the previous article, I only explain the changes I made, and don't repeat what I said in the previous article. The major change is the removal of the user interface logic: A separate program now manages the entire user interface. As a direct consequence of this change, the program gained readability and elegance.
The WORKING-STORAGE section includes all the variables whose value shouldn't be reinitialized in each program instance, but instead, should keep the same value across all instances. The rest of the variables were included in the LOCAL-STORAGE SECTION. A special first-instance flag was added to the WORKING-STORAGE SECTION, so that some routines are executed only in the first recursion instance.
01 switches-area.
03 first-instance-flag PIC 1 VALUE B"0".
88 is-first-instance VALUE B"0".
88 next-instances VALUE B"1".
IF is-first-instance
CALL PROCEDURE "CEEUTCO"
USING BY VALUE ADDRESS OF offset_hours
BY VALUE ADDRESS OF offset_minutes
BY VALUE ADDRESS OF offset_seconds
OMITTED
END-CALL
MOVE "/" TO root-directory-name
MOVE "/transfer" TO default-directory-name
PERFORM create-dtaq
PERFORM clear-dtaq
PERFORM go-root-directory
SET next-instances TO TRUE
END-IF.
PERFORM go-default-directory
On my system, "transfer" is the name of the first directory I wish to "visit". If, in the course of this visit, I encounter a subdirectory, I'll re-initialize the default-directory-name variable with the name of the subdirectory and call a new instance of the program.
EVALUATE st_objtype
WHEN "*STMF"
PERFORM write-dtaq
WHEN "*DIR"
IF d_name (1:d_namelen) NOT = "." AND
d_name (1:d_namelen) NOT = ".."
PERFORM build-default-directory-name
CALL "RECURDIR"
END-IF
WHEN OTHER
CONTINUE
END-EVALUATE
In this new instance, the first-instance-flag variable will have a value of "1", so the program will skip directly to the code that processes the subdirectory.
Please note, there are two special directories that I don't wish to visit: "."(single dot) and ".." (two dots). These two special directory entries indicate the current and the parent directory, respectively.
The number of program instances in the call stack depends on the number of subdirectories below the first directory visited. On my system, the program encountered a subdirectory called "infoviewer," and under this, one called "monthly." The new program that manages the user interface displayed the following on screen:
It's worth noting that the full path name of each stream file is loaded in a hidden field of the subfile. This is necessary because the directory and stream file names shown on the screen might be truncated if they are too long.
And that’s all! As with the program presented in my previous article, the directory information in this program is written in a keyed data queue. Also in this new version, a second program reads the data queue entries and builds a very simple "Work with" type user interface. Both programs are called from a CL program to ensure that they run in the correct sequence.
You can download the sample code for this article from the following link:
http://www.pentontech.com/IBMContent/Documents/article/54562_196_RecurseDirCobol.zip
More information about recursion in Cobol can be found in ILE Cobol Programmer’s Guide Version 5 (SC09-2540-05). You can read the PDF version of this book in the following link:
http://publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/books/sc092540.pdf
In particular, check out the section titled "Using Recursive Calls" in Chapter 9, "Calling and Sharing Data Between ILE Cobol Programs".
You can find an RPG program example written by Scott Klement that reads directories recursively at:
http://www.systeminetwork.com/article.cfm?id=20235