My Own Embedded SQL GnuCOBOL Program

Having successfully compiled and executed the OCESQL supplied test program, I want to write my own program.

My plan is to read data from the dvdrental database that I installed during the Installing PostgreSQL (for GnuCOBOL) post.

The sample database looks like this:

I would like to create a Rental history report that shows each customer and all of the DVD titles he has rented.

The query to provide the data I want looks like this:

select customer.customer_id, customer.last_name, customer.first_name,
    film.title, rental.return_date
from customer
    inner join rental    on customer.customer_id = rental.customer_id
    left  join inventory on rental.inventory_id  = inventory.inventory_id
    left  join film      on inventory.film_id    = film.film_id
order by customer.last_name, customer.first_name, film.title

Which provides data like this:

Using ocesql’s FETCHTBL.COB as an example I began writing my own program.

Immediately I found some unexpected issues.

First, you CANNOT use free format code. You must use the original COBOL standard where divisions/sections/paragraphs are in area A (columns 8-11) and the rest is in area B (columns 12-72). Ugh! I don’t know how I wrote so much code with a 72 column limit. It’s really annoying now.

Further, if you accidentally go past column 72, it will be the ocesql precompiler that catches it with a rather vague message:

precompile start: dvdRentalReport.cob
=======================================================
              LIST OF CALLED DB Library API            
=======================================================
000049:syntax error
=======================================================
translate error

The precompiler indicates the error is at line 49, but it wasn’t – it was at line 44. So watch for this!

I also found that I would have various problems if I tried to use lowercase characters in the program. This was really inconsistent. Somethings worked, some didn’t. The answer is to just keep everything upper case.

Due to these limitations, the COBOL being written for ocesql definitely looks old school:

           EXEC SQL DECLARE C1 CURSOR FOR
               SELECT 
                   CUSTOMER.CUSTOMER_ID,
                   CUSTOMER.LAST_NAME,
                   CUSTOMER.FIRST_NAME,
                   FILM.TITLE,
                   TO_CHAR(RENTAL.RETURN_DATE,'YYYYMMDD')
               FROM CUSTOMER
               INNER JOIN RENTAL    ON 
                   CUSTOMER.CUSTOMER_ID = RENTAL.CUSTOMER_ID
               LEFT  JOIN INVENTORY ON 
                   RENTAL.INVENTORY_ID  = INVENTORY.INVENTORY_ID
               LEFT  JOIN FILM      ON 
                   INVENTORY.FILM_ID    = FILM.FILM_ID
           END-EXEC.

Code Walk Through

Any variable that will be passed to SQL in the EXEC SQL statement must be defined in a EXEC SQL BEGIN DECLARE SECTION / END DECLARE SECTION block. Further, any variable used must be an elementary item (not a group item).

I declare these as:

       EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  DB-CONTROL.
           03  DB-NAME                 PIC X(30),          VALUE SPACES.
           03  DB-USER                 PIC X(30),          VALUE SPACES.
           03  DB-PASS                 PIC X(10),          VALUE SPACES.
       01  DB-RECCOUNT                 PIC 9(9).
       01  DB-REC.
           03  DB-CUSTID               PIC 9(9).
           03  DB-LASTNAME             PIC X(45).
           03  DB-FIRSTNAME            PIC X(45).
           03  DB-FILMTITLE            PIC X(45).
           03  DB-RETURNDATE           PIC 99999999.
       EXEC SQL END DECLARE SECTION END-EXEC.

I used some groups such as DB-CONTROL and DB-REC, but these are NOT passed to SQL.

DB-RETURNDATE is a great example of not using a group. The SQL will return the date as YYYYMMDD, but if I declare DB-RETURNDATE as:

           03  DB-RETURNDATE.
               05  DB-YY              PIC 9999.
               05  DB-MM              PIC 99.
               05  DB-DD              PIC 99.

Then the query would fail. So I resorted to moving DB-RETURN-DATE to TF-DATE which is defined as

       01  TF-TEMP-FIELDS.
           03  TF-DATE.
               05  TF-YY               PIC 9999.
               05  TF-MM               PIC 99.
               05  TF-DD               PIC 99.

I can then STRING the date together into the format I want.

      * *** DEFINE SQL CONTROL AREA

       EXEC SQL INCLUDE SQLCA END-EXEC.

This is the SQL control area which contains various error information.

To start, the database is opened with CONNECT and the credentials need to be set before the call.

           MOVE "dvdrental@chiefdude10"     TO DB-NAME.
           MOVE "postgres"                  TO DB-USER.
           MOVE "mypass"                    TO DB-PASS.
      
           DISPLAY "DOING CONNECT".
           EXEC SQL
               CONNECT :DB-USER IDENTIFIED BY :DB-PASS USING :DB-NAME;
           END-EXEC.
           
           IF SQLCODE <> ZERO,
               PERFORM Z1000-DB-ERROR  THRU Z1099-EXIT;
               STOP RUN.

If an error occurs, the DB error code is called. This should be done after every EXEC SQL.

Next, a cursor is declared using the SQL statement that will retrieve all of the data:

      * *** DECLARE CURSOR
      
           DISPLAY "DOING DECLARE CURSOR".

           EXEC SQL DECLARE C1 CURSOR FOR
               SELECT 
                   CUSTOMER.CUSTOMER_ID,
                   CUSTOMER.LAST_NAME,
                   CUSTOMER.FIRST_NAME,
                   FILM.TITLE,
                   TO_CHAR(RENTAL.RETURN_DATE,'YYYYMMDD')
               FROM CUSTOMER
               INNER JOIN RENTAL    ON 
                   CUSTOMER.CUSTOMER_ID = RENTAL.CUSTOMER_ID
               LEFT  JOIN INVENTORY ON 
                   RENTAL.INVENTORY_ID  = INVENTORY.INVENTORY_ID
               LEFT  JOIN FILM      ON 
                   INVENTORY.FILM_ID    = FILM.FILM_ID
           END-EXEC.
           IF SQLCODE <> ZERO,
               PERFORM Z1000-DB-ERROR  THRU Z1099-EXIT;
               STOP RUN.

This is the same SQL query I mentioned at the top of this post EXCEPT date must be formatted into something COBOL can handle, so I used

TO_CHAR(RENTAL.RETURN_DATE,'YYYYMMDD')

The rest of the program retrieves each record of data and prints it until there is no more. Retrieving is done with SQL EXEC FETCH:

           EXEC SQL 
               FETCH C1 INTO 
                   :DB-CUSTID,
                   :DB-LASTNAME,
                   :DB-FIRSTNAME,
                   :DB-FILMTITLE,
                   :DB-RETURNDATE
           END-EXEC.
           IF SQLCODE <> ZERO,
               PERFORM Z1000-DB-ERROR  THRU Z1099-EXIT;
               STOP RUN.

Each field of the record is retrieved into a COBOL variable. Then that data is formatted into a line to print:

               MOVE SPACES             TO DT-DETAIL1;
               MOVE DB-CUSTID          TO DT-CUSTID;
               MOVE DB-LASTNAME        TO DT-LASTNAME;
               MOVE DB-FIRSTNAME       TO DT-FIRSTNAME;
               MOVE DB-FILMTITLE       TO DT-FILMTITLE;
               
      * *** GROUP ITEMS CANNOT BE USED IN SQL STATEMENTS, SO MOVE 
      * *** DB-RETURNDATE TO TF-RETURNDATE SO IT CAN BE REFORMATTED.
      
               MOVE DB-RETURNDATE      TO TF-DATE;
               STRING TF-MM, "/", TF-DD, "/", TF-YY
                   INTO                DT-RETURNDATE;
                  
               DISPLAY DT-DETAIL1;

