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 absolutly 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.