Inside COBOL (01 Macros – March 1994)

by
SHAWN M. GORDON
PRESIDENT
S.M.GORDON & ASSOCIATES

Many articles and papers have been written extolling the virtues of one language over another, but the bottom line is that there are about as many COBOL programmers as there are people in CHINA, so what I would like to do is start a COBOL programmer tip column here. This means that I want to hear from EVERYONE that has a neat thing that they have done in COBOL that would be generally usefull. Some of the things that I will be touching on are portions of COBOL that have always been there, but many people never get around to using them. This might be as simple as explaining some of the features of COBOL 85 or the ANSI 89 addendum to the 85 standard. This month I am going to talk about MACROS as they relate to Data Base access. Macros can be used for virtually any type of code, but let’s start with a real example.

First off let’s explain what a Macro is in COBOL. It consists of a keyword that identifies the Macro, followed by the code associated with the Macro. This code can accept replacement parameters if you want to make it even more versatile. An important consideration with Macros is that the code in the Macro is resolved at compile time. This means that code is actually copied into the location where the reference to the Macro is. This is good because it keeps your code from branching around unnecessarily, but it can cause you some frustration
if your code segments are getting to large, and you forgot that the code was being copied in. Just make the code segment smaller and you should be fine.

I will show a small sample program using Macros and then explain what is happening;

01 MODE1         PIC S9(4) COMP VALUE 1. 
01 MODE5         PIC S9(4) COMP VALUE 5. 
01 MODE7         PIC S9(4) COMP VALUE 7.
01 DB-STATUS-AREA.   
    03 DB-CONDITION-WORD  PIC S9(4)  COMP VALUE 0.      
         88 DB-CALL-OK                          VALUE 0.
         88 DB-END-OF-CHAIN                 VALUE +15.
         88 DB-RECORD-NOT-FOUND             VALUE +17.
    03 DB-DATA-LENGTH     PIC S9(4)  COMP VALUE 0.  
    03 DB-RECORD-NUMBER   PIC S9(4)  COMP VALUE 0. 
    03 DB-CHAIN-LENGTH    PIC S9(4)  COMP VALUE 0.
    03 DB-BACK-ADDR       PIC S9(4)  COMP VALUE 0.
    03 DB-FORWARD-ADDR    PIC S9(4)  COMP VALUE 0.

01 DS-CUST-MAST          PIC X(26)  VALUE "CUST-MAST;".
01 DB-BASE-NAME          PIC X(28)  VALUE " MYDB.PUB;". 
01 DB-PASS-WORD          PIC X(08)  VALUE "READ;". 
01 DB-LIST-ALL           PIC X(02)  VALUE "@;". 
01 DB-SAME-LIST          PIC X(02)  VALUE "*;". 
01 DB-ARGUMENT           PIC X(06)  VALUE SPACES.
01 DI-CUST-NUM           PIC X(16)  VALUE "CUST-NUM;".
01 CUST-MAST             PIC X(256) VALUE SPACES.

PROCEDURE DIVISION.

 A0000-MACROS. 

* !1 = variable with data base name 
* !2 = db open mode. 
$DEFINE %DBOPEN =
        CALL "DBOPEN" USING !1, DB-PASS-WORD,
                            !2, DB-STATUS-AREA#
* 
* !1 = data base variable 
* !2 = data set variable 
* !3 = data set search item 
* !4 = search item argument 
$DEFINE %DBFIND=
        CALL "DBFIND" USING !1, !2, 
                            MODE1, DB-STATUS-AREA, 
                            !3,
                            !4# 
* 
* !1 = data base variable 
* !2 = data set variable 
* !3 = get mode 
* !4 = buffer to hold record returned 
* !5 = search item argument
$DEFINE %DBGET=
        CALL "DBGET" USING !1, !2,                          
                           !3, DB-STATUS-AREA,
                           DB-LIST-ALL,
                           !4, 
                           !5# 
* 
* !1 = data base variable
* !2 = data set variable 
* !3 = close mode 
$DEFINE %DBCLOSE=
        CALL "DBCLOSE" USING !1, !2,
                             !3, DB-STATUS-AREA# 

* 
$DEFINE %DBEXPLAIN=
        CALL "DBEXPLAIN" USING DB-STATUS-AREA# 
* 
A1000-INIT
     %DBOPEN(DB-BASE-NAME#,MODE1#).
     IF NOT DB-CALL-OK
        %DBEXPLAIN 
       STOP RUN.
 A1100-LOOP.
     MOVE "123456"            TO DB-ARGUMENT.
     %DBFIND(DB-BASE-NAME#, DS-CUST-MAST#, DI-CUST-NUM#,DB-ARGUMENT#).    
     IF DB-CALL-OK 
        PERFORM UNTIL DB-END-OF-CHAIN
                %DBGET(DB-BASE-NAME#, DS-CUST-MAST#, MODE5#,CUST-MAST#,DB-ARGUMENT#)
        END-PERFORM
     ELSE
        %DBEXPLAIN
     GO TO C9000-EOJ.

 C9000-EOJ
     %DBCLOSE(DB-BASE-NAME#,DS-CUST-MAST#,MODE1#).
     STOP RUN.

I think the use of replacement parameters should be pretty obvious, you just need to remember to end each argument with the # sign. There is a way to change this character, but I don’t want to get into it here.

What is interesting to note is that I didn’t put any periods inside the macros themselves. This is optional, but remember that the code will be copied exactly as you specified it in the macro. So if you put periods in and then try to us the macro inside of an IF statement, you could run into some real odd problems. You will notice that I put periods at the end of the call’s to some of the macros. This is optional depending on how the macro is set up and if you want it to terminate the sentence.

One thing that you might find confusing is when I had to split a macro across multiple lines. I didn’t put the comma at the end of the first line as you would normally do, but rather but it at the beginning of the next line. I don’t know why this is, but I found that putting it on the first line will cause the macro to not resolve at compile time, and give you an error. If someone knows the answer to this question I would enjoy hearing it. Another issue to make note of is that you cannot reference one macro from within another macro (as far as I know). This would be useful at times.

I go absolutely nuts with macros in my code, anything that happens more than twice I put into a macro. This might be a bit excessive, but it saves me a ton of typing which is what I am most concerned with.

I didn’t put in every data base call or set up as many replacement parameters per macro as possible on purpose. I leave it to the reader to fill in the gaps for themselves. The COBOL manual explains the macro facility fairly well, I suggest you take a look at it. Next month I am going to cover putting simple debug control into your code.