The full program can be found at:

http://www.xyfyx.com/files/dvdRentalReport.cob

Compiling the Program

Don’t forget you need to set a couple of environment variables:

    export COBCPY=~/Open-COBOL-ESQL-1.2/copy
    export COB_LDFLAGS=-Wl,--no-as-needed

With those set, now run the precompiler:

ocesql dvdRentalReport.cob dvdRentalReport.tmp
precompile start: dvdRentalReport.cob
=======================================================
              LIST OF CALLED DB Library API            
=======================================================
;
;
;
;
;
;
Generate:OCESQLConnect
Generate:OCESQLCursorDeclare
Generate:OCESQLCursorOpen
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorClose
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================

To compile:

cobc -locesql -x dvdRentalReport.tmp

and then run with:

./dvdRentalReport
DOING CONNECT
DOING DECLARE CURSOR
DOING OPEN CURSOR
------------------------DVD RENTAL HISTORY-----------------------
-ID- ------------NAME------------ -----FILM TITLE----- -RETURNED-
 459 Collazo         Tommy        Freaky Pocus         05/28/2005
 408 Murrell         Manuel       Graduate Lord        06/01/2005
 333 Purdy           Andrew       Love Suicides        06/03/2005
 222 Hansen          Delores      Idols Snatchers      06/02/2005
 549 Christenson     Nelson       Mystic Truman        05/27/2005
 269 Walters         Cassandra    Swarm Gold           05/29/2005
 239 Romero          Minnie       Lawless Vision       05/27/2005
 126 Simpson         Ellen        Matrix Snowman       05/28/2005
 399 Isom            Danny        Hanging Deep         05/31/2005
 142 Burns           April        Whale Bikini         06/02/2005
 261 Byrd            Deanna       Games Bowfinger      05/30/2005
 334 Mcwhorter       Raymond      King Evolution       05/30/2005
 446 Culp            Theodore     Monterey Labyrinth   05/26/2005
 319 Weiner          Ronald       Pelican Comforts     06/03/2005
 316 Curley          Steven       Boogie Amelie        05/26/2005
 575 Oglesby         Isaac        Contact Anonymous    05/27/2005
  19 Martinez        Ruth         Roman Punk           05/31/2005

Voilà! A COBOL report that looks like it is straight out of 1983!

Now that I can retrieve the data, the next step will be to use the COBOL Sort module to sort and the Report Writer to produce the actual report.

Posted in c-gnuCOBOL | Tagged , | 1 Comment

Embedded SQL for GnuCOBOL using ocesql

After 6 months of messing around with gnuCOBOL, I am finally to the point of trying to implement Embedded SQL.

What is Embedded SQL?

In all of the programming I’ve done / seen, SQL is accessed via some type of procedure calls. A query is setup, executed, then the results are extracted. There is a fair amount of extra code besides the actual SQL statement.

For example, in one of my Pascal programs, I setup an SQL command I want to execute:

query.SQL.Text := 'select * from devices where ownerId  = :ownerId and deviceId = :deviceId';

The variables in the where clause of the query, :ownerId and :deviceId, are then assigned values from program variables:

qpbn(query, deviceR.ownerId,  'ownerId');
qpbn(query, deviceR.deviceId, 'deviceId');

Then the query is executed on the server:

query.Open;

and finally I extract from the query result the values my program needs:

qfbn(query, macAddr,        'macAddr');

It works, but wouldn’t it by much nicer if I could just embed the query straight into the Pascal program something like

SQL('select :macdaddr from devices where ownderId = :ownerId and deviceId = :deviceId');

That’s exactly how embedded SQL in COBOL works. You just delimit the SQL command with EXEC SQL … END-EXEC. For example, the above in COBOL would be:

EXEC SQL
    SELECT :MACADDR FROM DEVICES WHERE OWNERID = :OWNERID AND DEVICEID = :DEVICEID
END-EXEC.

I won’t spend time explaining the operation of embedded SQL as mainframe examples can be easily found elsewhere. Instead, I’m going to cover how to implement it for gnuCOBOL.

Caveats

My initial plan was to do this running gnuCOBOL in Windows. There was a lot of wasted time and head banging trying to make whatever test I was trying to do in gnuCOBOL work in Windows. Eventually I would get the problem resolved.

This was not the case for embedded SQL. I am using OCESQL and I could not get it to compile properly in minGW. After wasting several hours on that, I gave up and implemented it in Linux. Honestly, I can’t imagine any scenario I would prefer using COBOL in Windows more than Linux.

I will give a quick summary regarding the Linux installation below.

Early on I installed PostgreSQL onto a Windows system. That is working fine and for this test, I will be using PostgreSQL for Windows.

Installing gnuCOBOL on Linux

I created a Virtualbox VM and installed Linux Mint 19.3 to do this testing.

I wanted to use the COBOLWorx distribution of gnuCOBOL so I can use their debugger if necessary.

The compiler and debugger can be found at:

Packages for the Debian Package Manager (DPKG)

Download both COBOLworx GnuCOBOL 3.1 and COBOLworx GnuCOBOL CBL-GDB Debugging Extensions.

Installing is very easy, just use apt install for each file:

sudo apt install <gnuCOBOL filename>
sudo apt install <debugger filename>

To verify the install, do the following:

cobc -v
cobc (GnuCOBOL) 3.1-dev.0
Built     Oct 09 2020 13:47:45  Packaged  Oct 26 2020 15:31:29 UTC
C version "7.5.0"
loading standard configuration file 'default.conf'
cobc: error: no input files

cobcd
This is the cobc debugging wrapper [Version 4.2]
Use it as you would 'cobc'

no-pie-link.specs Error

This probably won’t be an issue for you, but it was for me so I will document it. After installing gnuCOBOL I did a test compile and got the error

gcc: error: /usr/share/dpkg/no-pie-link.specs

For some reason my PC was missing the file specified above. I found it on another system and created it in the requested directory. The contents of the file are:

+ %{!shared:%{!r:%{!fPIE:%{!pie:-fno-PIE -no-pie}}}}

Install PostgreSQL Client

If you aren’t using PostgreSQL server on the same system as the compiler, you are going to want to install the client:

sudo apt install postgresql-client

Now verify you can get access to the PostgreSQL server using psql:

psql -h <yourhost> -U postgres
Password for user postgres: 
psql (10.14 (Ubuntu 10.14-0ubuntu0.18.04.1), server 12.3)
WARNING: psql major version 10, server major version 12.
Some psql features might not work.
Type "help" for help.

postgres=#

Fix Windows 10 Firewall

When I attempted to access PostgreSQL on my Windows 10 system, the firewall blocked access. I had to go into the firewall, add the PostgreSQL server, and allow access to it:

Create the testdb Database

The sample ocesql programs will make use of a database called testdb. Use psql to set that up now.

To list the existing databases use the psql command \l (that’s a lowercase L):

and then create the database:

Using the \l command again you will see the database:

Use \q to exit psql.

Installing Ocesql

To install ocesql, you will need C++. You can use g++ to verify it is there:

g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) 

If it is not, install it with:

sudo apt install build-essential g++

You are also going to need some postgresql libs:

sudo apt install libpq5 libpq-dev

Source for ocesql can be found at:

https://github.com/opensourcecobol/Open-COBOL-ESQL/releases/tag/v1.2.

Unzip the file into your home directory (~). You should now have the directory ~/Open-COBOL-ESQL-1.2.

