Inside COBOL
by
Shawn M. Gordon
President SMGA

I want to open this month with a caution about a couple of the tips I gave a while back. I showed how you could strip leading spaces by doing an INSPECT with REPLACING the spaces to a NULL value. This works fine, but it does put a NULL in the place of the space. This could cause you problems occasionally depending on what you are doing with the data.

The other trick was using page layout descriptions in your FD statement for formatting reports. I failed to mention that the HP LaserJet hooked up as a spooled device is only guaranteed to work if you use PCL code. Under most simple carriage control directives it will work fine and you won’t notice, but using the FD directives as I showed you can cause misalignment on the page.

I always recommend that you try these examples before you commit to them. No one has reported any problems, but I was re-reading the columns, and I noticed that I didn’t give strong enough warnings.

With that said I want to thank everyone for all of the positive feedback that I have been getting. As a matter of fact, this issue is going to be pretty much dedicated to Rick Gilligan at Computer and Software Enterprises Inc. up in San Luis Obispo and some code that he sent me. After my first column on some basic macro usage, I got a call from Rick and he wanted to show me some of the stuff he had been doing with macros. I was totally blown away, I had no idea you could go that far with macros and I felt it was worth revisiting the topic so I could show you these new samples. Thanks again Rick for taking the time.

I also want to make a note about the HP Cobol Macro facility. As far as I have been able to determine, HP is the ONLY Cobol out there that has this facility. As a matter of fact, many people that have switched to HP-UX and MicroFocus Cobol have been very disappointed by the lack of a Macro facility. So all you code jockeys that are ready to jump on UNIX with another Cobol compilier better think twice if you like using macros. There has been enough said about it now, that the next standard of Cobol which I think comes out in 1996 or 1997, has a good
chance of containing a Macro facility. I sure hope so, it makes the language so much more powerful.

Ok, the intro is done, now I am going to start on the code;

The first thing I want to touch on that Rick showed me was the ability to call Macros recursively, this ties in with another comment that I made about being able to change the character that is used as a delimited inside of a macro.

$DEFINE %DBCHECK=
        IF (DB-CONDITION-CODE <> 17) AND (DB-CONDITION-CODE <> 0)
           CALL "DBEXPLAIN" USING DB-STATUS-AREA
           CALL INTRINSIC "QUIT" USING \!1\
        END-IF#

$PREPROCESSOR KEYCHAR=\,DELIMITER=~
$DEFINE \DBGET=
         CALL "DBGET" USING !1, !2, !3, DB-STATUS-AREA,
                            DB-LIST-ALL, !4, !5
         %DBCHECK(!6)~

$PREPROCESSOR KEYCHAR=%,DELIMITER=#

Although this is far from an elegant example it does demonstrate both points. First we are defining the macro DBCHECK, we then use the $PREPROCESSOR command to redefine the two key characters that are used in a macro. This allows us to call another macro without the characters being defined as key characters. So now we define a DBGET macro loosely based on the example in my previous column. The end of the macro makes a call to the DBCHECK macro which will test the condition code and call DBEXPLAIN if necessary. We then use the $PREPROCESSOR command to set the key characters back to the default values.

Now I am going to cover what was the big revelation for me in terms of macros. Turns out you can use them pretty much anywhere in your program, this means that you can use them in the Working-Storage section of your program. For those of you who use COPY..REPLACING, this could be used as an alternative way of using replacement values. This also gives you something similar to the C ‘struct’ verb. You can essentially create templates for record structure layouts, and not have to retype all that stuff in. Here is a small example of how you could use it.

$CONTROL USLINIT
 IDENTIFICATION DIVISION.
 PROGRAM-ID. MACTEST.
 AUTHOR. SHAWN GORDON.
 DATE-WRITTEN. WED, MAY 11, 1994.
 DATE-COMPILED.
 ENVIRONMENT DIVISION.
 DATA DIVISION.
 WORKING-STORAGE SECTION.

$DEFINE %CUSTMAST=
 01 !1-RECORD.
    03 !1-CUST-NUM     PIC X(10).
    03 !1-CUST-NAME    PIC X(30).
    03 !1-ADDRESS      PIC X(30).
    03 !1-CITY         PIC X(20).
    03 !1-STATE        PIC X(02).
    03 !1-ZIP          PIC X(10).#

