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