To compile ocesql:

cd ~/Open-COBOL-ESQL-1.2
export CPATH=/usr/include/postgresql/
./configure
make

you will now have the file ./ocesql/ocesql. To install into /usr,

sudo make install

and test:

ocesql
Open Cobol ESQL (Ocesql)
Version 1.2.0

April 19, 2019

Tokyo System House Co., Ltd. <opencobol@tsh-world.co.jp>

Usage: ocesql [options] SOURCE [DESTFILE] [LOGFILE]

options
      --inc=include_dir      set INCLUDE FILE directory path.

usage
  -v, --version              show version.
  -h, --help                 show this usage.

Compiling and Running the Sample Programs

ocesql comes with two sample programs, INSERTTBL and FETCHTBL. I wanted to make sure I could get these to work before I tried my own code.

Fixing the Code

The program INSERTTBL is set to populate the database with data containing Japanese characters. Fortunately, the writer includes both versions of text, you just need to uncomment the English code and delete the Japanese code.

becomes

Go thru the rest of this program and you will find various literals that are in both Japanese and English. Convert them to use the English version.

The last thing, in both programs is to fix the database credentials to the appropriate values:

Preparing to Compile INSERTTBL

You need to make the ocesql copy book available to the COBOL compiler:

export COBCPY=/home/<yourName>/Open-COBOL-ESQL-1.2/copy

The following fixes the runtime error

libcob: error: module 'OCESQLConnect' not found'

(see https://stackoverflow.com/questions/26227458/gnucobol-failing-to-find-dynamic-symbols-only-on-recent-ubuntu):

export COB_LDFLAGS=-Wl,--no-as-needed

Precompiling  INSERTTBL

The first step is to run the precompiler ocesql against the source code. This will convert the EXEC SQL … END-EXEC code into actual COBOL code:

ocesql INSERTTBL.cbl INSERTTBL.cob
precompile start: INSERTTBL.cbl
=======================================================
LIST OF CALLED DB Library API
=======================================================
Generate:OCESQLConnect
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExecParams
Generate:COMMIT
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================

Here is an example of ocesql converting an INSERT statement into COBOL code:

     *    INSERT ROWS USING LITERAL
           EXEC SQL
               INSERT INTO EMP VALUES (46, 'KAGOSHIMA ROKURO', -320)
           END-EXEC.
           IF  SQLCODE NOT = ZERO PERFORM ERROR-RTN.

becomes

      *    INSERT ROWS USING LITERAL
OCESQL*    EXEC SQL
OCESQL*        INSERT INTO EMP VALUES (46, 'KAGOSHIMA ROKURO', -320)
OCESQL*    END-EXEC.
OCESQL     CALL "OCESQLExec" USING
OCESQL          BY REFERENCE SQLCA
OCESQL          BY REFERENCE SQ0003
OCESQL     END-CALL.
           IF  SQLCODE NOT = ZERO PERFORM ERROR-RTN.

Compiling and Running INSERTTBL

cobc -x -locesql INSERTTBL.cob
./INSERTTBL
*** INSERTTBL STARTED ***
NOTICE: table "emp" does not exist, skipping
*** INSERTTBL FINISHED ***

Reviewing the Results

Using psql, review the new EMP table:

psql -h chiefdude10 -U postgres 
Password for user postgres: 
psql (10.14 (Ubuntu 10.14-0ubuntu0.18.04.1), server 12.3)
WARNING: psql major version 10, server major version 12.
         Some psql features might not work.
Type "help" for help.

postgres=# \c testdb
psql (10.14 (Ubuntu 10.14-0ubuntu0.18.04.1), server 12.3)
WARNING: psql major version 10, server major version 12.
         Some psql features might not work.
You are now connected to database "testdb" as user "postgres".
testdb=# select * from emp;
 emp_no |       emp_name       | emp_salary 
--------+----------------------+------------
     46 | KAGOSHIMA ROKURO     |       -320
     47 | OKINAWA SHICHIRO     |        480
      1 | HOKKAI TARO          |        400
      2 | AOMORI JIRO          |        350
      3 | AKITA SABURO         |        300
      4 | IWATE SHIRO          |       -250
      5 | MIYAGI GORO          |       -200
      6 | FUKUSHIMA RIKURO     |        150
      7 | TOCHIGI SHICHIRO     |       -100
      8 | IBARAKI HACHIRO      |         50
      9 | GUMMA KURO           |       -200
     10 | SAITAMA JURO         |        350
(12 rows)

FETCHTBL

If you have been successful at running INSERTTBL, FETCHTBL is more of the same:

ocesql FETCHTBL.cbl FETCHTBL.cob
precompile start: FETCHTBL.cbl
=======================================================
              LIST OF CALLED DB Library API            
=======================================================
Generate:OCESQLConnect
Generate:OCESQLExecSelectIntoOne
Generate:OCESQLCursorDeclare
Generate:OCESQLCursorOpen
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorClose
Generate:COMMIT
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================

cobc -x -locesql FETCHTBL.cob

./FETCHTBL
*** FETCHTBL STARTED ***
TOTAL RECORD: 0012
---- -------------------- ------
NO   NAME                 SALARY
---- -------------------- ------
0001 HOKKAI TARO             400
0002 AOMORI JIRO             350
0003 AKITA SABURO            300
0004 IWATE SHIRO            -250
0005 MIYAGI GORO            -200
0006 FUKUSHIMA RIKURO        150
0007 TOCHIGI SHICHIRO       -100
0008 IBARAKI HACHIRO          50
0009 GUMMA KURO             -200
0010 SAITAMA JURO            350
0046 KAGOSHIMA ROKURO       -320
0047 OKINAWA SHICHIRO        480
*** FETCHTBL FINISHED ***

Now that I am able to use ocesql on the sample programs, next I will use it to access the dvdRental database that I installed in Installing PostgreSQL (for GnuCOBOL).

Posted in c-gnuCOBOL | Tagged , | Leave a comment

Create Demo Application Using Lazarus for Android

I created this video for myself showing the basic steps for creating a very simple Android program using Lazarus.

I am the intended audience and the intended audience cares about the content not the video quality, so don’t expect it to be pretty. BUT, if you want to see how simple it is to create an Android app in Lazarus, and can’t find another video with much detail (I wasn’t able), then you are welcome to have a look.

Posted in c-lazarus | Tagged , | 5 Comments

COBOLWorx GnuCOBOL and Debugger

The next step in delving back into COBOL is to get a working debugger. While using DISPLAY debug lines was the only debugging I had available when I did this for a living, there should be no reason to have to do that these days!

After going thru the gnuCOBOL FAQ, I found there is a way to make use of the GNU Debugger (gdb). Unfortunately the link to the debugger in the FAQ is dead. After a little digging I found cobcd, the COBOL debugger pre-processor  on the COBOLWorx site.

I spent quite some time getting cobcd to ‘make’. This is because if you want to run it on Windows you must compile using MinGW/MSYS2 and I have been using the older MSYS.

Even after I got it to compile under MSYS2, it failed to work with Arnold Trembley’s version of the gnuCOBOL I’ve been using. After several days of hair pulling, I finally decided to contact COBOLWorx directly. Bob from COBOLWorx responded quickly and confirmed that cobcd doesn’t play well with Anold Trembley’s version of gnuCOBOL.

Download and Install COBOLWorx GnuCOBOL and the Cobc Pre-Processor

If you are using Windows, the easiest way to get the cobcd pre-processor to work is to download the COBOLWorx’s Windows version of the compiler and debugger. In fact, their build of the compiler is so easy to install, I’d recommending using it even if you aren’t planning to use the cobcd debugger.

The download page for the COBOLWorx versions of gnuCOBOL is here.

Instead of messing with MinGW, I just downloaded the self-extracting installer for Windows found at the top of this page.

If, for some reason, the above link fails, the copy I downloaded is here. However, expect this  version to go out of date quickly.

Once you have the self-extracting EXE file downloaded, double-click on it and let it install. The program is a bit slow to install.

I installed the compiler into the default c:\gnucobol directory.

As the install completes you will see this dialog box:

I recommend you say yes and let it add these environment variables. Beats having to mess with the environment when you want to run the compiler.

Now reboot the PC to make sure everything gets setup properly.

Dealing with Microsoft Python

To test the install, type cobcd and you should see:

C:\cobol>cobcd
This is the cobc debugging wrapper [Version 3.17]
Use it as you would 'cobc'
C:\cobol>

If you instead only get a DOS prompt and no version information, chances are the MicroSoft Python is before gnuCOBOL’s python in your path. The following change will correct this problem.

Microsoft decided to put a stub program into your WindowsApps directory that does nothing except invoke the Microsoft Store. This breaks cobcd. Correcting the problem involves putting the gnuCOBOL path ahead of WindowsApps.

If you look at the path for your user, you will find WindowsApps is searched before gnucobol:

Simply move gnucobol above windowsapps:

The Test Program

Before using the debugger, we need a program to compile. Here is a sample program I’ll be using:

       >>source free
identification division.
program-id.
    testgdb.

data division.
working-storage section.

01  dst-rec.
    03  lastname                        pic x(20).
    03  firstname                       pic x(20).
    03  birthdate                       pic 999999.
    03  age                             pic zz9. 
    03  salary                          pic $$,$9.99.

01  kount                               pic s9(4), comp.

01  src-rec.
    03  lastname                        pic x(20),          value "SMITH".
    03  firstname                       pic x(20),          value "JOHN".
    03  birthdate                       pic 999999,         value 800715.
    03  age                             pic s9(4), comp,    value 35.
    03  salary                          pic s9(6)v99, comp-3, value 123456.78.

procedure division.

    move "TEST"                         to lastname of dst-rec.
    move 20                             to age      of dst-rec.
    move 50000                          to salary   of dst-rec.

    perform varying kount from 1 by 1 until kount > 10,
        add 1                           to age of src-rec;
        end-perform.

    move corresponding src-rec          to dst-rec.

    display "dst-rec:".
    display dst-rec.

    stop run.

Setting Up GDB

The first time you attempt to run GDB, you will get a warning:

C:\cobol>gdb -q testgdb.exe
Reading symbols from optfde01.exe...
warning: File "C:\cobol\optfde01-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path C:\cobol\optfde01-gdb.py
line to your configuration file "C:\Users\me/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "C:\Users\me/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
(gdb)

To fix this, we need to add set auto-load safe-path / to the file .gdbinit.

It easy enough to create c:\users\<username>\.gdbinit with the proper contents:

C:\Users\me>type \users\me\.gdbinit
set auto-load safe-path /

Compiling the Test Program

Here is an example of compiling the program with debugging and running it w/o invoking the debugger:

C:\cobol\gdbDebugging>cobcd -x testgdb.cbl
C:\cobol\gdbDebugging>testgdb.exe
dst-rec:
SMITH               JOHN                800715 45$123,456.78

C:\cobol\gdbDebugging>

Setting the Initial Breakpoint

To invoke the debugger (-q omits printing the header):

C:\cobol\gdbDebugging>gdb -q testgdb.exe
Reading symbols from testgdb.exe...
registering CPrint (Usage is "print  ") [Version 3.17]
registering CWatch (Usage is "cwatch ")
(gdb)

If you start execution of the program, it will run what I assume is part of the run-time library. I’ve found the best way to bypass this code is to manually set a breakpoint on the first executable line.

First use the search command to find the procedure division and then list to see the lines around it:

(gdb) search procedure
25      procedure division.
(gdb) l
20          03  firstname                       pic x(20),          value "JOHN".
21          03  birthdate                       pic 999999,         value 800715.
22          03  age                             pic s9(4), comp,    value 35.
23          03  salary                          pic s9(6)v99, comp-3, value 123456.78.
24
25      procedure division.
26
27          move "TEST"                         to lastname of dst-rec.
28          move 20                             to age      of dst-rec.
29          move 50000                          to salary   of dst-rec.

Line 27 is the first executable line, so set a breakpoint there and use continue to let the program run until it hits the breakpoint:

gdb) break 27
Breakpoint 1 at 0x4016e3: file testgdb.cbl, line 27.
(gdb) run
Starting program: C:\cobol\gdbDebugging\testgdb.exe
[New Thread 5136.0xc98]
[New Thread 5136.0x18a8]
[New Thread 5136.0x16e8]

