Environment Variables in Cobol? Why not?

Bad Joke of the Month:

Q: Why do programmers prefer dark mode?
A: Because light attracts bugs.

Environment Variables in Cobol?  Why not?

After posting last month’s blog post about environment variables, I was asked by a reader if it’s possible to use environment variables in a Cobol program. The answer?  Yes, of course!  You see, the RPG and CL code that I posted last month worked by calling ILE subprocedures.  Since Cobol is also an ILE language, it can also call these subprocedures.

Here’s a link to last month’s post, in case you missed it.

To provide a solution in Cobol, there are some considerations that you need to think about:

  1. Cobol uses CALL PROCEDURE to call a subprocedure. This is very similar to the CL CALLPRC command (or the obsolete RPG CALLB opcode.) It is not as elegant as the prototypes that RPG, C and C++ have available.  And there is nothing similar to CL’s *CMD interface.
  2. The getenv() and putenv() routines that I discussed last month work with variable-length, null-terminated character strings, which may seem strange to Cobol programmers, who are typically used to strings that are fixed-length and padded with blanks.

My solution to these challenges is to place the Cobol code in subroutines.  This way, you can code the needed logic to deal with the challenges into a separate block of code that can be called repeatedly in our programs as needed.

Unfortunately, Cobol does not provide very good tools for encapsulating logic. When using subroutines, we still have to use global variables and global definitions, which makes it hard to write truly modular code.  That said, this is what Cobol programmers are used to…  So, as the expression goes… “when in Rome, do as the Romans do.”

Subroutines:

  • GET-ENVIRONMENT-VARIABLE: Retrieves an environment variable.  For input, it expects the global WS-VARNAME variable to contain the name of the environment variable to retrieve, and WS-VARVAL will be returned to provide the value.  WS-VARVAL will be filled with spaces if the variable isn’t found.
  • SET-ENVIRONMENT-VARIABLE: Expects WS-VARNAME to contain the name of the variable to set, and WS-VARVAL to contain the value to set it to. Returns SETENV-STATUS set to 0 to indicate success, or -1 to indicate failure.  It also sets flags named SETENV-SUCCESS and SETENV-FAILURE as a nicer way to check for success or failure.

With that structure in mind, I’m providing the following cobol sample program. The main control section illustrates how to perform the two subroutines described above.  It may be a good idea to put the logic into copybooks to make it easier to re-use in other programs, but I have left this as an exercise for the reader.

       PROCESS varchar
               apost
               nomonoprc
               nosync
               nostdtrunc

