libzostd (Library for z/OS STanDard utilities, or z/OS REXX Standard Library) is a collection of REXX utility libraries that provide a foundation for cross-environment development on z/OS mainframes. The functions and wrappers included in these libraries can be used for environment detection, dataset operations, job monitoring, unified argument parsing, output logging, and more.
The purpose of this library, at its core, is to simplify REXX development on z/OS mainframes through standardization and interoperability. The code components provided with this repository can be used by z/OS programmers of varying skill levels in order to easily plan, develop, and deploy different types of programs or scripts. These libraries have been built to operate identically across the TSO/E, ISPF, MVS, and UNIX System Services (USS/OMVS) environments and to be highly portable.
This collection of libraries may also be referred to as rexx-libzostd in order to distinguish itself against other libraries with similar names.
- Components
- Repository Structure
- Quick Start
- Documentation
- Debugging
- Security Model
- PDS Member Library Names
- License
| File Name: | PDS Member Name: | Purpose: |
|---|---|---|
lib/global_vars.rexx |
GLBLVARS |
Shared constants, return codes, limits, and character sets |
lib/zoscore.rexx |
ZOSCORE |
Foundation library: environment, datasets, jobs, utilities, logging |
lib/zosargp.rexx |
ZOSARGP |
Standardized POSIX-style argument parsing for TSO, ISPF, MVS, and USS environments |
libzostd.rexx |
LIBZOSTD |
All three combined in one file (the main static library artifact) |
samples/template.rexx |
TEMPLATE |
Complete working program template and usage demo |
Dependency order:
global_vars.rexx->zoscore.rexx->zosargp.rexx(each library depends on the one before it).
libzostd/
├── libzostd.rexx Main static library file, all components combined;
│
├── lib/
│ ├── global_vars.rexx Shared constant variables definitions for libraries;
│ ├── zoscore.rexx Foundational base REXX library for z/OS;
│ └── zosargp.rexx Argument parsing REXX library for z/OS;
│
├── samples/
│ └── template.rexx Consumer sample template and executable demo;
│
└── scripts/ Repository helper scripts;
└── copy-lib2pds.sh Upload library members to a z/OS PDS via USS;
The fastest way to start developing a new z/OS REXX program that utilizes this library is to copy samples/template.rexx and modify the variables PROGRAM_NAME, PROGRAM_DESC, and the argparse_addoption calls in the main: function. Everything else (global vars, all library subroutines) is already prepared for use. After this, the core logic and code can be built or integrated around the rest of the template.
If you only need the static library artifact (e.g. to drop into a PDS or to implement into an existing REXX program), use the library code file named libzostd.rexx which contains all three components in one file.
Note: The difference between the
template.rexxandlibzostd.rexxlibrary files is that thetemplate.rexxalready includes amainfunction as well as the necessary structuring for running the program to completion (for demo purposes).
The code libraries provided in this repository are considered to be static, mainly because mainframe REXX has no standardized 'include' or 'import' mechanism as you would typically find with other programming languages. The terminology associated with the process of physically adding code libraries directly into a codebase or source code is known as static inclusion - this is referenced throughout the relevant documentation within this repository.
Global variable assignments must appear before the first subroutine call, at the top of the program. Library subroutines must appear after your exit statement so they are never executed as inline code, only via function calls (call). With regard to using this library, the best practice is to build a main: function underneath the global variable definitions which can be used for isolating the core program logic.
- See the Sample LibzOStd Template file, which demonstrates this pattern in full effect.
Example: The layout for statically including the code into a program or script is always:
/* REXX */
/* --- 1. Global variable assignments (from global_vars.rexx) --- */
GLOBAL.RC_SUCCESS = 0
...etc...
PROGRAM_NAME = 'MYPROG'
/* --- 2. Your main program --- */
main:
call argparse_init
...your logic...
exit GLOBAL.RC_SUCCESS
/* --- 3. Library subroutines (from zoscore + zosargp) --- */
environment_getaddress: procedure expose GLOBAL.
...etc...The global variables definition file defines all shared constants under a single stem named GLOBAL. located before any function declarations. This is used to define standard (and/or customized) globally-scoped variables that can be referenced throughout the rest of the program or script.
| Constant | Value | Meaning |
|---|---|---|
GLOBAL.RC_SUCCESS |
0 | Operation succeeded |
GLOBAL.RC_NOTFOUND |
16 | Resource not found |
GLOBAL.RC_ERROR |
8 | General error |
GLOBAL.RC_INVALIDINPUT |
12 | Bad input / validation failure |
GLOBAL.RC_TIMEOUT |
4 | Timeout elapsed |
| Constant | Value | Purpose |
|---|---|---|
GLOBAL.LIMITS_MAX_DATASET_NAME_LEN |
44 | Maximum MVS dataset name length |
GLOBAL.LIMITS_MAX_USERID_LEN |
8 | Maximum TSO user ID length |
GLOBAL.LIMITS_MAX_STRING_LEN |
255 | Maximum safe string length |
GLOBAL.LIMITS_MAX_COMMAND_OUTPUT_LINES |
1000 | Output trap line limit |
GLOBAL.LIMITS_MAX_LOOP_ITERATIONS |
10000 | Infinite-loop guard |
| Constant | Value |
|---|---|
GLOBAL.LIMITS_DEFAULT_SLEEP_SECS |
5 |
GLOBAL.LIMITS_DEFAULT_WAIT_TIME |
60 |
GLOBAL.LIMITS_SLEEP_INTERVAL |
10 |
GLOBAL.LIMITS_MAX_SLEEP_SECS |
3600 |
GLOBAL.LIMITS_MAX_WAIT_TIME |
86400 |
| Constant | Purpose |
|---|---|
GLOBAL.CHARS_SAFE_DATASET |
Valid dataset name characters |
GLOBAL.CHARS_SAFE_USERID |
Valid user ID characters |
GLOBAL.CHARS_SAFE_DEBUG |
Valid debug flag characters |
GLOBAL.CHARS_UPPERCASE / LOWERCASE |
Full alpha ranges (for translate()) |
SAFE_DATASET_CHARS, SAFE_USERID_CHARS, SAFE_DEBUG_CHARS, and SAFE_OPT_CHARS are provided as plain variable aliases for backwards compatibility.
DEBUG and VERBOSE are initialized to 0. Statically override them in your program or script after including global_vars.rexx, or enable them via flags using the zosargp.rexx library:
EXAMPLE: Statically enable or disable DEBUG and VERBOSE constants within a script or program:
DEBUG = 1 /* statically enable constant debug output */
VERBOSE = 1 /* statically enable constant verbose output */The z/OS core library (libzoscore, zoscore) is a foundational base library that exposes several functions and wrappers used for performing common operations in a uniform way. All functions within this library use a procedure expose GLOBAL. statement for securing variable scope through isolation. A debug_flag parameter (optional on most functions) accepts 'DEBUG', '1', or 1 to enable per-function trace output.
environment_getaddress(debug_flag)
- Detect the current REXX execution environment. Returns one of:
| Value | Environment |
|---|---|
SH |
USS / OMVS / UNIX System Services |
TSO |
TSO interactive session |
ISPEXEC |
ISPF dialog environment |
MVS |
MVS batch |
ERROR |
Could not determine |
EXAMPLE:
env = environment_getaddress('')
if env = 'SH' then call log_info 'Running in USS', use_verboseenvironment_currentlocation(debug_flag)
- Returns the current working directory (USS) or dataset/member context (TSO/MVS). Returns
'ERROR'on failure.
environment_onlyin_posix(environment, debug_flag)
- This guard clause logs an error and returns
GLOBAL.RC_ERRORifenvironmentis not'SH'or'OMVS'. Use at the top of USS-only scripts or to handle USS-only operation logic.
environment_onlyin_mvs(environment, debug_flag)
- This guard clause returns
GLOBAL.RC_ERRORifenvironmentis not TSO/ISPF/MVS. Essentially, this is the opposite (or TSO/E equivalent) ofenvironment_onlyin_posix().
dataset_validateinput(dataset_name, debug_flag)
- Full data set validation: empty check, numeric check, length limit, dangerous character check, character set validation, and qualifier structure. Returns
GLOBAL.RC_SUCCESSorGLOBAL.RC_ERROR.
dataset_validateformat(dataset_name)
- Validates qualifier structure (no leading/trailing dots, no consecutive dots, max 22 qualifiers). Called internally by
dataset_validateinput.
dataset_validatequalifier(qualifier)
- Validates a single qualifier: 1–8 chars, starts with alpha or national (
@#$). Called internally bydataset_validateformat.
dataset_parsetype(dataset_name, debug_flag)
- Best-effort classification based on naming patterns. Returns one of:
PDS_MEMBERPDSVSAMSEQUENTIALUNKNOWN
dataset_ismember(dataset_name)
- Returns
1if the name includes a(MEMBER)reference,0otherwise.
dataset_extractmember(dataset_name, debug_flag)
- Returns the member name from a
DATASET(MEMBER)reference, or''if there is no member portion.
dataset_extractname(dataset_name, debug_flag)
- Returns the dataset name without the
(MEMBER)portion. Returns the original name unchanged if there is no member reference.
dataset_normalizename(dataset_name)
- Adds surrounding single quotes if not already present (
'HLQ.DS').
dataset_testexistence(dataset_name, debug_flag)
- Wraps
SYSDSN()with full error handling for both plain datasets and member references.
| Return | Meaning |
|---|---|
0 |
Not found |
1 |
Found |
2 |
Invalid input |
3 |
Invalid dataset name (SYSDSN) |
4 |
Dataset unavailable |
5 |
Unexpected SYSDSN result |
6 |
SYSDSN function failed |
if dataset_testexistence('SYS1.PARMLIB(IEASYS00)') = 1 then
call log_info 'IEASYS00 found', use_verbosejob_waitforcompletion(job_name, max_wait, debug_flag)
- Polls the JES output queue at
GLOBAL.LIMITS_SLEEP_INTERVAL-second intervals until the job appears on the output queue ormax_waitseconds elapse. Returns1(completed) or0(timeout/error).
if job_waitforcompletion('MYJOB01', 120, '') = 1 then
call log_info 'MYJOB01 completed', use_verbose
else
call log_error 'Timeout waiting for MYJOB01'job_checkstatus(job_name, debug_flag)
- Single-pass check via the TSO
STATUScommand. Returns1if the job is on the output queue,0otherwise.
validate_flag(flag)
- Normalizes any flag argument to
1(on),0(off), or-1(invalid input). Accepts1,'1','VERBOSE','DEBUG','verbose','debug','dbg'. Nearly every other function calls this before acting on a flag parameter.
safe_userid()
- Validated wrapper around
USERID(). Returns the current user ID or''on error. Validates length and character set before returning.
prompt_confirm(message, force_flag, debug_flag)
- Prompts the user to type
CONFIRM,YES, orYbefore a destructive operation. Returns1(confirmed) or0(declined/cancelled).force_flag = 'Y'or1bypasses the prompt for non-interactive use.
if prompt_confirm('Delete all output datasets?', use_force, '') = 1 then
call do_deletesleep(sleep_seconds, debug_flag)
- Cross-platform sleep using
TIME('S')that works in TSO, USS, and MVS batch. This can be used in situations where the REXX environment is missing a native/adapted sleep routine. Handles midnight time rollover. EnforcesGLOBAL.LIMITS_MAX_SLEEP_SECS.
All log functions validate and truncate messages to GLOBAL.LIMITS_MAX_STRING_LEN.
log_error(message)
- Prints
ERROR: messageunconditionally.
log_warning(message)
- Prints
WARNING: messageunconditionally.
log_info(message, verbose_flag)
- Prints
INFO: messageonly whenverbose_flagis on.
log_verbose(message, verbose_flag)
- Prints raw
messageonly whenverbose_flagis on.
log_debug(message, debug_flag)
- Prints
_> DEBUG: messageonly whendebug_flagis on.
The z/OS argument parsing library (libzosargp, zosargp) is a simple argument parsing library implementation for REXX programs, with the primary goal being to standardize argument parsing across different z/OS environments. This implementation supports both TSO-style (OPTION=value) and POSIX-style (--option=value or -o=value) argument syntax parsing, so that programs and scripts can parse arguments easily from within both UNIX (via the $PATH variable) and TSO/E (via the SYSEXEC concatenation) environments.
- Requires
global_vars.rexxandzoscore.rexxlibraries.
Note: A common and historical pattern with REXX mainframe development is that nearly every program/script one may encounter will potentially implement a different form or style of argument parsing (input handling), which leads to an overall inconsistency across projects built with the language. This library aims to prevent inconsistency by providing mainframe developers with a portable and practical library for managing input parameters, similar to other well-known programming languages such as Python, Java, Golang, C, etc.
argparse_init
- Initializes all internal state. Must be called before any other
argparse_*function. Automatically registers-h/--help. ReturnsGLOBAL.RC_SUCCESS.
argparse_addoption(short, long, type, default, help, required)
- Registers one option. Both
shortandlongmay be provided; at least one is required.
| Parameter | Description |
|---|---|
short |
Single character ('f') or '' |
long |
Name string 1–20 chars ('file') or '' |
type |
'FLAG', 'STRING', or 'INTEGER' |
default |
Default value (empty string for FLAGS) |
help |
Description shown in --help output |
required |
'Y' or 'N' |
- Returns
GLOBAL.RC_SUCCESSon success,GLOBAL.RC_ERRORorGLOBAL.RC_INVALIDINPUTon failure. Maximum 19 user-defined options (slot 20 is reserved for--help).
argparse_parse(args)
- Parses a space-delimited argument string. Handles both
--long=valueandSHORT=valueformats. Returns the number of parse errors (0 = success).
argparse_getvalue(name)
- Returns the value of an option (by short or long name), or
''if not found. ForFLAGtypes, returns'Y'if provided, default ('') otherwise.
argparse_isprovided(name)
- Returns
1if the option was explicitly provided in the argument string,0if it was not (even if a default exists).
argparse_geterrors
- Prints all accumulated parse errors with hints. Returns the error count.
argparse_showhelp
- Prints usage, option table, and examples in both TSO and USS syntax. Requires
PROGRAM_NAMEand optionallyPROGRAM_DESCto be set.
argparse_checkrequired (internal)
- Called automatically by
argparse_parse. Reports errors for any option markedrequired = 'Y'that was not provided.
argparse_cleanup
- Drops all internal argparse state. Call at end of program or before a second
argparse_initcall in the same exec.
EXAMPLE: Below is a sample reference that displays the required call sequence of the zosargp.rexx library:
call argparse_init
rc = argparse_addoption('f', 'file', 'STRING', '', 'Input file', 'Y')
rc = argparse_addoption('v', 'verbose', 'FLAG', '', 'Verbose', 'N')
rc = argparse_addoption('c', 'count', 'INTEGER', '10', 'Record count','N')
parse arg input_args
error_count = argparse_parse(input_args)
if error_count > 0 then do
call argparse_geterrors
exit GLOBAL.RC_ERROR
end
filename = argparse_getvalue('file')
is_verbose = argparse_isprovided('verbose')
call argparse_cleanupThe zosargp.rexx argument parsing library accepts several syntax variations when supplying command line arguments to programs: TSO-style, POSIX-style, or a hybrid approach. The table below describes the basic syntax options for providing arguments to a program using this library, and is also available for reference within the zosargp.rexx help menu function (e.g. providing the -h, --help, or help argument to a program will also display a version of the table data as seen below, for user reference).
| Environment | Format |
|---|---|
| TSO / ISPF / MVS | OPTION=value or O=value |
| USS / OMVS / POSIX | --option=value or -o=value |
| FLAG (any) | --verbose or VERBOSE (no =value needed) |
This library has dedicated a large portion of code specifically towards output logging and debugging features. Although the REXX programming langauge's trace routine already provides exceptionally robust debugging capabilities, every function and wrapper in the library also accepts an optional debug_flag parameter to enable verbose debugging output. Pass 'DEBUG' or 1 to enable per-function or program-wide debug logging:
env = environment_getaddress('DEBUG')
rc = dataset_testexistence('SYS1.PARMLIB', 'DEBUG')Note:
log_debugoutput is prefixed with a stylized_> DEBUG:tag and can be grepped or filtered from normal program output in UNIX environments. Combining this with REXX's built-inTRACE Iprovides very precise execution tracing.
The security methodology associated with this style of REXX programming is based on the core concept that all potentially unsafe operations should be handled or prevented by the limitations and guardrails set within the library by default. The developer should have to either build their code around the existing limitations, or explicitly modify library source code to behave otherwise.
- If a programmer is developing code that utilizes this library according to the proper implementation and usage guidelines (as outlined throughout this documentation), then the path of least resistance should lead to safer code - secure patterns should be the default, and bypassing guardrails will require deliberate, explicit modification.
Note: Unlike other programming languages that already provide several standard libraries/modules and wrappers for handling these types of common situations, the REXX programming language implementations on mainframe operating systems can sometimes be very barebones/limited in terms of libraries, and some environments may often include nothing more than a REXX interpreter or compiler. As a byproduct of these circumstances, most REXX mainframe development requires more upfront labor to create secure routines for performing what would otherwise already be included in a standard library with other programming languages. Put more simply, when a developer is re-inventing the wheel every time a new tool/program needs to be created, the margin for errors or bugs greatly increases.
The security philosophy regarding this REXX library is that by providing security checks and strong debugging capabilities directly within the libraries themselves (that would otherwise need to be manually written every time a program or script is being built), there is less burden on the developer to physically and mentally plan security components such as validations, input sanitization, and consistency maintenance upfront - allowing for more focus on core program logic and results.
By following this philosophy, the libraries generally attempt to provide safer functions by default and will require the developer to make explicit changes to security components as necessary, which would ideally cause the developer to be more aware of the changes to safeguards that they are making or draw specific attention to areas of the libraries that have been modified.
-
Every input-accepting function validates length and character set before use (see
GLOBAL.CHARS_SAFE_*andGLOBAL.LIMITS_MAX_*). -
Destructive operations can/should be routed through
prompt_confirm()unless explicitly bypassed with the force flag. -
argparse_validateinputrejects command strings containing known command injection patterns (e.g.$(, which is unsafe when passed toADDRESS SYSTEMorbpxwunix()functions) and excessive special characters. -
All log functions truncate messages at
GLOBAL.LIMITS_MAX_STRING_LENto prevent potential runaway output.
When loading individual files into an MVS PDS, the equivalent 8-character member names are:
| File | PDS Member |
|---|---|
libzostd.rexx |
LIBZOSTD |
lib/global_vars.rexx |
GLBLVARS |
lib/zoscore.rexx |
ZOSCORE |
lib/zosargp.rexx |
ZOSARGP |
Use scripts/copy-lib2pds.sh to copy the library files into a PDS and automatically set member names. Run ./copy-lib2pds.sh --help for full usage, or ./copy-lib2pds.sh -n TARGET_DS for a dry run.
This repository is currently provided under the Apache License 2.0, see the License or the license header in each library file.
Not an officially supported IBM product; open-source, and provided as-is with no warranty.