Thread 1 hit Breakpoint 1, testgdb_ (entry=0) at testgdb.cbl:27
27          move "TEST"                         to lastname of dst-rec.
(gdb)

Use Continue to Execute Until Next Breakpoing

Once the program is executing, you use continue, rather than start, to execute code until the next breakpoint is encountered:

(gdb) break 28
Breakpoint 2 at 0x401713: file testgdb.cbl, line 28.
(gdb) continue
Continuing.

Thread 1 hit Breakpoint 2, testgdb_ (entry=0) at testgdb.cbl:28
28          move 20                             to age      of dst-rec.
(gdb)

Using Step and Next

Step executes the next line and stop, but if a procedure is called, it will stop inside of the called procedure.

Next executes the next line without stopping inside of any called procedures. This is typically what I want to have happen.

After you type either, simply pressing enter will execute the command again (this is true of any command).

(gdb) next
29          move 50000                          to salary   of dst-rec.
(gdb)<cr>
31          perform varying kount from 1 by 1 until kount > 10,
(gdb)

Examining Contents of Variables

Typing p* will display all variables:

(gdb) p*
 1 : 01 dst-rec/testgdb [W-S] : "TEST", ' ' , "000000 20 $50,000.00"
 2 : 03 lastname/dst-rec/testgdb [W-S] : "TEST                "
 3 : 03 firstname/dst-rec/testgdb [W-S] : "                    "
 4 : 03 birthdate/dst-rec/testgdb [W-S] : "000000"
 5 : 03 age/dst-rec/testgdb [W-S] :  20
 6 : 03 salary/dst-rec/testgdb [W-S] :  $50,000.00
 7 : 01 kount/testgdb [W-S] : +0000
 8 : 01 src-rec/testgdb [W-S] : [534d495448", 20 , 4a4f484e", 20 , 3830303731350023012345678c]
 9 : 03 lastname/src-rec/testgdb [W-S] : "SMITH               "
10 : 03 firstname/src-rec/testgdb [W-S] : "JOHN                "
11 : 03 birthdate/src-rec/testgdb [W-S] : "800715"
12 : 03 age/src-rec/testgdb [W-S] : +0035
13 : 03 salary/src-rec/testgdb [W-S] : [012345678c]

To see the value of only kount, type:

(gdb) p kount
01 kount/testgdb [W-S]  :  +0000

I don’t make a practice of using MOVE CORRESPONDING so I don’t normally need to qualify variables with OF, but I wanted to see how they work in the debugger. To see the value of LASTNAME OF SRC-REC:

(gdb) p lastname/src-rec
03 lastname/src-rec/testgdb [W-S]  :  "SMITH               "

Using cwatch to Display Variable When It Changes

