/****************************************************************************** * REXX Utility Functions - Extended (RXUTILEX.DLL) * * (C) 2011, 2019 Alex Taylor. * * * * LICENSE: * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions are * * met: * * * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * * * 3. The name of the author may not be used to endorse or promote products * * derived from this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * * POSSIBILITY OF SUCH DAMAGE. * * * ******************************************************************************/ // Uncomment to use DosQProcStatus() instead of DosQuerySysState(). // -- This was mostly put in place for early testing to see if either function // was more/less reliable than the other. In practice, DosQuerySysState() // should probably be used. // #define USE_DQPS // Uncomment to use legacy C style locale data instead of the OS/2 ULS library // #define LEGACY_C_LOCALE #define INCL_WINATOM #define INCL_WINCLIPBOARD #define INCL_WINERRORS #define INCL_WINSYS #define INCL_DOS #define INCL_DOSDEVIOCTL #define INCL_DOSERRORS #define INCL_DOSMISC #define INCL_DOSMODULEMGR #define INCL_DOSNMPIPES #define INCL_DOSPROCESS #define INCL_DOSPROFILE #define INCL_LONGLONG #ifndef OS2_INCLUDED #include #endif #include #include #include #include #include #include #ifdef LEGACY_C_LOCALE #include #include #else #include #include #endif #define INCL_RXSHV #define INCL_RXFUNC #include #include "shfuncs.h" #pragma import( DosGetPrty, "DosGetPrty", "DOSCALL1", 9 ) USHORT APIENTRY16 DosGetPrty( USHORT usScope, PUSHORT pusPriority, USHORT pid ); #ifdef USE_DQPS #pragma import( DosQProcStatus, "DosQProcStatus", "DOSCALL1", 154 ) USHORT APIENTRY16 DosQProcStatus( PVOID pBuf, USHORT cbBuf ); #endif // CONSTANTS #define SZ_LIBRARY_NAME "RXUTILEX" // Name of this library //#define SZ_ERROR_NAME "SYS2ERR" // REXX variable used to store error codes - must be defined in Makefile #define SZ_VERSION "0.1.7" // Current version of this library // Maximum string lengths... #ifdef NO_SHARED_SOURCE #define US_COMPOUND_MAXZ 250 // ...of a compound variable #define US_ERRSTR_MAXZ 250 // ...of an error string static const char *PSZ_ZERO = "0"; static const char *PSZ_ONE = "1"; #endif #define US_INTEGER_MAXZ 12 // ...of a 32-bit integer string #define US_LONGLONG_MAXZ 21 // ...of a 64-bit integer string #define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem #define US_PIDSTR_MAXZ ( CCHMAXPATH + 100 ) // ...of a process information string #define US_TIMESTR_MAXZ 256 // ...of a formatted time string #define US_NUMSTR_MAXZ 32 // ...of a formatted number string #define US_PIPESTATUS_MAXZ 128 // ...of a pipe status string #define US_DISKINFO_MAXZ 128 // ...of a disk information string #define US_FRACSTR_MAXZ 21 // ...of a fraction string #define UL_SSBUFSIZE 0xFFFF // Buffer size for the DosQuerySysState() data // Time string formats #define FL_TIME_DEFAULT 0 #define FL_TIME_ISO8601 1 #define FL_TIME_LOCALE 2 // List of functions to be registered by Sys2LoadFuncs or dropped by Sys2DropFuncs // Drop list starts at index 0, load list starts at index 1 static PSZ RxFunctionTbl[] = { "Sys2LoadFuncs", // Drop only 2015-05-06 SHL "Sys2DropFuncs", "Sys2GetClipboardText", "Sys2PutClipboardText", "Sys2QueryProcess", "Sys2QueryProcessList", "Sys2KillProcess", "Sys2QueryDriveInfo", "Sys2QueryForegroundProcess", "Sys2QueryPhysicalMemory", "Sys2FormatNumber", "Sys2FormatTime", "Sys2GetEpochTime", "Sys2ReplaceModule", "Sys2LocateDLL", "Sys2CreateNamedPipe", "Sys2ConnectNamedPipe", "Sys2DisconnectNamedPipe", "Sys2CheckNamedPipe", "Sys2Open", "Sys2Close", "Sys2Seek", "Sys2SetSize", "Sys2BytesRemaining", "Sys2Read", "Sys2ReadLine", "Sys2SyncBuffer", "Sys2Write", "Sys2QuerySysValue", "Sys2Exec", "Sys2Version" }; // FUNCTION DECLARATIONS // Exported REXX functions RexxFunctionHandler Sys2LoadFuncs; RexxFunctionHandler Sys2DropFuncs; RexxFunctionHandler Sys2Version; RexxFunctionHandler Sys2FormatNumber; RexxFunctionHandler Sys2FormatTime; RexxFunctionHandler Sys2GetEpochTime; RexxFunctionHandler Sys2GetClipboardText; RexxFunctionHandler Sys2PutClipboardText; RexxFunctionHandler Sys2QueryProcess; RexxFunctionHandler Sys2QueryProcessList; RexxFunctionHandler Sys2KillProcess; RexxFunctionHandler Sys2QueryForegroundProcess; RexxFunctionHandler Sys2Exec; RexxFunctionHandler Sys2QueryDriveInfo; RexxFunctionHandler Sys2QueryPhysicalMemory; RexxFunctionHandler Sys2QuerySysValue; RexxFunctionHandler Sys2LocateDLL; RexxFunctionHandler Sys2ReplaceModule; RexxFunctionHandler Sys2CreateNamedPipe; RexxFunctionHandler Sys2ConnectNamedPipe; RexxFunctionHandler Sys2DisconnectNamedPipe; RexxFunctionHandler Sys2CheckNamedPipe; RexxFunctionHandler Sys2Open; RexxFunctionHandler Sys2Close; RexxFunctionHandler Sys2Seek; RexxFunctionHandler Sys2Read; RexxFunctionHandler Sys2Write; RexxFunctionHandler Sys2SyncBuffer; RexxFunctionHandler Sys2SetSize; // Private internal functions ULONG GetProcess( PCSZ pszProgram, PSZ pszFullName, PULONG pulPID, PULONG pulPPID, PULONG pulType, PUSHORT pusPriority, PULONG pulCPU ); // 2016-02-20 SHL #ifndef LEGACY_C_LOCALE int GetLocaleString( PSZ *ppszItem, LocaleItem item ); void GroupNumber( PSZ buf, ULONGLONG val, PSZ sep ); #endif #ifdef NO_SHARED_SOURCE BOOL SaveResultString( PRXSTRING prsResult, PCSZ pchBytes, ULONG ulBytes ); // 2016-02-20 SHL BOOL WriteStemElement( PCSZ pszStem, ULONG ulIndex, PCSZ pszValue ); // 2016-02-20 SHL void WriteErrorCode( ULONG ulError, PCSZ pszContext ); // 2016-02-20 SHL #endif // MACROS #define TIME_SECONDS( timeval ) ( timeval / 32 ) #define TIME_HUNDREDTHS( timeval ) (( timeval % 32 ) * 100 / 32 ) /* ------------------------------------------------------------------------- * * Sys2LoadFuncs * * * * Register all Sys2* REXX functions (except this one, obviously). * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: "" * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2LoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { int entries, i; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); entries = sizeof(RxFunctionTbl) / sizeof(PSZ); // 2015-05-06 SHL No need to load self (i.e. Sys2LoadFuncs), but do want to drop self for ( i = 1; i < entries; i++ ) RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] ); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2DropFuncs * * * * Deregister all Sys2* REXX functions. * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: "" * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2DropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { int entries, i; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); entries = sizeof(RxFunctionTbl) / sizeof(PSZ); for ( i = 0; i < entries; i++ ) RexxDeregisterFunction( RxFunctionTbl[i] ); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Version * * * * Returns the current library version. * * * * REXX ARGUMENTS: None * * REXX RETURN VALUE: Current version in the form "major.minor.refresh" * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Version( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR szVersion[ 12 ]; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc > 0 ) return ( 40 ); sprintf( szVersion, "%s", SZ_VERSION ); SaveResultString( prsResult, szVersion, strlen(szVersion) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2PutClipboardText * * * * Write a string to the clipboard in plain-text format. Specifying either * * no value or an empty string in the first argument will simply clear the * * clipboard of CF_TEXT data. * * * * REXX ARGUMENTS: * * 1. String to be written to the clipboard (DEFAULT: "") * * 2. Flag indicating whether other clipboard formats should be cleared: * * Y = yes, call WinEmptyClipbrd() before writing text (DEFAULT) * * N = no, leave (non-CF_TEXT) clipboard data untouched * * * * REXX RETURN VALUE: 1 on success, 0 on failure * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2PutClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszShareMem; // text in clipboard ULONG ulRC = 0, // return code ulBytes = 0, // size of input string ulPType = 0; // process-type flag BOOL fEmptyCB = TRUE, // call WinEmptyClipbrd() first? fHabTerm = TRUE; // terminate HAB ourselves? HAB hab; // anchor-block handle (for Win*) HMQ hmq; // message-queue handle PPIB ppib; // process information block PTIB ptib; // thread information block // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the input string) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // The second argument is optional, but must be correct if specified if ( argc >= 2 ) { // second argument: flag to clear clipboard (Y/N, but also accept 0/1) if ( RXVALIDSTRING(argv[1]) ) { strupr( argv[1].strptr ); if ( strcspn(argv[1].strptr, "YN01") > 0 ) return ( 40 ); switch ( argv[1].strptr[0] ) { case 'N': case '0': fEmptyCB = FALSE; break; case 'Y': case '1': default : fEmptyCB = TRUE; break; } } else fEmptyCB = TRUE; } // Initialize the PM API DosGetInfoBlocks( &ptib, &ppib ); ulPType = ppib->pib_ultype; ppib->pib_ultype = 3; // Morph to PM hab = WinInitialize( 0 ); if ( !hab ) { fHabTerm = FALSE; hab = 1; } /* Try to create a message-queue if one doesn't exist. We don't need to * check the result, because it could fail if a message queue already exists * (in the calling process), which is also OK. */ hmq = WinCreateMsgQueue( hab, 0 ); // 2016-02-20 SHL Sync return values with docs // Place the string on the clipboard as CF_TEXT ulRC = WinOpenClipbrd( hab ); if ( ulRC ) { if ( fEmptyCB ) WinEmptyClipbrd( hab ); ulBytes = argv[0].strlength + 1; ulRC = DosAllocSharedMem( (PVOID) &pszShareMem, NULL, ulBytes, PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE ); if ( ulRC == 0 ) { memset( pszShareMem, 0, ulBytes ); strncpy( pszShareMem, argv[0].strptr, ulBytes - 1 ); if ( ! WinSetClipbrdData( hab, (ULONG) pszShareMem, CF_TEXT, CFI_POINTER ) ) { WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinSetClipbrdData" ); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } else SaveResultString( prsResult, PSZ_ONE, 1 ); // Success - 2016-02-20 SHL } else { WriteErrorCode( ulRC, "DosAllocSharedMem"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } WinCloseClipbrd( hab ); } else { // 2016-02-20 SHL Report PM error code WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinOpenClipbrd" ); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq ); if ( fHabTerm ) WinTerminate( hab ); ppib->pib_ultype = ulPType; // Restore return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2GetClipboardText * * * * Retrieve a plain-text string from the clipboard if one is available. * * * * REXX ARGUMENTS: * * None. * * * * REXX RETURN VALUE: The retrieved clipboard string or "" if fails. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2GetClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszClipText, // pointer to clipboard data pszLocalText; // our copy of the data (to return) ULONG ulRC = 0, // return code ulBytes = 0, // size in bytes of output string ulPType = 0; // process-type flag BOOL fHabTerm = TRUE; // terminate HAB ourselves? HAB hab; // anchor-block handle (for Win*) HMQ hmq; // message-queue handle PPIB ppib; // process information block PTIB ptib; // thread information block // Reset the error indicator WriteErrorCode( 0, NULL ); // Initialize the PM API DosGetInfoBlocks( &ptib, &ppib ); ulPType = ppib->pib_ultype; ppib->pib_ultype = 3; hab = WinInitialize( 0 ); if ( !hab ) { fHabTerm = FALSE; hab = 1; } /* Note: A message-queue must exist before we can access the clipboard. We * don't actually use the returned value. In fact, we don't even * verify it, because it could be NULLHANDLE if this function was * called from a PM process (e.g. VX-REXX) - in which case, a message * queue should already exist, and we can proceed anyway. */ hmq = WinCreateMsgQueue( hab, 0 ); // Open the clipboard ulRC = WinOpenClipbrd( hab ); if ( ulRC ) { // Read plain text from the clipboard, if available if (( pszClipText = (PSZ) WinQueryClipbrdData( hab, CF_TEXT ) ) != NULL ) { ulBytes = strlen(pszClipText) + 1; if ( ( pszLocalText = (PSZ) malloc( ulBytes ) ) != NULL ) { memset( pszLocalText, 0, ulBytes ); strncpy( pszLocalText, pszClipText, ulBytes - 1 ); SaveResultString( prsResult, pszLocalText, ulBytes - 1 ); // 2016-02-20 SHL free( pszLocalText ); } else { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL } } else { // Either no text exists, or clipboard is not readable SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL } WinCloseClipbrd( hab ); } else { // 2016-02-20 SHL Report PM error code WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinOpenClipbrd" ); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL } if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq ); if ( fHabTerm ) WinTerminate( hab ); ppib->pib_ultype = ulPType; return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QueryProcess * * * * Queries information about the specified process. * * * * REXX ARGUMENTS: * * 1. The process identifier (program name or process ID) (REQUIRED) * * 2. Flag indicicating the identifier type: * * 'P': decimal process ID * * 'H': hexadecimal process ID * * 'N': executable program name (with or without extension) (DEFAULT) * * * * REXX RETURN VALUE: * * A string of the format * * pid parent-pid process-type priority cpu-time executable-name * * "priority" is in hexadecimal notation, all other numbers are decimal. * * "" is returned if the process was not found or if an internal error * * occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2QueryProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszProcName; // Requested process name UCHAR szFullName[ CCHMAXPATH ] = {0}, // Fully-qualified name szReturn[ US_PIDSTR_MAXZ ] = {0}; // Buffer for return value ULONG ulPID = 0, // Process ID ulPPID = 0, // Parent process ID ulType = 0, // Process type ulTime = 0; // Process CPU time USHORT usPrty = 0; // Process priority APIRET rc; // API return code // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the input string) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // Parse the ID type flag if ( argc >= 2 && RXVALIDSTRING( argv[1] ) ) { strupr( argv[1].strptr ); if ( strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 ); switch ( argv[1].strptr[0] ) { case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 ); pszProcName = NULL; break; case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 ); pszProcName = NULL; break; default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszProcName == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) ); break; } } else { pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszProcName == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) ); } // See if the requested process is running and get its PID/PPID rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime ); if (( rc != NO_ERROR ) || ( ulPID == 0 )) { SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } sprintf( szReturn, "%u %u %u %04X %02u:%02u.%02u %s", ulPID, ulPPID, ulType, usPrty, TIME_SECONDS( ulTime ) / 60, TIME_SECONDS( ulTime ) % 60, TIME_HUNDREDTHS( ulTime ), szFullName ); SaveResultString( prsResult, szReturn, strlen(szReturn) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2KillProcess * * * * Terminate the (first) running process with the specified executable name * * or process-ID. * * * * REXX ARGUMENTS: * * 1. The process identifier (program name or process ID) (REQUIRED) * * 2. Flag indicicating the identifier type: * * 'P': decimal process ID * * 'H': hexadecimal process ID * * 'N': executable program name (with or without extension) (DEFAULT) * * * * REXX RETURN VALUE: 1 on success or 0 on failure. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2KillProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszProcName; // Requested process name UCHAR szFullName[ CCHMAXPATH ] = {0}; // Fully-qualified name ULONG ulPID = 0, // Process ID ulPPID = 0, // Parent process ID (not used) ulType = 0, // Process type (not used) ulTime = 0; // Process CPU time (not used) USHORT usPrty = 0; // Process priority (not used) APIRET rc; // API return code // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the input string) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // Parse the ID type flag if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) { strupr( argv[1].strptr ); if (strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 ); switch ( argv[1].strptr[0] ) { case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 ); pszProcName = NULL; break; case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 ); pszProcName = NULL; break; default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszProcName == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) ); break; } } else { pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszProcName == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) ); } if ( pszProcName ) { // Get the process PID rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime ); if (( rc != NO_ERROR ) || ( ulPID == 0 )) { free( pszProcName ); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } } // Now attempt to kill the process using DosKillProcess() rc = DosKillProcess( 1, ulPID ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosKillProcess"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } else SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL // 2016-02-20 SHL Avoid leak if ( pszProcName ) free( pszProcName ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QueryProcessList * * * * Gets the process ID of the specified executable, if it is running. * * The results will be returned in a stem variable, where stem.0 contains * * number of items, and each stem item is a string of the form: * * pid parent-pid process-type priority cpu-time executable-name * * "priority" is in hexadecimal notation, all other numbers are decimal. * * * * Notes: * * - "process-type" will be one of: * * 0 Full screen protect-mode session * * 1 Requires real mode. Dos emulation. * * 2 VIO windowable protect-mode session * * 3 Presentation Manager protect-mode session * * 4 Detached protect-mode process. * * - If "priority" is 0 then the priority class could not be determined. * * - If "executable-name" is "--" then the name could not be identified. * * * * REXX ARGUMENTS: * * 1. The name of the stem in which to return the results (REQUIRED) * * * * REXX RETURN VALUE: Number of processes found, or "" in case of error. * * ------------------------------------------------------------------------- */ ULONG Sys2QueryProcessList( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { QSPTRREC *pBuf; // Data returned by DosQProcStatus/DosQuerySysState() // 2015-04-23 SHL QSPREC *pPrec; // Pointer to process information block QSTREC *pTrec; // Pointer to thread information block CHAR szStem[ US_STEM_MAXZ ], // Buffers used for building strings ... szNumber[ US_INTEGER_MAXZ ], // ... szName[ CCHMAXPATH ], // Fully-qualified name of process szPInfo[ US_PIDSTR_MAXZ ]; // Stem item string ULONG ulCount, // Number of processes ulCPU; // Process CPU time USHORT usPriority, // Process priority class i; // Loop counter APIRET rc; // Return code // Reset the error indicator WriteErrorCode( 0, NULL ); // Do some validity checking on the arguments if (( argc != 1 ) || // Make sure we have exactly one argument... ( ! RXVALIDSTRING(argv[0]) ) || // ...which is a valid REXX string... ( RXSTRLEN(argv[0]) > US_STEM_MAXZ )) // ...and isn't too long. return ( 40 ); // Generate the stem variable name from the argument (stripping any final dot) if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--; strncpy( szStem, argv[0].strptr, RXSTRLEN(argv[0]) ); szStem[ RXSTRLEN(argv[0]) ] = '\0'; #ifdef USE_DQPS pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); #else pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); // 2015-04-23 SHL #endif if ( pBuf == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } #ifdef USE_DQPS // Get running process information using 16-bit DosQProcStatus() rc = DosQProcStatus( pBuf, UL_SSBUFSIZE ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQProcStatus"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } #else // Get running process information using 32-bit DosQuerySysState() rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQuerySysState"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } #endif // Now get the list of processes ulCount = 0; # if 1 // 2016-02-25 SHL FIXME debug bad pointer // 2016-02-26 SHL FIXME to be gone when sure this can not occur if ( (ULONG)pBuf->pProcRec < 0x10000 ) { sprintf( szName, "rxutilex#%u pBuf->pProcRec 0x%x < 0x10000", __LINE__, (ULONG)pBuf->pProcRec ); WriteErrorCode( ERROR_INVALID_ADDRESS, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif for (pPrec = pBuf->pProcRec; ; pPrec = (QSPREC *)(pPrec->pThrdRec + pPrec->cTCB) ) { # if 0 // 2015-06-19 SHL FIXME debug bad pointer // 2016-02-26 SHL FIMXE to be gone when sure no longer needed if ( (ULONG)pPrec < 0x10000 ) { sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000", __LINE__, (ULONG)pPrec ); WriteErrorCode( ERROR_INVALID_ADDRESS, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif // Check for documented end marker - RecType not QS_PROCESS if ( pPrec->RecType != QS_PROCESS ) { # if 0 // 2016-02-26 SHL FIXME debug fprintf( stderr, "* rxutilex#%u RecType != QS_PROCESS " "ulCount %u pBuf %p pPrec %p ->RecType %x ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->RecType, pPrec->pThrdRec, pPrec->pid, pPrec->ppid, pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB ); # endif break; // Must be end of list } // Check for alternate end marker - pThredRec NULL // This appears to be an undocumented end marker // Might be a defect - testing says only RecType is non-zero // 2015-06-12 SHL pThrdRec can be 0 - probably when process starting or dieing if ( (PVOID)pPrec->pThrdRec == NULL ) { # if 0 // 2016-02-26 SHL FIXME debug fprintf( stderr, "* rxutilex#%u pThrdRec NULL " "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->ppid, pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB ); # endif break; // Must be end of list } # if 1 // 2015-07-31 SHL FIXME debug bad pointer // 2016-02-26 SHL FIXME to be gone when sure can not occur if ( (ULONG)(pPrec->pThrdRec) < 0x10000 ) { sprintf( szName, "rxutilex#%u pPrec < 0x10000 " "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->ppid, pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB ); WriteErrorCode( ERROR_INVALID_ADDRESS, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif # if 1 // 2016-02-26 SHL FIXME debug bad count // 2016-02-26 SHL FIXME to be gone when sure can not occur // This is probably occurs only when pThrdRec NULL too which is already checked if ( !pPrec->cTCB ) { sprintf( szName, "rxutilex#%u cTCB 0 ulCount %u pBuf %p pPrec %p ->pid %u ->type %u ->stat %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->pid, pPrec->type, pPrec->stat ); WriteErrorCode( ERROR_INVALID_DATA, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif ulCount++; // Get the program name of each process (including path) if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR ) sprintf( szName, "--"); if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR ) usPriority = 0; // Get the CPU time of the process by querying each of its threads ulCPU = 0; pTrec = pPrec->pThrdRec; for ( i = 0; i < pPrec->cTCB; i++ ) { ulCPU += ( pTrec->systime + pTrec->usertime ); pTrec++; } // Now generate the stem item with all of this information sprintf( szPInfo, "%u %u %u %04X %02u:%02u.%02u %s", pPrec->pid, // PID pPrec->ppid, // Parent PID pPrec->type, // Process type usPriority, // Priority class TIME_SECONDS( ulCPU ) / 60, // CPU time (hours) TIME_SECONDS( ulCPU ) % 60, // CPU time (minutes) TIME_HUNDREDTHS( ulCPU ), // CPU time (seconds) szName ); // Executable name & path WriteStemElement( szStem, ulCount, szPInfo ); # if 0 // 2015-07-31 SHL FIXME debug bad count // 2016-02-26 SHL FIXME to be gone when sure no longer needed if ( !pPrec->cTCB ) { sprintf( szName, "rxutilex#%u: cTCB 0 " "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid ); WriteErrorCode( ERROR_INVALID_DATA, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif # if 0 // 2015-07-31 SHL FIXME debug bad pointer // 2016-02-26 SHL FIXME to be gone when sure no longer needed if ( (ULONG)pPrec->pThrdRec < 0x10000 ) { sprintf( szName, "rxutilex#%u: pBuf->pThrdRec < 0x10000 " "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->cTCB %u\n", __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->cTCB ); WriteErrorCode( rc, szName ); SaveResultString( prsResult, NULL, 0 ); free( pBuf ); return ( 0 ); } # endif } // for // Create the stem.0 element with the number of processes found sprintf( szNumber, "%d", ulCount ); WriteStemElement( szStem, 0, szNumber ); // And also return the number of processes as the REXX return string SaveResultString( prsResult, szNumber, strlen(szNumber) ); // 2016-02-20 SHL free( pBuf ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QueryPhysicalMemory * * * * Queries the amount of physical memory (RAM) installed in the system. * * * * REXX ARGUMENTS: None * * * * REXX RETURN VALUE: * * Integer representing the amount of installed memory, in KiB, or 0 if an * * error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2QueryPhysicalMemory( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR szMemSize[ US_INTEGER_MAXZ ]; ULONG ulMemBytes = 0, ulMemKBytes = 0; APIRET rc = 0; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have no arguments if ( argc > 0 ) return ( 40 ); // Query installed memory in bytes rc = DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &ulMemBytes, sizeof(ulMemBytes) ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQuerySysInfo"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } // Convert to binary kilobytes (any remainder is discarded) ulMemKBytes = ulMemBytes / 1024; sprintf( szMemSize, "%u", ulMemKBytes ); // Return the memory size as the REXX return string SaveResultString( prsResult, szMemSize, strlen(szMemSize) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QueryForegroundProcess * * * * Queries the PID of the current foreground process. * * * * REXX ARGUMENTS: None * * * * REXX RETURN VALUE: * * Integer representing the process ID (in decimal), or 0 if an error * * occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2QueryForegroundProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR szPID[ US_INTEGER_MAXZ ]; ULONG ulPID = 0; APIRET rc = 0; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have no arguments if ( argc > 0 ) return ( 40 ); // Query installed memory in bytes rc = DosQuerySysInfo( QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS, &ulPID, sizeof(ulPID) ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQuerySysInfo"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } sprintf( szPID, "%u", ulPID ); // Return the PID as the REXX return string SaveResultString( prsResult, szPID, strlen(szPID) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2ReplaceModule * * * * Unlocks and optionally replaces an in-use (locked) DLL or EXE. * * * * REXX ARGUMENTS: * * 1. The filespec of the module to be replaced. (REQUIRED) * * 2. The filespec of the new module to replace it with. (DEFAULT: none) * * 3. The filespec of the backup file to be created. (DEFAULT: none) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2ReplaceModule( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszOldModule = NULL, pszNewModule = NULL, pszBackup = NULL; APIRET rc = 0; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the module name) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); pszOldModule = calloc( argv[0].strlength + 1, sizeof(UCHAR) ); if ( pszOldModule == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszOldModule, argv[0].strptr, argv[0].strlength ); // Second argument: new module name (optional, but must be correct if specified) if ( argc >= 2 ) { if ( RXVALIDSTRING(argv[1]) ) { pszNewModule = calloc( argv[1].strlength + 1, sizeof(char) ); if ( pszNewModule == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszNewModule, argv[1].strptr, argv[1].strlength ); } else return ( 40 ); } // Third argument: backup filename (optional, but must be correct if specified) if ( argc >= 3 ) { if ( RXVALIDSTRING(argv[2]) ) { pszBackup = calloc( argv[2].strlength + 1, sizeof(char) ); if ( pszBackup == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszBackup, argv[2].strptr, argv[2].strlength ); } else return ( 40 ); } // Now replace the module using DosReplaceModule rc = DosReplaceModule( pszOldModule, pszNewModule, pszBackup ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosReplaceModule"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } // Return 1 on success SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2FormatNumber * * * * Format a number using locale-specific thousands separators. The input * * number may be a positive or negative integer or floating point value. It * * must not contain any separators already, and any decimal point which it * * contains must be a period (rather than any localized decimal symbol). * * The maximum value is that of a signed 64-bit integer, or a long double. * * * * REXX ARGUMENTS: * * 1. Number to be formatted. (REQUIRED) * * 2. Number of decimal places to use for floating point values * * (max 18). Ignored for integer values. (DEFAULT: 2) * * * * REXX RETURN VALUE: The formatted number, or "" on error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2FormatNumber( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR achNumber[ US_NUMSTR_MAXZ ]; // Formatted output string int iPrec; // Requested decimal precision PSZ pszSep = NULL; // Thousands separator string PSZ pszDec = NULL; // Decimal point string PSZ pszNeg = NULL; // Negative sign string #ifdef LEGACY_C_LOCALE float fVal = 0; // Input value as floating point int iVal; // Input value as integer #else LONGLONG llVal = 0; // Input value as integer long double ldVal = 0, // Input value as floating point ldFrac = 0; // Input fraction portion PSZ p, // Pointer to fraction a; // Pointer to absolute value CHAR achFrac[ US_FRACSTR_MAXZ ]; // Fraction portion as string int rc = 0; #endif // Make sure we have at least one valid argument (the input number) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); #ifdef LEGACY_C_LOCALE // Use the locale settings from the environment pszSep = nl_langinfo( THOUSEP ); if ( !pszSep || !strlen(pszSep) ) { /* If the current locale isn't known to the C runtime, use a common * known locale for the same language, if possible. */ PSZ pszLang, p; if ( DosScanEnv( "LANG", &pszLang ) == NO_ERROR && pszLang && strlen(pszLang) >= 2 ) { p = strdup( pszLang ); if ( !strnicmp( p, "en_us", 2 )) setlocale( LC_NUMERIC, "EN_US"); else if ( !strnicmp( p, "en_uk", 2 )) setlocale( LC_NUMERIC, "EN_GB"); else if ( !strnicmp( p, "de", 2 )) setlocale( LC_NUMERIC, "DE_DE"); else if ( !strnicmp( p, "es", 2 )) setlocale( LC_NUMERIC, "ES_ES"); else if ( !strnicmp( p, "fr", 2 )) setlocale( LC_NUMERIC, "FR_FR"); else if ( !strnicmp( p, "it", 2 )) setlocale( LC_NUMERIC, "IT_IT"); else if ( !strnicmp( p, "ja", 2 )) setlocale( LC_NUMERIC, "JA_JP"); /* -- it seems the VAC runtime doesn't recognize most of these... else if ( !strnicmp( p, "ar", 2 )) setlocale( LC_NUMERIC, "ar_AA"); else if ( !strnicmp( p, "be", 2 )) setlocale( LC_NUMERIC, "be_BY"); else if ( !strnicmp( p, "bg", 2 )) setlocale( LC_NUMERIC, "bg_BG"); else if ( !strnicmp( p, "be", 2 )) setlocale( LC_NUMERIC, "be_BY"); else if ( !strnicmp( p, "ca", 2 )) setlocale( LC_NUMERIC, "ca_ES"); else if ( !strnicmp( p, "cs", 2 )) setlocale( LC_NUMERIC, "cs_CZ"); else if ( !strnicmp( p, "da", 2 )) setlocale( LC_NUMERIC, "da_DK"); else if ( !strnicmp( p, "de", 2 )) setlocale( LC_NUMERIC, "de_DE"); else if ( !strnicmp( p, "el", 2 )) setlocale( LC_NUMERIC, "el_GR"); else if ( !strnicmp( p, "es", 2 )) setlocale( LC_NUMERIC, "es_ES"); else if ( !strnicmp( p, "fi", 2 )) setlocale( LC_NUMERIC, "fi_FI"); else if ( !strnicmp( p, "fr", 2 )) setlocale( LC_NUMERIC, "fr_FR"); else if ( !strnicmp( p, "hr", 2 )) setlocale( LC_NUMERIC, "hr_HR"); else if ( !strnicmp( p, "hu", 2 )) setlocale( LC_NUMERIC, "hu_HU"); else if ( !strnicmp( p, "is", 2 )) setlocale( LC_NUMERIC, "is_IS"); else if ( !strnicmp( p, "it", 2 )) setlocale( LC_NUMERIC, "it_IT"); else if ( !strnicmp( p, "iw", 2 )) setlocale( LC_NUMERIC, "iw_IL"); else if ( !strnicmp( p, "ja", 2 )) setlocale( LC_NUMERIC, "ja_JP"); else if ( !strnicmp( p, "ko", 2 )) setlocale( LC_NUMERIC, "ko_KR"); else if ( !strnicmp( p, "mk", 2 )) setlocale( LC_NUMERIC, "mk_MK"); else if ( !strnicmp( p, "nl", 2 )) setlocale( LC_NUMERIC, "nl_NL"); else if ( !strnicmp( p, "no", 2 )) setlocale( LC_NUMERIC, "no_NO"); else if ( !strnicmp( p, "pl", 2 )) setlocale( LC_NUMERIC, "pl_PL"); else if ( !strnicmp( p, "pt", 2 )) setlocale( LC_NUMERIC, "pt_PT"); else if ( !strnicmp( p, "ro", 2 )) setlocale( LC_NUMERIC, "ro_RO"); else if ( !strnicmp( p, "ru", 2 )) setlocale( LC_NUMERIC, "ru_RU"); else if ( !strnicmp( p, "sh", 2 )) setlocale( LC_NUMERIC, "sh_SP"); else if ( !strnicmp( p, "sk", 2 )) setlocale( LC_NUMERIC, "sk_SK"); else if ( !strnicmp( p, "sl", 2 )) setlocale( LC_NUMERIC, "sl_SI"); else if ( !strnicmp( p, "sq", 2 )) setlocale( LC_NUMERIC, "sq_AL"); else if ( !strnicmp( p, "sv", 2 )) setlocale( LC_NUMERIC, "sv_SE"); else if ( !strnicmp( p, "th", 2 )) setlocale( LC_NUMERIC, "th_TH"); else if ( !strnicmp( p, "tr", 2 )) setlocale( LC_NUMERIC, "tr_TR"); else if ( !strnicmp( p, "uk", 2 )) setlocale( LC_NUMERIC, "uk_UA"); else if ( !strnicmp( p, "zh", 2 )) setlocale( LC_NUMERIC, "zh_TW"); -- */ else setlocale( LC_NUMERIC, "EN_US"); free(p); } else setlocale( LC_NUMERIC, "en_us"); } else setlocale( LC_NUMERIC, ""); // Check for a decimal place and treat as float or integer accordingly if ( strchr( argv[0].strptr, '.') != NULL ) { if (( sscanf( argv[0].strptr, "%f", &fVal )) != 1 ) return ( 40 ); if ( argc >= 2 && ( RXVALIDSTRING(argv[1]) ) && (( sscanf( argv[1].strptr, "%d", &iPrec )) == 1 )) { // Use user-specified precision sprintf( achNumber, "%'.*f", iPrec, fVal ); } else sprintf( achNumber, "%'.2f", fVal ); } else { if (( sscanf( argv[0].strptr, "%d", &iVal )) != 1 ) return ( 40 ); sprintf( achNumber, "%'d", iVal ); } // Return the formatted number SaveResultString( prsResult, achNumber, strlen(achNumber) ); #else rc = GetLocaleString( &pszSep, LOCI_sThousand ); if ( rc ) { // GetLocaleString has already set the error indicator SaveResultString( prsResult, NULL, 0 ); goto finish; } rc = GetLocaleString( &pszDec, LOCI_sDecimal ); if ( rc ) { // GetLocaleString has already set the error indicator SaveResultString( prsResult, NULL, 0 ); goto finish; } rc = GetLocaleString( &pszNeg, LOCI_sNegativeSign ); if ( rc ) { // GetLocaleString has already set the error indicator SaveResultString( prsResult, NULL, 0 ); goto finish; } // Check for negative value //if ( argv[0].strptr == '-') bNeg = TRUE; // Check for a decimal place and treat as float or integer accordingly if ( strchr( argv[0].strptr, '.') != NULL ) { if (( sscanf( argv[0].strptr, "%Lf", &ldVal )) != 1 ) return ( 40 ); llVal = ldVal; ldFrac = ldVal - llVal; if ( argc >= 2 && ( RXVALIDSTRING(argv[1]) ) && (( sscanf( argv[1].strptr, "%d", &iPrec )) == 1 )) { // Use user-specified precision if ( iPrec > 18 ) iPrec = 18; sprintf( achFrac, "%.*Lf", iPrec, ldFrac ); } else sprintf( achFrac, "%.2Lf", ldFrac ); // Format the integer part a = achNumber; if ( llVal < 0 ) { sprintf( a, pszNeg ); a += strlen( pszNeg ); } GroupNumber( a, llabs( llVal ), pszSep ); // Append the fractional part if (( p = strchr( achFrac, '.')) != NULL ) { strncat( achNumber, pszDec, US_NUMSTR_MAXZ - 1 ); strncat( achNumber, p+1, US_NUMSTR_MAXZ - 1 ); } } else { if (( sscanf( argv[0].strptr, "%lld", &llVal )) != 1 ) return ( 40 ); a = achNumber; if ( llVal < 0 ) { sprintf( a, pszNeg ); a += strlen( pszNeg ); } GroupNumber( a, llabs( llVal ), pszSep ); } // Return the formatted number SaveResultString( prsResult, achNumber, strlen(achNumber) ); finish: if ( pszSep ) free( pszSep ); if ( pszDec ) free( pszDec ); if ( pszNeg ) free( pszNeg ); #endif return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2FormatTime * * * * Convert a number of seconds from the epoch (1970-01-01 0:00:00 UTC) into * * a formatted date and time string. * * * * REXX ARGUMENTS: * * 1. Number of seconds (a positive integer) to be converted. (REQUIRED) * * 2. Format type, one of: * * D = return in the form 'yyyy-mm-dd hh:mm:ss (w)' where w * * represents the weekday (0-6 where 0=Sunday) (DEFAULT) * * I = return in ISO8601 combined form 'yyyy-mm-ddThh:mm:ss[Z]' * * L = return in the form 'day month year (weekday) time' where month * * and weekday are language-dependent abbreviations * * Note: With D and I, time is returned in 24-hour format; L may vary. * * 3. TZ conversion flag (indicates whether to convert to UTC from local * * time), one of: * * U = return in Coordinated Universal Time * * L = convert to local time using the current TZ (DEFAULT) * * * * REXX RETURN VALUE: The formatted time string, or "" on error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2FormatTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { UCHAR szFormat[ US_TIMESTR_MAXZ ] = {0}, // strftime() format specifier szTime[ US_TIMESTR_MAXZ ] = {0}; // Formatted time string BYTE flFormat = FL_TIME_DEFAULT; // Time format flag BOOL fUTC = FALSE; // UTC/local conversion flag PSZ pszTZ, // Pointer to TZ environment var pszSetTZ; int iEpoch; // Input epoch time time_t ttSeconds; // Input timestamp (seconds) struct tm *timeptr; // Timestamp structure size_t stRC; // return code from strftime() // Reset the error indicator WriteErrorCode( 0, NULL ); // All arguments are optional but must be correct if specified if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) { // first argument: epoch time value if (( sscanf( argv[0].strptr, "%d", &iEpoch )) != 1 ) return ( 40 ); ttSeconds = (time_t) iEpoch; } if ( argc >= 2 ) { // second argument: format flag if ( RXVALIDSTRING(argv[1]) ) { strupr( argv[1].strptr ); if ( strcspn(argv[1].strptr, "DIL") > 0 ) return ( 40 ); switch ( argv[1].strptr[0] ) { case 'I': flFormat = FL_TIME_ISO8601; break; case 'L': flFormat = FL_TIME_LOCALE; break; default : flFormat = FL_TIME_DEFAULT; break; } } } if ( argc >= 3 ) { // third argument: conversion flag if ( RXVALIDSTRING(argv[2]) ) { strupr( argv[2].strptr ); if ( strcspn(argv[2].strptr, "UL") > 0 ) return ( 40 ); switch ( argv[2].strptr[0] ) { case 'U': fUTC = TRUE; break; default : fUTC = FALSE; break; } } } /* These next 4 lines really shouldn't be necessary, but without them * getenv() and (apparently) tzset() may see the value of TZ as NULL * if the environment variable was changed in the REXX script. */ DosScanEnv("TZ", &pszTZ ); pszSetTZ = (PSZ) malloc( strlen(pszTZ) + 5 ); if ( pszSetTZ ) { sprintf( pszSetTZ, "TZ=%s", pszTZ ); putenv( pszSetTZ ); } // Use the locale and timezone settings from the environment tzset(); setlocale( LC_TIME, ""); if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) { ttSeconds = time( NULL ); if ( ttSeconds == -1 ) { WriteErrorCode( ttSeconds, "time"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL if ( pszSetTZ ) free( pszSetTZ ); return 0; } } if ( fUTC ) { timeptr = gmtime( &ttSeconds ); if ( !timeptr ) { WriteErrorCode( 1, "gmtime"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL // 2016-02-20 SHL if ( pszSetTZ ) free( pszSetTZ ); return 0; } } else { timeptr = localtime( &ttSeconds ); if ( !timeptr ) { WriteErrorCode( 1, "localtime"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL if ( pszSetTZ ) free( pszSetTZ ); return 0; } } switch ( flFormat ) { default: case FL_TIME_DEFAULT: sprintf( szFormat, "%%Y-%%m-%%d %%T (%%w)"); break; case FL_TIME_ISO8601: sprintf( szFormat, "%%Y-%%m-%%dT%%T"); if ( fUTC ) strcat( szFormat, "Z"); break; case FL_TIME_LOCALE: sprintf( szFormat, "%%e %%b %%Y (%%a) %%X"); break; } stRC = strftime( szTime, US_TIMESTR_MAXZ-1, szFormat, timeptr ); if ( stRC == NO_ERROR ) { WriteErrorCode( stRC, "strftime"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL if ( pszSetTZ ) free( pszSetTZ ); return ( 0 ); } // Return the formatted time string SaveResultString( prsResult, szTime, strlen(szTime) ); // 2016-02-20 SHL if ( pszSetTZ ) free( pszSetTZ ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2GetEpochTime * * * * Convert formatted date and time into a number of seconds (UTC) from the * * epoch (defined as 1970-01-01 0:00:00). The input time is assumed to * * refer to the current timezone as defined in the TZ environment variable. * * * * If no parameters are specified, the current system time is used. If at * * least one parameter is specified, then any missing parameter is assumed * * to be its minimum possible value. * * * * Due to limitations in time_t, dates later than 2037 are not supported; * * the IBM library seems to convert them all to January 1 1970 00:00:00 UTC. * * * * REXX ARGUMENTS: * * 1. The year (0-99 or 1970+) (value <70 is assumed to be 20xx) * * 2. The month (1-12) * * 3. The day (1-31) * * 4. Hours (0-23) * * 5. Minutes (0-59) * * 6. Seconds (0-61) * * * * REXX RETURN VALUE: The number of seconds since the epoch, or 0 on error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2GetEpochTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { ULONG ulYear = 1970, // Input year ulMonth = 1, // Input month ulDay = 1, // Input day ulHour = 0, // Input hours ulMin = 0, // Input minutes ulSec = 0; // Input seconds BOOL fYear = FALSE, // Year parameter specified? fMonth = FALSE, // Month parameter specified? fDay = FALSE, // Day parameter specified? fHour = FALSE, // Hours parameter specified? fMin = FALSE, // Minutes parameter specified? fSec = FALSE; // Seconds parameter specified? //SHORT sDST = 0; // Input time is DST? time_t timeval; // Calculated epoch time struct tm tsTime = {0}; // Time structure for mktime() UCHAR szEpochTime[ US_INTEGER_MAXZ ]; // Output string PSZ pszTZ, pszSetTZ; // Reset the error indicator WriteErrorCode( 0, NULL ); // Parse the various time items if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) { if (( sscanf( argv[0].strptr, "%u", &ulYear )) != 1 ) return ( 40 ); if ( ulYear < 100 ) { ulYear += (ulYear < 70) ? 2000 : 1900; } if ( ulYear < 1970 ) return ( 40 ); fYear = TRUE; } if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) { if (( sscanf( argv[1].strptr, "%u", &ulMonth )) != 1 ) return ( 40 ); if ( ulMonth < 1 || ulMonth > 12 ) return ( 40 ); fMonth = TRUE; } if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) { if (( sscanf( argv[2].strptr, "%u", &ulDay )) != 1 ) return ( 40 ); if ( ulDay < 1 || ulDay > 31 ) return ( 40 ); fDay = TRUE; } if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) { if (( sscanf( argv[3].strptr, "%u", &ulHour )) != 1 ) return ( 40 ); if ( ulHour > 23 ) return ( 40 ); fHour = TRUE; } if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) { if (( sscanf( argv[4].strptr, "%u", &ulMin )) != 1 ) return ( 40 ); if ( ulMin > 59 ) return ( 40 ); fMin = TRUE; } if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) { if (( sscanf( argv[5].strptr, "%u", &ulSec )) != 1 ) return ( 40 ); if ( ulSec > 61 ) return ( 40 ); fSec = TRUE; } if ( argc >= 7 ) return ( 40 ); /* // Parse the conversion flag if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) { strupr( argv[6].strptr ); if ( strcspn(argv[6].strptr, "SD") > 0 ) return ( 40 ); switch ( argv[6].strptr[0] ) { case 'S': sDST = 0; break; case 'D': sDST = 1; break; default : sDST = -1; break; } } */ /* These next 4 lines really shouldn't be necessary, but without them * getenv() and (apparently) tzset() may see the value of TZ as NULL * if the environment variable was changed in the REXX script. */ DosScanEnv("TZ", &pszTZ ); pszSetTZ = (PSZ) malloc( strlen(pszTZ) + 5 ); sprintf( pszSetTZ, "TZ=%s", pszTZ ); putenv( pszSetTZ ); // This seems to conflict with time() under some shells -AT tzset(); // Use the locale settings from the environment setlocale( LC_TIME, ""); if ( !fYear && !fMonth && !fDay && !fHour && !fMin && !fSec ) { timeval = time( NULL ); if ( timeval == -1 ) { WriteErrorCode( timeval, "time"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL free( pszSetTZ ); return 0; } } else { //printf("TZ=%s\n", getenv("TZ")); tsTime.tm_sec = ulSec; tsTime.tm_min = ulMin; tsTime.tm_hour = ulHour; tsTime.tm_mday = ulDay; tsTime.tm_mon = ulMonth - 1; tsTime.tm_year = ulYear - 1900; tsTime.tm_isdst = -1; timeval = mktime( &tsTime ); if ( timeval == -1 ) { WriteErrorCode( timeval, "mktime"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL free( pszSetTZ ); return 0; } } // Return the calculated time value #if __IBMC__ >= 360 || __IBMCPP__ >= 360 sprintf( szEpochTime, "%.0f", timeval ); #else sprintf( szEpochTime, "%d", timeval ); #endif SaveResultString( prsResult, szEpochTime, strlen(szEpochTime) ); // 2016-02-20 SHL free( pszSetTZ ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2LocateDLL * * * * Search for an installed or loaded DLL by module name. * * Code derived from 'whichdll' by Alessandro Cantatore (public domain). * * * * REXX ARGUMENTS: * * 1. The name of the DLL to search for. (REQUIRED) * * 2. Flag to limit search context, must be one of: * * ALL : Search for both loaded and loadable DLLs (DEFAULT) * * LOADEDONLY: Search only for currently-loaded DLLs * * Only the first letter (A/L) is significant. * * * * REXX RETURN VALUE: * * The fully-qualified path of the DLL, if found (or "" if not found). * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2LocateDLL( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HMODULE hmod; CHAR achModuleName[ CCHMAXPATH ]; BOOL bLoadedOnly = FALSE, bUnload = FALSE; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // Second argument: flag if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) { strupr( argv[1].strptr ); if ( strcspn(argv[1].strptr, "AL") > 0 ) return ( 40 ); switch ( argv[1].strptr[0] ) { case 'A': bLoadedOnly = FALSE; break; case 'L': bLoadedOnly = TRUE; break; default : return ( 40 ); } } // See if the DLL is already loaded rc = DosQueryModuleHandle( argv[0].strptr, &hmod ); if ( rc ) { // Guess not... if ( bLoadedOnly ) { // Just return SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return 0; } // Try to load it now rc = DosLoadModule( NULL, 0, argv[0].strptr, &hmod ); if ( rc ) { WriteErrorCode( rc, "DosLoadModule"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return 0; } bUnload = TRUE; } // Get the full path name of the DLL rc = DosQueryModuleName( hmod, CCHMAXPATH, achModuleName ); if ( rc ) { WriteErrorCode( rc, "DosQueryModuleName"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL if ( bUnload ) DosFreeModule( hmod ); return 0; } // Free the module if we loaded it ourselves if ( bUnload ) DosFreeModule( hmod ); // Return the full path name SaveResultString( prsResult, achModuleName, strlen(achModuleName) ); // 2016-02-20 SHL return 0; } /* ------------------------------------------------------------------------- * * Sys2CreateNamedPipe * * * * Create a named pipe with the specified name and parameters. Only byte * * mode is supported; message mode is not. * * * * REXX ARGUMENTS: * * 1. The name of the pipe, in the form "\PIPE\something". (REQUIRED) * * 2. The size of the outbound buffer, in bytes. (REQUIRED) * * 3. The size of the inbound buffer, in bytes. (REQUIRED) * * 4. The pipe's timeout value, in milliseconds. (DEFAULT: 3000) * * 5. The number of simultaneous instances of this pipe which are allowed. * * Must be between 1 and 254, or 0 indicating no limit. (DEFAULT: 1) * * 6. Pipe blocking mode, one of: * * W = WAIT mode, read and write block waiting for data. (DEFAULT) * * N = NOWAIT mode, read and write return immediately. * * 7. Pipe mode, one of: * * I = Inbound pipe (DEFAULT) * * O = Outbound pipe * * D = Duplex (inbound/outbound) pipe * * 8. Privacy/inheritance flag, one of: * * 0 = The pipe handle is inherited by child processes. (DEFAULT) * * 1 = The pipe handle is private to the current process. * * 9. Write-through flag, one of: * * 0 = Allow delayed writes (write-behind) to remote pipes. (DEFAULT) * * 1 = Force immediate writes (write-through) to remote pipes. * * * * REXX RETURN VALUE: * * A four-byte pipe handle or 0 if create fails * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2CreateNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HPIPE hp; PSZ pszNPName; LONG iLimit; ULONG ulBufOut, ulBufIn, ulTimeout = 3000, flOpen = 0, flPipe = 1; CHAR achHandle[ 9 ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least three valid arguments (pipe name and sizes) if ( argc < 3 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ) || ( !RXVALIDSTRING(argv[2]) )) return ( 40 ); // (Validate the first argument last to simplify error processing) // Second argument: pipe outbound buffer size if (( sscanf( argv[1].strptr, "%u", &ulBufOut )) != 1 ) return ( 40 ); // Third argument: pipe outbound buffer size if (( sscanf( argv[2].strptr, "%u", &ulBufIn )) != 1 ) return ( 40 ); // Fourth argument: pipe timeout value if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) { if (( sscanf( argv[3].strptr, "%u", &ulTimeout )) != 1 ) return ( 40 ); } // Fifth argument: instances limit if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) { if (( sscanf( argv[4].strptr, "%d", &iLimit )) != 1 ) return ( 40 ); if (( iLimit > 1 ) && ( iLimit < 255 )) flPipe = iLimit; else if ( !iLimit || ( iLimit == -1 )) flPipe = NP_UNLIMITED_INSTANCES; else return ( 40 ); } // Sixth argument: blocking mode if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) { strupr( argv[5].strptr ); if ( argv[5].strptr[0] == 'N' ) flPipe |= NP_NOWAIT; else if ( argv[5].strptr[0] != 'W' ) return ( 40 ); } // Seventh argument: pipe mode (direction) if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) { strupr( argv[6].strptr ); if (strcspn(argv[6].strptr, "IOD") > 0 ) return ( 40 ); switch ( argv[6].strptr[0] ) { case 'O': flOpen |= NP_ACCESS_OUTBOUND; break; case 'D': flOpen |= NP_ACCESS_DUPLEX; break; default : break; // default is 0 } } // Eighth argument: inheritance mode if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) { strupr( argv[7].strptr ); if ( argv[7].strptr[0] == '1' ) flOpen |= NP_NOINHERIT; else if ( argv[7].strptr[0] != '0' ) return ( 40 ); } // Ninth argument: write mode if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) { strupr( argv[8].strptr ); if ( argv[8].strptr[0] == '1' ) flOpen |= NP_NOWRITEBEHIND; else if ( argv[8].strptr[0] != '0' ) return ( 40 ); } // Now the first argument: pipe name pszNPName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszNPName == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszNPName, argv[0].strptr, RXSTRLEN(argv[0]) ); // All good, now create the pipe rc = DosCreateNPipe( pszNPName, &hp, flOpen, flPipe, ulBufOut, ulBufIn, ulTimeout ); if (rc) { WriteErrorCode( rc, "DosCreateNPipe"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return 0; } // Return the handle as the REXX result string sprintf( achHandle, "%8X", hp ); SaveResultString( prsResult, achHandle, strlen(achHandle) ); // 2016-02-20 SHL free( pszNPName ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2ConnectNamedPipe * * * * Start 'listening' by allowing clients to connect to a previously-created * * named pipe. * * * * REXX ARGUMENTS: * * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2ConnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HPIPE hp; ULONG ulState = 0; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Parse the handle if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 ); if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 ); // Determine the pipe mode DosQueryNPHState( hp, &ulState ); // Connect the pipe rc = DosConnectNPipe( hp ); // A non-blocking pipe returns ERROR_PIPE_NOT_CONNECTED on success if ((( ulState & NP_NOWAIT ) && ( rc != ERROR_PIPE_NOT_CONNECTED )) || ( rc != NO_ERROR )) { WriteErrorCode( rc, "DosConnectNPipe"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } // Return 1 on success SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2DisconnectNamedPipe * * * * Unlocks a named pipe after a client has closed its connection. * * * * REXX ARGUMENTS: * * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2DisconnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HPIPE hp; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Parse the handle if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 ); if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 ); // Connect the pipe rc = DosDisConnectNPipe( hp ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosDisConnectNPipe"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } // Return 1 on success SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2CheckNamedPipe * * * * Check the status of a named pipe. * * * * REXX ARGUMENTS: * * 1. The pipe handle (from Sys2CreateNamedPipe or DosOpen). (REQUIRED) * * * * REXX RETURN VALUE: * * String of the format "bytes status", where bytes is the number of bytes * * currently waiting in the pipe, and status is one of: DISCONNECTED, * * LISTENING, CONNECTED, or CLOSING or "" if API error * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2CheckNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HPIPE hp; ULONG cbActual, ulState; AVAILDATA avd; CHAR szStatus[ US_PIPESTATUS_MAXZ ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Parse the handle if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 ); if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 ); rc = DosPeekNPipe( hp, NULL, 0, &cbActual, &avd, &ulState ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosPeekNPipe"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } sprintf( szStatus, "%u ", avd.cbpipe ); switch ( ulState ) { case NP_STATE_DISCONNECTED: strncat( szStatus, "DISCONNECTED", US_PIPESTATUS_MAXZ-1 ); break; case NP_STATE_LISTENING: strncat( szStatus, "LISTENING", US_PIPESTATUS_MAXZ-1 ); break; case NP_STATE_CONNECTED: strncat( szStatus, "CONNECTED", US_PIPESTATUS_MAXZ-1 ); break; case NP_STATE_CLOSING: strncat( szStatus, "CLOSING", US_PIPESTATUS_MAXZ-1 ); break; default: strncat( szStatus, "UNKNOWN", US_PIPESTATUS_MAXZ-1 ); break; } SaveResultString( prsResult, szStatus, strlen(szStatus) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Open * * * * Wrapper to DosOpenL: open a file or stream (with >2GB support). * * Direct-DASD mode is not supported by this function, nor is setting the * * initial extended attributes. * * * * REXX ARGUMENTS: * * 1. Name of file or stream to open. (REQUIRED) * * 2. Open action flags, must be either "O" (open if exists), "R" (replace * * if exists), or nothing (fail if exists), optionally followed by "C" * * (create if file does not exist). If "C" is not specified, the * * operation will fail if the file does not exist. Note that a value * * of "" alone will therefore fail automatically. (DEFAULT: "O") * * In summary, the possible combinations are: * * O = Open only (if file exists, open it; if not, fail) * * OC= Open/create (if file exists, open it; if not, create it) * * R = Replace only (if file exists, replace it; if not, fail) * * RC= Replace/create (if file exists, replace it; if not, create it) * * C = Create only (if file exists, fail; if not, create it) * * (empty) = No-op (if file exists, fail; if not, fail) * * 3. Access mode flags, one or both of: (DEFAULT: "RW") * * R = Open file with read access. * * W = Open file with write access. * * 4. Sharing mode flags, any combination of: (DEFAULT: "W") * * R = Deny read access to other processes * * W = Deny write access to other processes * * 5. Deny legacy DosOpen access, one of: * * 0 = Allow DosOpen to access the file (DEFAULT) * * 1 = Deny access using the DosOpen API * * 6. Privacy/inheritance flag, one of: * * 0 = The file handle is inherited by child processes. (DEFAULT) * * 1 = The file handle is private to the current process. * * 7. Initial file attributes when creating a file: (DEFAULT: "") * * A = Archive attribute set * * D = Directory attribute set * * S = System attribute set * * H = Hidden attribute set * * R = Read-only attribute set * * 8. Initial file size when creating or replacing a file; ignored if * * access mode is read-only. (DEFAULT: 0) * * 9. I/O mode flags, any or all of: (DEFAULT: "") * * T = Write-through mode (default is normal write) * * N = No-cache mode (default is to use filesystem cache) * * S = Sequential access * * R = Random access * * * S and R can combine as follows: * * Neither: No locality known (default) * * S only: Mainly sequential access * * R only: Mainly random access * * Both: Random/sequential (i.e. random with some locality) * * * * REXX RETURN VALUE: * * File handle, or "" in case of error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Open( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { PSZ pszFile; HFILE hf; ULONG fsAction = 0, fsMode = 0, ulResult = 0, ulAttr = FILE_NORMAL; LONGLONG llSize = {0}; CHAR achHandle[ 9 ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument (the file name) if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // (Validate the first argument last to simplify error processing) // Second argument: open action if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) { strupr( argv[1].strptr ); if ( strcspn(argv[1].strptr, "OCR") > 0 ) return ( 40 ); if ( strchr(argv[1].strptr, 'O')) fsAction |= OPEN_ACTION_OPEN_IF_EXISTS; else if ( strchr(argv[1].strptr, 'R')) fsAction |= OPEN_ACTION_REPLACE_IF_EXISTS; if ( strchr(argv[1].strptr, 'C')) fsAction |= OPEN_ACTION_CREATE_IF_NEW; } else fsAction = OPEN_ACTION_OPEN_IF_EXISTS; // Third argument: access mode if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) { strupr( argv[2].strptr ); if ( strcspn(argv[2].strptr, "RW") > 0 ) return ( 40 ); if ( strchr(argv[2].strptr, 'R')) { if (strchr(argv[2].strptr, 'W')) fsMode = OPEN_ACCESS_READWRITE; else fsMode = OPEN_ACCESS_READONLY; } else if (strchr(argv[2].strptr, 'W')) fsMode = OPEN_ACCESS_WRITEONLY; else return ( 40 ); } else fsMode = OPEN_ACCESS_READWRITE; // Fourth argument: sharing mode if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) { strupr( argv[3].strptr ); if ( strcspn(argv[3].strptr, "RW") > 0 ) return ( 40 ); if ( strchr(argv[3].strptr, 'R')) { if (strchr(argv[3].strptr, 'W')) fsMode |= OPEN_SHARE_DENYREADWRITE; else fsMode |= OPEN_SHARE_DENYREAD; } else if (strchr(argv[3].strptr, 'W')) fsMode |= OPEN_SHARE_DENYWRITE; else fsMode |= OPEN_SHARE_DENYNONE; } else fsMode |= OPEN_SHARE_DENYNONE; // Fifth argument: deny legacy mode if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) { strupr( argv[4].strptr ); if ( argv[4].strptr[0] == '1' ) fsMode |= OPEN_SHARE_DENYLEGACY; else if ( argv[4].strptr[0] != '0' ) return ( 40 ); } // Sixth argument: inheritance mode if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) { strupr( argv[5].strptr ); if ( argv[5].strptr[0] == '1' ) fsMode |= OPEN_FLAGS_NOINHERIT; else if ( argv[5].strptr[0] != '0' ) return ( 40 ); } // Seventh argument: attributes if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) { strupr( argv[6].strptr ); if (strcspn(argv[6].strptr, "ADSHR") > 0 ) return ( 40 ); if ( strchr(argv[6].strptr, 'A')) ulAttr |= FILE_ARCHIVED; if ( strchr(argv[6].strptr, 'D')) ulAttr |= FILE_DIRECTORY; if ( strchr(argv[6].strptr, 'S')) ulAttr |= FILE_SYSTEM; if ( strchr(argv[6].strptr, 'H')) ulAttr |= FILE_HIDDEN; if ( strchr(argv[6].strptr, 'R')) ulAttr |= FILE_READONLY; } // Eighth argument: initial size if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) { if (( sscanf( argv[7].strptr, "%lld", &llSize )) != 1 ) return ( 40 ); } // Ninth argument: I/O mode flags if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) { strupr( argv[8].strptr ); if (strcspn(argv[8].strptr, "TNSR") > 0 ) return ( 40 ); if ( strchr(argv[8].strptr, 'T')) fsMode |= OPEN_FLAGS_WRITE_THROUGH; if ( strchr(argv[8].strptr, 'N')) fsMode |= OPEN_FLAGS_NO_CACHE; if ( strchr(argv[8].strptr, 'S')) fsMode |= OPEN_FLAGS_SEQUENTIAL; if ( strchr(argv[8].strptr, 'R')) fsMode |= OPEN_FLAGS_RANDOM; } // Now the first argument: file name pszFile = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) ); if ( pszFile == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } strncpy( pszFile, argv[0].strptr, RXSTRLEN(argv[0]) ); // Try and open the file rc = DosOpenL( pszFile, &hf, &ulResult, llSize, ulAttr, fsAction, fsMode, NULL ); if (rc) { WriteErrorCode( rc, "DosOpenL"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL free( pszFile ); return ( 0 ); } // Return the handle as the REXX result string sprintf( achHandle, "%8X", hf ); SaveResultString( prsResult, achHandle, strlen(achHandle) ); // 2016-02-20 SHL free( pszFile ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Close * * * * Wrapper to DosClose: close a file/stream. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open) (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Close( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the file handle) if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Close the file rc = DosClose( hf ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosClose"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } else { SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL } return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Seek * * * * Wrapper to DosSetFilePtrL: move the read/write pointer to the specified * * location in a stream. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open) (REQUIRED) * * 2. The signed distance in bytes to move (REQUIRED) * * 3. Move method, one of: * * B = Beginning of file * * C = Current position (DEFAULT) * * E = End of file * * * * REXX RETURN VALUE: * * The new file position, in bytes or "" if error * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Seek( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; LONGLONG llPos, llActual; ULONG ulMethod = FILE_CURRENT; CHAR achActual[ US_LONGLONG_MAXZ ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least two valid arguments if ( argc < 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) )) return ( 40 ); // First argument: file handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Second argument: requested offset if (( sscanf( argv[1].strptr, "%lld", &llPos )) != 1 ) return ( 40 ); // Third argument: starting position if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) { strupr( argv[2].strptr ); if ( strcspn(argv[2].strptr, "BCE") > 0 ) return ( 40 ); switch ( argv[2].strptr[0] ) { case 'B': ulMethod = FILE_BEGIN; break; case 'E': ulMethod = FILE_END; break; default : ulMethod = FILE_CURRENT; break; } } rc = DosSetFilePtrL( hf, llPos, ulMethod, &llActual ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosSetFilePtrL"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL return ( 0 ); } // Return the new position as the REXX result string sprintf( achActual, "%lld", llActual ); SaveResultString( prsResult, achActual, strlen(achActual) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2SetSize * * * * Wrapper to DosSetFileSizeL: set the size of a file opened with Sys2Open. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open) (REQUIRED) * * 2. The new size of the file (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2SetSize( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; LONGLONG llSize; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have two valid arguments if ( argc < 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) )) return ( 40 ); // First argument: file handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Second argument: requested size if (( sscanf( argv[1].strptr, "%lld", &llSize )) != 1 ) return ( 40 ); rc = DosSetFileSizeL( hf, llSize ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosSetFileSizeL"); SaveResultString( prsResult, PSZ_ZERO, 1 ); } else { SaveResultString( prsResult, PSZ_ONE, 1 ); } return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2BytesRemaining * * * * Return the number bytes that remain in a stream following the current * * read/write position. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) * * * * REXX RETURN VALUE: * * The number of bytes remaining. In case of error, 0 will be returned and * * SYS2ERR will be set. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2BytesRemaining( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; FILESTATUS3L fst3l = {0}; LONGLONG ll = {0}, llPos, llSize; CHAR achActual[ US_LONGLONG_MAXZ ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have one valid argument if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // First argument: handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Get the current position rc = DosSetFilePtrL( hf, ll, FILE_CURRENT, &llPos ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosSetFilePtrL"); SaveResultString( prsResult, NULL, 0 ); return ( 0 ); } // Get the total file size rc = DosQueryFileInfo( hf, FIL_STANDARDL, &fst3l, sizeof( fst3l )); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQueryFileInfoL"); SaveResultString( prsResult, NULL, 0 ); return ( 0 ); } llSize = fst3l.cbFile - llPos; // Return the position as the REXX result string sprintf( achActual, "%lld", llSize ); SaveResultString( prsResult, achActual, strlen(achActual) ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Read * * * * Wrapper to DosRead: read bytes from a previously-opened stream. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) * * 2. Number of bytes to read (REQUIRED) * * * * REXX RETURN VALUE: * * String containing the bytes read, or "" in case of error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Read( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; ULONG cb, cbActual; PSZ pszData; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have two valid arguments if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) )) return ( 40 ); // First argument: handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Second argument: number of bytes to read if (( sscanf( argv[1].strptr, "%u", &cb )) != 1 ) return ( 40 ); if ( cb < 1 ) return ( 40 ); pszData = (PSZ) malloc( cb ); rc = DosRead( hf, pszData, cb, &cbActual ); if ( rc || !cbActual ) { WriteErrorCode( rc, "DosRead"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL goto cleanup; } SaveResultString( prsResult, pszData, cbActual ); // 2016-02-20 SHL cleanup: free( pszData ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2ReadLine * * * * Read a line (up to the next LF byte) from a previously-opened stream. * * Only line feed (0x0A) bytes are treated as line-end characters; they will * * be stripped from the result string. Carriage return bytes (0x0D) will * * be skipped over but otherwise ignored. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) * * * * REXX RETURN VALUE: * * String containing the text read. In the event of error, this will be "" * * and SYS2ERR will be set. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2ReadLine( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; ULONG cbBuf = 0, cbTotal = 0, cbActual = 0; PSZ pszData; BOOL fEOL = FALSE; CHAR ch; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have one valid argument if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // First argument: handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); cbBuf = 1024; pszData = (PSZ) malloc( cbBuf ); while ( !fEOL ) { rc = DosRead( hf, &ch, 1, &cbActual ); if ( rc ) { WriteErrorCode( rc, "DosRead"); SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL goto cleanup; } if ( !cbActual || ch == '\n') { fEOL = TRUE; break; } else if ( ch == '\r') continue; if ( cbTotal >= cbBuf ) { cbBuf += 256; pszData = (PSZ) realloc( pszData, cbBuf ); } pszData[ cbTotal++ ] = ch; } SaveResultString( prsResult, pszData, cbTotal ); // 2016-02-20 SHL cleanup: free( pszData ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Write * * * * Wrapper to DosWrite: write bytes to a previously-opened stream. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) * * 2. Data to be written (REQUIRED) * * * * REXX RETURN VALUE: * * Number of bytes written. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Write( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; ULONG cbActual; CHAR szActual[ US_INTEGER_MAXZ ]; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have two valid arguments if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) )) return ( 40 ); // First argument: handle if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // (Second argument can be left in standard RXSTRING form) rc = DosWrite( hf, argv[1].strptr, argv[1].strlength, &cbActual ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosWrite"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL return ( 0 ); } sprintf( szActual, "%d", cbActual ); SaveResultString( prsResult, szActual, strlen(szActual) ); // 2016-02-20 SHL return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2SyncBuffer * * * * Wrapper to DosResetBuffer: for external files, write the buffer to disk; * * for pipes, block until the far end of the pipe has read the contents. * * * * REXX ARGUMENTS: * * 1. File handle (returned by Sys2Open) (REQUIRED) * * * * REXX RETURN VALUE: * * 1 on success, or 0 if an error occurred. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2SyncBuffer( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { HFILE hf; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the file handle) if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 ); // Sync the buffer rc = DosResetBuffer( hf ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosResetBuffer"); SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL } else { SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL } return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QueryDriveInfo * * * * Get non-filesystem-dependent information about a logical drive (volume). * * * * REXX ARGUMENTS: * * 1. Drive/volume letter to query, trailing colon optional. (REQUIRED) * * * * REXX RETURN VALUE: * * On success, returns a string in the format * * * * where is the uppercase drive letter followed by a colon, * * is the total size of the drive/volume in binary kilobytes,* * is one of: * * FLOPPY_5L - 48 TPI low-density diskette drive * * FLOPPY_5H - 96 TPI high-density diskette drive * * FLOPPY_3L - 3.5-inch 720KB drive * * FLOPPY_3H - 3.5-inch high-density 1.44MB diskette drive * * FLOPPY_3X - 3.5-inch ext-density 2.88MB diskette drive * * FLOPPY_8L - 8-inch single-density diskette drive * * FLOPPY_8H - 8-inch double-density diskette drive * * OTHER - other (including CD drive with no media) * * HDD - hard disk drive (including PRM) * * TAPE - tape drive * * OPTICAL - read/write optical drive * * and is 1 for non-partitionable removable media (e.g. floppies) * * or 0 otherwise (including both fixed and PRM disks) * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2QueryDriveInfo( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { BIOSPARAMETERBLOCK data; CHAR szDiskInfo[ US_DISKINFO_MAXZ ]; UCHAR achPP[ 2 ], chVol; ULONG cbPP, cbData, ulSectors, ulSize; BOOL bRemovable; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument (the drive letter) if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); chVol = toupper( argv[0].strptr[0] ); if (( chVol < 'A') || ( chVol > 'Z')) return ( 40 ); cbPP = 2; achPP[ 0 ] = 0; achPP[ 1 ] = chVol - 65; cbData = sizeof( data ); rc = DosDevIOCtl( (HFILE) -1, IOCTL_DISK, DSK_GETDEVICEPARAMS, (PVOID) achPP, 2, &cbPP, &data, cbData, &cbData ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosDevIOCtl"); SaveResultString( prsResult, NULL, 0 ); return ( 0 ); } ulSectors = data.cSectors? data.cSectors: data.cLargeSectors; ulSize = (data.usBytesPerSector > 1024) ? (ULONG) (ulSectors * ( data.usBytesPerSector / 1024 )) : (ULONG) (ulSectors / ( 1024 / data.usBytesPerSector )); bRemovable = !( data.fsDeviceAttr & 1 ); sprintf( szDiskInfo, "%c: %u ", chVol, ulSize ); switch( data.bDeviceType ) { case 0: // 48 TPI low-density diskette drive strncat( szDiskInfo, "FLOPPY_5L", US_DISKINFO_MAXZ-1 ); break; case 1: // 96 TPI high-density diskette drive strncat( szDiskInfo, "FLOPPY_5H", US_DISKINFO_MAXZ-1 ); break; case 2: // Small (3.5-inch) 720KB drive strncat( szDiskInfo, "FLOPPY_3L", US_DISKINFO_MAXZ-1 ); break; case 3: // 8-inch single-density diskette drive strncat( szDiskInfo, "FLOPPY_8L", US_DISKINFO_MAXZ-1 ); break; case 4: // 8-inch double-density diskette drive strncat( szDiskInfo, "FLOPPY_8H", US_DISKINFO_MAXZ-1 ); break; case 5: // Fixed disk strncat( szDiskInfo, "HDD", US_DISKINFO_MAXZ-1 ); break; case 6: // Tape drive strncat( szDiskInfo, "TAPE", US_DISKINFO_MAXZ-1 ); break; case 7: // Other (includes 1.44MB 3.5-inch diskette drive) if ( ulSize == 1440 ) strncat( szDiskInfo, "FLOPPY_3H", US_DISKINFO_MAXZ-1 ); else strncat( szDiskInfo, "OTHER", US_DISKINFO_MAXZ-1 ); break; case 8: // R/W optical disk strncat( szDiskInfo, "OPTICAL", US_DISKINFO_MAXZ-1 ); break; case 9: // 3.5-inch 4.0MB diskette drive (2.88MB formatted) strncat( szDiskInfo, "FLOPPY_3X", US_DISKINFO_MAXZ-1 ); break; default: strncat( szDiskInfo, "UNKNOWN", US_DISKINFO_MAXZ-1 ); break; } strncat( szDiskInfo, ( bRemovable? " 1": " 0" ), US_DISKINFO_MAXZ-1 ); SaveResultString( prsResult, szDiskInfo, strlen(szDiskInfo) ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2QuerySysValue * * * * Query the given system value. * * * * REXX ARGUMENTS: * * 1. The system value identifier (REQUIRED) * * * * REXX RETURN VALUE: The requested system value, or "" on error. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2QuerySysValue( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { CHAR szResult[ US_INTEGER_MAXZ ]; LONG lID, lValue; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have exactly one valid argument if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 ); // Parse the identifier lID = -1; strupr( argv[0].strptr ); if (( sscanf( argv[0].strptr, "%d", &lID )) != 1 ) { if ( ! stricmp( argv[0].strptr, "ALARM")) lID = SV_ALARM; else if ( ! stricmp( argv[0].strptr, "ALTMNEMONIC")) lID = SV_ALTMNEMONIC; else if ( ! stricmp( argv[0].strptr, "ANIMATIONSPEED")) lID = SV_ANIMATIONSPEED; else if ( ! stricmp( argv[0].strptr, "ANIMATION")) lID = SV_ANIMATION; else if ( ! stricmp( argv[0].strptr, "BEGINDRAG")) lID = SV_BEGINDRAG; else if ( ! stricmp( argv[0].strptr, "BEGINDRAGKB")) lID = SV_BEGINDRAGKB; else if ( ! stricmp( argv[0].strptr, "BEGINSELECT")) lID = SV_BEGINSELECT; else if ( ! stricmp( argv[0].strptr, "BEGINSELECTKB")) lID = SV_BEGINSELECTKB; else if ( ! stricmp( argv[0].strptr, "CHORDTIME")) lID = SV_CHORDTIME; else if ( ! stricmp( argv[0].strptr, "CICONTEXTLINES")) lID = SV_CICONTEXTLINES; else if ( ! stricmp( argv[0].strptr, "CMOUSEBUTTONS")) lID = SV_CMOUSEBUTTONS; else if ( ! stricmp( argv[0].strptr, "CONTEXTHELPKB")) lID = SV_CONTEXTHELPKB; else if ( ! stricmp( argv[0].strptr, "CONTEXTHELP")) lID = SV_CONTEXTHELP; else if ( ! stricmp( argv[0].strptr, "CONTEXTMENU")) lID = SV_CONTEXTMENU; else if ( ! stricmp( argv[0].strptr, "CONTEXTMENUKB")) lID = SV_CONTEXTMENUKB; else if ( ! stricmp( argv[0].strptr, "CPOINTERBUTTONS")) lID = SV_CPOINTERBUTTONS; else if ( ! stricmp( argv[0].strptr, "CTIMERS")) lID = SV_CTIMERS; else if ( ! stricmp( argv[0].strptr, "CURSORLEVEL")) lID = SV_CURSORLEVEL; else if ( ! stricmp( argv[0].strptr, "CURSORRATE")) lID = SV_CURSORRATE; else if ( ! stricmp( argv[0].strptr, "CXALIGN")) lID = SV_CXALIGN; else if ( ! stricmp( argv[0].strptr, "CXBORDER")) lID = SV_CXBORDER; else if ( ! stricmp( argv[0].strptr, "CXBYTEALIGN")) lID = SV_CXBYTEALIGN; else if ( ! stricmp( argv[0].strptr, "CXCHORD")) lID = SV_CXCHORD; else if ( ! stricmp( argv[0].strptr, "CXDBLCLK")) lID = SV_CXDBLCLK; else if ( ! stricmp( argv[0].strptr, "CXDLGFRAME")) lID = SV_CXDLGFRAME; else if ( ! stricmp( argv[0].strptr, "CXFULLSCREEN")) lID = SV_CXFULLSCREEN; else if ( ! stricmp( argv[0].strptr, "CXHSCROLLARROW")) lID = SV_CXHSCROLLARROW; else if ( ! stricmp( argv[0].strptr, "CXHSLIDER")) lID = SV_CXHSLIDER; else if ( ! stricmp( argv[0].strptr, "CXICONTEXTWIDTH")) lID = SV_CXICONTEXTWIDTH; else if ( ! stricmp( argv[0].strptr, "CXICON")) lID = SV_CXICON; else if ( ! stricmp( argv[0].strptr, "CXMINMAXBUTTON")) lID = SV_CXMINMAXBUTTON; else if ( ! stricmp( argv[0].strptr, "CXMOTIONSTART")) lID = SV_CXMOTIONSTART; else if ( ! stricmp( argv[0].strptr, "CXPOINTER")) lID = SV_CXPOINTER; else if ( ! stricmp( argv[0].strptr, "CXSCREEN")) lID = SV_CXSCREEN; else if ( ! stricmp( argv[0].strptr, "CXSIZEBORDER")) lID = SV_CXSIZEBORDER; else if ( ! stricmp( argv[0].strptr, "CXVSCROLL")) lID = SV_CXVSCROLL; else if ( ! stricmp( argv[0].strptr, "CYALIGN")) lID = SV_CYALIGN; else if ( ! stricmp( argv[0].strptr, "CYBORDER")) lID = SV_CYBORDER; else if ( ! stricmp( argv[0].strptr, "CYBYTEALIGN")) lID = SV_CYBYTEALIGN; else if ( ! stricmp( argv[0].strptr, "CYCHORD")) lID = SV_CYCHORD; else if ( ! stricmp( argv[0].strptr, "CYDBLCLK")) lID = SV_CYDBLCLK; else if ( ! stricmp( argv[0].strptr, "CYDLGFRAME")) lID = SV_CYDLGFRAME; else if ( ! stricmp( argv[0].strptr, "CYFULLSCREEN")) lID = SV_CYFULLSCREEN; else if ( ! stricmp( argv[0].strptr, "CYHSCROLL")) lID = SV_CYHSCROLL; else if ( ! stricmp( argv[0].strptr, "CYICON")) lID = SV_CYICON; else if ( ! stricmp( argv[0].strptr, "CYMENU")) lID = SV_CYMENU; else if ( ! stricmp( argv[0].strptr, "CYMINMAXBUTTON")) lID = SV_CYMINMAXBUTTON; else if ( ! stricmp( argv[0].strptr, "CYMOTIONSTART")) lID = SV_CYMOTIONSTART; else if ( ! stricmp( argv[0].strptr, "CYPOINTER")) lID = SV_CYPOINTER; else if ( ! stricmp( argv[0].strptr, "CYSCREEN")) lID = SV_CYSCREEN; else if ( ! stricmp( argv[0].strptr, "CYSIZEBORDER")) lID = SV_CYSIZEBORDER; else if ( ! stricmp( argv[0].strptr, "CYTITLEBAR")) lID = SV_CYTITLEBAR; else if ( ! stricmp( argv[0].strptr, "CYVSCROLLARROW")) lID = SV_CYVSCROLLARROW; else if ( ! stricmp( argv[0].strptr, "CYVSLIDER")) lID = SV_CYVSLIDER; else if ( ! stricmp( argv[0].strptr, "DBLCLKTIME")) lID = SV_DBLCLKTIME; else if ( ! stricmp( argv[0].strptr, "DEBUG")) lID = SV_DEBUG; else if ( ! stricmp( argv[0].strptr, "ENDDRAG")) lID = SV_ENDDRAG; else if ( ! stricmp( argv[0].strptr, "ENDDRAGKB")) lID = SV_ENDDRAGKB; else if ( ! stricmp( argv[0].strptr, "ENDSELECTKB")) lID = SV_ENDSELECTKB; else if ( ! stricmp( argv[0].strptr, "ENDSELECT")) lID = SV_ENDSELECT; else if ( ! stricmp( argv[0].strptr, "ERRORDURATION")) lID = SV_ERRORDURATION; else if ( ! stricmp( argv[0].strptr, "ERRORFREQ")) lID = SV_ERRORFREQ; else if ( ! stricmp( argv[0].strptr, "FIRSTSCROLLRATE")) lID = SV_FIRSTSCROLLRATE; else if ( ! stricmp( argv[0].strptr, "INSERTMODE")) lID = SV_INSERTMODE; else if ( ! stricmp( argv[0].strptr, "KBDALTERED")) lID = SV_KBDALTERED; else if ( ! stricmp( argv[0].strptr, "LOCKSTARTINPUT")) lID = SV_LOCKSTARTINPUT; else if ( ! stricmp( argv[0].strptr, "MENUROLLDOWNDELAY")) lID = SV_MENUROLLDOWNDELAY; else if ( ! stricmp( argv[0].strptr, "MENUROLLUPDELAY")) lID = SV_MENUROLLUPDELAY; else if ( ! stricmp( argv[0].strptr, "MONOICONS")) lID = SV_MONOICONS; else if ( ! stricmp( argv[0].strptr, "MOUSEPRESENT")) lID = SV_MOUSEPRESENT; else if ( ! stricmp( argv[0].strptr, "NOTEDURATION")) lID = SV_NOTEDURATION; else if ( ! stricmp( argv[0].strptr, "NOTEFREQ")) lID = SV_NOTEFREQ; else if ( ! stricmp( argv[0].strptr, "NUMBEREDLISTS")) lID = SV_NUMBEREDLISTS; else if ( ! stricmp( argv[0].strptr, "OPEN")) lID = SV_OPEN; else if ( ! stricmp( argv[0].strptr, "OPENKB")) lID = SV_OPENKB; else if ( ! stricmp( argv[0].strptr, "POINTERLEVEL")) lID = SV_POINTERLEVEL; else if ( ! stricmp( argv[0].strptr, "PRINTSCREEN")) lID = SV_PRINTSCREEN; else if ( ! stricmp( argv[0].strptr, "SCROLLRATE")) lID = SV_SCROLLRATE; else if ( ! stricmp( argv[0].strptr, "SELECTKB")) lID = SV_SELECTKB; else if ( ! stricmp( argv[0].strptr, "SETLIGHTS")) lID = SV_SETLIGHTS; else if ( ! stricmp( argv[0].strptr, "SINGLESELECT")) lID = SV_SINGLESELECT; else if ( ! stricmp( argv[0].strptr, "SWAPBUTTON")) lID = SV_SWAPBUTTON; else if ( ! stricmp( argv[0].strptr, "TASKLISTMOUSEACCESS")) lID = SV_TASKLISTMOUSEACCESS; else if ( ! stricmp( argv[0].strptr, "TEXTEDIT")) lID = SV_TEXTEDIT; else if ( ! stricmp( argv[0].strptr, "TEXTEDITKB")) lID = SV_TEXTEDITKB; else if ( ! stricmp( argv[0].strptr, "TRACKRECTLEVEL")) lID = SV_TRACKRECTLEVEL; else if ( ! stricmp( argv[0].strptr, "WARNINGDURATION")) lID = SV_WARNINGDURATION; else if ( ! stricmp( argv[0].strptr, "WARNINGFREQ")) lID = SV_WARNINGFREQ; } if ( lID < 0 ) return ( 40 ); lValue = WinQuerySysValue( HWND_DESKTOP, lID ); if ( lValue == 0 ) { /* Not elegant but probably true if the function failed. Anyway, * we don't have a HAB so we can't really use WinGetLastError()... */ WriteErrorCode( PMERR_PARAMETER_OUT_OF_RANGE, "WinQuerySysValue"); SaveResultString( prsResult, PSZ_ZERO, 1 ); return ( 0 ); } // Return the value as the REXX return string sprintf( szResult, "%d", lValue ); SaveResultString( prsResult, szResult, strlen(szResult) ); return ( 0 ); } /* ------------------------------------------------------------------------- * * Sys2Exec * * * * Wrapper to DosExecPgm. * * * * REXX ARGUMENTS: * * 1. Name of executable program (REQUIRED) * * 2. Arguments to pass to program (default "") * * 3. Execution mode flag, one of: * * 'W' or 'S': Wait for program to return (EXEC_SYNC) * * 'N' or 'A': No wait (EXEC_ASYNC) * * 'D' or 'B': Detach process (EXEC_BACKGROUND) * * * * REXX RETURN VALUE: * * If EXEC_SYNC mode was requested, returns the termination status from * * the started session. Otherwise, returns the process ID. * * ------------------------------------------------------------------------- */ ULONG APIENTRY Sys2Exec( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult ) { RESULTCODES st_RC = {0}; CHAR szLoadErr[ CCHMAXPATH ] = {0}, szTerminate[ US_INTEGER_MAXZ ]; PSZ pszProg, pszArgs, s; ULONG cbArgs, ulExec; APIRET rc; // Reset the error indicator WriteErrorCode( 0, NULL ); // Make sure we have at least one valid argument if (( argc < 1 ) || ( !RXVALIDSTRING( argv[0]) )) return ( 40 ); // Second argument is optional but must be a valid string if specified if (( argc >= 2 ) && ( !RXVALIDSTRING(argv[1]) )) return ( 40 ); // Third argument: execution mode flag if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) { strupr( argv[2].strptr ); switch ( argv[2].strptr[0] ) { case 'N': case 'A': ulExec = EXEC_ASYNC; break; case 'W': case 'S': ulExec = EXEC_SYNC; break; case 'D': case 'B': ulExec = EXEC_BACKGROUND; break; default: return ( 40 ); } } // Build the argument strings (first arg must be the program name) pszProg = strrchr( argv[0].strptr, '\\'); if ( pszProg == NULL ) pszProg = strrchr( argv[0].strptr, ':'); if ( pszProg == NULL ) pszProg = argv[0].strptr; cbArgs = strlen( pszProg + 1 ); if ( argc >= 2 ) { cbArgs += argv[1].strlength + 1; } pszArgs = calloc( cbArgs + 1, sizeof(char) ); if ( pszArgs == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); SaveResultString( prsResult, NULL, 0 ); return ( 0 ); } // Concatenate the program and argument strings, null-separated // (We use 0x1A while building the string to avoid termination issues) if ( argc >= 2 ) sprintf( pszArgs, "%s\x1A%s\x1A", pszProg, argv[1].strptr ); else sprintf( pszArgs, "%s\x1A", pszProg ); // Now replace the 0x1A's with nulls (starting from string end) while (( s = strrchr( pszArgs, 0x1A )) != NULL ) *s = 0; // Launch the program rc = DosExecPgm( szLoadErr, sizeof( szLoadErr), ulExec, pszArgs, NULL, &st_RC, pszProg ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosExecPgm"); SaveResultString( prsResult, PSZ_ZERO, 1 ); return ( 0 ); } sprintf( szTerminate, "%u", st_RC.codeTerminate ); SaveResultString( prsResult, szTerminate, strlen(szTerminate) ); return ( 0 ); } // ------------------------------------------------------------------------- // INTERNAL FUNCTIONS // ------------------------------------------------------------------------- /* ------------------------------------------------------------------------- * * GetProcess * * * * Gets information about the specified process (if found). If pszProgram * * is NULL, the search is done on the process ID in pulPID; otherwise, the * * search is done on the executable name in pszProgram (which may or may not * * include the extension). * * * * ARGUMENTS: * * PSZ pszProgram : The requested executable (process name). (I) * * PSZ pszFullName: The returned fully-qualified process name. (O) * * PULONG pulPID : The process ID. (IO) * * PULONG pulPPID : The returned process parent ID. (O) * * PULONG pulType : The returned process type. (O) * * PUSHORT pusPriority: The returned process priority. (O) * * PULONG pulCPU : The returned process CPU time. (O) * * * * RETURNS: ULONG * * 0 on success, or a non-zero API return code in the case of an error. * * ------------------------------------------------------------------------- */ // 2016-02-20 SHL Rework to avoid traps ULONG GetProcess( PCSZ pszProgram, PSZ pszFullName, PULONG pulPID, PULONG pulPPID, PULONG pulType, PUSHORT pusPriority, PULONG pulCPU ) { #ifdef USE_DQPS QSPTRREC *pBuf; // Data returned by DosQProcStatus() #else QSPTRREC *pBuf; // Data returned by DosQuerySysState() // 2015-04-23 SHL #endif QSPREC *pPrec; // Pointer to process information block QSTREC *pTrec; // Pointer to thread information block CHAR szName[ CCHMAXPATH ] = {0}, // Fully-qualified name of process szNoExt[ CCHMAXPATH ] = {0}; // Program name without extension PPIB ppib; // pointer to current process info block PSZ pszCurrent, // Program name of a queried process c; // Pointer to substring ULONG ulCPU; // Process CPU time USHORT usPriority, // Process priority class i; // index BOOL fMatch = FALSE; // The current process is a match? APIRET rc; // Return code // Use current process when PID is 0 and program name is not specified if (( pszProgram == NULL ) && ( *pulPID == 0 )) { rc = DosGetInfoBlocks( NULL, &ppib ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosGetInfoBlocks"); return ( rc ); } *pulPID = ppib->pib_ulpid; } #ifdef USE_DQPS pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); #else pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); // 2015-04-23 SHL #endif if ( pBuf == NULL ) { WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc"); return ( ERROR_NOT_ENOUGH_MEMORY ); } #ifdef USE_DQPS // Get running process information using DosQProcStatus() rc = DosQProcStatus( pBuf, UL_SSBUFSIZE ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQProcStatus"); return ( rc ); } pPrec = pBuf->pProcRec; #else // Get running process information using DosQuerySysState() rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE ); if ( rc != NO_ERROR ) { WriteErrorCode( rc, "DosQuerySysState"); free( pBuf ); return ( rc ); } pPrec = (QSPREC *)(((QSPTRREC*)pBuf) -> pProcRec); // 2015-04-23 SHL #endif *pulPPID = 0; *pulType = 0; *pusPriority = 0; *pulCPU = 0; if ( pszProgram != NULL ) *pulPID = 0; else if ( *pulPID == 0 ) return 0; # if 1 // 2016-02-25 SHL FIXME debug bad pointer // 2016-02-26 SHL FIXME to be gone when sure can not occur if ( (ULONG)pPrec < 0x10000 ) { sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000", __LINE__, (ULONG)pPrec ); WriteErrorCode( ERROR_INVALID_ADDRESS, szName); free( pBuf ); return ( 0 ); } # endif // Now look for the specified process // List ends with RecType not QS_PROCESS or pThrdRec NULL while ( pPrec->RecType == QS_PROCESS && pPrec->pThrdRec != NULL && !fMatch ) { if ( pszProgram == NULL ) { // Match by pid if ( pPrec->pid == *pulPID ) { fMatch = TRUE; // Get the program name if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR ) sprintf( pszFullName, "--"); else strcpy( pszFullName, szName ); // Get the process priority if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR ) usPriority = 0; // Get the CPU time of the process by querying each of its threads ulCPU = 0; pTrec = pPrec->pThrdRec; for ( i = 0; i < pPrec->cTCB; i++ ) { ulCPU += ( pTrec->systime + pTrec->usertime ); pTrec++; } *pulPPID = pPrec->ppid; *pulType = pPrec->type; *pusPriority = usPriority; *pulCPU = ulCPU; } } else { // Get the program name (without the path) if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR ) sprintf( pszCurrent, "--"); else pszCurrent = strrchr( szName, '\\') + 1; // Create a copy without the extension strcpy( szNoExt, pszCurrent ); if ( ( c = strrchr( szNoExt, '.') ) != NULL ) memset( c, 0, strlen(c) ); if ( pszCurrent != NULL && ( stricmp(pszCurrent, pszProgram) == 0 || stricmp(szNoExt, pszProgram) == 0 ) ) { fMatch = TRUE; // Get the process priority if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR ) usPriority = 0; // Get the CPU time of the process by querying each of its threads ulCPU = 0; pTrec = pPrec->pThrdRec; for ( i = 0; i < pPrec->cTCB; i++ ) { ulCPU += ( pTrec->systime + pTrec->usertime ); pTrec++; } *pulPID = pPrec->pid; *pulPPID = pPrec->ppid; *pulType = pPrec->type; *pusPriority = usPriority; *pulCPU = ulCPU; strcpy( pszFullName, szName ); } } pPrec = (QSPREC *)(pPrec->pThrdRec + pPrec->cTCB); # if 1 // 2016-02-25 SHL FIXME debug pointer - can this occur? // 2016-02-26 SHL FIXME to be gone when sure can not occur if ( (ULONG)pPrec < 0x10000 ) { sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000", __LINE__, (ULONG)pPrec ); WriteErrorCode( ERROR_INVALID_ADDRESS, szName); free( pBuf ); return ( 0 ); } # endif } // while if ( !fMatch ) *pulPID = 0; free( pBuf ); return ( 0 ); } #ifndef LEGACY_C_LOCALE /* ------------------------------------------------------------------------- * * GetLocaleString * * * * Get the requested representation string for the current locale. * * The argument is a pointer to a string which will be allocated by this * * function. It is the caller's responsibility to free() it. * * * * ARGUMENTS: * * PSZ *ppszItem: Pointer to string to be allocated. (O) * * LocaleItem item: Locale item to query. (I) * * * * RETURNS: int * * The ULS function return code. * * ------------------------------------------------------------------------- */ int GetLocaleString( PSZ *ppszItem, LocaleItem item ) { UconvObject uconv = NULL; LocaleObject locale = NULL; UniChar *puzSep; int rc = 0; int buf_size; PSZ pszBuffer; rc = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale ); if ( rc != ULS_SUCCESS ) { WriteErrorCode( rc, "UniCreateLocaleObject"); return ( rc ); } rc = UniQueryLocaleItem( locale, item, &puzSep ); if ( rc == ULS_SUCCESS ) { buf_size = UniStrlen( puzSep ) * 3; pszBuffer = (PSZ) calloc( 1, buf_size + 1 ); if ( pszBuffer ) { if ( UniCreateUconvObject(L"", &uconv ) == ULS_SUCCESS ) { if ( UniStrFromUcs( uconv, pszBuffer, puzSep, buf_size )) sprintf( pszBuffer, "%ls", puzSep ); UniFreeUconvObject( uconv ); } else sprintf( pszBuffer, "%ls", puzSep ); *ppszItem = pszBuffer; } else { rc = ERROR_NOT_ENOUGH_MEMORY; WriteErrorCode( rc, "calloc"); } UniFreeMem( puzSep ); } else WriteErrorCode( rc, "UniQueryLocaleItem"); UniFreeLocaleObject( locale ); return ( rc ); } /* ------------------------------------------------------------------------- * * GroupNumber * * * * Format an unsigned number into three-digit (thousands) groups, separated * * by the designated separator string. The output buffer must be allocated, * * and must be large enough to hold a 64-bit integer with the added * * separators, i.e. 20 + (separator length * 6 ). This function does not * * perform any bounds checking, so the caller must ensure this. * * * * ARGUMENTS: * * PSZ buf: The output string buffer. (IO) * * ULONGLONG val: The number value to format. (I) * * PSZ sep: The group-separator string. (I) * * * * RETURNS: N/A * * ------------------------------------------------------------------------- */ void GroupNumber( PSZ buf, ULONGLONG val, PSZ sep ) { if ( val < 1000 ) { sprintf( buf, "%u", val ); return; } GroupNumber( buf, val / 1000, sep ); sprintf( buf+strlen(buf), "%s%03u", sep, val % 1000 ); } #endif // #ifndef LEGACY_C_LOCALE #ifdef NO_SHARED_SOURCE /**** **** MOVED TO shfuncs.c ****/ /* ------------------------------------------------------------------------- * * SaveResultString * * * * Writes new string contents to the specified RXSTRING, allocating any * * additional memory that may be required. * * * * ARGUMENTS: * * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. * * PCH pchBytes : The string contents to write to prsResult or NULL * * ULONG ulBytes : The number of bytes in pchBytes to write 0..N. * * * * RETURNS: BOOL * * TRUE if prsResult was successfully updated. FALSE otherwise. * * ------------------------------------------------------------------------- */ BOOL SaveResultString( PRXSTRING prsResult, PCSZ pchBytes, ULONG ulBytes ) { ULONG ulRC; PCH pchNew; // 2016-02-20 SHL Rework for easier usage if (!pchBytes) ulBytes = 0; // Sync for caller if ( ulBytes > 256 ) { // REXX provides 256 bytes by default; allocate more if necessary ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT ); if ( ulRC != 0 ) { WriteErrorCode( ulRC, "DosAllocMem"); prsResult->strlength = 0; // 2016-02-20 SHL Force result to empty string return ( FALSE ); } // 2015-06-03 SHL dropped DosFreeMem(prsResult->strptr); // 2015-06-03 SHL Pointer not allocated by DosAllocMem prsResult->strptr = pchNew; } if (ulBytes) memcpy( prsResult->strptr, pchBytes, ulBytes ); prsResult->strlength = ulBytes; return ( TRUE ); } /* ------------------------------------------------------------------------- * * WriteStemElement * * * * Creates a stem element (compound variable) in the calling REXX program * * using the REXX shared variable pool interface. * * * * ARGUMENTS: * * PSZ pszStem : The name of the stem (before the '.') * * ULONG ulIndex : The number of the stem element (after the '.') * * PSZ pszValue : The value to write to the compound variable. * * * * RETURNS: BOOL * * TRUE on success, FALSE on failure. * * ------------------------------------------------------------------------- */ // 2016-02-20 SHL BOOL WriteStemElement( PCSZ pszStem, ULONG ulIndex, PCSZ pszValue ) { SHVBLOCK shvVar; // REXX shared variable pool block ULONG ulRc, ulBytes; CHAR szCompoundName[ US_COMPOUND_MAXZ ]; sprintf( szCompoundName, "%s.%d", pszStem, ulIndex ); if ( pszValue == NULL ) { pszValue = ""; ulBytes = 0; } else { // 2015-06-03 SHL Was using DosAllocMem and leaking memory // REXX API does not free this kind of buffer ulBytes = strlen(pszValue); } MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) ); shvVar.shvvalue.strptr = (PCH)pszValue; shvVar.shvvalue.strlength = ulBytes; shvVar.shvnamelen = RXSTRLEN( shvVar.shvname ); shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue ); shvVar.shvcode = RXSHV_SYSET; shvVar.shvnext = NULL; ulRc = RexxVariablePool( &shvVar ); if ( ulRc > 1 ) { WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)"); return FALSE; } return TRUE; } /* ------------------------------------------------------------------------- * * WriteErrorCode * * * * Writes an error code to a special variable in the calling REXX program * * using the REXX shared variable pool interface. This is used to return * * API error codes to the REXX program, since the REXX functions themselves * * normally return string values. * * * * ARGUMENTS: * * ULONG ulError : The error code returned by the failing API call. * * PSZ pszContext: A string describing the API call that failed. * * * * RETURNS: N/A * * ------------------------------------------------------------------------- */ void WriteErrorCode( ULONG ulError, PCSZ pszContext ) { SHVBLOCK shvVar; // REXX shared variable pool block ULONG ulRc; CHAR szErrorText[ US_ERRSTR_MAXZ ]; if ( pszContext == NULL ) sprintf( szErrorText, "%u", ulError ); else sprintf( szErrorText, "%u: %s", ulError, pszContext ); MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) ); MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) ); shvVar.shvnamelen = RXSTRLEN( shvVar.shvname ); shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue ); shvVar.shvcode = RXSHV_SYSET; shvVar.shvnext = NULL; shvVar.shvret = 0; // 2016-02-26 SHL ulRc = RexxVariablePool( &shvVar ); // 2016-02-26 SHL Correct if if ( ulRc & ~RXSHV_NEWV ) printf("* Unable to set %s: shvret = 0x%x, apiret = 0x%x\n", shvVar.shvname.strptr, (UCHAR)shvVar.shvret, ulRc ); // 2016-02-26 SHL Correct formatting } #endif // #ifdef NO_SHARED_SOURCE