IDENTIFICATION DIVISION.
PROGRAM-ID.     ENVVAR.
AUTHOR.       Scott Klement.

      *  Compile with:
      *
      *>  CRTCBLMOD MODULE(ENVVAR) SRCFILE(QCBLSRC) DBGVIEW(*ALL) –
      *>            OPTION(*SOURCE *EVENTF *IMBEDERR)
      *>  CRTPGM PGM(ENVVAR) BNDDIR(QC2LE) ACTGRP(*NEW)
      *

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SOURCE-COMPUTER. IBM-I.
       OBJECT-COMPUTER. IBM-I.

 

      *****************************************************************
      DATA DIVISION.

      *****************************************************************

       WORKING-STORAGE SECTION.

       01  WS-VARNAME        PIC X(256).
       01  WS-VARVAL         PIC X(5000).

       01  WS-VARNAMEZ       PIC X(256).
       01  WS-VARVALZ        PIC X(5000).
       77  WS-VARVAL-PTR     USAGE IS POINTER VALUE NULL.
       77  WS-VARVALZ-PTR    USAGE IS POINTER VALUE NULL.
       01  WS-VARVALZ-LEN    PIC S9(8) USAGE BINARY VALUE 0.

       01  SETENV-STATUS     PIC S9(8) USAGE BINARY VALUE 0.
           88 SETENV-SUCCESS VALUE 0.
           88 SETENV-FAILURE VALUE -1.
  *‚===============================================================

       LINKAGE SECTION.

       PROCEDURE DIVISION.

       000-MAIN-CONTROL SECTION.               
       000.

           MOVE ‘TAXRATE’ TO WS-VARNAME.
           PERFORM GET-ENVIRONMENT-VARIABLE.
           IF WS-VARVAL = SPACES
             DISPLAY ‘TAXRATE NOT SET’
           ELSE
             DISPLAY WS-VARVAL
           END-IF.

           MOVE ‘QIBM_QSH_CMD_ESCAPE_MSG’ TO WS-VARNAME.
           MOVE ‘Y’ TO WS-VARVAL.
           PERFORM SET-ENVIRONMENT-VARIABLE.
           IF SETENV-FAILURE
             DISPLAY ‘SETENV FAILED’
           END-IF.

           GOBACK.

      *****************************************************************
      *  ROUTINE TO GET AN ENVIRONMENT VARIABLE
      *
      *  INPUT:
      *     -WS-VARNAME = name of variable to retrieve
      *
      *  OUTPUT:
      *     -WS-VARVAL  = value of variable returned
      *****************************************************************

       GET-ENVIRONMENT-VARIABLE SECTION.
       BEG.

           MOVE SPACES TO WS-VARVAL

      * the getenv system API requires that strings end with a
      * x’00’ value (because they are designed for C programs)
      * This creates a WS-VARNAMEZ that contains the zeros at
      * the end.

           string function trimr(WS-VARNAME)
                  x’00’ delimited by size
                  into WS-VARNAMEZ

      * Retrieve the environment variable. Get back a pointer.

           CALL PROCEDURE ‘getenv’
                      USING WS-VARNAMEZ
                  RETURNING WS-VARVALZ-PTR
           END-CALL

           IF WS-VARVALZ-PTR = NULL
             GO TO XIT
           END-IF

      * Get the length of the environment variable
      * If it is larger than 5000 (the length of the WS-VARVAL
      * variable) then set it to 5000. This way, we will not
      * copy more than can fit

           CALL PROCEDURE ‘strlen’
                    USING BY VALUE WS-VARVALZ-PTR
               RETURNING WS-VARVALZ-LEN
           END-CALL

           IF WS-VARVALZ-LEN > 5000
             MOVE 5000 TO WS-VARVALZ-LEN
           END-IF

      * Copy variable value into WS-VARVAL

           CALL PROCEDURE ‘memcpy’
                    USING WS-VARVAL
                          BY VALUE WS-VARVALZ-PTR
                          BY VALUE WS-VARVALZ-LEN
               RETURNING  WS-VARVAL-PTR
           END-CALL
           .
       XIT.
           EXIT.

      *****************************************************************
      *  ROUTINE TO SET AN ENVIRONMENT VARIABLE
      *
      *  INPUT:
      *     -WS-VARNAME = name of variable to set
      *     -WS-VARVAL  = value to set variable to
      *
      *  OUTPUT:
      *     -SETENV-STATUS will be 0 for success -1 for failure
      *****************************************************************

       SET-ENVIRONMENT-VARIABLE SECTION.
       BEG.
           initialize WS-VARVALZ

      * putenv() needs a variable in the format of
      *   MYVAR=MYVAL

      * and must be terminated with X’00’.

           string function trimr(WS-VARNAME)
                  ‘=’
                  function trimr(WS-VARVAL)
                  x’00’
                  delimited by size
                  into WS-VARVALZ

           CALL PROCEDURE ‘putenv’
                      USING WS-VARVALZ
                  RETURNING SETENV-STATUS
           END-CALL
           .

       XIT.
           EXIT.

Scott Klement

Scott Klement
Midrange Dynamics
Development & Solutions Architect

Scott Klement is an IT professional with a passion for both programming and mentoring. He joined Midrange Dynamics at the beginning of October 2022. He formerly was the Director of Product Development and Support at Profound Logic and the IT Manager and Senior Programmer at Klement’s Sausage 

Co., Inc. Scott also serves on the Board of Directors of COMMON, where he represents the Education, Innovation, and Certification teams. He is an IBM Champion for Power Systems.

Subscribe to our newsletter and join us next month to see what is happening in Scott’s Corner. Add a great dad joke to your arsenal and gain an even better IT insight from this recognized industry expert as he continues his quest to educate and support the IBM i community.

Similar Posts