Here I ‘watch’ the value of kount change in the PERFORM loop. Every time the value changes, the program pauses execution:

(gdb) l
26
27          move "TEST"                         to lastname of dst-rec.
28          move 20                             to age      of dst-rec.
29          move 50000                          to salary   of dst-rec.
30
31          perform varying kount from 1 by 1 until kount > 10,
32              add 1                           to age of src-rec;
33              end-perform.
34
35          move corresponding src-rec          to dst-rec.
(gdb) break 35
Breakpoint 3 at 0x40189d: file testgdb.cbl, line 35.
(gdb) cwatch kount
Hardware watchpoint 4: *(char(*)[2])(0x406060)

(gdb) continue
Continuing.

Thread 1 hit Hardware watchpoint 4: *(char(*)[2])(0x406060)

Old value = "\000"
New value = "\000\001"
0x630cf909 in ?? () from c:\gnucobol\bin\libcob-4.dll
(gdb)<cr>
Continuing.

Thread 1 hit Hardware watchpoint 4: *(char(*)[2])(0x406060)

Old value = "\000\001"
New value = "\000\002"
0x0040188a in cob_addswp_s16 (val=, p=0x406060 ) at testgdb.cbl:32
32              add 1                           to age of src-rec;
(gdb) info break
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x004016e3 in testgdb_ at testgdb.cbl:27
        breakpoint already hit 1 time
2       breakpoint     keep y   0x00401713 in testgdb_ at testgdb.cbl:28
        breakpoint already hit 1 time
3       breakpoint     keep y   0x0040189d in testgdb_ at testgdb.cbl:35
4       hw watchpoint  keep y              *(char(*)[2])(0x406060)
        breakpoint already hit 2 times
(gdb) disable 4
(gdb) continue
Continuing.

Thread 1 hit Breakpoint 3, testgdb_ (entry=0) at testgdb.cbl:35
35          move corresponding src-rec          to dst-rec.
(gdb)

Finally, let the MOVE CORRESPONDING execute and compare records:

gdb) n
37          display "dst-rec:".
(gdb) p *
 1 : 01 dst-rec/testgdb [W-S] : "SMITH               JOHN                800715 45$123,456.78"
 2 : 03 lastname/dst-rec/testgdb [W-S] : "SMITH               "
 3 : 03 firstname/dst-rec/testgdb [W-S] : "JOHN                "
 4 : 03 birthdate/dst-rec/testgdb [W-S] : "800715"
 5 : 03 age/dst-rec/testgdb [W-S] :  45
 6 : 03 salary/dst-rec/testgdb [W-S] : $123,456.78
 7 : 01 kount/testgdb [W-S] : +0011
 8 : 01 src-rec/testgdb [W-S] : [534d495448", 20 , 4a4f484e", 20 , 383030373135002d012345678c]
 9 : 03 lastname/src-rec/testgdb [W-S] : "SMITH               "
10 : 03 firstname/src-rec/testgdb [W-S] : "JOHN                "
11 : 03 birthdate/src-rec/testgdb [W-S] : "800715"
12 : 03 age/src-rec/testgdb [W-S] : +0045
13 : 03 salary/src-rec/testgdb [W-S] : [012345678c]
(gdb) c
Continuing.
dst-rec:
SMITH JOHN 800715 45$123,456.78
[Thread 8156.0x11d8 exited with code 0]
[Thread 8156.0xf14 exited with code 0]
[Thread 8156.0x96c exited with code 0]
[Inferior 1 (process 8156) exited normally]
(gdb)

There you go, gdb for gnuCOBOL. Pretty slick!

Posted in c-gnuCOBOL | Tagged , | Leave a comment

Debugging GnuCOBOL Using WITH DEBUGGING MODE

Prior to COBOL85, COBOL had its own debug module accessed in the DECLARATIVES. This was removed in the 1985 standard. It appears gnuCOBOL still supports the older debugging, but given gdb can be used (next blog post), I won’t go down that route.

Instead, I will examine the debugging that COBOL85 does still natively support.

Debugging Lines

When debugging is enabled, lines with a D in column 7 (fixed format) or start with >>D (free format) are compiled. If not enabled, they are not compiled.

>>D display "debugging line hit".

OR

      D    display "debugging line hit".

Normally one indicates these lines should be compiled using by specifying WITH DEBUGGING MODE in the SOURCE-COMPUTER paragraph:

source-computer. 
    x86                                 with debugging mode.

However, you can leave this clause off, and use the -fdebugging-line switch when invoking the compiler.

Here is code and a sample run using debugging statements:

C:\cobol\oldDebugging>cobc -t- -fdebugging-line -xj sw0debug.cbl
GnuCOBOL 3.1-rc1.0      sw0debug.cbl         Mon Aug 31 15:31:40 2020  Page 0001

LINE    PG/LN  A...B............................................................

000001         >>source free
000002  identification division.
000003  program-id.
000004      test.
000005
000006  environment division.
000007  configuration section.
000008  source-computer.
000009      x86.
000010
000011  procedure division.
000012
000013  >>D display "debugging line hit".
000014      display "non debugging line hit".
000015
000016      stop run.


0 warnings in compilation group
0 errors in compilation group
debugging line hit
non debugging line hit

Using SW0 to Control Debugging

When I was writing COBOL on the HP3000, I almost always wanted my debugging lines compiled. If there was a problem, I didn’t want to have to recompile code. This was probably because minicomputers were so slow and compilers took so many resources, each programmer might be lucky to get in 3 compiles a day. I often worked nights just so I wouldn’t have to fight as much for compile time!

I needed a simple mechanism to enable/disable debugging at run time rather than compile time. There were several ways to do this on the HP3000 and I did it using the switch register with SPECIAL-NAMES of SW0 and I see gnuCOBOL supports a switch register as well.

The first HP3000 I programmed (Series III) had an actual 16 bit switch register on the front panel that primarily was used to enter a start address to boot the system. However, it could also be read programmatically by COBOL.

The switch register went away on later models, but there needed to be a way to continue to support programs that made use of that register. On the HP3000, this was done by passing the value of the 16 bit switch register, in decimal, to the program in the PARM keyword such as:

:RUN MYPROG;PARM=1

This would set bit 0 of the switch register to 1 rather than 0. (PARM=2 would have set switch register bit 1 to 1). Thus, when I ran a COBOL program on the HP3000 with PARM=1, it would display debugging so I would not have to recompile.

Here is the modified program that uses the SWITCH-0 register in special names to create the condition-name called fl-debug which is tested to determine is debugging output should be displayed. Since SWITCH-0 has not yet been set fl-debug will return false and the debugging will not be displayed:

C:\cobol\oldDebugging>cobc -t- -xj sw0debug.cbl
GnuCOBOL 3.1-rc1.0      sw0debug.cbl         Mon Aug 31 16:09:01 2020  Page 0001

LINE    PG/LN  A...B............................................................

000001         >>source free
000002  identification division.
000003  program-id.
000004      test.
000005
000006  environment division.
000007  configuration section.
000008  source-computer.
000009      x86                                 with debugging mode.
000010  special-names.
000011      SWITCH-0                            is fl-debug-flag,
000012          on status                       is fl-debug.
000013
000014  procedure division.
000015
000016  >>D if fl-debug,
000017  >>D     display "debugging line hit".
000018
000019      display "non debugging line hit".
000020
000021      stop run.


0 warnings in compilation group
0 errors in compilation group
non debugging line hit

For gnuCOBOL, the SWITCH-0 register is set to 1 by setting an environment variable:

C:\cobol\oldDebugging>set COB_SWITCH_0=on

