Files
gottfriedleibniz d79382bbfe Run codespell on README, build, and source files. (#824)
* chore: run codespell on source/header files

Fixes most reported typos discovered by codespell (while trying to be
unopinionated about American vs. British English).

* chore: run codespell on build/readme files
2026-04-10 11:01:50 -07:00

1889 lines
63 KiB
C

/* HREXX.C (C) Copyright Enrico Sorichetti, 2012 */
/* (C) Copyright "Fish" (David B. Trout), 2017 */
/* Hercules Rexx Support */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/* inspired by the previous Rexx implementation by Jan Jaeger */
/*-------------------------------------------------------------------*/
/* This module contains Rexx Interpreter support code. It supports */
/* both Regina Rexx as well as Open Object Rexx (OORexx). */
/* */
/* PLEASE NOTE that some of the below code is only activated when */
/* the HAVE_OBJECT_REXX *and* HAVE_REGINA_REXX build macros are */
/* BOTH #defined at build time, which provides the ability to choose */
/* at runtime which interpreter should be used. If only one of the */
/* build constants is #defined then support for that interpreter */
/* and ONLY that interpreter is generated and the ability to choose */
/* at runtime which one to use is disabled. */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#define _HREXX_C_
#define _HENGINE_DLL_
#include "hercules.h"
#include "hRexx.h"
#ifdef HAVE_REXX
/*-------------------------------------------------------------------*/
/* Hercules Rexx implementation default values */
/*-------------------------------------------------------------------*/
#define DEFAULT_PACKAGE ""
#define DEFAULT_REXXPATH ""
#define DEFAULT_EXTENSIONS EXTENSIONS
#define MODE_COMMAND 0
#define MODE_SUBROUTINE 1
#define DEFAULT_MODE MODE_COMMAND
#define DEFAULT_MSGLEVEL FALSE
#define DEFAULT_MSGPREFIX FALSE
#define DEFAULT_ERRPREFIX FALSE
#define DEFAULT_USERESOLVER TRUE
#define DEFAULT_USESYSPATH TRUE
static char* DefaultPackage; // see InitializeDefaults() function
static char* DefaultRexxPath; // see InitializeDefaults() function
static char* DefaultExtensions; // see InitializeDefaults() function
static BYTE DefaultMode; // see InitializeDefaults() function
/*-------------------------------------------------------------------*/
/* Hercules Rexx implementation publicly visible global variables */
/*-------------------------------------------------------------------*/
char* PackageName = NULL;
char* PackageVersion = NULL;
char* PackageSource = NULL;
char PackageMajorVers = '0';
BYTE MsgLevel = DEFAULT_MSGLEVEL;
BYTE MsgPrefix = DEFAULT_MSGPREFIX;
BYTE ErrPrefix = DEFAULT_ERRPREFIX;
PFNEXECCMDFUNC ExecCmd = NULL;
PFNEXECSUBFUNC ExecSub = NULL;
PFNHALTEXECFUNC HaltExec = NULL;
/*-------------------------------------------------------------------*/
/* Hercules Rexx implementation private global variables */
/*-------------------------------------------------------------------*/
static char* PkgNames[ PKGS ] = { OOREXX_PKGNAME, REGINA_PKGNAME };
static BYTE OORexxAvailable = FALSE;
static BYTE ReginaAvailable = FALSE;
static BYTE RexxMode = DEFAULT_MODE;
static BYTE useResolver = DEFAULT_USERESOLVER;
static BYTE useSysPath = DEFAULT_USESYSPATH;
static char* RexxPath = NULL;
static char** RexxPathArray = NULL;
static int RexxPathCount = 0;
static char* SysPath = NULL;
static char** SysPathArray = NULL;
static int SysPathCount = 0;
static char* Extensions = NULL;
static char** ExtensionsArray = NULL;
static int ExtensionsCount = 0;
/*-------------------------------------------------------------------*/
/* Script processing control */
/*-------------------------------------------------------------------*/
struct SCRCTL { /* Script control structure */
LIST_ENTRY link; /* Just a link in the chain */
struct timeval scr_tv; /* When script was started */
char* scr_name; /* Name of script being run */
TID scr_tid; /* Script thread id */
BYTE scr_mode; /* Execution mode (cmd/sub) */
BYTE scr_ended; /* Script ended flag */
BYTE scr_pkg; /* Rexx package that started it */
};
typedef struct SCRCTL SCRCTL; /* typedef is easier to use */
static LIST_ENTRY scr_list = {0,0}; /* Script list anchor entry */
static LOCK scr_lock; /* Lock for accessing list */
static COND scr_cond; /* Thread signalling condition */
struct THRARG { /* Create async thread arg */
SCRCTL* scr_ctl; /* Ptr to SCRCTL */
void* scr_args; /* Script argument(s) */
int scr_numargs; /* Number of arguments */
};
typedef struct THRARG THRARG; /* typedef is easier to use */
/*-------------------------------------------------------------------*/
/* Macro to build package function name */
/*-------------------------------------------------------------------*/
#define PASTEM( _p, _n ) _p ## _n
#define PKG_FUNC2( _p, _n ) PASTEM( _p, _n )
#define PKG_FUNC( _pkg, _name ) PKG_FUNC2( _pkg, _name )
/*-------------------------------------------------------------------*/
/* Package functions to Load or Activate the Rexx package */
/*-------------------------------------------------------------------*/
#ifdef OBJECT_REXX
extern BYTE PKG_FUNC( OOREXX_PKG, Load )( BYTE verbose );
extern BYTE PKG_FUNC( OOREXX_PKG, Enable )( BYTE verbose );
#else
static BYTE PKG_FUNC( OOREXX_PKG, Load )( BYTE verbose ) {UNREFERENCED(verbose);return FALSE;}
static BYTE PKG_FUNC( OOREXX_PKG, Enable )( BYTE verbose ) {UNREFERENCED(verbose);return FALSE;}
#endif
#ifdef REGINA_REXX
extern BYTE PKG_FUNC( REGINA_PKG, Load )( BYTE verbose );
extern BYTE PKG_FUNC( REGINA_PKG, Enable )( BYTE verbose );
#else
static BYTE PKG_FUNC( REGINA_PKG, Load )( BYTE verbose ) {UNREFERENCED(verbose);return FALSE;}
static BYTE PKG_FUNC( REGINA_PKG, Enable )( BYTE verbose ) {UNREFERENCED(verbose);return FALSE;}
#endif
/*-------------------------------------------------------------------*/
/* Function forward references */
/*-------------------------------------------------------------------*/
static void InitializeDefaults ();
static void InitializePaths ( char* wPath );
static void InitializeExtensions ( char* wExtensions );
static int Enable ( char* wPackage );
static void Disable ();
static void DisplayOptions ();
static BYTE CancelRexxExec ( TID tid );
static BYTE CancelAllRexxExecs ();
/*-------------------------------------------------------------------*/
/* ReportVersionSource: display package's VERSION and SOURCE info. */
/*-------------------------------------------------------------------*/
static void ReportVersionSource()
{
// "REXX(%s) VERSION: %s"
// "REXX(%s) SOURCE: %s"
WRMSG( HHC17528, "I", PackageName, PackageVersion );
WRMSG( HHC17529, "I", PackageName, PackageSource );
}
/*-------------------------------------------------------------------*/
/* Convert Package Name to Package Number */
/*-------------------------------------------------------------------*/
static BYTE PkgName2Num( const char* pkgname )
{
BYTE pkgnum;
for (pkgnum = 0; pkgnum < _countof( PkgNames ); pkgnum++)
if (strcasecmp( PkgNames[ pkgnum ], pkgname ) == 0)
return pkgnum;
return -1;
}
/*-------------------------------------------------------------------*/
/* Convert Package Number to Package Name */
/*-------------------------------------------------------------------*/
static const char* PkgNum2Name( BYTE pkgnum )
{
if (pkgnum < _countof( PkgNames ))
return PkgNames[ pkgnum ];
return "??????";
}
/*-------------------------------------------------------------------*/
/* Check if valid package name (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE IsValidPackage( const char* pkgname )
{
if (pkgname)
{
BYTE pkgnum;
for (pkgnum=0; pkgnum < PKGS; pkgnum++)
if (strcasecmp( pkgname, PkgNum2Name( pkgnum )) == 0)
return TRUE;
}
return FALSE;
}
/*-------------------------------------------------------------------*/
/* Check if package is enabled or not (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE IsEnabled( const char* pkgname )
{
return
(1
&& PackageName
&& PackageVersion
&& PackageSource
&& ExecCmd
&& ExecSub
&& HaltExec
&& (0
|| (1
&& pkgname
&& IsValidPackage( pkgname )
&& strcasecmp( PackageName, pkgname ) == 0
)
|| (1
&& !pkgname
&& IsValidPackage( PackageName )
)
)
);
}
/*-------------------------------------------------------------------*/
/* Check if package is disabled or not (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE IsDisabled()
{
return !IsEnabled( NULL );
}
/*-------------------------------------------------------------------*/
/* Load all supported Rexx packages */
/*-------------------------------------------------------------------*/
void InitializeRexx() // (called by impl.c at system startup)
{
static const BYTE verbose = TRUE; // (always during startup)
/* Initialize script control */
initialize_lock ( &scr_lock );
initialize_condition ( &scr_cond );
InitializeListHead ( &scr_list );
/* Initialize default options */
InitializeDefaults();
/* Load all available Rexx packages */
OORexxAvailable = PKG_FUNC( OOREXX_PKG, Load )( verbose );
ReginaAvailable = PKG_FUNC( REGINA_PKG, Load )( verbose );
/* Automatically enable default Rexx package if possible */
if (Enable( DefaultPackage ) != 0)
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", "", "default " );
}
/* Always show options at start regardless of Rexx start success */
DisplayOptions();
}
/*-------------------------------------------------------------------*/
/* Initialize Rexx options to their starting default values */
/*-------------------------------------------------------------------*/
static void InitializeDefaults()
{
const char* sym;
/* Retrieve and save the user's preferred default values */
if (!(sym = get_symbol( "HREXX_PACKAGE" )) || !*sym)
DefaultPackage = strdup( DEFAULT_PACKAGE );
else
DefaultPackage = strdup( sym );
if (!(sym = get_symbol( "HREXX_MODE" )) || !*sym)
DefaultMode = DEFAULT_MODE;
else
{
if (strcasecmp( sym, "Command" ) == 0)
DefaultMode = MODE_COMMAND;
else if (strcasecmp( sym, "Subroutine" ) == 0)
DefaultMode = MODE_SUBROUTINE;
else
DefaultMode = DEFAULT_MODE;
}
if (!(sym = get_symbol( "HREXX_PATH" )) || !*sym)
DefaultRexxPath = strdup( DEFAULT_REXXPATH );
else
DefaultRexxPath = strdup( sym );
if (!(sym = get_symbol( "HREXX_EXTENSIONS" )) || !*sym)
DefaultExtensions = strdup( DEFAULT_EXTENSIONS );
else
DefaultExtensions = strdup( sym );
/* Initialize the defaults */
PackageName = DefaultPackage;
RexxMode = DefaultMode;
InitializePaths( NULL );
InitializeExtensions( NULL );
}
/*-------------------------------------------------------------------*/
/* InitializePaths and InitializeExtensions helper function */
/*-------------------------------------------------------------------*/
static void InitArray( const char* str, const char* delims, const char* def,
BYTE empty0, /* array[0] == empty string? */
char** arraystr, char*** array, int* count )
{
int i;
/* Free the old information */
if (*count)
{
char** arrayents = *array;
for (i=0; i < *count; i++)
free( arrayents[i] );
free( *array );
*array = NULL;
free( *arraystr );
*arraystr = NULL;
*count = 0;
}
/* Initialize string to its new value */
if (str)
*arraystr = strdup( str );
else if (def)
*arraystr = strdup( def );
if (!*arraystr && empty0)
*arraystr = strdup( "" );
/* Rebuild the array using its new string value */
if (*arraystr)
{
char* work;
char* token;
char** arrayents;
work = strdup( *arraystr );
/* The 'empty0' option requests an extra empty string
entry at index 0 followed by the other entries.
*/
*count = (empty0 ? 1 : 0) + tkcount( work, delims );
arrayents = *array = malloc( *count * sizeof( char* ));
if (empty0)
arrayents[0] = strdup("");
for (i = empty0 ? 1 : 0, token = strtok( work, delims );
token; token = strtok( NULL, delims ), i++)
{
arrayents[i] = strdup( token );
}
free( work );
}
}
/*-------------------------------------------------------------------*/
/* Initialize Rexx search paths */
/*-------------------------------------------------------------------*/
static void InitializePaths( char* wPath )
{
const char* path = get_symbol( "PATH" );
BYTE empty0 = FALSE;
if (path && !*path)
path = NULL;
InitArray( wPath, PATHDELIM, DefaultRexxPath, empty0,
&RexxPath, &RexxPathArray, &RexxPathCount );
InitArray( path, PATHDELIM, NULL, empty0,
&SysPath, &SysPathArray, &SysPathCount );
}
/*-------------------------------------------------------------------*/
/* Initialize Rexx extensions */
/*-------------------------------------------------------------------*/
static void InitializeExtensions( char* wExtensions )
{
/* The 'empty0' option requests an extra empty string
entry at index 0 followed by the other entries. Thus
our ExtensionsArray will always have at least one
entry and ExtensionsCount will always be at least 1.
*/
BYTE empty0 = TRUE;
InitArray( wExtensions, EXTDELIM, DefaultExtensions, empty0,
&Extensions, &ExtensionsArray, &ExtensionsCount );
ASSERT( ExtensionsCount >= 1 );
}
/*-------------------------------------------------------------------*/
/* Disable Rexx */
/*-------------------------------------------------------------------*/
static void Disable()
{
PackageName = "";
PackageVersion = NULL;
PackageSource = NULL;
ExecCmd = NULL;
ExecSub = NULL;
HaltExec = NULL;
}
/*-------------------------------------------------------------------*/
/* Enable Rexx Package by Package Number (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE EnablePkg( BYTE pkgnum, BYTE verbose )
{
switch (pkgnum)
{
case OOREXX_PKGNUM:
return PKG_FUNC( OOREXX_PKG, Enable )( verbose );
case REGINA_PKGNUM:
return PKG_FUNC( REGINA_PKG, Enable )( verbose );
}
if (verbose)
{
// "REXX(%s) Unknown/unsupported Rexx package '%s'"
WRMSG( HHC17527, "E", PackageName, PkgNum2Name( pkgnum ));
}
return FALSE;
}
/*-------------------------------------------------------------------*/
/* Enable Rexx: 0 == success, -1 error, +1 "none" specified. */
/*-------------------------------------------------------------------*/
static int Enable( char* wPackage )
{
static const BYTE verbose = FALSE;
if (!wPackage) // (if not specified)
wPackage = DefaultPackage; // (then use default)
if (!wPackage[0]) // (unspecified same as "auto")
wPackage = "auto"; // (unspecified same as "auto")
/* If the desired package is already enabled then just return */
if (IsEnabled( wPackage ))
return 0;
/* Enabling package "none" same as Disabling/Stopping Rexx */
if (strcasecmp( wPackage, "none" ) == 0)
{
if (IsEnabled( NULL ))
{
char* oldpkg = strdup( PackageName );
Disable();
// "REXX(%s) Rexx has been stopped/disabled"
WRMSG( HHC17526, "W", oldpkg );
free( oldpkg );
}
else
{
// "REXX(%s) Rexx already stopped/disabled"
WRMSG( HHC17523, "W", "" );
}
return +1; // ("none" requested)
}
/* Can't Enable a different Rexx package until the
currently enabled package is disabled first. */
if (IsEnabled( NULL ))
{
// "REXX(%s) Rexx already started/enabled"
WRMSG( HHC17522, "E", PackageName );
return -1;
}
if (strcasecmp( wPackage, "auto" ) == 0)
{
if (1
&& OORexxAvailable
&& Enable( OOREXX_PKGNAME ) == 0
)
{
// "REXX(%s) Rexx has been started/enabled"
WRMSG( HHC17525, "I", OOREXX_PKGNAME );
}
else if (1
&& ReginaAvailable
&& Enable( REGINA_PKGNAME ) == 0
)
{
// "REXX(%s) Rexx has been started/enabled"
WRMSG( HHC17525, "I", REGINA_PKGNAME );
}
else
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", "", "either " );
return -1;
}
}
else if (strcasecmp( OOREXX_PKGNAME, wPackage ) == 0)
{
if (!EnablePkg( OOREXX_PKGNUM, verbose ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", wPackage, "requested " );
return -1;
}
ReportVersionSource();
}
else if (strcasecmp( REGINA_PKGNAME, wPackage ) == 0)
{
if (!EnablePkg( REGINA_PKGNUM, verbose ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", wPackage, "requested " );
return -1;
}
ReportVersionSource();
}
else
{
// "REXX(%s) Unknown/unsupported Rexx package '%s'"
WRMSG( HHC17527, "E", "", wPackage );
return -1;
}
return 0;
}
/*-------------------------------------------------------------------*/
/* Discard SCRCTL entry */
/*-------------------------------------------------------------------*/
static void FreeSCRCTL( SCRCTL* pCtl )
{
/* Free list entry and return */
free( pCtl->scr_name );
free( pCtl );
}
/*-------------------------------------------------------------------*/
/* Prune SCRCTL entries */
/*-------------------------------------------------------------------*/
static void PruneSCRCTL()
{
LIST_ENTRY* pLink = NULL;
SCRCTL* pCtl = NULL;
for (pLink = scr_list.Flink; pLink != &scr_list; pLink = pLink->Flink)
{
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
if (pCtl->scr_ended)
{
pLink = pLink->Blink; // (since entry is going away)
RemoveListEntry( &pCtl->link );
FreeSCRCTL( pCtl ); // (remove from list and free)
}
}
}
/*-------------------------------------------------------------------*/
/* Create SCRCTL entry */
/*-------------------------------------------------------------------*/
static SCRCTL* NewSCRCTL
(
char* scr_name, /* Name of script being run */
TID scr_tid, /* Script thread id */
BYTE scr_mode /* Execution mode (cmd/sub) */
)
{
SCRCTL* pCtl;
/* Prune list on regular basis */
PruneSCRCTL();
/* Create a new entry */
if (0
|| !(pCtl = malloc( sizeof( SCRCTL )))
|| !(pCtl->scr_name = strdup( scr_name ))
)
{
// "Out of memory"
WRMSG( HHC00152, "E" );
free( pCtl );
return NULL;
}
/* Initialize the new entry */
InitializeListLink( &pCtl->link );
gettimeofday( &pCtl->scr_tv, NULL );
pCtl->scr_tid = scr_tid;
pCtl->scr_mode = scr_mode;
pCtl->scr_ended = FALSE;
pCtl->scr_pkg = PkgName2Num( PackageName );
return pCtl;
}
/*-------------------------------------------------------------------*/
/* Find SCRCTL entry */
/*-------------------------------------------------------------------*/
static SCRCTL* FindSCRCTL( TID tid )
{
LIST_ENTRY* pLink = NULL;
SCRCTL* pCtl = NULL;
/* Prune list on regular basis */
PruneSCRCTL();
/* Search the list to locate the desired entry */
for (pLink = scr_list.Flink; pLink != &scr_list; pLink = pLink->Flink)
{
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
if (equal_threads( pCtl->scr_tid, tid ))
return pCtl; // found
}
return NULL; // not found
}
/*-------------------------------------------------------------------*/
/* List SCRCTL entries */
/*-------------------------------------------------------------------*/
static void ListSCRCTLs()
{
LIST_ENTRY* pLink = NULL;
SCRCTL* pCtl = NULL;
char tod[32]; // "YYYY-MM-DD HH:MM:SS.uuuuuu"
/* Prune list on regular basis */
PruneSCRCTL();
/* Check for empty list */
if (IsListEmpty( &scr_list ))
{
// "No asynchronous Rexx scripts are currently running"
WRMSG( HHC17516, "I" );
return;
}
// "Started Package ThreadId Mode Name..."
WRMSG( HHC17514, "I", "Started Package ThreadId Mode Name..." );
WRMSG( HHC17514, "I", "------- ------ -------- --- -------------" );
/* Display all entries in list */
for (pLink = scr_list.Flink; pLink != &scr_list; pLink = pLink->Flink)
{
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
FormatTIMEVAL( &pCtl->scr_tv, tod, sizeof( tod ));
tod[ 19 ] = 0; // "YYYY-MM-DD HH:MM:SS"
// "%8s %6s "TIDPAT" %3s %s"
WRMSG( HHC17515, "I"
, &tod[ 11 ] // HH:MM:SS
, PkgNum2Name( pCtl->scr_pkg )
, TID_CAST( pCtl->scr_tid )
, pCtl->scr_mode == MODE_COMMAND ? "cmd" :
pCtl->scr_mode == MODE_SUBROUTINE ? "sub" : "???"
, pCtl->scr_name
);
}
}
/*-------------------------------------------------------------------*/
/* rexx_cmd parsing control */
/*-------------------------------------------------------------------*/
enum
{
// the following enums must match the below ARGSDESC array
_NEEDPACKAGE = 1,
_NEEDPATH,
_NEEDSYSPATH,
_NEEDEXTENSIONS,
_NEEDRESOLVER,
_NEEDMSGLEVL,
_NEEDMSGPREF,
_NEEDERRPREF,
_NEEDMODE,
_NEEDCANCEL
};
static char* ARGSDESC[] =
{
"",
"Start/Enable", // _NEEDPACKAGE
"Path", // _NEEDPATH
"SysPath", // _NEEDSYSPATH
"Extensions", // _NEEDEXTENSIONS
"Resolver", // _NEEDRESOLVER
"MsgLevel", // _NEEDMSGLEVL
"MsgPrefix", // _NEEDMSGPREF
"ErrPrefix", // _NEEDERRPREF
"Mode", // _NEEDMODE
"Cancel" // _NEEDCANCEL
};
/*-------------------------------------------------------------------*/
/* rexx Command - manage the Rexx environment */
/*-------------------------------------------------------------------*/
/* */
/* Format: 'rexx [optname optvalue] ...' */
/* */
/* Ena[ble]/Sta[rt] Enable/Start a Rexx Package, where package */
/* is either 'OORexx' (the default) or 'Regina'. */
/* Use the HREXX_PACKAGE environment variable */
/* to define a preferred default value. "auto" */
/* will automatically start the default package. */
/* Use "none" to prevent automatic enablement. */
/* Disa[ble]/Sto[p] Disable/Stop the Rexx package. */
/* */
/* RexxP[ath]/Path List of directories to search for scripts. */
/* No default. Use the HREXX_PATH environment */
/* variable to define your preferred default. */
/* SysP[ath] Extend the search to the System Paths too. */
/* 'On' (default) or 'Off'. */
/* Ext[ensions] List of extensions to use when searching for */
/* scripts. A search with no extension is always */
/* done first. The HREXX_EXTENSIONS environment */
/* can be used to set a different default list. */
/* Suf[fixes] Alias for 'Ext[ensions]'. */
/* Resolv[er] 'On' (default): Hercules will resolve the */
/* script's full path. 'Off': the scriptname */
/* is used as-is. */
/* MsgL[evel] 'Off' (default) or 'On' to disable or enable */
/* Hercules messages HHC17503I and HHC17504I */
/* that display a script's return code and value */
/* when it finishes executing. */
/* MsgP[refix] 'Off' (default) or 'On' to disable or enable */
/* prefixing Rexx script 'say' messages with */
/* Hercules message number HHC17540I. */
/* ErrP[refix] 'Off' (default) or 'On' to disable or enable */
/* prefixing Rexx script 'TRACE messages with */
/* Hercules message number HHC17541D. */
/* Mode Define the preferred argument passing style. */
/* 'Com[mand]' (default) or 'Sub[routine]'. Use */
/* the HREXX_MODE environment variable to define */
/* your preferred default mode. */
/* List Lists currently running asynchronous scripts. */
/* Cancel <tid> to halt a running asynchronous script. */
/* */
/* Setting any option to 'reset' resets the option to its default. */
/* Enter the command without arguments to display current values. */
/* */
/*-------------------------------------------------------------------*/
int rexx_cmd( int argc, char* argv[], char* cmdline )
{
int iarg = 0;
int argl = 0;
BYTE haveEnable = FALSE;
BYTE haveDisable = FALSE;
char* wPackage = NULL;
BYTE havePath = FALSE;
BYTE haveSysPath = FALSE;
BYTE haveExtensions = FALSE;
BYTE haveResolver = FALSE;
BYTE haveMsgLevel = FALSE;
BYTE haveMsgPrefix = FALSE;
BYTE haveErrPrefix = FALSE;
BYTE haveMode = FALSE;
BYTE haveCancel = FALSE;
BYTE wSysPath = useSysPath;
BYTE wResolver = useResolver;
BYTE wMsgLevel = MsgLevel;
BYTE wMsgPrefix = MsgPrefix;
BYTE wErrPrefix = ErrPrefix;
BYTE wMode = RexxMode;
char* wPath = NULL;
char* wExtensions = NULL;
int whatValue = 0;
UNREFERENCED( cmdline );
/* Just display the current options when no arguments are given */
if (argc < 2)
{
DisplayOptions();
return 0;
}
//----------------------------------------------------------------
// First, parse and save their options. Once parsing is complete,
// We will then process their options if no errors were detected.
//----------------------------------------------------------------
for (iarg=1; iarg < argc; iarg++)
{
// Get length of next token (option's name or its value).
argl = strlen( argv[ iarg ]);
if (!whatValue)
{
//-----------------------
// Parse the option name
//-----------------------
if (HAVKEYW( "List" ))
{
obtain_lock( &scr_lock );
{
ListSCRCTLs();
}
release_lock( &scr_lock );
return 0;
}
if (!haveCancel && HAVKEYW( "Cancel" ))
{
haveCancel = TRUE;
whatValue = _NEEDCANCEL;
continue;
}
if (!haveMode && HAVKEYW( "mode" ))
{
haveMode = TRUE;
whatValue = _NEEDMODE;
continue;
}
if (!haveDisable &&
((argl >= 3 && argl <= 7 && HAVABBR( "Disable" )) ||
(argl >= 3 && argl <= 4 && HAVABBR( "Stop" ))))
{
if (IsDisabled())
{
// "REXX(%s) Rexx already stopped/disabled"
WRMSG( HHC17523, "E", "" );
return -1;
}
haveDisable = TRUE;
continue;
}
if (!haveEnable &&
((argl >= 3 && argl <= 6 && HAVABBR( "Enable" )) ||
(argl >= 3 && argl <= 5 && HAVABBR( "Start" ))))
{
if (IsEnabled( NULL ))
{
// REXX(%s) Rexx already started/enabled"
WRMSG( HHC17522, "E", PackageName );
return -1;
}
haveEnable = TRUE;
whatValue = _NEEDPACKAGE;
continue;
}
if (!havePath &&
((argl >= 4 && argl <= 5 && HAVABBR( "Path" )) ||
(argl >= 5 && argl <= 9 && HAVABBR( "RexxPath" ))))
{
havePath = TRUE;
whatValue = _NEEDPATH;
continue;
}
if (!haveSysPath &&
(argl >= 4 && argl <= 7 && HAVABBR( "SysPath" )))
{
haveSysPath = TRUE;
whatValue = _NEEDSYSPATH;
continue;
}
if (!haveExtensions &&
((argl >= 3 && argl <= 10 && HAVABBR( "Extensions" )) ||
(argl >= 3 && argl <= 8 && HAVABBR( "Suffixes" ))))
{
haveExtensions = TRUE;
whatValue = _NEEDEXTENSIONS;
continue;
}
if (!haveResolver &&
(argl >= 5 && argl <= 8 && HAVABBR( "Resolver" )))
{
haveResolver = TRUE;
whatValue = _NEEDRESOLVER;
continue;
}
if (!haveMsgLevel &&
(argl >= 4 && argl <= 8 && HAVABBR( "MsgLevel" )))
{
haveMsgLevel = TRUE;
whatValue = _NEEDMSGLEVL;
continue;
}
if (!haveMsgPrefix &&
(argl >= 4 && argl <= 9 && HAVABBR( "MsgPrefix" )))
{
haveMsgPrefix = TRUE;
whatValue = _NEEDMSGPREF;
continue;
}
if (!haveErrPrefix &&
(argl >= 4 && argl <= 9 && HAVABBR( "ErrPrefix" )))
{
haveErrPrefix = TRUE;
whatValue = _NEEDERRPREF;
continue;
}
// "REXX(%s) Argument %d: unknown option '%s'"
WRMSG( HHC17508, "E", PackageName, iarg, argv[ iarg ]);
return -1;
}
else // (whatValue > 0)
{
//------------------------
// Parse the option value
//------------------------
switch (whatValue)
{
case _NEEDCANCEL:
{
if (HAVKEYW( "All" ))
return CancelAllRexxExecs() ? 0 : -1;
else
{
TID_INT tid_int;
char c;
if (sscanf( argv[ iarg ], SCN_TIDPAT "%c", &tid_int, &c ) == 1)
return CancelRexxExec( (TID)tid_int ) ? 0 : -1;
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDPACKAGE:
{
if (0
|| HAVKEYW( REGINA_PKGNAME )
|| HAVKEYW( OOREXX_PKGNAME )
|| HAVKEYW( "auto" )
|| HAVKEYW( "none" )
)
{
wPackage = argv[ iarg ];
}
else if (HAVKEYW( "reset" ))
{
wPackage = DefaultPackage;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDEXTENSIONS:
{
if (HAVKEYW( "reset" ))
wExtensions = DefaultExtensions;
else
wExtensions = argv[ iarg ];
}
break;
case _NEEDPATH:
{
if (HAVKEYW( "reset" ))
wPath = DefaultRexxPath;
else
wPath = argv[ iarg ];
}
break;
case _NEEDSYSPATH:
{
if (0
|| HAVKEYW( "Enable" )
|| HAVKEYW( "On" )
)
{
wSysPath = TRUE;
}
else if (0
|| HAVKEYW( "Disable" )
|| HAVKEYW( "Off" )
)
{
wSysPath = FALSE;
}
else if (HAVKEYW( "reset" ))
{
wSysPath = DEFAULT_USESYSPATH;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDRESOLVER:
{
if (0
|| HAVKEYW( "Enable" )
|| HAVKEYW( "On" )
)
{
wResolver = TRUE;
}
else if (0
|| HAVKEYW( "Disable" )
|| HAVKEYW( "Off" )
)
{
wResolver = FALSE;
}
else if (HAVKEYW( "reset" ))
{
wResolver = DEFAULT_USERESOLVER;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDMSGLEVL:
{
if (0
|| HAVKEYW( "Disable" )
|| HAVKEYW( "Off" )
)
{
wMsgLevel = FALSE;
}
else if (0
|| HAVKEYW( "Enable" )
|| HAVKEYW( "On" )
)
{
wMsgLevel = TRUE;
}
else if (HAVKEYW( "reset" ))
{
wMsgLevel = DEFAULT_MSGLEVEL;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDMSGPREF:
{
if (0
|| HAVKEYW( "Disable" )
|| HAVKEYW( "Off" )
)
{
wMsgPrefix = FALSE;
}
else if (0
|| HAVKEYW( "Enable" )
|| HAVKEYW( "On" )
)
{
wMsgPrefix = TRUE;
}
else if (HAVKEYW( "reset" ))
{
wMsgPrefix = DEFAULT_MSGPREFIX;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDERRPREF:
{
if (0
|| HAVKEYW( "Disable" )
|| HAVKEYW( "Off" )
)
{
wErrPrefix = FALSE;
}
else if (0
|| HAVKEYW( "Enable" )
|| HAVKEYW( "On" )
)
{
wErrPrefix = TRUE;
}
else if (HAVKEYW( "reset" ))
{
wErrPrefix = DEFAULT_ERRPREFIX;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
case _NEEDMODE:
{
if (argl >= 3 && argl <= 7 && HAVABBR( "Command" ))
{
wMode = MODE_COMMAND;
}
else if (argl >= 3 && argl <= 10 && HAVABBR( "Subroutine" ))
{
wMode = MODE_SUBROUTINE;
}
else if (HAVKEYW( "reset" ))
{
wMode = DefaultMode;
}
else
{
// "REXX(%s) Option %s value '%s' is invalid"
WRMSG( HHC17509, "E", PackageName,
ARGSDESC[ whatValue ], argv[ iarg ]);
return -1;
}
}
break;
}
// end switch (whatValue)
whatValue = 0;
continue;
}
}
// end for (iarg=1; iarg < argc; iarg++)
if (whatValue == _NEEDPACKAGE)
{
// (they entered "rexx start" without specifying package)
wPackage = DefaultPackage;
}
else if (whatValue > 0)
{
// "REXX(%s) Option '%s' needs a value"
WRMSG( HHC17507, "E", PackageName, ARGSDESC[ whatValue ]);
return -1;
}
//----------------------------------------------------------------
// Option parsing complete. Process their requested options.
//----------------------------------------------------------------
if (haveMode)
RexxMode = wMode;
if (haveMsgLevel)
MsgLevel = wMsgLevel;
if (haveMsgPrefix)
MsgPrefix = wMsgPrefix;
if (haveErrPrefix)
ErrPrefix = wErrPrefix;
if (haveResolver)
useResolver = wResolver;
if (haveSysPath)
useSysPath = wSysPath;
if (havePath)
InitializePaths( wPath );
if (haveExtensions)
InitializeExtensions( wExtensions );
/* Disable/Enable the Rexx package, as needed */
if (haveDisable)
{
if (IsEnabled( NULL ))
{
char* saved_pkgname = PackageName;
Disable();
// "REXX(%s) Rexx has been stopped/disabled"
WRMSG( HHC17526, "I", saved_pkgname );
}
else
{
// "REXX(%s) Rexx already stopped/disabled"
WRMSG( HHC17523, "W", PackageName );
}
}
else if (haveEnable)
{
if (IsDisabled())
{
// Enable function will report version/source, etc.
if (Enable( wPackage ) == 0)
DisplayOptions();
}
else
{
// "REXX(%s) Rexx already started/enabled"
WRMSG( HHC17522, "W", PackageName );
}
}
return 0;
} // end rexx_cmd
/*-------------------------------------------------------------------*/
/* Display current options */
/*-------------------------------------------------------------------*/
static void DisplayOptions()
{
char buf[ 256 ] = {0};
// "REXX(%s) %s"
MSGBUF( buf, "Mode : %s", RexxMode == MODE_COMMAND ? "Command" : "Subroutine" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// "REXX(%s) %s"
MSGBUF( buf, "MsgLevel : %s", MsgLevel ? "On" : "Off" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// "REXX(%s) %s"
MSGBUF( buf, "MsgPrefix : %s", MsgPrefix ? "On" : "Off" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// "REXX(%s) %s"
MSGBUF( buf, "ErrPrefix : %s", ErrPrefix ? "On" : "Off" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// "REXX(%s) %s"
MSGBUF( buf, "Resolver : %s", useResolver ? "On" : "Off" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// "REXX(%s) %s"
MSGBUF( buf, "SysPath (%2d) : %s", SysPathCount, useSysPath ? "On" : "Off" );
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
MSGBUF( buf,"RexxPath (%2d) : %s", RexxPathCount, RexxPathCount ? RexxPath : "" );
// "REXX(%s) %s"
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
// NOTE: we use "ExtensionsCount-1" because the
// first entry is our dummy empty-string entry.
MSGBUF( buf, "Extensions (%2d) : %s", ExtensionsCount-1, ExtensionsCount > 1 ? Extensions : "" );
// "REXX(%s) %s"
WRMSG( HHC17500, "I", PackageName, RTRIM( buf ));
}
/*-------------------------------------------------------------------*/
/* Thread to run Rexx script in the background */
/*-------------------------------------------------------------------*/
static void* exec_async_thread( void* p )
{
SCRCTL* pCtl; // Pointer to our SCRCTL entry
void* args; // (depends on mode)
int argc; // (depends on mode)
int rc; // ExecCmd/ExecSub return code
char* cmd_cmdline = NULL; // (MODE_COMMAND script cmdline)
/* Retrieve needed arguments */
obtain_lock( &scr_lock );
{
THRARG* arg = p;
pCtl = arg->scr_ctl;
args = arg->scr_args;
argc = arg->scr_numargs;
pCtl->scr_ended = FALSE;
//* Make copy of MODE_COMMAND cmdline before it disappears! */
if (pCtl->scr_mode == MODE_COMMAND)
cmd_cmdline = args ? strdup( args ) : NULL;
/* Indicate arguments retrieved */
signal_condition( &scr_cond );
}
release_lock( &scr_lock );
/* Execute Rexx script asynchronously */
if (pCtl->scr_mode == MODE_COMMAND)
{
rc = ExecCmd( pCtl->scr_name, cmd_cmdline );
free( cmd_cmdline );
}
else // (pCtl->scr_mode == MODE_SUBROUTINE)
{
rc = ExecSub( pCtl->scr_name, argc, args );
}
/* Indicate script has ended */
obtain_lock( &scr_lock );
{
pCtl->scr_ended = TRUE;
}
release_lock( &scr_lock );
return INT2VOIDP( rc ); // (in case anyone is interested)
}
/*-------------------------------------------------------------------*/
/* Execute a Rexx script asynchronously */
/*-------------------------------------------------------------------*/
static int exec_async
(
char* scr_name,
BYTE scr_mode,
void* scr_args,
int scr_numargs
)
{
int rc;
THRARG thr_args;
obtain_lock( &scr_lock );
{
TID dummy = 0; // temp for NewSCRCTL()
/* Initialize THRARG structure. Note that the SCRCTL struct's
'tid' field (thr_args.scr_ctl->scr_tid) will be initially
created with a null (0) dummy value but will get fixed
automatically whenever the exec_async_thread is created.
*/
thr_args.scr_args = scr_args;
thr_args.scr_numargs = scr_numargs;
thr_args.scr_ctl = NewSCRCTL( scr_name, dummy, scr_mode );
if (!thr_args.scr_ctl)
rc = -1; // (out of memory)
else
{
/* Create async thread and update thread-id */
rc = create_thread( &thr_args.scr_ctl->scr_tid, DETACHED,
exec_async_thread, &thr_args, scr_name );
if (rc != 0)
{
// "Error in function create_thread(): %s"
WRMSG( HHC00102, "E", strerror( rc ));
FreeSCRCTL( thr_args.scr_ctl );
}
else
{
/* Wait for thread to initialize */
wait_condition( &scr_cond, &scr_lock );
/* Add this script to our list */
InsertListTail( &scr_list, &thr_args.scr_ctl->link );
}
}
}
release_lock( &scr_lock );
return rc;
}
/*-------------------------------------------------------------------*/
/* Halt an executing Rexx script (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE CancelRexxExec( TID tid )
{
BYTE rc = TRUE;
obtain_lock( &scr_lock );
{
SCRCTL* pCtl;
BYTE cur_pkg;
if (!(pCtl = FindSCRCTL( tid )))
{
// "Asynchronous Rexx script "TIDPAT" not found"
WRMSG( HHC17517, "E", TID_CAST( tid ));
release_lock( &scr_lock );
return FALSE;
}
/* Save current package */
cur_pkg = PkgName2Num( PackageName );
/* Temporarily enable same package as script's */
if (!EnablePkg( pCtl->scr_pkg, FALSE ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", "", "temporary " );
rc = FALSE;
}
else
{
/* Now ask Rexx to HALT that script */
if (HaltExec( getpid(), pCtl->scr_tid ) != 0)
{
// "REXX(%s) Signal HALT "TIDPAT" %s"
WRMSG( HHC17520, "E", PkgNum2Name( pCtl->scr_pkg ),
TID_CAST( pCtl->scr_tid ), "failed" );
rc = FALSE;
}
}
/* Re-enable the original package */
if (!EnablePkg( cur_pkg, FALSE ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", PackageName, "original " );
rc = FALSE;
}
}
release_lock( &scr_lock );
return rc;
}
/*-------------------------------------------------------------------*/
/* Halt an ALL asynchronous Rexx scripts (boolean function) */
/*-------------------------------------------------------------------*/
static BYTE CancelAllRexxExecs()
{
BYTE rc = TRUE;
obtain_lock( &scr_lock );
{
LIST_ENTRY* pLink = NULL;
SCRCTL* pCtl = NULL;
BYTE cur_pkg = PkgName2Num( PackageName );
for (pLink = scr_list.Flink; pLink != &scr_list; pLink = pLink->Flink)
{
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
/* Temporarily enable same package as script's */
if (!EnablePkg( pCtl->scr_pkg, FALSE ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", "", "temporary " );
rc = FALSE;
}
else
{
/* Now ask Rexx to HALT that script */
if (HaltExec( getpid(), pCtl->scr_tid ) != 0)
{
// "REXX(%s) Signal HALT "TIDPAT" %s"
WRMSG( HHC17520, "E", PkgNum2Name( pCtl->scr_pkg ),
TID_CAST( pCtl->scr_tid ), "failed" );
rc = FALSE;
}
}
}
/* Re-enable the original package */
if (!EnablePkg( cur_pkg, FALSE ))
{
// "REXX(%s) Could not enable %sRexx package"
WRMSG( HHC17511, "E", PackageName, "original " );
rc = FALSE;
}
}
release_lock( &scr_lock );
return rc;
}
/*-------------------------------------------------------------------*/
/* exec Command - execute a rexx script */
/*-------------------------------------------------------------------*/
int exec_cmd( int argc, char* argv[], char* cmdline )
{
char* pscriptname;
char scriptname[ MAX_FULLPATH_LEN ];
struct stat fstat;
int rc, mode, iarg, i, n;
BYTE haveMode = FALSE;
BYTE found = FALSE;
BYTE async = FALSE;
if (!(sysblk.shcmdopt & SHCMDOPT_ENABLE))
{
// "Shell/Exec commands are disabled"
WRMSG( HHC02227, "E" );
return -1;
}
/*-----------------------------------------------------------------
-------------------
PROGRAMMING NOTE
-------------------
argv[0] is the Hercules command ("exec").
argv[1] is the optional execution mode ("sub" or "cmd").
argv[2] (or argv[1] if the optional mode wasn't specified)
is the name of the Rexx script to be executed.
argv[3] (or argv[2] if the optional mode wasn't specified)
through "argv[argc-1]" are the script's arguments.
When the script is called in subroutine mode, it is passed
multiple arguments, so our job is easy: we simply call the
"ExecSub" function passing it argv[3] (or argv[2]) through
argv[argc-1].
*******************
** IMPORTANT! **
*******************
When the script is called in command mode however, then we
call "ExecCmd" and pass it just a single argument: the re-
mainder of the unparsed command-line immediately following
the script name, passed as a single long string.
This means we need to pass it a pointer to the above "cmdline"
argument that was passed to us, but indexed to where argv[2]
(or argv[1]) begins, i.e. past the command name ("exec") and
optional execution mode ("sub" or "cmd") and script name arg-
uments, to where the argument immediately following the script
name actually begins within the "cmdline" string.
This means the "argv" array passed to us MUST correspond to
a proper Hercules command parsing of the "cmdline" string.
That is to say, you should NEVER call the "exec_cmd" function
directly yourself. Instead, you should call "panel_command"
and let it call this function as expected.
Refer to the "(mode == MODE_COMMAND)" statement block further
below for details regarding this requirement.
-----------------------------------------------------------------*/
/* Make sure the Rexx Package is enabled for use */
if (IsDisabled())
{
if (Enable( NULL ) != 0)
return -1;
}
/* At the very minimum we need to have a script name */
if (argc < 2)
{
// "REXX(%s) Exec: script name not specified"
WRMSG( HHC17505, "E", PackageName );
return -1;
}
/* If they specified an execution mode on their 'exec' command,
then after honoring it, remove it from the arguments array
(but KEEP argv[0], the
*/
mode = RexxMode;
iarg = 1;
if (0
|| HAVKEYW( "cmd" ) || HAVKEYW( "com" )
|| HAVKEYW( "-cmd" ) || HAVKEYW( "-com" )
|| HAVKEYW( "/cmd" ) || HAVKEYW( "/com" )
)
{
mode = MODE_COMMAND;
haveMode = TRUE;
}
else if (0
|| HAVKEYW( "sub" )
|| HAVKEYW( "-sub" )
|| HAVKEYW( "/sub" )
)
{
mode = MODE_SUBROUTINE;
haveMode = TRUE;
}
/* Remove the script execution mode option from the command line */
if (haveMode)
{
for (i=2; i < argc; i++)
argv[ i-1 ] = argv[ i ];
--argc;
}
/* AT THIS POINT...
argv[0] is still the Hercules command ("exec").
argv[1] is now the name of the Rexx script to be executed.
argv[2] to argv[argc-1] are now the script's arguments (if any)
*/
/* Check for asynchronous execution option ("&" as last arg) */
if (argc > 2 && strcmp( argv[ argc-1 ], "&" ) == 0)
{
argc--; // (drop last argument)
async = TRUE;
}
/* Save the name of the script they want to execute */
pscriptname = argv[1];
STRLCPY( scriptname, pscriptname );
/* If the resolver option isn't enabled, use their name as-is */
found = TRUE;
if (!useResolver)
goto resolved;
found = FALSE;
/* Otherwise we need to resolve the script name's full path.
As the first attempt, use the scriptname exactly as they
specified it.
*/
rc = stat( pscriptname, &fstat );
if (rc == 0 && S_ISREG( fstat.st_mode ))
{
found = TRUE;
goto resolved;
}
/* If they specified a specific location then
it obviously doesn't exist there.
*/
if (strcmp( basename( scriptname ), pscriptname ) != 0)
goto resolved;
/* Otherwise it's only a scriptname without a path (and maybe
without any extension either). Search the defined Rexx paths
using no extension followed by each of the defined extensions.
The first entry in our extensions array is an empty string
to cause us to always try their name without any extension
first, followed by all the remaining defined extensions.
*/
if (RexxPathCount)
{
/* Check each directory in this list... */
for (i = 0; i < RexxPathCount; i++)
{
/* Does this directory exist? */
rc = stat( RexxPathArray[ i ], &fstat );
if (rc != 0 || !S_ISDIR( fstat.st_mode ))
continue;
/* This directory does exist. Check to see if the script
is in this directory by trying each defined extension.
*/
for (n = 0; n < ExtensionsCount; n++)
{
/* Build full path scriptname with this extension */
MSGBUF( scriptname, PATHFORMAT, RexxPathArray[ i ],
pscriptname, ExtensionsArray[ n ]);
/* Is it in this directory with this extension? */
rc = stat( scriptname, &fstat );
if (rc == 0 && S_ISREG( fstat.st_mode ))
{
found = TRUE;
goto resolved;
}
}
}
}
if (useSysPath && SysPathCount)
{
/* Check each directory in this list... */
for (i = 0; i < SysPathCount; i++)
{
/* Does this directory exist? */
rc = stat( SysPathArray[ i ], &fstat );
if (rc != 0 || !S_ISDIR( fstat.st_mode ))
continue;
/* This directory does exist. Check to see if the script
is in this directory by trying each defined extension.
*/
for (n = 0; n < ExtensionsCount; n++)
{
/* Build full path scriptname with this extension */
MSGBUF( scriptname, PATHFORMAT, SysPathArray[ i ],
pscriptname, ExtensionsArray[ n ]);
/* Is it in this directory with this extension? */
rc = stat( scriptname, &fstat );
if (rc == 0 && S_ISREG( fstat.st_mode ))
{
found = TRUE;
goto resolved;
}
}
}
}
resolved:
if (!found)
{
// "REXX(%s) Exec: script \"%s\" not found"
WRMSG( HHC17506, "E", PackageName, pscriptname );
return -1;
}
/* Call the proper Rexx package function to execute their script.
AT THIS POINT...
argv[0] is still the Hercules command ("exec").
argv[1] is still the name of the Rexx script to be executed.
argv[2] to argv[argc-1] are still the script's arguments.
*/
if (mode == MODE_SUBROUTINE)
{
/* The arguments passed to a Rexx script called in subroutine
mode is the array of individual arguments (if any) following
the script name. This is easy since they've already been
parsed for us by the Hercules's command-line parser, so we
just pass them to the script as-is.
*/
int num_sub_args; // (number of script arguments)
char** sub_args_array; // (array of script arguments)
if (argc > 2)
{
num_sub_args = (argc - 2);
sub_args_array = &argv[ 2 ];
}
else
{
num_sub_args = 0;
sub_args_array = NULL;
}
if (!async)
{
/* Execute Rexx script synchronously */
rc = ExecSub( scriptname, num_sub_args, sub_args_array );
}
else
{
/* Execute Rexx script asynchronously */
rc = exec_async( scriptname, mode, sub_args_array, num_sub_args );
}
}
else // (mode == MODE_COMMAND)
{
/*-------------------------------------------------------------
PROGRAMMING NOTE
The only argument to a Rexx script called in command mode
is the remainder of the command-line arguments immediately
following the script name, passed as a single string, which
is more difficult. We can't simply build a string from the
individual pre-parsed arguments passed to us as the result
may not exactly match what was entered on the command line
(e.g. we cannot presume multiple blanks between arguments
are not important to the Rexx script being called). Thus
we instead simply point to the original unmodified Hercules
"cmdline" string indexed to where the script's argument(s)
string actually begins.
-------------------------------------------------------------*/
char* script_argstring; // (script's only argument)
if (argc < 3 || !cmdline)
script_argstring = NULL; // (no arguments)
else
{
char* execcmd; // (herc cmdline "exec" token)
char* script_arg; // (arg following script name)
size_t script_arg_idx; // (index to script_argstring)
execcmd = argv[0];
script_arg = argv[2];
script_arg_idx = (script_arg - execcmd);
/* Check we were called properly: 4+1+len+1="exec name " */
if (0
|| script_arg_idx < (4 + 1 + strlen( scriptname ) + 1)
|| script_arg_idx >= strlen( cmdline )
)
{
// "REXX(%s) Malformed call to 'exec_cmd' function"
WRMSG( HHC17513, "E", PackageName );
return -1;
}
/* Left-trim the cmdline so its first char is argv[0][0] */
cmdline = LTRIM( cmdline );
/* If the first argument contains any blanks then it was
originally surrounded by quotes which were removed by
Hercules command-line parsing, so we need to backup by
one position to point to the double quote where that
argument actually began.
*/
if (strchr( script_arg, ' ' ))
--script_arg_idx;
script_argstring = cmdline + script_arg_idx;
}
if (!async)
{
/* Execute Rexx script synchronously */
rc = ExecCmd( scriptname, script_argstring );
}
else
{
/* Remove the " &&" async argument from script's cmdline.
PLEASE NOTE that we *MUST* keep any resulting trailing
blanks that might result since for command mode scripts
we mustn't presume they aren't significant! (Refer to
the PROGRAMMING NOTE further above for justification!)
*/
if (script_argstring)
script_argstring[ strlen( script_argstring ) - 3 ] = 0;
/* Execute Rexx script asynchronously */
rc = exec_async( scriptname, mode, script_argstring, 0 );
}
}
return rc;
} // end exec_cmd
#endif // HAVE_REXX