%CUSTMAST(WS#)
%CUSTMAST(CM#)

 PROCEDURE DIVISION.
 A1000-INIT.
     DISPLAY 'HELLO WORLD'.
     STOP RUN.

Now here is what the code looks like when you compile it.

PAGE 0001   HP32233A.02.05  <[>85] Copyright Hewlett-Packard CO. 1989

000100*  The following are defaults for Compatibility mode compiler.
*CONTROL LIST,SOURCE,NOCODE,NOCROSSREF,ERRORS=100,NOVERBS,WARN
*CONTROL LINES=60,NOMAP,MIXED,QUOTE=",NOSTDWARN,SYNC16,INDEX16
000400*
000500*  The following are defaults for Native compiler.
000600*
*CONTROL LIST,SOURCE,NOCODE,NOCROSSREF,ERRORS=100,NOVERBS,WARN
*CONTROL LINES=60,NOMAP,MIXED,QUOTE=",NOSTDWARN,SYNC32,INDEX32
000900*CONTROL NOVALIDATE,OPTIMIZE=0
001000*
001100*   For any other options, redirect COBCNTL.PUB.SYS by using
001200*   a file equation.
001300*
001000$CONTROL USLINIT
001100 IDENTIFICATION DIVISION.
001200 PROGRAM-ID. MACTEST.
001300 AUTHOR. SHAWN GORDON.
001400 DATE-WRITTEN. WED, MAY 11, 1994.
01500 DATE-COMPILED.  WED, MAY 11, 1994, 10:59 AM
001600 ENVIRONMENT DIVISION.
001700 DATA DIVISION.
001800 WORKING-STORAGE SECTION.
001900
002000$DEFINE %CUSTMAST=
002100 01 !1-RECORD.
002200    03 !1-CUST-NUM     PIC X(10).
002300    03 !1-CUST-NAME    PIC X(30).
002400    03 !1-ADDRESS      PIC X(30).
002500    03 !1-CITY         PIC X(20).
002600    03 !1-STATE        PIC X(02).
002700    03 !1-ZIP          PIC X(10).#
002800
002900%CUSTMAST(WS#)
002100 01 WS-RECORD.
002200    03 WS-CUST-NUM     PIC X(10).
002300    03 WS-CUST-NAME    PIC X(30).
002400    03 WS-ADDRESS      PIC X(30).
002500    03 WS-CITY         PIC X(20).
002600    03 WS-STATE        PIC X(02).
002700    03 WS-ZIP          PIC X(10).
003000
003100%CUSTMAST(CM#)
002100 01 CM-RECORD.
002200    03 CM-CUST-NUM     PIC X(10).
002300    03 CM-CUST-NAME    PIC X(30).
002400    03 CM-ADDRESS      PIC X(30).
002500    03 CM-CITY         PIC X(20).
002600    03 CM-STATE        PIC X(02).
002700    03 CM-ZIP          PIC X(10).
003200
003300 PROCEDURE DIVISION.
003400 A1000-INIT.
003500     DISPLAY 'HELLO WORLD'.
003600     STOP RUN.

0 ERROR(s), 0 QUESTIONABLE, 0 WARNING(s)
    DATA AREA IS %000345 WORDS.
    CPU TIME = 0:00:00.  WALL TIME = 0:00:02.
END OF COMPILE

This is also a very simplistic example, but I hope it sparks your imagination of what might be possible and how you might streamline
your coding process.

There is one last point with macros I want to cover, and this is also a feature I didn’t realize was available. This is the ability to define constants, this is a very popular and powerful feature in the C language, and it turns out we have it available to us in Cobol. I am going to quote directly from Rick’s example that he sent me where they use this, it is a variation on the previous example.

WORKING-STORAGE SECTION.

$DEFINE %CurMo  =1#
$DEFINE %M1Mo   =2#
$DEFINE %M12Mo  =3#
$DEFINE %M2Mo   =4#
$DEFINE %M3Mo   =5#
$DEFINE %M13Mo  =6#
$DEFINE %M14Mo  =7#
$DEFINE %LastMo =7#

$DEFINE %MCABOMDef =
         10  MCA-BOM-!1.
             15  MCA-BOM-!1-YRMO PIC X(06).
             15  MCA-BOM-!1-DAY  PIC X(02).#

$DEFINE %MCAEOMDef =
         10  MCA-EOM-!1.
             15  MCA-EOM-!1-YRMO PIC X(06).
             15  MCA-EOM-!1-DAY  PIC X(02).#

$DEFINE %MCARgnSrcDef =
         10  MCA-RGN-SRC-!1      PIC S9(09) COMP SYNC.
             88  MCA-RGN-SRC-!1-NA  VALUE 0.
             88  MCA-RGN-SRC-!2-DTD VALUE 1.
             88  MCA-RGN-SRC-!3-MSD VALUE 2.#

$DEFINE %MCALnSrcDef =
         10  MCA-LN-SRC-!1       PIC S9(09) COMP SYNC.
             88  MCA-LN-SRC-!1-NA  VALUE 0.
             88  MCA-LN-SRC-!2-DTD VALUE 1.
             88  MCA-LN-SRC-!3-MSD VALUE 2.#
*
*    order of items by month must match definitions of pointer
*    macros.
*
 01  MONTH-CTL-AREA.
     05  MCA-BOMS.
         10  MCA-BOM             OCCURS %LastMo.
             15  MCA-BOM-YRMO    PIC X(06).
             15  MCA-BOM-DAY     PIC X(02).
     05  FILLER                  REDEFINES MCA-BOMS.
         %MCABOMDef(CURMO#)
         %MCABOMDef(1MO#)
         %MCABOMDef(12MO#)
         %MCABOMDef(2MO#)
         %MCABOMDef(3MO#)
         %MCABOMDef(13MO#)
         %MCABOMDef(14MO#)
     05  MCA-EOMS.
         10  MCA-EOM             OCCURS %LastMo.
             15  MCA-EOM-YRMO    PIC X(06).
             15  MCA-EOM-DAY     PIC X(02).
     05  FILLER                  REDEFINES MCA-EOMS.
         %MCAEOMDef(CURMO#)
         %MCAEOMDef(1MO#)
         %MCAEOMDef(12MO#)
         %MCAEOMDef(2MO#)
         %MCAEOMDef(3MO#)
         %MCAEOMDef(13MO#)
         %MCAEOMDef(14MO#)
     05  MCA-RGN-SRCS.
         10  MCA-RGN-SRC         PIC S9(09) COMP SYNC
                                 OCCURS %LastMo.
             88  MCA-RGN-SRC-NA  VALUE 0.
             88  MCA-RGN-SRC-DTD VALUE 1.
             88  MCA-RGN-SRC-MSD VALUE 2.
     05  FILLER                  REDEFINES MCA-RGN-SRCS.
         %MCARgnSrcDef(CURMO#)
         %MCARgnSrcDef(1MO#)
         %MCARgnSrcDef(12MO#)
         %MCARgnSrcDef(2MO#)
         %MCARgnSrcDef(3MO#)
         %MCARgnSrcDef(13MO#)
         %MCARgnSrcDef(14MO#)
     05  MCA-LN-SRCS.
         10  MCA-LN-SRC          PIC S9(09) COMP SYNC
                                 OCCURS %LastMo.
             88  MCA-LN-SRC-NA   VALUE 0.
             88  MCA-LN-SRC-DTD  VALUE 1.
             88  MCA-LN-SRC-MSD  VALUE 2.
     05  FILLER                  REDEFINES MCA-LN-SRCS.
         %MCALnSrcDef(CURMO#)
         %MCALnSrcDef(1MO#)
         %MCALnSrcDef(12MO#)
         %MCALnSrcDef(2MO#)
         %MCALnSrcDef(3MO#)
         %MCALnSrcDef(13MO#)
         %MCALnSrcDef(14MO#)

I am really excited about the possibilities that this example opens up for us Cobol folks. The first series of $DEFINE statements create a set of constants, so any reference to say ‘CurMo’ will resolve to the value ‘1’. If you change the $DEFINE statement for CurMo, then everything in the program will change automatically. It is important
to note that placement of constants is critical in this context, the value MUST be the next character after the equal ‘=’ sign.

Now combining the constants and some generic working-storage macro
definitions, we can then make totally dynamic record structure declarations. Stop, look, and think about this for a minute, it
might take a little longer thinking up how your are going to set up your program the first time, but used correctly you can get to a point
where the bulk of your program can be defined with a handful of macros. Data set definitions, data base access, V/Plus routines, KSAM and MPE file access, virtually anything you want. Makes the mind spin a bit, but wouldn’t you rather learn some new concepts in a language you are familiar with than learn a whole new language?

To wrap up this month I want to point out a couple of things. Some of what I have shown you this month are ways to simulate features found in languages such as C and C++, but in a much easier fashion, and in a language you already know. You don’t have wait for COOL (Cobol Object Oriented Language) to take advantage of some it’s concepts such as code re usability. I just finished up a class on C++, and there are some really great conceptual features that I would love to see in Cobol. I have to say though that C++ is the most cludged language I have ever seen. I hope the Cobol ANSI committee do a better job putting Object Oriented extensions into Cobol (there is no ANSI standard for C++).

I am hoping to show some more user examples of neat tricks in the
next issue, I am just waiting for the stuff (Bob and Bruce). Thanks
again to Rick Gilligan for his input and ideas for this months column.