C:\cobol\oldDebugging>sw0debug.exe
debugging line hit
non debugging line hit

Setting COB_SWITCH_0 to off will disable the debugging.

Next time I’ll discuss using gdb with gnuCOBOL. Compared to using a modern debugger like gdb, having only DISPLAY is pretty primitive, but can still work. I still have to debug MCU code just using printf or even worse, just flashing a LED.

Posted in c-gnuCOBOL | Tagged | Leave a comment

Calling Pascal procedures to Manipulate the GnuCOBOL String Data Structure

Last episode saw the definition of a string data structure for COBOL. This episode will show how to write Pascal Code to manipulate the string data structure (cst-string).

Using these examples, it would be easy enough to implement any needed string processing procedure.

I have created 3 Pascal procedures: cstset, cstdeb, and cstrightj.

cstset is used to initialize cst-string by moving text from a buffer into cst-string and setting the cst-len appropriately. It replaces the COBOL-only code I did in the prior post.

cstdeb (deblank) will remove leading and trailing spaces from cst-string.

cstrightj (right justify) will right justify the text in cst-string using the specified field width.

To make using these procedures easier, there is a copy book (.cpy file) for each call: cfCstSet, cfCstDeb, and cfCstRightJ.

Rather than describe all of the parts, the following file contains a listing of the Pascal program, commands to compile and execute the Pascal and COBOL code with the resulting run’s output, and finally the compiler’s listing of the COBOL code.

All Listings

 

Posted in c-gnuCOBOL | Tagged , | Leave a comment

A String Data Structure for GnuCOBOL

When I started to learn COBOL in the late 70’s, HP BASIC was my primary language. BASIC is adept at handling strings. As I quickly found, COBOL (particularly the 1968 standard I was using), was not.

I know there are some string processing functions available in gnuCOBOL such as TRIM. For this post, I want to implement the string data structure I used for all of my string processing needs.

Simple Example of Lack of Strings

Here is a very rudimentary example of a problem that occurs by not having some type of string data structure. Say you define 2 variables:

01  buffer                     pic x(40).
01  formattedAmount            pic ---,--9.99.

Then initialize and output them:

move "JOHN SMITH"             to buffer.
move 1.98                     to formattedAmount.

display ">", buffer, "|", formattedAmount, "<".

The display would show:

>JOHN SMITH                              |      1.98<

Lining up data in columns might be suitable for a line printer but is ugly on a terminal. If you want to see the output as

>JOHN SMITH|1.98<

it really wasn’t doable.

A COBOL String Data Structure

At my first job, on a DEC-10, I stumbled upon a simple way to control how much of a string buffer could be displayed:

01 len                           pic s9(4), comp.
01 str.
   03  str-byte                  pic x,
         occurs                  0 to 80 times,
         depending on            len.

To display str without trailing spaces, I just needed to:

move 80                         to len.
move "JOHN SMITH"               to str.
move 10                         to len.
display ">", str, "<".

>JOHN SMITH<

Having to move 80 to len first was a bit clumsy. In more careful reading of the manual I found I could create the following data structure which became my standard COBOL string data structure:

01  cst-string.
    03  cst-max                            pic s9(4), comp, value 80.
    03  cst-len                            pic s9(4), comp.
    03  cst-in                             pic x(80).
    03  cst-out redefines                  cst-in.
        05  cst-byte                       pic x,
                occurs                     0 to 80 times,
                depending on               cst-len.

This worked like a charm. Now to setup a string it just required this:

move "JOHN SMITH"                         to str-in.
move 10                                   to str-len.
display ">", str-out, "<".

When I moved to a job programming HP3000’s in COBOL, I took this idea with me where I used it for several decades. More than likely some of that code is still running.

If you are a COBOL programmer, you may be saying “WHOA, you can’t do that!” Per the COBOL standards, you cannot redefine a variable length field!

That is true in gnuCOBOL and is true according to the ’74 and ’85 standard manuals I have. BUT it did work on the DEC10 and the HP3000 COBOL compilers.

Making the COBOL String Structure Work for gnuCOBOL

I screwed around with trying to get past that limitation for a while and finally gave up. Instead gnuCOBOL requires this data structure:

01  cst-string.
    03  cst-max                                   binary-short unsigned, value 256.
    03  cst-len                                   binary-short unsigned.
    03  cst-out.
        05 cst-byte                               pic x,
             occurs                               0 to 256 times,
             depending on                         cst-len.

The downside to this structure is you cannot move a string to it without first setting cst-len to cst-max before initializing cst-out. So to create a string you would do this:

move cst-max                                     to cst-len.
move "JOHN SMITH"                                to cst-out.
compute cst-len = 
    function length( 
        function trim( cst-out, trailing)).
if cst-len = 1 and cst-byte(1) = space then
    move zero                                    to cst-len.

Still kind of a lot of work to do, but one can create a copy book code fragment to do the work:

       >>source free
*>cfCstSet:
*>  01/31/12:   DWH.                 
*>              Created for openCOBOL.            
*>  08/15/20:   DWH.
*>              Updated for gnuCOBOL.
                                                                  
*>--------------------------------------------------------------   
*>    COPY cfstrset
*>    REPLACING cst-in BY <literal|word>].                           
*>--------------------------------------------------------------   
                                                                  
*>    openCOBOL doesn't allow my old STR string structure. The new
*>    structure requires the following code frag to move something
*>    into the structure and set the size.

*>    This code fragment can be copied anywhere such as:
*>        if 1=1 then
*>            copy cfCstSet replacing cst-in with "test";
*>        else,
*>            next sentence.
                             
    move cst-max                        to cst-len;
    move cst-in                         to cst-out;
    compute cst-len = 
        function length( 
            function trim( cst-out, trailing));
    if cst-len = 1 and cst-byte(1) = space then
        move zero                      to cst-len;
    end-if;

Now, to move create a string of “JOHN SMITH”, I just:

copy cfcstset,
    replacing cst-in by "JOHN SMITH".

Here is a listing of an example program (with expanded copy books) so you can see everything:

GnuCOBOL 3.1-rc1.0      cfcstsetEx.cbl       Sun Aug 16 15:21:34 2020  Page 0001
000011
LINE    PG/LN  A...B............................................................
000013
000001         >>SOURCE FREE
000002  identification division.                by "JOHN SMITH".
000003  program-id.urce free
000004      cfcstsetEx.
000005
000006  data division.  
000007
000008  working-storage section.
000009
000010  copy wscst.
000001C        >>SOURCE FREE
000002C *>wscst
000003C *>    AA0000: 01/08/88:    DWH.
000004C *>            STR COBOL string routine data names.
000005C *>    AA0001: 01/31/12:    DWH.
000006C *>            Converted to run in OpenCobol.                                   
000007C
000008C *>--------------------------------------------------------------
000009C *>    COPY wscst
000010C *>    [REPLACING 256 BY ].
000011C *>--------------------------------------------------------------ng
000012C *>    into the structure and set the size.
000013C *>    STANDARD COBOL STRING DATA STRUCTURE.
000014C *>    You can change the maximum string size by using the
000015C *>    REPLACING clause.
000016C *>            copy cfCstSet replacing cst-in with "test";
000017C 01  cst-string.
000018C     03  cst-max                         binary-short unsigned, value 256
000018+ .
000019C     03  cst-len                         binary-short unsigned.
000020C     03  cst-out.MITH" to cst-out;
000021C         05  cst-byte                    pic x,
000022C            occurs                       0 to 256 times,
000023C            depending on                 cst-len.
000024C
000011
000012  procedure division.
000013
000014      copy cfCstSet
000015          replacing cst-in                by "JOHN SMITH".
000001C        >>source free
000002C *>cfCstSet:
000003C *>  01/31/12:   DWH.
000004C *>              Created for openCOBOL.
000005C *>  08/15/20:   DWH.
000006C *>              Updated for gnuCOBOL.
000007C
000008C *>--------------------------------------------------------------
000009C *>    COPY cfstrset
000010C *>    REPLACING cst-in BY <literal|word>].
000011C *>--------------------------------------------------------------
000012C
GnuCOBOL 3.1-rc1.0      cfcstsetEx.cbl       Sun Aug 16 15:21:34 2020  Page 0002

LINE    PG/LN  A...B............................................................

000013C *>    openCOBOL doesn't allow my old STR string structure. The new
000014C *>    structure requires the following code frag to move something
000015C *>    into the structure and set the size.
000016C
000017C *>    This code fragment can be copied anywhere such as:
000018C *>        if 1=1 then
000019C *>            copy cfCstSet replacing cst-in with "test";
000020C *>        else,
000021C *>            next sentence.
000022C
000023C     move cst-max                        to cst-len;
000024C     move "JOHN SMITH" to cst-out;
000025C     compute cst-len =
000026C         function length(
000027C             function trim( cst-out, trailing));
000028C     if cst-len = 1 and cst-byte(1) = space then
000029C         move zero                      to cst-len;
000030C     end-if;
000031C
000032C
000016
000017      display ">", cst-out, "<".
000018
000019      stop run.


0 warnings in compilation group
0 errors in compilation group

And the resulting output:

>JOHN SMITH<

Next time, I will show how to pass the COBOL string data structure to Pascal and let it do some basic string processing functions.

Posted in c-gnuCOBOL | Tagged | Leave a comment

Parameter Passing when calling Free Pascal from GnuCOBOL

In our last exciting episode of gnuCOBOL, I showed the rudiments of calling a Pascal program from COBOL. Today I will delve deeper into passing parameters.

Example Code

Rather than put a lot of code straight into this post, here are links to a COBOL program and Pascal procedures that perform many different types of calls:

The Primary COBOL program.

The Primary Pascal routines.

The output to this program is:

calling Pascal
Hello World!
@proc0
@proc1. p1: 1
@proc2. p1: 1; p2: 2
@func1. p1: 1
@procbyref. p1: 1; p2: 2
func1 returned: +0002
@procrec: memory dump of rec:
0000: 534D 4954 4820 2020 2020 2020 2020 2020 2020 2020 SMITH
0014: 2020 2020 2020 2020 2020 4A4F 484E 2020 2020 2020 JOHN
0028: 2020 2020 2020 2020 2020 2020 2020 2020 2020 2020
003C: 0026 0001 2345 678C .&..#Eg.
called Pascal

Matching Data Types

By default, COBOL does a pretty good job of hiding underlying data types. It is meant to be a machine dependent language and it does a great job of it where data types are concerned.

If I want a 4 digit number I use PIC 9999. If it might be negative, then I use PIC S9999. If I will be doing a lot of computations with it then I say PIC S9999, COMP. I never have to think about which type of integer I need.

You can call Pascal directly with variables defined using PIC clauses, but it sometimes it takes some trial and error. To pass numbers to Pascal you are better off using gnuCOBOL’s numeric usage defintions. In the GnuCOBOL 2.2 Nov 2017 manual, these can be found in section 9.8.3 such as:

So rather than

01  mydataP                        pic s9(4), comp.

use

01  mydataI                       binary-short signed.

Here are a couple of the more esoteric issues one could have when converting from COBOL PIC to numeric usage types: size errors and implied decimal points.

Size Errors

There can be a problem with mixing PIC variables and usage type variables. Moving the above mydataI to mydataP can cause leading digit truncation.

For example, if mydataI contains 12345 (it can contain up to 32767) and is moved to the 4 digit mydataP field, mydataP will contain 2345, not 12345.

If you must move mydataI to mydataP do so using compute like this:

compute mydataP = mydataI,
    on size error,
        display "Overflow occurred!!".

A sample program showing the various types of moves between these two data types can be seen here.

The output from this program:

After p(1234) to i move:
p: +1234
i: +01234
After i(9876) to p move:
i: +09876
p: +9876
After i(12345) to p move:
i: +12345
p: +2345
Trying compute:
Overflow occurred!!

Implied decimal point

There are several reasons I like COBOL and one is that it handles dollar & cents so easily with perfect precision. One should NEVER put dollar amounts into a floating point data type regardless of the language! That leaves (in most lanaguages) just integers. That means the programmer is responsible for knowing that

salary: integer;

must contain pennies. The programmer is responsible for knowing where the decimal point is at. If he wants more precision than pennies then he is responsible for that as well. Addition/subtraction is hard enough, but multiplication and division are even more cumbersome.

In Pascal, if  I want to put $1000 into salary I must use 10,000 pennies:

salary := 100000;

In COBOL, you define salary as:

77  Salary      PIC S9(6)v99, comp.

The compiler determines the correct underlying data type to use. Note that ‘v’ means that is where an implied decimal point is located. When you say:

move 1000 to salary.

you get a Salary of $1000.00 as you would expect.

If you must pass salary to a Pascal program you either need to determine the underlying data type used, or move it to a numeric usage field.

s9(6)v99 contains 7 digits which will fit in a 32bit integer just fine so I can use

01  salaryP                     pic s9(6)v99.
01  salaryI                     usage binary-int signed.

In this test program, I move salaryP to salaryI and call a Pascal routine to simply display it.

The COBOL code

The Pascal Code

When run, the output is:

size of salaryI: 4
size of salaryP: 4
Passing salaryP(+123456.78) to prtSalary:
@prtSalary. Salary: 12345678
Passing salaryI(+0000123456) to prtSalary:
@prtSalary. Salary: 123456

There is a REALLY IMPORTANT side effect highlighted above!!

After moving salaryP to salaryI, the fractional part of salaryP is lost:

move 123456.78 to salaryP.
move salaryP to salaryI.

As seen in the program output above, salaryI contains 123456 after the move NOT 12345678.  To retain the fractional part you would  need to compute rather than move:

compute salaryI = salaryP * 100.

This would result in salaryI of 12345678 being passed to prtSalary.

It seems to me, when mixing variables defined using PIC COMP and moving them to/from numeric usage variables you want to use COMPUTE and NOT MOVE.

In the real world, I can’t ever recall wanting to pass dollar amounts to a non-COBOL subroutine. COBOL excels at handling dollar amounts, so use it instead!

Endian Issues

When I initially did this testing and write-up, I didn’t realize there were endian issues until the first time I passed an integer to pascal and found it was garbage.

When using PIC S9(n), COMP, gnuCOBOL stores and passes the field as big-endian in keeping with the format of old mainframe storage formats.

When you pass that field to Pascal as an integer, it will expect it to be in little-endian format. The value isn’t so the pascal routine receives what amounts to garbage.

In summary, using COMP or BINARY for gnuCOBOL results in big-endian variables. Using BINARY-SHORT or other BINARY- formats will result in little-endian variables. So, make sure you use the BINARY- numeric usage clause on any integer that will be passed to an external Pascal routine.

At this time, there is an explanation in the gnuCOBOL FAQ here.

Matching Parameter Passing Methods

Back when I was coding in HP’s COBOL85, there was only one way to pass variables to the outside world: BY REFERENCE. GnuCOBOL allows by reference, by value, and by contents (see section 9.6.1.1 of the gnuCOBOL manual).

Because by reference was the only way you once could pass parameters, I would have expected that to be the default, but I have found in these test programs you must specify how to pass each parameter.

call "procrec",
    using       by reference    test-rec,
                by value        function byte-length(test-rec).

Dealing with Function Returns

gnuCOBOL the ability to receive integers from Pascal functions.

Thus, if you want to return a numerical value in a Pascal function, do so using this type of definition:

function func: integer;

You would call func in this manner:

01  i-long                    binary-long signed.

call "func"
    returning                 i-long.

I have been able to return long and short integers in this manner.

Here is some example code:

COBOL Program

Pascal Routines

The results of the program were:

Value of returned in i-long: +0123456789
Value of returned in i-short: +12345
Calling func w/o a return:
done

Returning Omitted

If COBOL calls a Pascal function, but the functional result is not used, then you must use omitted:

call "func"
    returning omitted.

I found that if you left the “returning omitted” clause off, the linker would not find the external “func” probably because the parameters didn’t match up properly.

Passing COBOL Records

The primary test COBOL program at the top of this blog entry contains a record to be passed to Pascal defined as:

01  test-rec.
    03   lastname                           pic x(30).
    03   firstname                          pic x(30).
    03   age                                pic s9(4), comp.
    03   salary                             pic s9(9)v99, comp-3.

Calling the pascal program is simple:

call "procrec",
    using                           by reference test-rec,
                                    by value     function byte-length(test-rec).

In the Pascal procedure, there are 2 ways to handle the incoming test-rec. If I need to get to every field I would define a Pascal record to do so along the lines of:

Type
  recT                    = record
    lastname              : packed array of char[1..30];
    firstname             : packed array of char[1..30];
    age                   : smallint;
    salary                : ??
    end;

OK, off the top of my head, I don’t know exactly how large this COMP-3 field is in Pascal, I’d have to look it up, but I’m just going to represent the record as an array of bytes so I use:

type 
    aobT                                = array of byte;

procedure procrec(
    var rec                             : aobT;
    reclen                              : integer
    );

In this sample program, I just want to dump the entire record, not read any specific fields, so an array of bytes works just fine. When I dump this record I find I have:

0000: 534D 4954 4820 2020 2020 2020 2020 2020 2020 2020 SMITH
0014: 2020 2020 2020 2020 2020 4A4F 484E 2020 2020 2020 JOHN
0028: 2020 2020 2020 2020 2020 2020 2020 2020 2020 2020
003C: 0026 0001 2345 678C .&..#Eg.

I can also see from this dump that the comp-3 field takes 6 bytes or 12 nibbles (each digit and the sign takes a nibble).

Strings

I have been slowly working toward strings as that is my primary interest in calling Pascal routines. I have a very large Pascal string processing library that would be nice to access. That will be covered in my next post.

Posted in c-gnuCOBOL | Tagged , | Leave a comment

Calling Free Pascal Procedures from GnuCOBOL in Windows

I’m slowly working through various experiments with gnuCOBOL. One experiment is to call Pascal code from a gnuCOBOL program. The gnuCOBOL FAQ has an example, but I presume this only works for Linux. It definitely doesn’t work for MinGW/Windows.

I spent entirely too much time trying to get this experiment to work; thus, the blog post. I don’t want to forget how to do this.

The sample Pascal program (pascalCode.pas):

library pascalCode;
{$mode objfpc}{$H+}

procedure testp;
begin
writeln('Hello World!');
end;

procedure zippy;
begin
writeln('zippy');
end; // zippy

exports
    testp,
    zippy;

begin
end.

Here is the COBOL program(test.cbl):

       >>SOURCE FREE
identification division.
program-id.
    test.

procedure division.

0000-mainline.

    display "calling Pascal".
    call "testp",
        returning omitted.
    call "zippy",
        returning omitted.
    display "called Pascal".

0099-exit.

    stop run.

To compile and run:

export COB_PRE_LOAD=pascalCode
fpc pascalCode.pas
cobc -x test.cbl -L. -l:pascalCode.dll
./test

and the results:

./test
calling Pascal
Hello World!
zippy
called Pascal

 

Posted in c-gnuCOBOL | Tagged , | Leave a comment

Installing gnuCOBOL into MinGW

I’ve been away from this project for several months and managed to forget exactly how I was supposed to invoke the COBOL compiler. I had made a note in the post
Installing gnuCOBOL on Windows, but that just tells where to find the compiler, not how to incorporate it into minGW.

I decided to incorporate gnuCOBOL into minGW again and document it for future use.

Download the Compiler

You can find the latest MinGW version at Arnold Trembley’s website. I want the version:

(NEW) GnuCOBOL 3.1-rc1 (30JUN2020) MinGW compiler for Windows XP/7/8/10 with COBOL ReportWriter. Includes VBISAM 2.0.1 for Indexed Sequential file access support, GMP 6.2.0, and PDCurses 4.1.99 (17.0 megabytes). Rename .7z to .exe for self-extracting archive.

If this website goes away, the above file (which will go out of date quickly) can also be found here.

Install the Compiler

I assume you have already installed MinGW. My notes on doing so can be found here.

Arnold Trembley’s version of gnuCOBOL is configured to reside at c:\mingw\share. Therefore, create the directory c:\mingw\share, and unzip the contects of the gnuCOBOL zip file into it.

Add C:\MinGW\share\gnucobol\bin to your path.

This almost is done. There is one problem yet: the path will be setup to run GCC out of /mingw/bin and that will cause a problem (mingw’s gcc compiler will be unable to find <libcobol.h>. You need to run it out of /mingw/share/gnucobol/bin.

To do this you must edit /etc/profile and insert /mingw/share/gnucobol/bin as shown:

# My decision to add a . to the PATH and as the first item in the path list
# is to mimick the Win32 method of finding executables.
#
# I filter the PATH value setting in order to get ready for self hosting the
# MSYS runtime and wanting different paths searched first for files.
if [ $MSYSTEM == MINGW32 ]; then
export PATH=".:/mingw/share/gnucobol/bin:/usr/local/bin:/mingw/bin:/bin:$PATH"
else
export PATH=".:/usr/local/bin:/bin:/mingw/bin:$PATH"
fi

To Test

Start minGW bash and type cobc -v

chiefdude10/~:cobc -v cobc (GnuCOBOL) 3.1-rc1.0
Built Jul 04 2020 17:17:54 Packaged Jul 01 2020 00:39:30 UTC
C version (MinGW) "6.3.0"
loading standard configuration file 'default.conf'
cobc: error: no input files
chiefdude10/~:

You should see the bold line above, indicating that the default configuration is loaded.

The same will work in CMD:

C:\>cobc -v
cobc (GnuCOBOL) 3.1-rc1.0
Built Jul 04 2020 17:17:54 Packaged Jul 01 2020 00:39:30 UTC
C version (MinGW) "6.3.0"
loading standard configuration file 'default.conf'
cobc: error: no input files

C:\>

You now have a working gnuCOBOL compiler!

To test, I created the following file (there are 7 spaces before ‘>>SOURCE FREE’):

       >>SOURCE FREE
*> Sample COBOL program
IDENTIFICATION DIVISION.
PROGRAM-ID. hello.
DATA DIVISION.
PROCEDURE DIVISION.
DISPLAY "Hello, world!".
STOP RUN.

To compile and run:

C:\cobol\helloworld>cobc -x helloW.cob

C:\cobol\helloworld>helloW
Hello, world!
Posted in c-gnuCOBOL | Tagged , | Leave a comment