source: rxutilex/trunk/rxutilex.c@ 42

Last change on this file since 42 was 42, checked in by Alex Taylor, 7 years ago

Make Sys2FormatNumber support negative values

File size: 142.0 KB
Line 
1/******************************************************************************
2 * REXX Utility Functions - Extended (RXUTILEX.DLL) *
3 * (C) 2011, 2017 Alex Taylor. *
4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35// Uncomment to use DosQProcStatus() instead of DosQuerySysState().
36// -- This was mostly put in place for early testing to see if either function
37// was more/less reliable than the other. In practice, DosQuerySysState()
38// should probably be used.
39// #define USE_DQPS
40
41// Uncomment to use legacy C style locale data instead of the OS/2 ULS library
42// #define LEGACY_C_LOCALE
43
44#define INCL_WINATOM
45#define INCL_WINCLIPBOARD
46#define INCL_WINERRORS
47#define INCL_WINSYS
48#define INCL_DOS
49#define INCL_DOSDEVIOCTL
50#define INCL_DOSERRORS
51#define INCL_DOSMISC
52#define INCL_DOSMODULEMGR
53#define INCL_DOSNMPIPES
54#define INCL_DOSPROCESS
55#define INCL_DOSPROFILE
56#define INCL_LONGLONG
57#ifndef OS2_INCLUDED
58 #include <os2.h>
59#endif
60
61#include <ctype.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <time.h>
66#include <locale.h>
67
68#ifdef LEGACY_C_LOCALE
69 #include <nl_types.h>
70 #include <langinfo.h>
71#else
72 #include <unidef.h>
73 #include <uconv.h>
74#endif
75
76#define INCL_RXSHV
77#define INCL_RXFUNC
78#include <rexxsaa.h>
79
80
81#include "shfuncs.h"
82
83
84#pragma import( DosGetPrty, "DosGetPrty", "DOSCALL1", 9 )
85USHORT APIENTRY16 DosGetPrty( USHORT usScope, PUSHORT pusPriority, USHORT pid );
86
87#ifdef USE_DQPS
88#pragma import( DosQProcStatus, "DosQProcStatus", "DOSCALL1", 154 )
89USHORT APIENTRY16 DosQProcStatus( PVOID pBuf, USHORT cbBuf );
90#endif
91
92// CONSTANTS
93
94#define SZ_LIBRARY_NAME "RXUTILEX" // Name of this library
95//#define SZ_ERROR_NAME "SYS2ERR" // REXX variable used to store error codes - must be defined in Makefile
96#define SZ_VERSION "0.1.6" // Current version of this library
97
98// Maximum string lengths...
99#ifdef NO_SHARED_SOURCE
100 #define US_COMPOUND_MAXZ 250 // ...of a compound variable
101 #define US_ERRSTR_MAXZ 250 // ...of an error string
102
103 static const char *PSZ_ZERO = "0";
104 static const char *PSZ_ONE = "1";
105#endif
106
107#define US_INTEGER_MAXZ 12 // ...of a 32-bit integer string
108#define US_LONGLONG_MAXZ 21 // ...of a 64-bit integer string
109#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem
110#define US_PIDSTR_MAXZ ( CCHMAXPATH + 100 ) // ...of a process information string
111#define US_TIMESTR_MAXZ 256 // ...of a formatted time string
112#define US_NUMSTR_MAXZ 32 // ...of a formatted number string
113#define US_PIPESTATUS_MAXZ 128 // ...of a pipe status string
114#define US_DISKINFO_MAXZ 128 // ...of a disk information string
115
116#define UL_SSBUFSIZE 0xFFFF // Buffer size for the DosQuerySysState() data
117
118 // Time string formats
119#define FL_TIME_DEFAULT 0
120#define FL_TIME_ISO8601 1
121#define FL_TIME_LOCALE 2
122
123// List of functions to be registered by Sys2LoadFuncs or dropped by Sys2DropFuncs
124// Drop list starts at index 0, load list starts at index 1
125static PSZ RxFunctionTbl[] = {
126 "Sys2LoadFuncs", // Drop only 2015-05-06 SHL
127 "Sys2DropFuncs",
128 "Sys2GetClipboardText",
129 "Sys2PutClipboardText",
130 "Sys2QueryProcess",
131 "Sys2QueryProcessList",
132 "Sys2KillProcess",
133 "Sys2QueryDriveInfo",
134 "Sys2QueryForegroundProcess",
135 "Sys2QueryPhysicalMemory",
136 "Sys2FormatNumber",
137 "Sys2FormatTime",
138 "Sys2GetEpochTime",
139 "Sys2ReplaceModule",
140 "Sys2LocateDLL",
141 "Sys2CreateNamedPipe",
142 "Sys2ConnectNamedPipe",
143 "Sys2DisconnectNamedPipe",
144 "Sys2CheckNamedPipe",
145 "Sys2Open",
146 "Sys2Close",
147 "Sys2Seek",
148 "Sys2BytesRemaining",
149 "Sys2Read",
150 "Sys2ReadLine",
151 "Sys2SyncBuffer",
152 "Sys2Write",
153 "Sys2QuerySysValue",
154 "Sys2Version"
155};
156
157// FUNCTION DECLARATIONS
158
159// Exported REXX functions
160RexxFunctionHandler Sys2LoadFuncs;
161RexxFunctionHandler Sys2DropFuncs;
162RexxFunctionHandler Sys2Version;
163
164RexxFunctionHandler Sys2FormatNumber;
165RexxFunctionHandler Sys2FormatTime;
166RexxFunctionHandler Sys2GetEpochTime;
167
168RexxFunctionHandler Sys2GetClipboardText;
169RexxFunctionHandler Sys2PutClipboardText;
170
171RexxFunctionHandler Sys2QueryProcess;
172RexxFunctionHandler Sys2QueryProcessList;
173RexxFunctionHandler Sys2KillProcess;
174RexxFunctionHandler Sys2QueryForegroundProcess;
175
176RexxFunctionHandler Sys2QueryDriveInfo;
177
178RexxFunctionHandler Sys2QueryPhysicalMemory;
179RexxFunctionHandler Sys2QuerySysValue;
180
181RexxFunctionHandler Sys2LocateDLL;
182RexxFunctionHandler Sys2ReplaceModule;
183
184RexxFunctionHandler Sys2CreateNamedPipe;
185RexxFunctionHandler Sys2ConnectNamedPipe;
186RexxFunctionHandler Sys2DisconnectNamedPipe;
187RexxFunctionHandler Sys2CheckNamedPipe;
188
189RexxFunctionHandler Sys2Open;
190RexxFunctionHandler Sys2Close;
191RexxFunctionHandler Sys2Seek;
192RexxFunctionHandler Sys2Read;
193RexxFunctionHandler Sys2Write;
194RexxFunctionHandler Sys2SyncBuffer;
195
196
197// Private internal functions
198ULONG GetProcess( PCSZ pszProgram, PSZ pszFullName, PULONG pulPID, PULONG pulPPID, PULONG pulType, PUSHORT pusPriority, PULONG pulCPU ); // 2016-02-20 SHL
199
200#ifndef LEGACY_C_LOCALE
201int GetLocaleString( PSZ *ppszItem, LocaleItem item );
202 void GroupNumber( PSZ buf, ULONGLONG val, PSZ sep );
203#endif
204
205#ifdef NO_SHARED_SOURCE
206 BOOL SaveResultString( PRXSTRING prsResult, PCSZ pchBytes, ULONG ulBytes ); // 2016-02-20 SHL
207 BOOL WriteStemElement( PCSZ pszStem, ULONG ulIndex, PCSZ pszValue ); // 2016-02-20 SHL
208 void WriteErrorCode( ULONG ulError, PCSZ pszContext ); // 2016-02-20 SHL
209#endif
210
211// MACROS
212#define TIME_SECONDS( timeval ) ( timeval / 32 )
213#define TIME_HUNDREDTHS( timeval ) (( timeval % 32 ) * 100 / 32 )
214
215/* ------------------------------------------------------------------------- *
216 * Sys2LoadFuncs *
217 * *
218 * Register all Sys2* REXX functions (except this one, obviously). *
219 * *
220 * REXX ARGUMENTS: None *
221 * REXX RETURN VALUE: "" *
222 * ------------------------------------------------------------------------- */
223ULONG APIENTRY Sys2LoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
224{
225 int entries,
226 i;
227
228 // Reset the error indicator
229 WriteErrorCode( 0, NULL );
230
231 if ( argc > 0 ) return ( 40 );
232 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
233 // 2015-05-06 SHL No need to load self (i.e. Sys2LoadFuncs), but do want to drop self
234 for ( i = 1; i < entries; i++ )
235 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
236
237 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
238 return ( 0 );
239}
240
241
242/* ------------------------------------------------------------------------- *
243 * Sys2DropFuncs *
244 * *
245 * Deregister all Sys2* REXX functions. *
246 * *
247 * REXX ARGUMENTS: None *
248 * REXX RETURN VALUE: "" *
249 * ------------------------------------------------------------------------- */
250ULONG APIENTRY Sys2DropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
251{
252 int entries,
253 i;
254
255 // Reset the error indicator
256 WriteErrorCode( 0, NULL );
257
258 if ( argc > 0 ) return ( 40 );
259 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
260 for ( i = 0; i < entries; i++ )
261 RexxDeregisterFunction( RxFunctionTbl[i] );
262
263 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
264 return ( 0 );
265}
266
267
268/* ------------------------------------------------------------------------- *
269 * Sys2Version *
270 * *
271 * Returns the current library version. *
272 * *
273 * REXX ARGUMENTS: None *
274 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
275 * ------------------------------------------------------------------------- */
276ULONG APIENTRY Sys2Version( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
277{
278 CHAR szVersion[ 12 ];
279
280 // Reset the error indicator
281 WriteErrorCode( 0, NULL );
282
283 if ( argc > 0 ) return ( 40 );
284 sprintf( szVersion, "%s", SZ_VERSION );
285
286 SaveResultString( prsResult, szVersion, strlen(szVersion) ); // 2016-02-20 SHL
287 return ( 0 );
288}
289
290
291/* ------------------------------------------------------------------------- *
292 * Sys2PutClipboardText *
293 * *
294 * Write a string to the clipboard in plain-text format. Specifying either *
295 * no value or an empty string in the first argument will simply clear the *
296 * clipboard of CF_TEXT data. *
297 * *
298 * REXX ARGUMENTS: *
299 * 1. String to be written to the clipboard (DEFAULT: "") *
300 * 2. Flag indicating whether other clipboard formats should be cleared: *
301 * Y = yes, call WinEmptyClipbrd() before writing text (DEFAULT) *
302 * N = no, leave (non-CF_TEXT) clipboard data untouched *
303 * *
304 * REXX RETURN VALUE: 1 on success, 0 on failure *
305 * ------------------------------------------------------------------------- */
306ULONG APIENTRY Sys2PutClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
307{
308 PSZ pszShareMem; // text in clipboard
309 ULONG ulRC = 0, // return code
310 ulBytes = 0, // size of input string
311 ulPType = 0; // process-type flag
312 BOOL fEmptyCB = TRUE, // call WinEmptyClipbrd() first?
313 fHabTerm = TRUE; // terminate HAB ourselves?
314 HAB hab; // anchor-block handle (for Win*)
315 HMQ hmq; // message-queue handle
316 PPIB ppib; // process information block
317 PTIB ptib; // thread information block
318
319
320 // Reset the error indicator
321 WriteErrorCode( 0, NULL );
322
323 // Make sure we have at least one valid argument (the input string)
324 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
325
326 // The second argument is optional, but must be correct if specified
327 if ( argc >= 2 ) {
328 // second argument: flag to clear clipboard (Y/N, but also accept 0/1)
329 if ( RXVALIDSTRING(argv[1]) ) {
330 strupr( argv[1].strptr );
331 if ( strcspn(argv[1].strptr, "YN01") > 0 ) return ( 40 );
332 switch ( argv[1].strptr[0] ) {
333 case 'N':
334 case '0': fEmptyCB = FALSE; break;
335 case 'Y':
336 case '1':
337 default : fEmptyCB = TRUE; break;
338 }
339 } else fEmptyCB = TRUE;
340 }
341
342 // Initialize the PM API
343 DosGetInfoBlocks( &ptib, &ppib );
344 ulPType = ppib->pib_ultype;
345 ppib->pib_ultype = 3; // Morph to PM
346 hab = WinInitialize( 0 );
347 if ( !hab ) {
348 fHabTerm = FALSE;
349 hab = 1;
350 }
351
352 /* Try to create a message-queue if one doesn't exist. We don't need to
353 * check the result, because it could fail if a message queue already exists
354 * (in the calling process), which is also OK.
355 */
356 hmq = WinCreateMsgQueue( hab, 0 );
357
358 // 2016-02-20 SHL Sync return values with docs
359
360 // Place the string on the clipboard as CF_TEXT
361 ulRC = WinOpenClipbrd( hab );
362 if ( ulRC ) {
363
364 if ( fEmptyCB ) WinEmptyClipbrd( hab );
365
366 ulBytes = argv[0].strlength + 1;
367 ulRC = DosAllocSharedMem( (PVOID) &pszShareMem,
368 NULL,
369 ulBytes,
370 PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
371 if ( ulRC == 0 ) {
372 memset( pszShareMem, 0, ulBytes );
373 strncpy( pszShareMem, argv[0].strptr, ulBytes - 1 );
374 if ( ! WinSetClipbrdData( hab, (ULONG) pszShareMem, CF_TEXT, CFI_POINTER ) ) {
375 WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinSetClipbrdData" );
376 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
377 }
378 else
379 SaveResultString( prsResult, PSZ_ONE, 1 ); // Success - 2016-02-20 SHL
380 } else {
381 WriteErrorCode( ulRC, "DosAllocSharedMem");
382 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
383 }
384
385 WinCloseClipbrd( hab );
386 } else {
387 // 2016-02-20 SHL Report PM error code
388 WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinOpenClipbrd" );
389 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
390 }
391
392 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
393 if ( fHabTerm ) WinTerminate( hab );
394 ppib->pib_ultype = ulPType; // Restore
395
396 return ( 0 );
397}
398
399
400/* ------------------------------------------------------------------------- *
401 * Sys2GetClipboardText *
402 * *
403 * Retrieve a plain-text string from the clipboard if one is available. *
404 * *
405 * REXX ARGUMENTS: *
406 * None. *
407 * *
408 * REXX RETURN VALUE: The retrieved clipboard string or "" if fails. *
409 * ------------------------------------------------------------------------- */
410ULONG APIENTRY Sys2GetClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
411{
412 PSZ pszClipText, // pointer to clipboard data
413 pszLocalText; // our copy of the data (to return)
414 ULONG ulRC = 0, // return code
415 ulBytes = 0, // size in bytes of output string
416 ulPType = 0; // process-type flag
417 BOOL fHabTerm = TRUE; // terminate HAB ourselves?
418 HAB hab; // anchor-block handle (for Win*)
419 HMQ hmq; // message-queue handle
420 PPIB ppib; // process information block
421 PTIB ptib; // thread information block
422
423
424 // Reset the error indicator
425 WriteErrorCode( 0, NULL );
426
427 // Initialize the PM API
428 DosGetInfoBlocks( &ptib, &ppib );
429 ulPType = ppib->pib_ultype;
430 ppib->pib_ultype = 3;
431 hab = WinInitialize( 0 );
432 if ( !hab ) {
433 fHabTerm = FALSE;
434 hab = 1;
435 }
436
437 /* Note: A message-queue must exist before we can access the clipboard. We
438 * don't actually use the returned value. In fact, we don't even
439 * verify it, because it could be NULLHANDLE if this function was
440 * called from a PM process (e.g. VX-REXX) - in which case, a message
441 * queue should already exist, and we can proceed anyway.
442 */
443 hmq = WinCreateMsgQueue( hab, 0 );
444
445 // Open the clipboard
446 ulRC = WinOpenClipbrd( hab );
447 if ( ulRC ) {
448
449 // Read plain text from the clipboard, if available
450 if (( pszClipText = (PSZ) WinQueryClipbrdData( hab, CF_TEXT ) ) != NULL ) {
451
452 ulBytes = strlen(pszClipText) + 1;
453 if ( ( pszLocalText = (PSZ) malloc( ulBytes ) ) != NULL ) {
454 memset( pszLocalText, 0, ulBytes );
455 strncpy( pszLocalText, pszClipText, ulBytes - 1 );
456 SaveResultString( prsResult, pszLocalText, ulBytes - 1 ); // 2016-02-20 SHL
457 free( pszLocalText );
458 } else {
459 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
460 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
461 }
462
463 } else {
464 // Either no text exists, or clipboard is not readable
465 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
466 }
467
468 WinCloseClipbrd( hab );
469 } else {
470 // 2016-02-20 SHL Report PM error code
471 WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinOpenClipbrd" );
472 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
473 }
474
475 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
476 if ( fHabTerm ) WinTerminate( hab );
477
478 ppib->pib_ultype = ulPType;
479
480 return ( 0 );
481}
482
483
484/* ------------------------------------------------------------------------- *
485 * Sys2QueryProcess *
486 * *
487 * Queries information about the specified process. *
488 * *
489 * REXX ARGUMENTS: *
490 * 1. The process identifier (program name or process ID) (REQUIRED) *
491 * 2. Flag indicicating the identifier type: *
492 * 'P': decimal process ID *
493 * 'H': hexadecimal process ID *
494 * 'N': executable program name (with or without extension) (DEFAULT) *
495 * *
496 * REXX RETURN VALUE: *
497 * A string of the format *
498 * pid parent-pid process-type priority cpu-time executable-name *
499 * "priority" is in hexadecimal notation, all other numbers are decimal. *
500 * "" is returned if the process was not found or if an internal error *
501 * occurred. *
502 * ------------------------------------------------------------------------- */
503ULONG APIENTRY Sys2QueryProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
504{
505 PSZ pszProcName; // Requested process name
506 UCHAR szFullName[ CCHMAXPATH ] = {0}, // Fully-qualified name
507 szReturn[ US_PIDSTR_MAXZ ] = {0}; // Buffer for return value
508 ULONG ulPID = 0, // Process ID
509 ulPPID = 0, // Parent process ID
510 ulType = 0, // Process type
511 ulTime = 0; // Process CPU time
512 USHORT usPrty = 0; // Process priority
513 APIRET rc; // API return code
514
515
516 // Reset the error indicator
517 WriteErrorCode( 0, NULL );
518
519 // Make sure we have at least one valid argument (the input string)
520 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
521
522 // Parse the ID type flag
523 if ( argc >= 2 && RXVALIDSTRING( argv[1] ) ) {
524 strupr( argv[1].strptr );
525 if ( strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 );
526 switch ( argv[1].strptr[0] ) {
527
528 case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 );
529 pszProcName = NULL;
530 break;
531
532 case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 );
533 pszProcName = NULL;
534 break;
535
536 default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
537 if ( pszProcName == NULL ) {
538 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
539 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
540 return ( 0 );
541 }
542 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
543 break;
544 }
545 } else {
546 pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
547 if ( pszProcName == NULL ) {
548 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
549 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
550 return ( 0 );
551 }
552 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
553 }
554
555 // See if the requested process is running and get its PID/PPID
556 rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime );
557 if (( rc != NO_ERROR ) || ( ulPID == 0 )) {
558 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
559 return ( 0 );
560 }
561
562 sprintf( szReturn, "%u %u %u %04X %02u:%02u.%02u %s",
563 ulPID, ulPPID, ulType, usPrty, TIME_SECONDS( ulTime ) / 60,
564 TIME_SECONDS( ulTime ) % 60, TIME_HUNDREDTHS( ulTime ), szFullName );
565
566 SaveResultString( prsResult, szReturn, strlen(szReturn) ); // 2016-02-20 SHL
567
568 return ( 0 );
569}
570
571
572/* ------------------------------------------------------------------------- *
573 * Sys2KillProcess *
574 * *
575 * Terminate the (first) running process with the specified executable name *
576 * or process-ID. *
577 * *
578 * REXX ARGUMENTS: *
579 * 1. The process identifier (program name or process ID) (REQUIRED) *
580 * 2. Flag indicicating the identifier type: *
581 * 'P': decimal process ID *
582 * 'H': hexadecimal process ID *
583 * 'N': executable program name (with or without extension) (DEFAULT) *
584 * *
585 * REXX RETURN VALUE: 1 on success or 0 on failure. *
586 * ------------------------------------------------------------------------- */
587ULONG APIENTRY Sys2KillProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
588{
589 PSZ pszProcName; // Requested process name
590 UCHAR szFullName[ CCHMAXPATH ] = {0}; // Fully-qualified name
591 ULONG ulPID = 0, // Process ID
592 ulPPID = 0, // Parent process ID (not used)
593 ulType = 0, // Process type (not used)
594 ulTime = 0; // Process CPU time (not used)
595 USHORT usPrty = 0; // Process priority (not used)
596 APIRET rc; // API return code
597
598
599 // Reset the error indicator
600 WriteErrorCode( 0, NULL );
601
602 // Make sure we have at least one valid argument (the input string)
603 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
604
605 // Parse the ID type flag
606 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
607 strupr( argv[1].strptr );
608 if (strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 );
609 switch ( argv[1].strptr[0] ) {
610
611 case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 );
612 pszProcName = NULL;
613 break;
614
615 case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 );
616 pszProcName = NULL;
617 break;
618
619 default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
620 if ( pszProcName == NULL ) {
621 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
622 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
623 return ( 0 );
624 }
625 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
626 break;
627 }
628 } else {
629 pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
630 if ( pszProcName == NULL ) {
631 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
632 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
633 return ( 0 );
634 }
635 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
636 }
637
638 if ( pszProcName ) {
639 // Get the process PID
640 rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime );
641 if (( rc != NO_ERROR ) || ( ulPID == 0 )) {
642 free( pszProcName );
643 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
644 return ( 0 );
645 }
646 }
647
648 // Now attempt to kill the process using DosKillProcess()
649 rc = DosKillProcess( 1, ulPID );
650 if ( rc != NO_ERROR ) {
651 WriteErrorCode( rc, "DosKillProcess");
652 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
653 }
654 else
655 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
656
657 // 2016-02-20 SHL Avoid leak
658 if ( pszProcName )
659 free( pszProcName );
660
661 return ( 0 );
662}
663
664
665/* ------------------------------------------------------------------------- *
666 * Sys2QueryProcessList *
667 * *
668 * Gets the process ID of the specified executable, if it is running. *
669 * The results will be returned in a stem variable, where stem.0 contains *
670 * number of items, and each stem item is a string of the form: *
671 * pid parent-pid process-type priority cpu-time executable-name *
672 * "priority" is in hexadecimal notation, all other numbers are decimal. *
673 * *
674 * Notes: *
675 * - "process-type" will be one of: *
676 * 0 Full screen protect-mode session *
677 * 1 Requires real mode. Dos emulation. *
678 * 2 VIO windowable protect-mode session *
679 * 3 Presentation Manager protect-mode session *
680 * 4 Detached protect-mode process. *
681 * - If "priority" is 0 then the priority class could not be determined. *
682 * - If "executable-name" is "--" then the name could not be identified. *
683 * *
684 * REXX ARGUMENTS: *
685 * 1. The name of the stem in which to return the results (REQUIRED) *
686 * *
687 * REXX RETURN VALUE: Number of processes found, or "" in case of error. *
688 * ------------------------------------------------------------------------- */
689ULONG Sys2QueryProcessList( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
690{
691 QSPTRREC *pBuf; // Data returned by DosQProcStatus/DosQuerySysState() // 2015-04-23 SHL
692 QSPREC *pPrec; // Pointer to process information block
693 QSTREC *pTrec; // Pointer to thread information block
694 CHAR szStem[ US_STEM_MAXZ ], // Buffers used for building strings ...
695 szNumber[ US_INTEGER_MAXZ ], // ...
696 szName[ CCHMAXPATH ], // Fully-qualified name of process
697 szPInfo[ US_PIDSTR_MAXZ ]; // Stem item string
698 ULONG ulCount, // Number of processes
699 ulCPU; // Process CPU time
700 USHORT usPriority, // Process priority class
701 i; // Loop counter
702 APIRET rc; // Return code
703
704
705 // Reset the error indicator
706 WriteErrorCode( 0, NULL );
707
708 // Do some validity checking on the arguments
709 if (( argc != 1 ) || // Make sure we have exactly one argument...
710 ( ! RXVALIDSTRING(argv[0]) ) || // ...which is a valid REXX string...
711 ( RXSTRLEN(argv[0]) > US_STEM_MAXZ )) // ...and isn't too long.
712 return ( 40 );
713
714 // Generate the stem variable name from the argument (stripping any final dot)
715 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
716 strncpy( szStem, argv[0].strptr, RXSTRLEN(argv[0]) );
717 szStem[ RXSTRLEN(argv[0]) ] = '\0';
718
719#ifdef USE_DQPS
720 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE );
721#else
722 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); // 2015-04-23 SHL
723#endif
724
725 if ( pBuf == NULL ) {
726 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
727 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
728 return ( 0 );
729 }
730
731#ifdef USE_DQPS
732 // Get running process information using 16-bit DosQProcStatus()
733 rc = DosQProcStatus( pBuf, UL_SSBUFSIZE );
734 if ( rc != NO_ERROR ) {
735 WriteErrorCode( rc, "DosQProcStatus");
736 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
737 return ( 0 );
738 }
739#else
740 // Get running process information using 32-bit DosQuerySysState()
741 rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE );
742 if ( rc != NO_ERROR ) {
743 WriteErrorCode( rc, "DosQuerySysState");
744 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
745 return ( 0 );
746 }
747#endif
748
749 // Now get the list of processes
750 ulCount = 0;
751# if 1 // 2016-02-25 SHL FIXME debug bad pointer
752 // 2016-02-26 SHL FIXME to be gone when sure this can not occur
753 if ( (ULONG)pBuf->pProcRec < 0x10000 ) {
754 sprintf( szName, "rxutilex#%u pBuf->pProcRec 0x%x < 0x10000",
755 __LINE__, (ULONG)pBuf->pProcRec );
756 WriteErrorCode( ERROR_INVALID_ADDRESS, szName );
757 SaveResultString( prsResult, NULL, 0 );
758 free( pBuf );
759 return ( 0 );
760 }
761# endif
762
763 for (pPrec = pBuf->pProcRec;
764 ;
765 pPrec = (QSPREC *)(pPrec->pThrdRec + pPrec->cTCB)
766 )
767
768 {
769# if 0 // 2015-06-19 SHL FIXME debug bad pointer
770 // 2016-02-26 SHL FIMXE to be gone when sure no longer needed
771 if ( (ULONG)pPrec < 0x10000 ) {
772 sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000",
773 __LINE__, (ULONG)pPrec );
774 WriteErrorCode( ERROR_INVALID_ADDRESS, szName );
775 SaveResultString( prsResult, NULL, 0 );
776 free( pBuf );
777 return ( 0 );
778 }
779# endif
780
781 // Check for documented end marker - RecType not QS_PROCESS
782 if ( pPrec->RecType != QS_PROCESS ) {
783# if 0 // 2016-02-26 SHL FIXME debug
784 fprintf( stderr,
785 "* rxutilex#%u RecType != QS_PROCESS "
786 "ulCount %u pBuf %p pPrec %p ->RecType %x ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n",
787 __LINE__, ulCount, pBuf, pPrec, pPrec->RecType, pPrec->pThrdRec,
788 pPrec->pid, pPrec->ppid,
789 pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB );
790# endif
791 break; // Must be end of list
792 }
793
794 // Check for alternate end marker - pThredRec NULL
795 // This appears to be an undocumented end marker
796 // Might be a defect - testing says only RecType is non-zero
797 // 2015-06-12 SHL pThrdRec can be 0 - probably when process starting or dieing
798 if ( (PVOID)pPrec->pThrdRec == NULL ) {
799# if 0 // 2016-02-26 SHL FIXME debug
800 fprintf( stderr,
801 "* rxutilex#%u pThrdRec NULL "
802 "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n",
803 __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->ppid,
804 pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB );
805# endif
806 break; // Must be end of list
807 }
808
809# if 1 // 2015-07-31 SHL FIXME debug bad pointer
810 // 2016-02-26 SHL FIXME to be gone when sure can not occur
811 if ( (ULONG)(pPrec->pThrdRec) < 0x10000 ) {
812 sprintf( szName,
813 "rxutilex#%u pPrec < 0x10000 "
814 "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->ppid %u ->type %u ->stat %u ->hmte %x ->cTCB %u\n",
815 __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->ppid,
816 pPrec->type, pPrec->stat, pPrec->hMte, pPrec->cTCB );
817 WriteErrorCode( ERROR_INVALID_ADDRESS, szName );
818 SaveResultString( prsResult, NULL, 0 );
819 free( pBuf );
820 return ( 0 );
821 }
822# endif
823# if 1 // 2016-02-26 SHL FIXME debug bad count
824 // 2016-02-26 SHL FIXME to be gone when sure can not occur
825 // This is probably occurs only when pThrdRec NULL too which is already checked
826 if ( !pPrec->cTCB ) {
827 sprintf( szName, "rxutilex#%u cTCB 0 ulCount %u pBuf %p pPrec %p ->pid %u ->type %u ->stat %u\n",
828 __LINE__, ulCount, pBuf, pPrec, pPrec->pid, pPrec->type, pPrec->stat );
829 WriteErrorCode( ERROR_INVALID_DATA, szName );
830 SaveResultString( prsResult, NULL, 0 );
831 free( pBuf );
832 return ( 0 );
833 }
834# endif
835
836 ulCount++;
837
838 // Get the program name of each process (including path)
839 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
840 sprintf( szName, "--");
841 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
842 usPriority = 0;
843
844 // Get the CPU time of the process by querying each of its threads
845 ulCPU = 0;
846 pTrec = pPrec->pThrdRec;
847 for ( i = 0; i < pPrec->cTCB; i++ ) {
848 ulCPU += ( pTrec->systime + pTrec->usertime );
849 pTrec++;
850 }
851
852 // Now generate the stem item with all of this information
853 sprintf( szPInfo, "%u %u %u %04X %02u:%02u.%02u %s",
854 pPrec->pid, // PID
855 pPrec->ppid, // Parent PID
856 pPrec->type, // Process type
857 usPriority, // Priority class
858 TIME_SECONDS( ulCPU ) / 60, // CPU time (hours)
859 TIME_SECONDS( ulCPU ) % 60, // CPU time (minutes)
860 TIME_HUNDREDTHS( ulCPU ), // CPU time (seconds)
861 szName ); // Executable name & path
862 WriteStemElement( szStem, ulCount, szPInfo );
863
864# if 0 // 2015-07-31 SHL FIXME debug bad count
865 // 2016-02-26 SHL FIXME to be gone when sure no longer needed
866 if ( !pPrec->cTCB ) {
867 sprintf( szName, "rxutilex#%u: cTCB 0 "
868 "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u\n",
869 __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid );
870 WriteErrorCode( ERROR_INVALID_DATA, szName );
871 SaveResultString( prsResult, NULL, 0 );
872 free( pBuf );
873 return ( 0 );
874 }
875# endif
876# if 0 // 2015-07-31 SHL FIXME debug bad pointer
877 // 2016-02-26 SHL FIXME to be gone when sure no longer needed
878 if ( (ULONG)pPrec->pThrdRec < 0x10000 ) {
879 sprintf( szName,
880 "rxutilex#%u: pBuf->pThrdRec < 0x10000 "
881 "ulCount %u pBuf %p pPrec %p ->pThrdRec %p ->pid %u ->cTCB %u\n",
882 __LINE__, ulCount, pBuf, pPrec, pPrec->pThrdRec, pPrec->pid, pPrec->cTCB );
883 WriteErrorCode( rc, szName );
884 SaveResultString( prsResult, NULL, 0 );
885 free( pBuf );
886 return ( 0 );
887 }
888# endif
889 } // for
890
891 // Create the stem.0 element with the number of processes found
892 sprintf( szNumber, "%d", ulCount );
893 WriteStemElement( szStem, 0, szNumber );
894
895 // And also return the number of processes as the REXX return string
896 SaveResultString( prsResult, szNumber, strlen(szNumber) ); // 2016-02-20 SHL
897
898 free( pBuf );
899 return ( 0 );
900}
901
902
903/* ------------------------------------------------------------------------- *
904 * Sys2QueryPhysicalMemory *
905 * *
906 * Queries the amount of physical memory (RAM) installed in the system. *
907 * *
908 * REXX ARGUMENTS: None *
909 * *
910 * REXX RETURN VALUE: *
911 * Integer representing the amount of installed memory, in KiB, or 0 if an *
912 * error occurred. *
913 * ------------------------------------------------------------------------- */
914ULONG APIENTRY Sys2QueryPhysicalMemory( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
915{
916 CHAR szMemSize[ US_INTEGER_MAXZ ];
917 ULONG ulMemBytes = 0,
918 ulMemKBytes = 0;
919 APIRET rc = 0;
920
921 // Reset the error indicator
922 WriteErrorCode( 0, NULL );
923
924 // Make sure we have no arguments
925 if ( argc > 0 ) return ( 40 );
926
927 // Query installed memory in bytes
928 rc = DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
929 &ulMemBytes, sizeof(ulMemBytes) );
930 if ( rc != NO_ERROR ) {
931 WriteErrorCode( rc, "DosQuerySysInfo");
932 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
933 return ( 0 );
934 }
935
936 // Convert to binary kilobytes (any remainder is discarded)
937 ulMemKBytes = ulMemBytes / 1024;
938 sprintf( szMemSize, "%u", ulMemKBytes );
939
940 // Return the memory size as the REXX return string
941 SaveResultString( prsResult, szMemSize, strlen(szMemSize) ); // 2016-02-20 SHL
942
943 return ( 0 );
944}
945
946
947/* ------------------------------------------------------------------------- *
948 * Sys2QueryForegroundProcess *
949 * *
950 * Queries the PID of the current foreground process. *
951 * *
952 * REXX ARGUMENTS: None *
953 * *
954 * REXX RETURN VALUE: *
955 * Integer representing the process ID (in decimal), or 0 if an error *
956 * occurred. *
957 * ------------------------------------------------------------------------- */
958ULONG APIENTRY Sys2QueryForegroundProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
959{
960 CHAR szPID[ US_INTEGER_MAXZ ];
961 ULONG ulPID = 0;
962 APIRET rc = 0;
963
964 // Reset the error indicator
965 WriteErrorCode( 0, NULL );
966
967 // Make sure we have no arguments
968 if ( argc > 0 ) return ( 40 );
969
970 // Query installed memory in bytes
971 rc = DosQuerySysInfo( QSV_FOREGROUND_PROCESS,
972 QSV_FOREGROUND_PROCESS,
973 &ulPID, sizeof(ulPID) );
974 if ( rc != NO_ERROR ) {
975 WriteErrorCode( rc, "DosQuerySysInfo");
976 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
977 return ( 0 );
978 }
979 sprintf( szPID, "%u", ulPID );
980
981 // Return the PID as the REXX return string
982 SaveResultString( prsResult, szPID, strlen(szPID) ); // 2016-02-20 SHL
983
984 return ( 0 );
985}
986
987
988/* ------------------------------------------------------------------------- *
989 * Sys2ReplaceModule *
990 * *
991 * Unlocks and optionally replaces an in-use (locked) DLL or EXE. *
992 * *
993 * REXX ARGUMENTS: *
994 * 1. The filespec of the module to be replaced. (REQUIRED) *
995 * 2. The filespec of the new module to replace it with. (DEFAULT: none) *
996 * 3. The filespec of the backup file to be created. (DEFAULT: none) *
997 * *
998 * REXX RETURN VALUE: *
999 * 1 on success, or 0 if an error occurred. *
1000 * ------------------------------------------------------------------------- */
1001ULONG APIENTRY Sys2ReplaceModule( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1002{
1003 PSZ pszOldModule = NULL,
1004 pszNewModule = NULL,
1005 pszBackup = NULL;
1006 APIRET rc = 0;
1007
1008 // Reset the error indicator
1009 WriteErrorCode( 0, NULL );
1010
1011 // Make sure we have at least one valid argument (the module name)
1012 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1013 pszOldModule = calloc( argv[0].strlength + 1, sizeof(UCHAR) );
1014 if ( pszOldModule == NULL ) {
1015 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1016 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1017 return ( 0 );
1018 }
1019 strncpy( pszOldModule, argv[0].strptr, argv[0].strlength );
1020
1021 // Second argument: new module name (optional, but must be correct if specified)
1022 if ( argc >= 2 ) {
1023 if ( RXVALIDSTRING(argv[1]) ) {
1024 pszNewModule = calloc( argv[1].strlength + 1, sizeof(char) );
1025 if ( pszNewModule == NULL ) {
1026 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1027 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1028 return ( 0 );
1029 }
1030 strncpy( pszNewModule, argv[1].strptr, argv[1].strlength );
1031 } else return ( 40 );
1032 }
1033
1034 // Third argument: backup filename (optional, but must be correct if specified)
1035 if ( argc >= 3 ) {
1036 if ( RXVALIDSTRING(argv[2]) ) {
1037 pszBackup = calloc( argv[2].strlength + 1, sizeof(char) );
1038 if ( pszBackup == NULL ) {
1039 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1040 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1041 return ( 0 );
1042 }
1043 strncpy( pszBackup, argv[2].strptr, argv[2].strlength );
1044 } else return ( 40 );
1045 }
1046
1047 // Now replace the module using DosReplaceModule
1048 rc = DosReplaceModule( pszOldModule, pszNewModule, pszBackup );
1049 if ( rc != NO_ERROR ) {
1050 WriteErrorCode( rc, "DosReplaceModule");
1051 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1052 return ( 0 );
1053 }
1054
1055 // Return 1 on success
1056 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
1057
1058 return ( 0 );
1059}
1060
1061
1062/* ------------------------------------------------------------------------- *
1063 * Sys2FormatNumber *
1064 * *
1065 * Format a number using locale-specific thousands separators. The input *
1066 * number may be a positive or negative integer or floating point value. It *
1067 * must not contain any separators already, and any decimal point which it *
1068 * contains must be a period (rather than any localized decimal symbol). *
1069 * The maximum value is that of a signed 64-bit integer, or a long double. *
1070 * *
1071 * REXX ARGUMENTS: *
1072 * 1. Number to be formatted. (REQUIRED) *
1073 * 2. Number of decimal places to use for floating point *
1074 * values. Ignored for integer values. (DEFAULT: 2) *
1075 * *
1076 * REXX RETURN VALUE: The formatted number, or "" on error. *
1077 * ------------------------------------------------------------------------- */
1078ULONG APIENTRY Sys2FormatNumber( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1079{
1080 CHAR achNumber[ US_NUMSTR_MAXZ ]; // Formatted output string
1081 int iPrec; // Requested decimal precision
1082 PSZ pszSep = NULL; // Thousands separator string
1083 PSZ pszDec = NULL; // Decimal point string
1084 PSZ pszNeg = NULL; // Negative sign string
1085#ifdef LEGACY_C_LOCALE
1086 float fVal = 0; // Input value as floating point
1087 int iVal; // Input value as integer
1088#else
1089 LONGLONG llVal = 0;
1090 long double ldVal = 0,
1091 ldFrac = 0;
1092 PSZ p,
1093 a;
1094 BOOL bNeg = FALSE;
1095 CHAR achFrac[ 16 ];
1096 int rc = 0;
1097#endif
1098
1099 // Make sure we have at least one valid argument (the input number)
1100 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1101
1102#ifdef LEGACY_C_LOCALE
1103
1104 // Use the locale settings from the environment
1105 pszSep = nl_langinfo( THOUSEP );
1106 if ( !pszSep || !strlen(pszSep) ) {
1107 /* If the current locale isn't known to the C runtime, use a common
1108 * known locale for the same language, if possible.
1109 */
1110 PSZ pszLang, p;
1111 if ( DosScanEnv( "LANG", &pszLang ) == NO_ERROR &&
1112 pszLang &&
1113 strlen(pszLang) >= 2 )
1114 {
1115 p = strdup( pszLang );
1116 if ( !strnicmp( p, "en_us", 2 )) setlocale( LC_NUMERIC, "EN_US");
1117 else if ( !strnicmp( p, "en_uk", 2 )) setlocale( LC_NUMERIC, "EN_GB");
1118 else if ( !strnicmp( p, "de", 2 )) setlocale( LC_NUMERIC, "DE_DE");
1119 else if ( !strnicmp( p, "es", 2 )) setlocale( LC_NUMERIC, "ES_ES");
1120 else if ( !strnicmp( p, "fr", 2 )) setlocale( LC_NUMERIC, "FR_FR");
1121 else if ( !strnicmp( p, "it", 2 )) setlocale( LC_NUMERIC, "IT_IT");
1122 else if ( !strnicmp( p, "ja", 2 )) setlocale( LC_NUMERIC, "JA_JP");
1123/* -- it seems the VAC runtime doesn't recognize most of these...
1124 else if ( !strnicmp( p, "ar", 2 )) setlocale( LC_NUMERIC, "ar_AA");
1125 else if ( !strnicmp( p, "be", 2 )) setlocale( LC_NUMERIC, "be_BY");
1126 else if ( !strnicmp( p, "bg", 2 )) setlocale( LC_NUMERIC, "bg_BG");
1127 else if ( !strnicmp( p, "be", 2 )) setlocale( LC_NUMERIC, "be_BY");
1128 else if ( !strnicmp( p, "ca", 2 )) setlocale( LC_NUMERIC, "ca_ES");
1129 else if ( !strnicmp( p, "cs", 2 )) setlocale( LC_NUMERIC, "cs_CZ");
1130 else if ( !strnicmp( p, "da", 2 )) setlocale( LC_NUMERIC, "da_DK");
1131 else if ( !strnicmp( p, "de", 2 )) setlocale( LC_NUMERIC, "de_DE");
1132 else if ( !strnicmp( p, "el", 2 )) setlocale( LC_NUMERIC, "el_GR");
1133 else if ( !strnicmp( p, "es", 2 )) setlocale( LC_NUMERIC, "es_ES");
1134 else if ( !strnicmp( p, "fi", 2 )) setlocale( LC_NUMERIC, "fi_FI");
1135 else if ( !strnicmp( p, "fr", 2 )) setlocale( LC_NUMERIC, "fr_FR");
1136 else if ( !strnicmp( p, "hr", 2 )) setlocale( LC_NUMERIC, "hr_HR");
1137 else if ( !strnicmp( p, "hu", 2 )) setlocale( LC_NUMERIC, "hu_HU");
1138 else if ( !strnicmp( p, "is", 2 )) setlocale( LC_NUMERIC, "is_IS");
1139 else if ( !strnicmp( p, "it", 2 )) setlocale( LC_NUMERIC, "it_IT");
1140 else if ( !strnicmp( p, "iw", 2 )) setlocale( LC_NUMERIC, "iw_IL");
1141 else if ( !strnicmp( p, "ja", 2 )) setlocale( LC_NUMERIC, "ja_JP");
1142 else if ( !strnicmp( p, "ko", 2 )) setlocale( LC_NUMERIC, "ko_KR");
1143 else if ( !strnicmp( p, "mk", 2 )) setlocale( LC_NUMERIC, "mk_MK");
1144 else if ( !strnicmp( p, "nl", 2 )) setlocale( LC_NUMERIC, "nl_NL");
1145 else if ( !strnicmp( p, "no", 2 )) setlocale( LC_NUMERIC, "no_NO");
1146 else if ( !strnicmp( p, "pl", 2 )) setlocale( LC_NUMERIC, "pl_PL");
1147 else if ( !strnicmp( p, "pt", 2 )) setlocale( LC_NUMERIC, "pt_PT");
1148 else if ( !strnicmp( p, "ro", 2 )) setlocale( LC_NUMERIC, "ro_RO");
1149 else if ( !strnicmp( p, "ru", 2 )) setlocale( LC_NUMERIC, "ru_RU");
1150 else if ( !strnicmp( p, "sh", 2 )) setlocale( LC_NUMERIC, "sh_SP");
1151 else if ( !strnicmp( p, "sk", 2 )) setlocale( LC_NUMERIC, "sk_SK");
1152 else if ( !strnicmp( p, "sl", 2 )) setlocale( LC_NUMERIC, "sl_SI");
1153 else if ( !strnicmp( p, "sq", 2 )) setlocale( LC_NUMERIC, "sq_AL");
1154 else if ( !strnicmp( p, "sv", 2 )) setlocale( LC_NUMERIC, "sv_SE");
1155 else if ( !strnicmp( p, "th", 2 )) setlocale( LC_NUMERIC, "th_TH");
1156 else if ( !strnicmp( p, "tr", 2 )) setlocale( LC_NUMERIC, "tr_TR");
1157 else if ( !strnicmp( p, "uk", 2 )) setlocale( LC_NUMERIC, "uk_UA");
1158 else if ( !strnicmp( p, "zh", 2 )) setlocale( LC_NUMERIC, "zh_TW");
1159-- */
1160 else setlocale( LC_NUMERIC, "EN_US");
1161 free(p);
1162 }
1163 else setlocale( LC_NUMERIC, "en_us");
1164 }
1165 else setlocale( LC_NUMERIC, "");
1166
1167 // Check for a decimal place and treat as float or integer accordingly
1168 if ( strchr( argv[0].strptr, '.') != NULL ) {
1169 if (( sscanf( argv[0].strptr, "%f", &fVal )) != 1 ) return ( 40 );
1170 if ( argc >= 2 && ( RXVALIDSTRING(argv[1]) ) &&
1171 (( sscanf( argv[1].strptr, "%d", &iPrec )) == 1 ))
1172 {
1173 // Use user-specified precision
1174 sprintf( achNumber, "%'.*f", iPrec, fVal );
1175 }
1176 else
1177 sprintf( achNumber, "%'.2f", fVal );
1178 }
1179 else {
1180 if (( sscanf( argv[0].strptr, "%d", &iVal )) != 1 ) return ( 40 );
1181 sprintf( achNumber, "%'d", iVal );
1182 }
1183
1184 // Return the formatted number
1185 SaveResultString( prsResult, achNumber, strlen(achNumber) );
1186
1187#else
1188
1189 rc = GetLocaleString( &pszSep, LOCI_sThousand );
1190 if ( rc ) {
1191 // GetLocaleString has already set the error indicator
1192 SaveResultString( prsResult, NULL, 0 );
1193 goto finish;
1194 }
1195 rc = GetLocaleString( &pszDec, LOCI_sDecimal );
1196 if ( rc ) {
1197 // GetLocaleString has already set the error indicator
1198 SaveResultString( prsResult, NULL, 0 );
1199 goto finish;
1200 }
1201 rc = GetLocaleString( &pszNeg, LOCI_sNegativeSign );
1202 if ( rc ) {
1203 // GetLocaleString has already set the error indicator
1204 SaveResultString( prsResult, NULL, 0 );
1205 goto finish;
1206 }
1207
1208 // Check for negative value
1209 //if ( argv[0].strptr == '-') bNeg = TRUE;
1210
1211 // Check for a decimal place and treat as float or integer accordingly
1212 if ( strchr( argv[0].strptr, '.') != NULL ) {
1213 if (( sscanf( argv[0].strptr, "%Lf", &ldVal )) != 1 ) return ( 40 );
1214
1215 llVal = ldVal;
1216 ldFrac = ldVal - llVal;
1217 if ( argc >= 2 && ( RXVALIDSTRING(argv[1]) ) &&
1218 (( sscanf( argv[1].strptr, "%d", &iPrec )) == 1 ))
1219 {
1220 // Use user-specified precision
1221 sprintf( achFrac, "%.*Lf", iPrec, ldFrac );
1222 }
1223 else
1224 sprintf( achFrac, "%.2Lf", ldFrac );
1225
1226 // Format the integer part
1227 a = achNumber;
1228 if ( llVal < 0 ) {
1229 sprintf( a, pszNeg );
1230 a += strlen( pszNeg );
1231 }
1232 GroupNumber( a, llabs( llVal ), pszSep );
1233 // Append the fractional part
1234 if (( p = strchr( achFrac, '.')) != NULL ) {
1235 strncat( achNumber, pszDec, US_NUMSTR_MAXZ - 1 );
1236 strncat( achNumber, p+1, US_NUMSTR_MAXZ - 1 );
1237 }
1238 }
1239 else {
1240 if (( sscanf( argv[0].strptr, "%lld", &llVal )) != 1 ) return ( 40 );
1241 a = achNumber;
1242 if ( llVal < 0 ) {
1243 sprintf( a, pszNeg );
1244 a += strlen( pszNeg );
1245 }
1246 GroupNumber( a, llabs( llVal ), pszSep );
1247 }
1248
1249 // Return the formatted number
1250 SaveResultString( prsResult, achNumber, strlen(achNumber) );
1251
1252finish:
1253 if ( pszSep ) free( pszSep );
1254 if ( pszDec ) free( pszDec );
1255 if ( pszNeg ) free( pszNeg );
1256
1257#endif
1258
1259 return ( 0 );
1260}
1261
1262
1263/* ------------------------------------------------------------------------- *
1264 * Sys2FormatTime *
1265 * *
1266 * Convert a number of seconds from the epoch (1970-01-01 0:00:00 UTC) into *
1267 * a formatted date and time string. *
1268 * *
1269 * REXX ARGUMENTS: *
1270 * 1. Number of seconds (a positive integer) to be converted. (REQUIRED) *
1271 * 2. Format type, one of: *
1272 * D = return in the form 'yyyy-mm-dd hh:mm:ss (w)' where w *
1273 * represents the weekday (0-6 where 0=Sunday) (DEFAULT) *
1274 * I = return in ISO8601 combined form 'yyyy-mm-ddThh:mm:ss[Z]' *
1275 * L = return in the form 'day month year (weekday) time' where month *
1276 * and weekday are language-dependent abbreviations *
1277 * Note: With D and I, time is returned in 24-hour format; L may vary. *
1278 * 3. TZ conversion flag (indicates whether to convert to UTC from local *
1279 * time), one of: *
1280 * U = return in Coordinated Universal Time *
1281 * L = convert to local time using the current TZ (DEFAULT) *
1282 * *
1283 * REXX RETURN VALUE: The formatted time string, or "" on error. *
1284 * ------------------------------------------------------------------------- */
1285ULONG APIENTRY Sys2FormatTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1286{
1287 UCHAR szFormat[ US_TIMESTR_MAXZ ] = {0}, // strftime() format specifier
1288 szTime[ US_TIMESTR_MAXZ ] = {0}; // Formatted time string
1289 BYTE flFormat = FL_TIME_DEFAULT; // Time format flag
1290 BOOL fUTC = FALSE; // UTC/local conversion flag
1291 PSZ pszTZ, // Pointer to TZ environment var
1292 pszSetTZ;
1293 int iEpoch; // Input epoch time
1294 time_t ttSeconds; // Input timestamp (seconds)
1295 struct tm *timeptr; // Timestamp structure
1296 size_t stRC; // return code from strftime()
1297
1298 // Reset the error indicator
1299 WriteErrorCode( 0, NULL );
1300
1301 // All arguments are optional but must be correct if specified
1302
1303 if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) {
1304 // first argument: epoch time value
1305 if (( sscanf( argv[0].strptr, "%d", &iEpoch )) != 1 ) return ( 40 );
1306 ttSeconds = (time_t) iEpoch;
1307 }
1308
1309 if ( argc >= 2 ) {
1310 // second argument: format flag
1311 if ( RXVALIDSTRING(argv[1]) ) {
1312 strupr( argv[1].strptr );
1313 if ( strcspn(argv[1].strptr, "DIL") > 0 ) return ( 40 );
1314 switch ( argv[1].strptr[0] ) {
1315 case 'I': flFormat = FL_TIME_ISO8601; break;
1316 case 'L': flFormat = FL_TIME_LOCALE; break;
1317 default : flFormat = FL_TIME_DEFAULT; break;
1318 }
1319 }
1320 }
1321
1322 if ( argc >= 3 ) {
1323 // third argument: conversion flag
1324 if ( RXVALIDSTRING(argv[2]) ) {
1325 strupr( argv[2].strptr );
1326 if ( strcspn(argv[2].strptr, "UL") > 0 ) return ( 40 );
1327 switch ( argv[2].strptr[0] ) {
1328 case 'U': fUTC = TRUE; break;
1329 default : fUTC = FALSE; break;
1330 }
1331 }
1332 }
1333
1334 /* These next 4 lines really shouldn't be necessary, but without them
1335 * getenv() and (apparently) tzset() may see the value of TZ as NULL
1336 * if the environment variable was changed in the REXX script.
1337 */
1338 DosScanEnv("TZ", &pszTZ );
1339 pszSetTZ = (PSZ) malloc( strlen(pszTZ) + 5 );
1340 if ( pszSetTZ ) {
1341 sprintf( pszSetTZ, "TZ=%s", pszTZ );
1342 putenv( pszSetTZ );
1343 }
1344
1345 // Use the locale and timezone settings from the environment
1346 tzset();
1347 setlocale( LC_TIME, "");
1348
1349 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) {
1350 ttSeconds = time( NULL );
1351 if ( ttSeconds == -1 ) {
1352 WriteErrorCode( ttSeconds, "time");
1353 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1354 if ( pszSetTZ ) free( pszSetTZ );
1355 return 0;
1356 }
1357 }
1358
1359 if ( fUTC ) {
1360 timeptr = gmtime( &ttSeconds );
1361 if ( !timeptr ) {
1362 WriteErrorCode( 1, "gmtime");
1363 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL // 2016-02-20 SHL
1364 if ( pszSetTZ ) free( pszSetTZ );
1365 return 0;
1366 }
1367 }
1368 else {
1369 timeptr = localtime( &ttSeconds );
1370 if ( !timeptr ) {
1371 WriteErrorCode( 1, "localtime");
1372 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1373 if ( pszSetTZ ) free( pszSetTZ );
1374 return 0;
1375 }
1376 }
1377
1378 switch ( flFormat ) {
1379 default:
1380 case FL_TIME_DEFAULT:
1381 sprintf( szFormat, "%%Y-%%m-%%d %%T (%%w)");
1382 break;
1383
1384 case FL_TIME_ISO8601:
1385 sprintf( szFormat, "%%Y-%%m-%%dT%%T");
1386 if ( fUTC ) strcat( szFormat, "Z");
1387 break;
1388
1389 case FL_TIME_LOCALE:
1390 sprintf( szFormat, "%%e %%b %%Y (%%a) %%X");
1391 break;
1392 }
1393
1394 stRC = strftime( szTime, US_TIMESTR_MAXZ-1, szFormat, timeptr );
1395 if ( stRC == NO_ERROR ) {
1396 WriteErrorCode( stRC, "strftime");
1397 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1398 if ( pszSetTZ ) free( pszSetTZ );
1399 return ( 0 );
1400 }
1401
1402 // Return the formatted time string
1403 SaveResultString( prsResult, szTime, strlen(szTime) ); // 2016-02-20 SHL
1404
1405 if ( pszSetTZ ) free( pszSetTZ );
1406 return ( 0 );
1407}
1408
1409
1410/* ------------------------------------------------------------------------- *
1411 * Sys2GetEpochTime *
1412 * *
1413 * Convert formatted date and time into a number of seconds (UTC) from the *
1414 * epoch (defined as 1970-01-01 0:00:00). The input time is assumed to *
1415 * refer to the current timezone as defined in the TZ environment variable. *
1416 * *
1417 * If no parameters are specified, the current system time is used. If at *
1418 * least one parameter is specified, then any missing parameter is assumed *
1419 * to be its minimum possible value. *
1420 * *
1421 * Due to limitations in time_t, dates later than 2037 are not supported; *
1422 * the IBM library seems to convert them all to January 1 1970 00:00:00 UTC. *
1423 * *
1424 * REXX ARGUMENTS: *
1425 * 1. The year (0-99 or 1970+) (value <70 is assumed to be 20xx) *
1426 * 2. The month (1-12) *
1427 * 3. The day (1-31) *
1428 * 4. Hours (0-23) *
1429 * 5. Minutes (0-59) *
1430 * 6. Seconds (0-61) *
1431 * *
1432 * REXX RETURN VALUE: The number of seconds since the epoch, or 0 on error. *
1433 * ------------------------------------------------------------------------- */
1434ULONG APIENTRY Sys2GetEpochTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1435{
1436 ULONG ulYear = 1970, // Input year
1437 ulMonth = 1, // Input month
1438 ulDay = 1, // Input day
1439 ulHour = 0, // Input hours
1440 ulMin = 0, // Input minutes
1441 ulSec = 0; // Input seconds
1442 BOOL fYear = FALSE, // Year parameter specified?
1443 fMonth = FALSE, // Month parameter specified?
1444 fDay = FALSE, // Day parameter specified?
1445 fHour = FALSE, // Hours parameter specified?
1446 fMin = FALSE, // Minutes parameter specified?
1447 fSec = FALSE; // Seconds parameter specified?
1448 //SHORT sDST = 0; // Input time is DST?
1449 time_t timeval; // Calculated epoch time
1450 struct tm tsTime = {0}; // Time structure for mktime()
1451 UCHAR szEpochTime[ US_INTEGER_MAXZ ]; // Output string
1452 PSZ pszTZ,
1453 pszSetTZ;
1454
1455
1456 // Reset the error indicator
1457 WriteErrorCode( 0, NULL );
1458
1459 // Parse the various time items
1460 if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) {
1461 if (( sscanf( argv[0].strptr, "%u", &ulYear )) != 1 ) return ( 40 );
1462 if ( ulYear < 100 ) {
1463 ulYear += (ulYear < 70) ? 2000 : 1900;
1464 }
1465 if ( ulYear < 1970 ) return ( 40 );
1466 fYear = TRUE;
1467 }
1468 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1469 if (( sscanf( argv[1].strptr, "%u", &ulMonth )) != 1 ) return ( 40 );
1470 if ( ulMonth < 1 || ulMonth > 12 ) return ( 40 );
1471 fMonth = TRUE;
1472 }
1473 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
1474 if (( sscanf( argv[2].strptr, "%u", &ulDay )) != 1 ) return ( 40 );
1475 if ( ulDay < 1 || ulDay > 31 ) return ( 40 );
1476 fDay = TRUE;
1477 }
1478 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
1479 if (( sscanf( argv[3].strptr, "%u", &ulHour )) != 1 ) return ( 40 );
1480 if ( ulHour > 23 ) return ( 40 );
1481 fHour = TRUE;
1482 }
1483 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
1484 if (( sscanf( argv[4].strptr, "%u", &ulMin )) != 1 ) return ( 40 );
1485 if ( ulMin > 59 ) return ( 40 );
1486 fMin = TRUE;
1487 }
1488 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
1489 if (( sscanf( argv[5].strptr, "%u", &ulSec )) != 1 ) return ( 40 );
1490 if ( ulSec > 61 ) return ( 40 );
1491 fSec = TRUE;
1492 }
1493 if ( argc >= 7 ) return ( 40 );
1494/*
1495 // Parse the conversion flag
1496 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1497 strupr( argv[6].strptr );
1498 if ( strcspn(argv[6].strptr, "SD") > 0 ) return ( 40 );
1499 switch ( argv[6].strptr[0] ) {
1500 case 'S': sDST = 0; break;
1501 case 'D': sDST = 1; break;
1502 default : sDST = -1; break;
1503 }
1504 }
1505*/
1506
1507 /* These next 4 lines really shouldn't be necessary, but without them
1508 * getenv() and (apparently) tzset() may see the value of TZ as NULL
1509 * if the environment variable was changed in the REXX script.
1510 */
1511 DosScanEnv("TZ", &pszTZ );
1512 pszSetTZ = (PSZ) malloc( strlen(pszTZ) + 5 );
1513 sprintf( pszSetTZ, "TZ=%s", pszTZ );
1514 putenv( pszSetTZ );
1515
1516// This seems to conflict with time() under some shells -AT
1517 tzset();
1518
1519 // Use the locale settings from the environment
1520 setlocale( LC_TIME, "");
1521
1522 if ( !fYear && !fMonth && !fDay && !fHour && !fMin && !fSec ) {
1523 timeval = time( NULL );
1524 if ( timeval == -1 ) {
1525 WriteErrorCode( timeval, "time");
1526 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1527 free( pszSetTZ );
1528 return 0;
1529 }
1530 }
1531 else {
1532//printf("TZ=%s\n", getenv("TZ"));
1533 tsTime.tm_sec = ulSec;
1534 tsTime.tm_min = ulMin;
1535 tsTime.tm_hour = ulHour;
1536 tsTime.tm_mday = ulDay;
1537 tsTime.tm_mon = ulMonth - 1;
1538 tsTime.tm_year = ulYear - 1900;
1539 tsTime.tm_isdst = -1;
1540 timeval = mktime( &tsTime );
1541 if ( timeval == -1 ) {
1542 WriteErrorCode( timeval, "mktime");
1543 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1544 free( pszSetTZ );
1545 return 0;
1546 }
1547 }
1548
1549 // Return the calculated time value
1550#if __IBMC__ >= 360 || __IBMCPP__ >= 360
1551 sprintf( szEpochTime, "%.0f", timeval );
1552#else
1553 sprintf( szEpochTime, "%d", timeval );
1554#endif
1555 SaveResultString( prsResult, szEpochTime, strlen(szEpochTime) ); // 2016-02-20 SHL
1556
1557 free( pszSetTZ );
1558 return ( 0 );
1559}
1560
1561
1562/* ------------------------------------------------------------------------- *
1563 * Sys2LocateDLL *
1564 * *
1565 * Search for an installed or loaded DLL by module name. *
1566 * Code derived from 'whichdll' by Alessandro Cantatore (public domain). *
1567 * *
1568 * REXX ARGUMENTS: *
1569 * 1. The name of the DLL to search for. (REQUIRED) *
1570 * 2. Flag to limit search context, must be one of: *
1571 * ALL : Search for both loaded and loadable DLLs (DEFAULT) *
1572 * LOADEDONLY: Search only for currently-loaded DLLs *
1573 * Only the first letter (A/L) is significant. *
1574 * *
1575 * REXX RETURN VALUE: *
1576 * The fully-qualified path of the DLL, if found (or "" if not found). *
1577 * ------------------------------------------------------------------------- */
1578ULONG APIENTRY Sys2LocateDLL( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1579{
1580 HMODULE hmod;
1581 CHAR achModuleName[ CCHMAXPATH ];
1582 BOOL bLoadedOnly = FALSE,
1583 bUnload = FALSE;
1584 APIRET rc;
1585
1586 // Reset the error indicator
1587 WriteErrorCode( 0, NULL );
1588
1589 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1590
1591 // Second argument: flag
1592 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1593 strupr( argv[1].strptr );
1594 if ( strcspn(argv[1].strptr, "AL") > 0 ) return ( 40 );
1595 switch ( argv[1].strptr[0] ) {
1596 case 'A': bLoadedOnly = FALSE; break;
1597 case 'L': bLoadedOnly = TRUE; break;
1598 default : return ( 40 );
1599 }
1600 }
1601
1602 // See if the DLL is already loaded
1603 rc = DosQueryModuleHandle( argv[0].strptr, &hmod );
1604 if ( rc ) {
1605 // Guess not...
1606 if ( bLoadedOnly ) {
1607 // Just return
1608 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1609 return 0;
1610 }
1611 // Try to load it now
1612 rc = DosLoadModule( NULL, 0, argv[0].strptr, &hmod );
1613 if ( rc ) {
1614 WriteErrorCode( rc, "DosLoadModule");
1615 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1616 return 0;
1617 }
1618 bUnload = TRUE;
1619 }
1620
1621 // Get the full path name of the DLL
1622 rc = DosQueryModuleName( hmod, CCHMAXPATH, achModuleName );
1623 if ( rc ) {
1624 WriteErrorCode( rc, "DosQueryModuleName");
1625 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1626 if ( bUnload ) DosFreeModule( hmod );
1627 return 0;
1628 }
1629
1630 // Free the module if we loaded it ourselves
1631 if ( bUnload ) DosFreeModule( hmod );
1632
1633 // Return the full path name
1634 SaveResultString( prsResult, achModuleName, strlen(achModuleName) ); // 2016-02-20 SHL
1635
1636 return 0;
1637}
1638
1639
1640/* ------------------------------------------------------------------------- *
1641 * Sys2CreateNamedPipe *
1642 * *
1643 * Create a named pipe with the specified name and parameters. Only byte *
1644 * mode is supported; message mode is not. *
1645 * *
1646 * REXX ARGUMENTS: *
1647 * 1. The name of the pipe, in the form "\PIPE\something". (REQUIRED) *
1648 * 2. The size of the outbound buffer, in bytes. (REQUIRED) *
1649 * 3. The size of the inbound buffer, in bytes. (REQUIRED) *
1650 * 4. The pipe's timeout value, in milliseconds. (DEFAULT: 3000) *
1651 * 5. The number of simultaneous instances of this pipe which are allowed. *
1652 * Must be between 1 and 254, or 0 indicating no limit. (DEFAULT: 1) *
1653 * 6. Pipe blocking mode, one of: *
1654 * W = WAIT mode, read and write block waiting for data. (DEFAULT) *
1655 * N = NOWAIT mode, read and write return immediately. *
1656 * 7. Pipe mode, one of: *
1657 * I = Inbound pipe (DEFAULT) *
1658 * O = Outbound pipe *
1659 * D = Duplex (inbound/outbound) pipe *
1660 * 8. Privacy/inheritance flag, one of: *
1661 * 0 = The pipe handle is inherited by child processes. (DEFAULT) *
1662 * 1 = The pipe handle is private to the current process. *
1663 * 9. Write-through flag, one of: *
1664 * 0 = Allow delayed writes (write-behind) to remote pipes. (DEFAULT) *
1665 * 1 = Force immediate writes (write-through) to remote pipes. *
1666 * *
1667 * REXX RETURN VALUE: *
1668 * A four-byte pipe handle or 0 if create fails *
1669 * ------------------------------------------------------------------------- */
1670ULONG APIENTRY Sys2CreateNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1671{
1672 HPIPE hp;
1673 PSZ pszNPName;
1674 LONG iLimit;
1675 ULONG ulBufOut,
1676 ulBufIn,
1677 ulTimeout = 3000,
1678 flOpen = 0,
1679 flPipe = 1;
1680 CHAR achHandle[ 9 ];
1681 APIRET rc;
1682
1683 // Reset the error indicator
1684 WriteErrorCode( 0, NULL );
1685
1686 // Make sure we have at least three valid arguments (pipe name and sizes)
1687 if ( argc < 3 || ( !RXVALIDSTRING(argv[0]) ) ||
1688 ( !RXVALIDSTRING(argv[1]) ) || ( !RXVALIDSTRING(argv[2]) ))
1689 return ( 40 );
1690
1691 // (Validate the first argument last to simplify error processing)
1692
1693 // Second argument: pipe outbound buffer size
1694 if (( sscanf( argv[1].strptr, "%u", &ulBufOut )) != 1 ) return ( 40 );
1695
1696 // Third argument: pipe outbound buffer size
1697 if (( sscanf( argv[2].strptr, "%u", &ulBufIn )) != 1 ) return ( 40 );
1698
1699 // Fourth argument: pipe timeout value
1700 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
1701 if (( sscanf( argv[3].strptr, "%u", &ulTimeout )) != 1 ) return ( 40 );
1702 }
1703
1704 // Fifth argument: instances limit
1705 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
1706 if (( sscanf( argv[4].strptr, "%d", &iLimit )) != 1 ) return ( 40 );
1707 if (( iLimit > 1 ) && ( iLimit < 255 ))
1708 flPipe = iLimit;
1709 else if ( !iLimit || ( iLimit == -1 ))
1710 flPipe = NP_UNLIMITED_INSTANCES;
1711 else
1712 return ( 40 );
1713 }
1714
1715 // Sixth argument: blocking mode
1716 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
1717 strupr( argv[5].strptr );
1718 if ( argv[5].strptr[0] == 'N' )
1719 flPipe |= NP_NOWAIT;
1720 else if ( argv[5].strptr[0] != 'W' )
1721 return ( 40 );
1722 }
1723
1724 // Seventh argument: pipe mode (direction)
1725 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1726 strupr( argv[6].strptr );
1727 if (strcspn(argv[6].strptr, "IOD") > 0 ) return ( 40 );
1728 switch ( argv[6].strptr[0] ) {
1729 case 'O': flOpen |= NP_ACCESS_OUTBOUND; break;
1730 case 'D': flOpen |= NP_ACCESS_DUPLEX; break;
1731 default : break; // default is 0
1732 }
1733 }
1734
1735 // Eighth argument: inheritance mode
1736 if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) {
1737 strupr( argv[7].strptr );
1738 if ( argv[7].strptr[0] == '1' )
1739 flOpen |= NP_NOINHERIT;
1740 else if ( argv[7].strptr[0] != '0' )
1741 return ( 40 );
1742 }
1743
1744 // Ninth argument: write mode
1745 if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) {
1746 strupr( argv[8].strptr );
1747 if ( argv[8].strptr[0] == '1' )
1748 flOpen |= NP_NOWRITEBEHIND;
1749 else if ( argv[8].strptr[0] != '0' )
1750 return ( 40 );
1751 }
1752
1753 // Now the first argument: pipe name
1754 pszNPName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
1755 if ( pszNPName == NULL ) {
1756 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1757 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1758 return ( 0 );
1759 }
1760 strncpy( pszNPName, argv[0].strptr, RXSTRLEN(argv[0]) );
1761
1762 // All good, now create the pipe
1763 rc = DosCreateNPipe( pszNPName, &hp, flOpen, flPipe, ulBufOut, ulBufIn, ulTimeout );
1764 if (rc) {
1765 WriteErrorCode( rc, "DosCreateNPipe");
1766 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1767 return 0;
1768 }
1769
1770 // Return the handle as the REXX result string
1771 sprintf( achHandle, "%8X", hp );
1772 SaveResultString( prsResult, achHandle, strlen(achHandle) ); // 2016-02-20 SHL
1773
1774 free( pszNPName );
1775 return ( 0 );
1776}
1777
1778
1779/* ------------------------------------------------------------------------- *
1780 * Sys2ConnectNamedPipe *
1781 * *
1782 * Start 'listening' by allowing clients to connect to a previously-created *
1783 * named pipe. *
1784 * *
1785 * REXX ARGUMENTS: *
1786 * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) *
1787 * *
1788 * REXX RETURN VALUE: *
1789 * 1 on success, or 0 if an error occurred. *
1790 * ------------------------------------------------------------------------- */
1791ULONG APIENTRY Sys2ConnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1792{
1793 HPIPE hp;
1794 ULONG ulState = 0;
1795 APIRET rc;
1796
1797 // Reset the error indicator
1798 WriteErrorCode( 0, NULL );
1799
1800 // Parse the handle
1801 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1802 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1803
1804 // Determine the pipe mode
1805 DosQueryNPHState( hp, &ulState );
1806
1807 // Connect the pipe
1808 rc = DosConnectNPipe( hp );
1809
1810 // A non-blocking pipe returns ERROR_PIPE_NOT_CONNECTED on success
1811 if ((( ulState & NP_NOWAIT ) && ( rc != ERROR_PIPE_NOT_CONNECTED )) ||
1812 ( rc != NO_ERROR ))
1813 {
1814 WriteErrorCode( rc, "DosConnectNPipe");
1815 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1816 return ( 0 );
1817 }
1818
1819 // Return 1 on success
1820 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
1821 return ( 0 );
1822}
1823
1824
1825/* ------------------------------------------------------------------------- *
1826 * Sys2DisconnectNamedPipe *
1827 * *
1828 * Unlocks a named pipe after a client has closed its connection. *
1829 * *
1830 * REXX ARGUMENTS: *
1831 * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) *
1832 * *
1833 * REXX RETURN VALUE: *
1834 * 1 on success, or 0 if an error occurred. *
1835 * ------------------------------------------------------------------------- */
1836ULONG APIENTRY Sys2DisconnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1837{
1838 HPIPE hp;
1839 APIRET rc;
1840
1841 // Reset the error indicator
1842 WriteErrorCode( 0, NULL );
1843
1844 // Parse the handle
1845 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1846 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1847
1848 // Connect the pipe
1849 rc = DosDisConnectNPipe( hp );
1850 if ( rc != NO_ERROR ) {
1851 WriteErrorCode( rc, "DosDisConnectNPipe");
1852 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
1853 return ( 0 );
1854 }
1855
1856 // Return 1 on success
1857 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
1858 return ( 0 );
1859}
1860
1861
1862/* ------------------------------------------------------------------------- *
1863 * Sys2CheckNamedPipe *
1864 * *
1865 * Check the status of a named pipe. *
1866 * *
1867 * REXX ARGUMENTS: *
1868 * 1. The pipe handle (from Sys2CreateNamedPipe or DosOpen). (REQUIRED) *
1869 * *
1870 * REXX RETURN VALUE: *
1871 * String of the format "bytes status", where bytes is the number of bytes *
1872 * currently waiting in the pipe, and status is one of: DISCONNECTED, *
1873 * LISTENING, CONNECTED, or CLOSING or "" if API error *
1874 * ------------------------------------------------------------------------- */
1875ULONG APIENTRY Sys2CheckNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1876{
1877 HPIPE hp;
1878 ULONG cbActual, ulState;
1879 AVAILDATA avd;
1880 CHAR szStatus[ US_PIPESTATUS_MAXZ ];
1881 APIRET rc;
1882
1883 // Reset the error indicator
1884 WriteErrorCode( 0, NULL );
1885
1886 // Parse the handle
1887 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1888 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1889
1890 rc = DosPeekNPipe( hp, NULL, 0, &cbActual, &avd, &ulState );
1891 if ( rc != NO_ERROR ) {
1892 WriteErrorCode( rc, "DosPeekNPipe");
1893 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
1894 return ( 0 );
1895 }
1896 sprintf( szStatus, "%u ", avd.cbpipe );
1897 switch ( ulState ) {
1898 case NP_STATE_DISCONNECTED: strncat( szStatus, "DISCONNECTED", US_PIPESTATUS_MAXZ-1 ); break;
1899 case NP_STATE_LISTENING: strncat( szStatus, "LISTENING", US_PIPESTATUS_MAXZ-1 ); break;
1900 case NP_STATE_CONNECTED: strncat( szStatus, "CONNECTED", US_PIPESTATUS_MAXZ-1 ); break;
1901 case NP_STATE_CLOSING: strncat( szStatus, "CLOSING", US_PIPESTATUS_MAXZ-1 ); break;
1902 default: strncat( szStatus, "UNKNOWN", US_PIPESTATUS_MAXZ-1 ); break;
1903 }
1904
1905 SaveResultString( prsResult, szStatus, strlen(szStatus) ); // 2016-02-20 SHL
1906
1907 return ( 0 );
1908}
1909
1910
1911/* ------------------------------------------------------------------------- *
1912 * Sys2Open *
1913 * *
1914 * Wrapper to DosOpenL: open a file or stream (with >2GB support). *
1915 * Direct-DASD mode is not supported by this function, nor is setting the *
1916 * initial extended attributes. *
1917 * *
1918 * REXX ARGUMENTS: *
1919 * 1. Name of file or stream to open. (REQUIRED) *
1920 * 2. Open action flags, must be either "O" (open if exists), "R" (replace *
1921 * if exists), or nothing (fail if exists), optionally followed by "C" *
1922 * (create if file does not exist). If "C" is not specified, the *
1923 * operation will fail if the file does not exist. Note that a value *
1924 * of "" alone will therefore fail automatically. (DEFAULT: "O") *
1925 * In summary, the possible combinations are: *
1926 * O = Open only (if file exists, open it; if not, fail) *
1927 * OC= Open/create (if file exists, open it; if not, create it) *
1928 * R = Replace only (if file exists, replace it; if not, fail) *
1929 * RC= Replace/create (if file exists, replace it; if not, create it) *
1930 * C = Create only (if file exists, fail; if not, create it) *
1931 * (empty) = No-op (if file exists, fail; if not, fail) *
1932 * 3. Access mode flags, one or both of: (DEFAULT: "RW") *
1933 * R = Open file with read access. *
1934 * W = Open file with write access. *
1935 * 4. Sharing mode flags, any combination of: (DEFAULT: "W") *
1936 * R = Deny read access to other processes *
1937 * W = Deny write access to other processes *
1938 * 5. Deny legacy DosOpen access, one of: *
1939 * 0 = Allow DosOpen to access the file (DEFAULT) *
1940 * 1 = Deny access using the DosOpen API *
1941 * 6. Privacy/inheritance flag, one of: *
1942 * 0 = The file handle is inherited by child processes. (DEFAULT) *
1943 * 1 = The file handle is private to the current process. *
1944 * 7. Initial file attributes when creating a file: (DEFAULT: "") *
1945 * A = Archive attribute set *
1946 * D = Directory attribute set *
1947 * S = System attribute set *
1948 * H = Hidden attribute set *
1949 * R = Read-only attribute set *
1950 * 8. Initial file size when creating or replacing a file; ignored if *
1951 * access mode is read-only. (DEFAULT: 0) *
1952 * 9. I/O mode flags, any or all of: (DEFAULT: "") *
1953 * T = Write-through mode (default is normal write) *
1954 * N = No-cache mode (default is to use filesystem cache) *
1955 * S = Sequential access *
1956 * R = Random access *
1957 * * S and R can combine as follows: *
1958 * Neither: No locality known (default) *
1959 * S only: Mainly sequential access *
1960 * R only: Mainly random access *
1961 * Both: Random/sequential (i.e. random with some locality) *
1962 * *
1963 * REXX RETURN VALUE: *
1964 * File handle, or "" in case of error. *
1965 * ------------------------------------------------------------------------- */
1966ULONG APIENTRY Sys2Open( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1967{
1968 PSZ pszFile;
1969 HFILE hf;
1970 ULONG fsAction = 0,
1971 fsMode = 0,
1972 ulResult = 0,
1973 ulAttr = FILE_NORMAL;
1974 LONGLONG llSize = {0};
1975 CHAR achHandle[ 9 ];
1976 APIRET rc;
1977
1978
1979 // Reset the error indicator
1980 WriteErrorCode( 0, NULL );
1981
1982 // Make sure we have at least one valid argument (the file name)
1983 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) ))
1984 return ( 40 );
1985
1986 // (Validate the first argument last to simplify error processing)
1987
1988 // Second argument: open action
1989 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1990 strupr( argv[1].strptr );
1991 if ( strcspn(argv[1].strptr, "OCR") > 0 ) return ( 40 );
1992 if ( strchr(argv[1].strptr, 'O'))
1993 fsAction |= OPEN_ACTION_OPEN_IF_EXISTS;
1994 else if ( strchr(argv[1].strptr, 'R'))
1995 fsAction |= OPEN_ACTION_REPLACE_IF_EXISTS;
1996 if ( strchr(argv[1].strptr, 'C'))
1997 fsAction |= OPEN_ACTION_CREATE_IF_NEW;
1998 }
1999 else
2000 fsAction = OPEN_ACTION_OPEN_IF_EXISTS;
2001
2002 // Third argument: access mode
2003 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
2004 strupr( argv[2].strptr );
2005 if ( strcspn(argv[2].strptr, "RW") > 0 ) return ( 40 );
2006 if ( strchr(argv[2].strptr, 'R')) {
2007 if (strchr(argv[2].strptr, 'W'))
2008 fsMode = OPEN_ACCESS_READWRITE;
2009 else
2010 fsMode = OPEN_ACCESS_READONLY;
2011 }
2012 else if (strchr(argv[2].strptr, 'W'))
2013 fsMode = OPEN_ACCESS_WRITEONLY;
2014 else
2015 return ( 40 );
2016 }
2017 else
2018 fsMode = OPEN_ACCESS_READWRITE;
2019
2020 // Fourth argument: sharing mode
2021 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
2022 strupr( argv[3].strptr );
2023 if ( strcspn(argv[3].strptr, "RW") > 0 ) return ( 40 );
2024 if ( strchr(argv[3].strptr, 'R')) {
2025 if (strchr(argv[3].strptr, 'W'))
2026 fsMode |= OPEN_SHARE_DENYREADWRITE;
2027 else
2028 fsMode |= OPEN_SHARE_DENYREAD;
2029 }
2030 else if (strchr(argv[3].strptr, 'W'))
2031 fsMode |= OPEN_SHARE_DENYWRITE;
2032 else
2033 fsMode |= OPEN_SHARE_DENYNONE;
2034 }
2035 else
2036 fsMode |= OPEN_SHARE_DENYNONE;
2037
2038 // Fifth argument: deny legacy mode
2039 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
2040 strupr( argv[4].strptr );
2041 if ( argv[4].strptr[0] == '1' )
2042 fsMode |= OPEN_SHARE_DENYLEGACY;
2043 else if ( argv[4].strptr[0] != '0' )
2044 return ( 40 );
2045 }
2046
2047 // Sixth argument: inheritance mode
2048 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
2049 strupr( argv[5].strptr );
2050 if ( argv[5].strptr[0] == '1' )
2051 fsMode |= OPEN_FLAGS_NOINHERIT;
2052 else if ( argv[5].strptr[0] != '0' )
2053 return ( 40 );
2054 }
2055
2056 // Seventh argument: attributes
2057 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
2058 strupr( argv[6].strptr );
2059 if (strcspn(argv[6].strptr, "ADSHR") > 0 ) return ( 40 );
2060 if ( strchr(argv[6].strptr, 'A')) ulAttr |= FILE_ARCHIVED;
2061 if ( strchr(argv[6].strptr, 'D')) ulAttr |= FILE_DIRECTORY;
2062 if ( strchr(argv[6].strptr, 'S')) ulAttr |= FILE_SYSTEM;
2063 if ( strchr(argv[6].strptr, 'H')) ulAttr |= FILE_HIDDEN;
2064 if ( strchr(argv[6].strptr, 'R')) ulAttr |= FILE_READONLY;
2065 }
2066
2067 // Eighth argument: initial size
2068 if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) {
2069 if (( sscanf( argv[7].strptr, "%lld", &llSize )) != 1 ) return ( 40 );
2070 }
2071
2072 // Ninth argument: I/O mode flags
2073 if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) {
2074 strupr( argv[8].strptr );
2075 if (strcspn(argv[8].strptr, "TNSR") > 0 ) return ( 40 );
2076 if ( strchr(argv[8].strptr, 'T')) fsMode |= OPEN_FLAGS_WRITE_THROUGH;
2077 if ( strchr(argv[8].strptr, 'N')) fsMode |= OPEN_FLAGS_NO_CACHE;
2078 if ( strchr(argv[8].strptr, 'S')) fsMode |= OPEN_FLAGS_SEQUENTIAL;
2079 if ( strchr(argv[8].strptr, 'R')) fsMode |= OPEN_FLAGS_RANDOM;
2080 }
2081
2082 // Now the first argument: file name
2083 pszFile = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
2084 if ( pszFile == NULL ) {
2085 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
2086 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
2087 return ( 0 );
2088 }
2089 strncpy( pszFile, argv[0].strptr, RXSTRLEN(argv[0]) );
2090
2091 // Try and open the file
2092 rc = DosOpenL( pszFile, &hf, &ulResult, llSize, ulAttr, fsAction, fsMode, NULL );
2093 if (rc) {
2094 WriteErrorCode( rc, "DosOpenL");
2095 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
2096 free( pszFile );
2097 return ( 0 );
2098 }
2099
2100 // Return the handle as the REXX result string
2101 sprintf( achHandle, "%8X", hf );
2102 SaveResultString( prsResult, achHandle, strlen(achHandle) ); // 2016-02-20 SHL
2103
2104 free( pszFile );
2105 return ( 0 );
2106}
2107
2108
2109/* ------------------------------------------------------------------------- *
2110 * Sys2Close *
2111 * *
2112 * Wrapper to DosClose: close a file/stream. *
2113 * *
2114 * REXX ARGUMENTS: *
2115 * 1. File handle (returned by Sys2Open) (REQUIRED) *
2116 * *
2117 * REXX RETURN VALUE: *
2118 * 1 on success, or 0 if an error occurred. *
2119 * ------------------------------------------------------------------------- */
2120ULONG APIENTRY Sys2Close( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2121{
2122 HFILE hf;
2123 APIRET rc;
2124
2125 // Reset the error indicator
2126 WriteErrorCode( 0, NULL );
2127
2128 // Make sure we have exactly one valid argument (the file handle)
2129 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) ))
2130 return ( 40 );
2131 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2132
2133 // Close the file
2134 rc = DosClose( hf );
2135 if ( rc != NO_ERROR ) {
2136 WriteErrorCode( rc, "DosClose");
2137 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
2138 }
2139 else {
2140 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
2141 }
2142
2143 return ( 0 );
2144}
2145
2146
2147/* ------------------------------------------------------------------------- *
2148 * Sys2Seek *
2149 * *
2150 * Wrapper to DosSetFilePtrL: move the read/write pointer to the specified *
2151 * location in a stream. *
2152 * *
2153 * REXX ARGUMENTS: *
2154 * 1. File handle (returned by Sys2Open) (REQUIRED) *
2155 * 2. The signed distance in bytes to move (REQUIRED) *
2156 * 3. Move method, one of: *
2157 * B = Beginning of file *
2158 * C = Current position (DEFAULT) *
2159 * E = End of file *
2160 * *
2161 * REXX RETURN VALUE: *
2162 * The new file position, in bytes or "" if error *
2163 * ------------------------------------------------------------------------- */
2164ULONG APIENTRY Sys2Seek( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2165{
2166 HFILE hf;
2167 LONGLONG llPos,
2168 llActual;
2169 ULONG ulMethod = FILE_CURRENT;
2170 CHAR achActual[ US_LONGLONG_MAXZ ];
2171 APIRET rc;
2172
2173 // Reset the error indicator
2174 WriteErrorCode( 0, NULL );
2175
2176 // Make sure we have at least two valid arguments
2177 if ( argc < 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
2178 return ( 40 );
2179
2180 // First argument: file handle
2181 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2182
2183 // Second argument: requested offset
2184 if (( sscanf( argv[1].strptr, "%lld", &llPos )) != 1 ) return ( 40 );
2185
2186 // Third argument: starting position
2187 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
2188 strupr( argv[2].strptr );
2189 if ( strcspn(argv[2].strptr, "BCE") > 0 ) return ( 40 );
2190 switch ( argv[2].strptr[0] ) {
2191 case 'B': ulMethod = FILE_BEGIN; break;
2192 case 'E': ulMethod = FILE_END; break;
2193 default : ulMethod = FILE_CURRENT; break;
2194 }
2195 }
2196
2197 rc = DosSetFilePtrL( hf, llPos, ulMethod, &llActual );
2198 if ( rc != NO_ERROR ) {
2199 WriteErrorCode( rc, "DosSetFilePtrL");
2200 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
2201 return ( 0 );
2202 }
2203
2204 // Return the new position as the REXX result string
2205 sprintf( achActual, "%lld", llActual );
2206 SaveResultString( prsResult, achActual, strlen(achActual) ); // 2016-02-20 SHL
2207
2208 return ( 0 );
2209}
2210
2211
2212/* ------------------------------------------------------------------------- *
2213 * Sys2BytesRemaining *
2214 * *
2215 * Return the number bytes that remain in a stream following the current *
2216 * read/write position. *
2217 * *
2218 * REXX ARGUMENTS: *
2219 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
2220 * *
2221 * REXX RETURN VALUE: *
2222 * The number of bytes remaining. In case of error, 0 will be returned and *
2223 * SYS2ERR will be set. *
2224 * ------------------------------------------------------------------------- */
2225ULONG APIENTRY Sys2BytesRemaining( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2226{
2227 HFILE hf;
2228 FILESTATUS3L fst3l = {0};
2229 LONGLONG ll = {0},
2230 llPos,
2231 llSize;
2232 CHAR achActual[ US_LONGLONG_MAXZ ];
2233 APIRET rc;
2234
2235 // Reset the error indicator
2236 WriteErrorCode( 0, NULL );
2237
2238 // Make sure we have one valid argument
2239 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
2240
2241 // First argument: handle
2242 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2243
2244 // Get the current position
2245 rc = DosSetFilePtrL( hf, ll, FILE_CURRENT, &llPos );
2246 if ( rc != NO_ERROR ) {
2247 WriteErrorCode( rc, "DosSetFilePtrL");
2248 SaveResultString( prsResult, NULL, 0 );
2249 return ( 0 );
2250 }
2251
2252 // Get the total file size
2253 rc = DosQueryFileInfo( hf, FIL_STANDARDL, &fst3l, sizeof( fst3l ));
2254 if ( rc != NO_ERROR ) {
2255 WriteErrorCode( rc, "DosQueryFileInfoL");
2256 SaveResultString( prsResult, NULL, 0 );
2257 return ( 0 );
2258 }
2259 llSize = fst3l.cbFile - llPos;
2260
2261 // Return the position as the REXX result string
2262 sprintf( achActual, "%lld", llSize );
2263 SaveResultString( prsResult, achActual, strlen(achActual) );
2264
2265 return ( 0 );
2266}
2267
2268
2269/* ------------------------------------------------------------------------- *
2270 * Sys2Read *
2271 * *
2272 * Wrapper to DosRead: read bytes from a previously-opened stream. *
2273 * *
2274 * REXX ARGUMENTS: *
2275 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
2276 * 2. Number of bytes to read (REQUIRED) *
2277 * *
2278 * REXX RETURN VALUE: *
2279 * String containing the bytes read, or "" in case of error. *
2280 * ------------------------------------------------------------------------- */
2281ULONG APIENTRY Sys2Read( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2282{
2283 HFILE hf;
2284 ULONG cb,
2285 cbActual;
2286 PSZ pszData;
2287 APIRET rc;
2288
2289 // Reset the error indicator
2290 WriteErrorCode( 0, NULL );
2291
2292 // Make sure we have two valid arguments
2293 if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
2294 return ( 40 );
2295
2296 // First argument: handle
2297 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2298
2299 // Second argument: number of bytes to read
2300 if (( sscanf( argv[1].strptr, "%u", &cb )) != 1 ) return ( 40 );
2301 if ( cb < 1 ) return ( 40 );
2302 pszData = (PSZ) malloc( cb );
2303
2304 rc = DosRead( hf, pszData, cb, &cbActual );
2305 if ( rc || !cbActual ) {
2306 WriteErrorCode( rc, "DosRead");
2307 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
2308 goto cleanup;
2309 }
2310 SaveResultString( prsResult, pszData, cbActual ); // 2016-02-20 SHL
2311
2312cleanup:
2313 free( pszData );
2314 return ( 0 );
2315}
2316
2317
2318/* ------------------------------------------------------------------------- *
2319 * Sys2ReadLine *
2320 * *
2321 * Read a line (up to the next LF byte) from a previously-opened stream. *
2322 * Only line feed (0x0A) bytes are treated as line-end characters; they will *
2323 * be stripped from the result string. Carriage return bytes (0x0D) will *
2324 * be skipped over but otherwise ignored. *
2325 * *
2326 * REXX ARGUMENTS: *
2327 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
2328 * *
2329 * REXX RETURN VALUE: *
2330 * String containing the text read. In the event of error, this will be "" *
2331 * and SYS2ERR will be set. *
2332 * ------------------------------------------------------------------------- */
2333ULONG APIENTRY Sys2ReadLine( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2334{
2335 HFILE hf;
2336 ULONG cbBuf = 0,
2337 cbTotal = 0,
2338 cbActual = 0;
2339 PSZ pszData;
2340 BOOL fEOL = FALSE;
2341 CHAR ch;
2342 APIRET rc;
2343
2344 // Reset the error indicator
2345 WriteErrorCode( 0, NULL );
2346
2347 // Make sure we have one valid argument
2348 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
2349
2350 // First argument: handle
2351 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2352
2353 cbBuf = 1024;
2354 pszData = (PSZ) malloc( cbBuf );
2355 while ( !fEOL ) {
2356 rc = DosRead( hf, &ch, 1, &cbActual );
2357 if ( rc ) {
2358 WriteErrorCode( rc, "DosRead");
2359 SaveResultString( prsResult, NULL, 0 ); // 2016-02-20 SHL
2360 goto cleanup;
2361 }
2362 if ( !cbActual || ch == '\n') {
2363 fEOL = TRUE;
2364 break;
2365 }
2366 else if ( ch == '\r')
2367 continue;
2368 if ( cbTotal >= cbBuf ) {
2369 cbBuf += 256;
2370 pszData = (PSZ) realloc( pszData, cbBuf );
2371 }
2372 pszData[ cbTotal++ ] = ch;
2373 }
2374 SaveResultString( prsResult, pszData, cbTotal ); // 2016-02-20 SHL
2375
2376cleanup:
2377 free( pszData );
2378 return ( 0 );
2379}
2380
2381
2382/* ------------------------------------------------------------------------- *
2383 * Sys2Write *
2384 * *
2385 * Wrapper to DosWrite: write bytes to a previously-opened stream. *
2386 * *
2387 * REXX ARGUMENTS: *
2388 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
2389 * 2. Data to be written (REQUIRED) *
2390 * *
2391 * REXX RETURN VALUE: *
2392 * Number of bytes written. *
2393 * ------------------------------------------------------------------------- */
2394ULONG APIENTRY Sys2Write( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2395{
2396 HFILE hf;
2397 ULONG cbActual;
2398 CHAR szActual[ US_INTEGER_MAXZ ];
2399 APIRET rc;
2400
2401 // Reset the error indicator
2402 WriteErrorCode( 0, NULL );
2403
2404 // Make sure we have two valid arguments
2405 if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
2406 return ( 40 );
2407
2408 // First argument: handle
2409 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2410
2411 // (Second argument can be left in standard RXSTRING form)
2412
2413 rc = DosWrite( hf, argv[1].strptr, argv[1].strlength, &cbActual );
2414 if ( rc != NO_ERROR ) {
2415 WriteErrorCode( rc, "DosWrite");
2416 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
2417 return ( 0 );
2418 }
2419
2420 sprintf( szActual, "%d", cbActual );
2421 SaveResultString( prsResult, szActual, strlen(szActual) ); // 2016-02-20 SHL
2422 return ( 0 );
2423}
2424
2425
2426/* ------------------------------------------------------------------------- *
2427 * Sys2SyncBuffer *
2428 * *
2429 * Wrapper to DosResetBuffer: for external files, write the buffer to disk; *
2430 * for pipes, block until the far end of the pipe has read the contents. *
2431 * *
2432 * REXX ARGUMENTS: *
2433 * 1. File handle (returned by Sys2Open) (REQUIRED) *
2434 * *
2435 * REXX RETURN VALUE: *
2436 * 1 on success, or 0 if an error occurred. *
2437 * ------------------------------------------------------------------------- */
2438ULONG APIENTRY Sys2SyncBuffer( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2439{
2440 HFILE hf;
2441 APIRET rc;
2442
2443 // Reset the error indicator
2444 WriteErrorCode( 0, NULL );
2445
2446 // Make sure we have exactly one valid argument (the file handle)
2447 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) ))
2448 return ( 40 );
2449 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
2450
2451 // Sync the buffer
2452 rc = DosResetBuffer( hf );
2453 if ( rc != NO_ERROR ) {
2454 WriteErrorCode( rc, "DosResetBuffer");
2455 SaveResultString( prsResult, PSZ_ZERO, 1 ); // 2016-02-20 SHL
2456 }
2457 else {
2458 SaveResultString( prsResult, PSZ_ONE, 1 ); // 2016-02-20 SHL
2459 }
2460
2461 return ( 0 );
2462}
2463
2464
2465/* ------------------------------------------------------------------------- *
2466 * Sys2QueryDriveInfo *
2467 * *
2468 * Get non-filesystem-dependent information about a logical drive (volume). *
2469 * *
2470 * REXX ARGUMENTS: *
2471 * 1. Drive/volume letter to query, trailing colon optional. (REQUIRED) *
2472 * *
2473 * REXX RETURN VALUE: *
2474 * On success, returns a string in the format *
2475 * <drive> <size> <type> <flag> *
2476 * where <drive> is the uppercase drive letter followed by a colon, *
2477 * <size> is the total size of the drive/volume in binary kilobytes,*
2478 * <type> is one of: *
2479 * FLOPPY_5L - 48 TPI low-density diskette drive *
2480 * FLOPPY_5H - 96 TPI high-density diskette drive *
2481 * FLOPPY_3L - 3.5-inch 720KB drive *
2482 * FLOPPY_3H - 3.5-inch high-density 1.44MB diskette drive *
2483 * FLOPPY_3X - 3.5-inch ext-density 2.88MB diskette drive *
2484 * FLOPPY_8L - 8-inch single-density diskette drive *
2485 * FLOPPY_8H - 8-inch double-density diskette drive *
2486 * OTHER - other (including CD drive with no media) *
2487 * HDD - hard disk drive (including PRM) *
2488 * TAPE - tape drive *
2489 * OPTICAL - read/write optical drive *
2490 * and <flag> is 1 for non-partitionable removable media (e.g. floppies) *
2491 * or 0 otherwise (including both fixed and PRM disks) *
2492 * ------------------------------------------------------------------------- */
2493ULONG APIENTRY Sys2QueryDriveInfo( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2494{
2495 BIOSPARAMETERBLOCK data;
2496 CHAR szDiskInfo[ US_DISKINFO_MAXZ ];
2497 UCHAR achPP[ 2 ],
2498 chVol;
2499 ULONG cbPP,
2500 cbData,
2501 ulSectors,
2502 ulSize;
2503 BOOL bRemovable;
2504 APIRET rc;
2505
2506
2507 // Reset the error indicator
2508 WriteErrorCode( 0, NULL );
2509
2510 // Make sure we have exactly one valid argument (the drive letter)
2511 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) ))
2512 return ( 40 );
2513 chVol = toupper( argv[0].strptr[0] );
2514 if (( chVol < 'A') || ( chVol > 'Z'))
2515 return ( 40 );
2516
2517 cbPP = 2;
2518 achPP[ 0 ] = 0;
2519 achPP[ 1 ] = chVol - 65;
2520 cbData = sizeof( data );
2521 rc = DosDevIOCtl( (HFILE) -1, IOCTL_DISK, DSK_GETDEVICEPARAMS,
2522 (PVOID) achPP, 2, &cbPP, &data, cbData, &cbData );
2523 if ( rc != NO_ERROR ) {
2524 WriteErrorCode( rc, "DosDevIOCtl");
2525 SaveResultString( prsResult, NULL, 0 );
2526 return ( 0 );
2527 }
2528
2529 ulSectors = data.cSectors? data.cSectors: data.cLargeSectors;
2530 ulSize = (data.usBytesPerSector > 1024) ?
2531 (ULONG) (ulSectors * ( data.usBytesPerSector / 1024 )) :
2532 (ULONG) (ulSectors / ( 1024 / data.usBytesPerSector ));
2533 bRemovable = !( data.fsDeviceAttr & 1 );
2534
2535 sprintf( szDiskInfo, "%c: %u ", chVol, ulSize );
2536 switch( data.bDeviceType ) {
2537 case 0: // 48 TPI low-density diskette drive
2538 strncat( szDiskInfo, "FLOPPY_5L", US_DISKINFO_MAXZ-1 );
2539 break;
2540 case 1: // 96 TPI high-density diskette drive
2541 strncat( szDiskInfo, "FLOPPY_5H", US_DISKINFO_MAXZ-1 );
2542 break;
2543 case 2: // Small (3.5-inch) 720KB drive
2544 strncat( szDiskInfo, "FLOPPY_3L", US_DISKINFO_MAXZ-1 );
2545 break;
2546 case 3: // 8-inch single-density diskette drive
2547 strncat( szDiskInfo, "FLOPPY_8L", US_DISKINFO_MAXZ-1 );
2548 break;
2549 case 4: // 8-inch double-density diskette drive
2550 strncat( szDiskInfo, "FLOPPY_8H", US_DISKINFO_MAXZ-1 );
2551 break;
2552 case 5: // Fixed disk
2553 strncat( szDiskInfo, "HDD", US_DISKINFO_MAXZ-1 );
2554 break;
2555 case 6: // Tape drive
2556 strncat( szDiskInfo, "TAPE", US_DISKINFO_MAXZ-1 );
2557 break;
2558 case 7: // Other (includes 1.44MB 3.5-inch diskette drive)
2559 if ( ulSize == 1440 )
2560 strncat( szDiskInfo, "FLOPPY_3H", US_DISKINFO_MAXZ-1 );
2561 else
2562 strncat( szDiskInfo, "OTHER", US_DISKINFO_MAXZ-1 );
2563 break;
2564 case 8: // R/W optical disk
2565 strncat( szDiskInfo, "OPTICAL", US_DISKINFO_MAXZ-1 );
2566 break;
2567 case 9: // 3.5-inch 4.0MB diskette drive (2.88MB formatted)
2568 strncat( szDiskInfo, "FLOPPY_3X", US_DISKINFO_MAXZ-1 );
2569 break;
2570 default:
2571 strncat( szDiskInfo, "UNKNOWN", US_DISKINFO_MAXZ-1 );
2572 break;
2573 }
2574 strncat( szDiskInfo, ( bRemovable? " 1": " 0" ), US_DISKINFO_MAXZ-1 );
2575
2576 SaveResultString( prsResult, szDiskInfo, strlen(szDiskInfo) );
2577 return ( 0 );
2578}
2579
2580
2581
2582/* ------------------------------------------------------------------------- *
2583 * Sys2QuerySysValue *
2584 * *
2585 * Query the given system value. *
2586 * *
2587 * REXX ARGUMENTS: *
2588 * 1. The system value identifier (REQUIRED) *
2589 * *
2590 * REXX RETURN VALUE: The requested system value, or "" on error. *
2591 * ------------------------------------------------------------------------- */
2592ULONG APIENTRY Sys2QuerySysValue( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
2593{
2594 CHAR szResult[ US_INTEGER_MAXZ ];
2595 LONG lID,
2596 lValue;
2597
2598 // Reset the error indicator
2599 WriteErrorCode( 0, NULL );
2600
2601 // Make sure we have exactly one valid argument
2602 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
2603
2604 // Parse the identifier
2605 lID = -1;
2606 strupr( argv[0].strptr );
2607 if (( sscanf( argv[0].strptr, "%d", &lID )) != 1 ) {
2608 if ( ! stricmp( argv[0].strptr, "ALARM")) lID = SV_ALARM;
2609 else if ( ! stricmp( argv[0].strptr, "ALTMNEMONIC")) lID = SV_ALTMNEMONIC;
2610 else if ( ! stricmp( argv[0].strptr, "ANIMATIONSPEED")) lID = SV_ANIMATIONSPEED;
2611 else if ( ! stricmp( argv[0].strptr, "ANIMATION")) lID = SV_ANIMATION;
2612 else if ( ! stricmp( argv[0].strptr, "BEGINDRAG")) lID = SV_BEGINDRAG;
2613 else if ( ! stricmp( argv[0].strptr, "BEGINDRAGKB")) lID = SV_BEGINDRAGKB;
2614 else if ( ! stricmp( argv[0].strptr, "BEGINSELECT")) lID = SV_BEGINSELECT;
2615 else if ( ! stricmp( argv[0].strptr, "BEGINSELECTKB")) lID = SV_BEGINSELECTKB;
2616 else if ( ! stricmp( argv[0].strptr, "CHORDTIME")) lID = SV_CHORDTIME;
2617 else if ( ! stricmp( argv[0].strptr, "CICONTEXTLINES")) lID = SV_CICONTEXTLINES;
2618 else if ( ! stricmp( argv[0].strptr, "CMOUSEBUTTONS")) lID = SV_CMOUSEBUTTONS;
2619 else if ( ! stricmp( argv[0].strptr, "CONTEXTHELPKB")) lID = SV_CONTEXTHELPKB;
2620 else if ( ! stricmp( argv[0].strptr, "CONTEXTHELP")) lID = SV_CONTEXTHELP;
2621 else if ( ! stricmp( argv[0].strptr, "CONTEXTMENU")) lID = SV_CONTEXTMENU;
2622 else if ( ! stricmp( argv[0].strptr, "CONTEXTMENUKB")) lID = SV_CONTEXTMENUKB;
2623 else if ( ! stricmp( argv[0].strptr, "CPOINTERBUTTONS")) lID = SV_CPOINTERBUTTONS;
2624 else if ( ! stricmp( argv[0].strptr, "CTIMERS")) lID = SV_CTIMERS;
2625 else if ( ! stricmp( argv[0].strptr, "CURSORLEVEL")) lID = SV_CURSORLEVEL;
2626 else if ( ! stricmp( argv[0].strptr, "CURSORRATE")) lID = SV_CURSORRATE;
2627 else if ( ! stricmp( argv[0].strptr, "CXALIGN")) lID = SV_CXALIGN;
2628 else if ( ! stricmp( argv[0].strptr, "CXBORDER")) lID = SV_CXBORDER;
2629 else if ( ! stricmp( argv[0].strptr, "CXBYTEALIGN")) lID = SV_CXBYTEALIGN;
2630 else if ( ! stricmp( argv[0].strptr, "CXCHORD")) lID = SV_CXCHORD;
2631 else if ( ! stricmp( argv[0].strptr, "CXDBLCLK")) lID = SV_CXDBLCLK;
2632 else if ( ! stricmp( argv[0].strptr, "CXDLGFRAME")) lID = SV_CXDLGFRAME;
2633 else if ( ! stricmp( argv[0].strptr, "CXFULLSCREEN")) lID = SV_CXFULLSCREEN;
2634 else if ( ! stricmp( argv[0].strptr, "CXHSCROLLARROW")) lID = SV_CXHSCROLLARROW;
2635 else if ( ! stricmp( argv[0].strptr, "CXHSLIDER")) lID = SV_CXHSLIDER;
2636 else if ( ! stricmp( argv[0].strptr, "CXICONTEXTWIDTH")) lID = SV_CXICONTEXTWIDTH;
2637 else if ( ! stricmp( argv[0].strptr, "CXICON")) lID = SV_CXICON;
2638 else if ( ! stricmp( argv[0].strptr, "CXMINMAXBUTTON")) lID = SV_CXMINMAXBUTTON;
2639 else if ( ! stricmp( argv[0].strptr, "CXMOTIONSTART")) lID = SV_CXMOTIONSTART;
2640 else if ( ! stricmp( argv[0].strptr, "CXPOINTER")) lID = SV_CXPOINTER;
2641 else if ( ! stricmp( argv[0].strptr, "CXSCREEN")) lID = SV_CXSCREEN;
2642 else if ( ! stricmp( argv[0].strptr, "CXSIZEBORDER")) lID = SV_CXSIZEBORDER;
2643 else if ( ! stricmp( argv[0].strptr, "CXVSCROLL")) lID = SV_CXVSCROLL;
2644 else if ( ! stricmp( argv[0].strptr, "CYALIGN")) lID = SV_CYALIGN;
2645 else if ( ! stricmp( argv[0].strptr, "CYBORDER")) lID = SV_CYBORDER;
2646 else if ( ! stricmp( argv[0].strptr, "CYBYTEALIGN")) lID = SV_CYBYTEALIGN;
2647 else if ( ! stricmp( argv[0].strptr, "CYCHORD")) lID = SV_CYCHORD;
2648 else if ( ! stricmp( argv[0].strptr, "CYDBLCLK")) lID = SV_CYDBLCLK;
2649 else if ( ! stricmp( argv[0].strptr, "CYDLGFRAME")) lID = SV_CYDLGFRAME;
2650 else if ( ! stricmp( argv[0].strptr, "CYFULLSCREEN")) lID = SV_CYFULLSCREEN;
2651 else if ( ! stricmp( argv[0].strptr, "CYHSCROLL")) lID = SV_CYHSCROLL;
2652 else if ( ! stricmp( argv[0].strptr, "CYICON")) lID = SV_CYICON;
2653 else if ( ! stricmp( argv[0].strptr, "CYMENU")) lID = SV_CYMENU;
2654 else if ( ! stricmp( argv[0].strptr, "CYMINMAXBUTTON")) lID = SV_CYMINMAXBUTTON;
2655 else if ( ! stricmp( argv[0].strptr, "CYMOTIONSTART")) lID = SV_CYMOTIONSTART;
2656 else if ( ! stricmp( argv[0].strptr, "CYPOINTER")) lID = SV_CYPOINTER;
2657 else if ( ! stricmp( argv[0].strptr, "CYSCREEN")) lID = SV_CYSCREEN;
2658 else if ( ! stricmp( argv[0].strptr, "CYSIZEBORDER")) lID = SV_CYSIZEBORDER;
2659 else if ( ! stricmp( argv[0].strptr, "CYTITLEBAR")) lID = SV_CYTITLEBAR;
2660 else if ( ! stricmp( argv[0].strptr, "CYVSCROLLARROW")) lID = SV_CYVSCROLLARROW;
2661 else if ( ! stricmp( argv[0].strptr, "CYVSLIDER")) lID = SV_CYVSLIDER;
2662 else if ( ! stricmp( argv[0].strptr, "DBLCLKTIME")) lID = SV_DBLCLKTIME;
2663 else if ( ! stricmp( argv[0].strptr, "DEBUG")) lID = SV_DEBUG;
2664 else if ( ! stricmp( argv[0].strptr, "ENDDRAG")) lID = SV_ENDDRAG;
2665 else if ( ! stricmp( argv[0].strptr, "ENDDRAGKB")) lID = SV_ENDDRAGKB;
2666 else if ( ! stricmp( argv[0].strptr, "ENDSELECTKB")) lID = SV_ENDSELECTKB;
2667 else if ( ! stricmp( argv[0].strptr, "ENDSELECT")) lID = SV_ENDSELECT;
2668 else if ( ! stricmp( argv[0].strptr, "ERRORDURATION")) lID = SV_ERRORDURATION;
2669 else if ( ! stricmp( argv[0].strptr, "ERRORFREQ")) lID = SV_ERRORFREQ;
2670 else if ( ! stricmp( argv[0].strptr, "FIRSTSCROLLRATE")) lID = SV_FIRSTSCROLLRATE;
2671 else if ( ! stricmp( argv[0].strptr, "INSERTMODE")) lID = SV_INSERTMODE;
2672 else if ( ! stricmp( argv[0].strptr, "KBDALTERED")) lID = SV_KBDALTERED;
2673 else if ( ! stricmp( argv[0].strptr, "LOCKSTARTINPUT")) lID = SV_LOCKSTARTINPUT;
2674 else if ( ! stricmp( argv[0].strptr, "MENUROLLDOWNDELAY")) lID = SV_MENUROLLDOWNDELAY;
2675 else if ( ! stricmp( argv[0].strptr, "MENUROLLUPDELAY")) lID = SV_MENUROLLUPDELAY;
2676 else if ( ! stricmp( argv[0].strptr, "MONOICONS")) lID = SV_MONOICONS;
2677 else if ( ! stricmp( argv[0].strptr, "MOUSEPRESENT")) lID = SV_MOUSEPRESENT;
2678 else if ( ! stricmp( argv[0].strptr, "NOTEDURATION")) lID = SV_NOTEDURATION;
2679 else if ( ! stricmp( argv[0].strptr, "NOTEFREQ")) lID = SV_NOTEFREQ;
2680 else if ( ! stricmp( argv[0].strptr, "NUMBEREDLISTS")) lID = SV_NUMBEREDLISTS;
2681 else if ( ! stricmp( argv[0].strptr, "OPEN")) lID = SV_OPEN;
2682 else if ( ! stricmp( argv[0].strptr, "OPENKB")) lID = SV_OPENKB;
2683 else if ( ! stricmp( argv[0].strptr, "POINTERLEVEL")) lID = SV_POINTERLEVEL;
2684 else if ( ! stricmp( argv[0].strptr, "PRINTSCREEN")) lID = SV_PRINTSCREEN;
2685 else if ( ! stricmp( argv[0].strptr, "SCROLLRATE")) lID = SV_SCROLLRATE;
2686 else if ( ! stricmp( argv[0].strptr, "SELECTKB")) lID = SV_SELECTKB;
2687 else if ( ! stricmp( argv[0].strptr, "SETLIGHTS")) lID = SV_SETLIGHTS;
2688 else if ( ! stricmp( argv[0].strptr, "SINGLESELECT")) lID = SV_SINGLESELECT;
2689 else if ( ! stricmp( argv[0].strptr, "SWAPBUTTON")) lID = SV_SWAPBUTTON;
2690 else if ( ! stricmp( argv[0].strptr, "TASKLISTMOUSEACCESS")) lID = SV_TASKLISTMOUSEACCESS;
2691 else if ( ! stricmp( argv[0].strptr, "TEXTEDIT")) lID = SV_TEXTEDIT;
2692 else if ( ! stricmp( argv[0].strptr, "TEXTEDITKB")) lID = SV_TEXTEDITKB;
2693 else if ( ! stricmp( argv[0].strptr, "TRACKRECTLEVEL")) lID = SV_TRACKRECTLEVEL;
2694 else if ( ! stricmp( argv[0].strptr, "WARNINGDURATION")) lID = SV_WARNINGDURATION;
2695 else if ( ! stricmp( argv[0].strptr, "WARNINGFREQ")) lID = SV_WARNINGFREQ;
2696 }
2697 if ( lID < 0 ) return ( 40 );
2698
2699 lValue = WinQuerySysValue( HWND_DESKTOP, lID );
2700 if ( lValue == 0 ) {
2701 /* Not elegant but probably true if the function failed. Anyway,
2702 * we don't have a HAB so we can't really use WinGetLastError()...
2703 */
2704 WriteErrorCode( PMERR_PARAMETER_OUT_OF_RANGE, "WinQuerySysValue");
2705 SaveResultString( prsResult, PSZ_ZERO, 1 );
2706 return ( 0 );
2707 }
2708
2709 // Return the value as the REXX return string
2710 sprintf( szResult, "%d", lValue );
2711 SaveResultString( prsResult, szResult, strlen(szResult) );
2712
2713 return ( 0 );
2714}
2715
2716
2717
2718// -------------------------------------------------------------------------
2719// INTERNAL FUNCTIONS
2720// -------------------------------------------------------------------------
2721
2722
2723/* ------------------------------------------------------------------------- *
2724 * GetProcess *
2725 * *
2726 * Gets information about the specified process (if found). If pszProgram *
2727 * is NULL, the search is done on the process ID in pulPID; otherwise, the *
2728 * search is done on the executable name in pszProgram (which may or may not *
2729 * include the extension). *
2730 * *
2731 * ARGUMENTS: *
2732 * PSZ pszProgram : The requested executable (process name). (I) *
2733 * PSZ pszFullName: The returned fully-qualified process name. (O) *
2734 * PULONG pulPID : The process ID. (IO) *
2735 * PULONG pulPPID : The returned process parent ID. (O) *
2736 * PULONG pulType : The returned process type. (O) *
2737 * PUSHORT pusPriority: The returned process priority. (O) *
2738 * PULONG pulCPU : The returned process CPU time. (O) *
2739 * *
2740 * RETURNS: ULONG *
2741 * 0 on success, or a non-zero API return code in the case of an error. *
2742 * ------------------------------------------------------------------------- */
2743// 2016-02-20 SHL Rework to avoid traps
2744ULONG GetProcess( PCSZ pszProgram,
2745 PSZ pszFullName,
2746 PULONG pulPID,
2747 PULONG pulPPID,
2748 PULONG pulType,
2749 PUSHORT pusPriority,
2750 PULONG pulCPU )
2751{
2752#ifdef USE_DQPS
2753 QSPTRREC *pBuf; // Data returned by DosQProcStatus()
2754#else
2755 QSPTRREC *pBuf; // Data returned by DosQuerySysState() // 2015-04-23 SHL
2756#endif
2757 QSPREC *pPrec; // Pointer to process information block
2758 QSTREC *pTrec; // Pointer to thread information block
2759 CHAR szName[ CCHMAXPATH ] = {0}, // Fully-qualified name of process
2760 szNoExt[ CCHMAXPATH ] = {0}; // Program name without extension
2761 PPIB ppib; // pointer to current process info block
2762 PSZ pszCurrent, // Program name of a queried process
2763 c; // Pointer to substring
2764 ULONG ulCPU; // Process CPU time
2765 USHORT usPriority, // Process priority class
2766 i; // index
2767 BOOL fMatch = FALSE; // The current process is a match?
2768 APIRET rc; // Return code
2769
2770 // Use current process when PID is 0 and program name is not specified
2771 if (( pszProgram == NULL ) && ( *pulPID == 0 )) {
2772 rc = DosGetInfoBlocks( NULL, &ppib );
2773 if ( rc != NO_ERROR ) {
2774 WriteErrorCode( rc, "DosGetInfoBlocks");
2775 return ( rc );
2776 }
2777 *pulPID = ppib->pib_ulpid;
2778 }
2779
2780#ifdef USE_DQPS
2781 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE );
2782#else
2783 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE ); // 2015-04-23 SHL
2784#endif
2785
2786 if ( pBuf == NULL ) {
2787 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
2788 return ( ERROR_NOT_ENOUGH_MEMORY );
2789 }
2790
2791#ifdef USE_DQPS
2792 // Get running process information using DosQProcStatus()
2793 rc = DosQProcStatus( pBuf, UL_SSBUFSIZE );
2794 if ( rc != NO_ERROR ) {
2795 WriteErrorCode( rc, "DosQProcStatus");
2796 return ( rc );
2797 }
2798 pPrec = pBuf->pProcRec;
2799#else
2800 // Get running process information using DosQuerySysState()
2801 rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE );
2802 if ( rc != NO_ERROR ) {
2803 WriteErrorCode( rc, "DosQuerySysState");
2804 free( pBuf );
2805 return ( rc );
2806 }
2807 pPrec = (QSPREC *)(((QSPTRREC*)pBuf) -> pProcRec); // 2015-04-23 SHL
2808#endif
2809
2810 *pulPPID = 0;
2811 *pulType = 0;
2812 *pusPriority = 0;
2813 *pulCPU = 0;
2814 if ( pszProgram != NULL ) *pulPID = 0;
2815 else if ( *pulPID == 0 ) return 0;
2816
2817# if 1 // 2016-02-25 SHL FIXME debug bad pointer
2818 // 2016-02-26 SHL FIXME to be gone when sure can not occur
2819 if ( (ULONG)pPrec < 0x10000 ) {
2820 sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000", __LINE__, (ULONG)pPrec );
2821 WriteErrorCode( ERROR_INVALID_ADDRESS, szName);
2822 free( pBuf );
2823 return ( 0 );
2824 }
2825# endif
2826
2827 // Now look for the specified process
2828 // List ends with RecType not QS_PROCESS or pThrdRec NULL
2829 while ( pPrec->RecType == QS_PROCESS && pPrec->pThrdRec != NULL && !fMatch ) {
2830
2831 if ( pszProgram == NULL ) {
2832 // Match by pid
2833 if ( pPrec->pid == *pulPID ) {
2834 fMatch = TRUE;
2835 // Get the program name
2836 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
2837 sprintf( pszFullName, "--");
2838 else
2839 strcpy( pszFullName, szName );
2840
2841 // Get the process priority
2842 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
2843 usPriority = 0;
2844
2845 // Get the CPU time of the process by querying each of its threads
2846 ulCPU = 0;
2847 pTrec = pPrec->pThrdRec;
2848 for ( i = 0; i < pPrec->cTCB; i++ ) {
2849 ulCPU += ( pTrec->systime + pTrec->usertime );
2850 pTrec++;
2851 }
2852
2853 *pulPPID = pPrec->ppid;
2854 *pulType = pPrec->type;
2855 *pusPriority = usPriority;
2856 *pulCPU = ulCPU;
2857 }
2858 }
2859 else {
2860 // Get the program name (without the path)
2861 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
2862 sprintf( pszCurrent, "--");
2863 else
2864 pszCurrent = strrchr( szName, '\\') + 1;
2865
2866 // Create a copy without the extension
2867 strcpy( szNoExt, pszCurrent );
2868 if ( ( c = strrchr( szNoExt, '.') ) != NULL )
2869 memset( c, 0, strlen(c) );
2870 if ( pszCurrent != NULL &&
2871 ( stricmp(pszCurrent, pszProgram) == 0 || stricmp(szNoExt, pszProgram) == 0 ) )
2872 {
2873 fMatch = TRUE;
2874
2875 // Get the process priority
2876 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
2877 usPriority = 0;
2878
2879 // Get the CPU time of the process by querying each of its threads
2880 ulCPU = 0;
2881 pTrec = pPrec->pThrdRec;
2882 for ( i = 0; i < pPrec->cTCB; i++ ) {
2883 ulCPU += ( pTrec->systime + pTrec->usertime );
2884 pTrec++;
2885 }
2886
2887 *pulPID = pPrec->pid;
2888 *pulPPID = pPrec->ppid;
2889 *pulType = pPrec->type;
2890 *pusPriority = usPriority;
2891 *pulCPU = ulCPU;
2892 strcpy( pszFullName, szName );
2893 }
2894 }
2895 pPrec = (QSPREC *)(pPrec->pThrdRec + pPrec->cTCB);
2896
2897# if 1 // 2016-02-25 SHL FIXME debug pointer - can this occur?
2898 // 2016-02-26 SHL FIXME to be gone when sure can not occur
2899 if ( (ULONG)pPrec < 0x10000 ) {
2900 sprintf( szName, "rxutilex#%u pPrec 0x%x < 0x10000", __LINE__, (ULONG)pPrec );
2901 WriteErrorCode( ERROR_INVALID_ADDRESS, szName);
2902 free( pBuf );
2903 return ( 0 );
2904 }
2905# endif
2906
2907 } // while
2908 if ( !fMatch ) *pulPID = 0;
2909
2910 free( pBuf );
2911 return ( 0 );
2912}
2913
2914
2915#ifndef LEGACY_C_LOCALE
2916
2917/* ------------------------------------------------------------------------- *
2918 * GetLocaleString *
2919 * *
2920 * Get the requested representation string for the current locale. *
2921 * The argument is a pointer to a string which will be allocated by this *
2922 * function. It is the caller's responsibility to free() it. *
2923 * *
2924 * ARGUMENTS: *
2925 * PSZ *ppszItem: Pointer to string to be allocated. (O) *
2926 * LocaleItem item: Locale item to query. (I) *
2927 * *
2928 * RETURNS: int *
2929 * The ULS function return code. *
2930 * ------------------------------------------------------------------------- */
2931int GetLocaleString( PSZ *ppszItem, LocaleItem item )
2932{
2933 UconvObject uconv = NULL;
2934 LocaleObject locale = NULL;
2935 UniChar *puzSep;
2936 int rc = 0;
2937 int buf_size;
2938 PSZ pszBuffer;
2939
2940 rc = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
2941 if ( rc != ULS_SUCCESS ) {
2942 WriteErrorCode( rc, "UniCreateLocaleObject");
2943 return ( rc );
2944 }
2945 rc = UniQueryLocaleItem( locale, item, &puzSep );
2946 if ( rc == ULS_SUCCESS ) {
2947 buf_size = UniStrlen( puzSep ) * 3;
2948 pszBuffer = (PSZ) calloc( 1, buf_size + 1 );
2949 if ( pszBuffer ) {
2950 if ( UniCreateUconvObject(L"", &uconv ) == ULS_SUCCESS ) {
2951 if ( UniStrFromUcs( uconv, pszBuffer, puzSep, buf_size ))
2952 sprintf( pszBuffer, "%ls", puzSep );
2953 UniFreeUconvObject( uconv );
2954 }
2955 else
2956 sprintf( pszBuffer, "%ls", puzSep );
2957 *ppszItem = pszBuffer;
2958 }
2959 else {
2960 rc = ERROR_NOT_ENOUGH_MEMORY;
2961 WriteErrorCode( rc, "calloc");
2962 }
2963 UniFreeMem( puzSep );
2964 }
2965 else WriteErrorCode( rc, "UniQueryLocaleItem");
2966
2967 UniFreeLocaleObject( locale );
2968 return ( rc );
2969}
2970
2971
2972/* ------------------------------------------------------------------------- *
2973 * GroupNumber *
2974 * *
2975 * Format an unsigned number into three-digit (thousands) groups, separated *
2976 * by the designated separator string. The output buffer must be allocated, *
2977 * and must be large enough to hold a 64-bit integer with the added *
2978 * separators, i.e. 20 + (separator length * 6 ). This function does not *
2979 * perform any bounds checking, so the caller must ensure this. *
2980 * *
2981 * ARGUMENTS: *
2982 * PSZ buf: The output string buffer. (IO) *
2983 * ULONGLONG val: The number value to format. (I) *
2984 * PSZ sep: The group-separator string. (I) *
2985 * *
2986 * RETURNS: N/A *
2987 * ------------------------------------------------------------------------- */
2988void GroupNumber( PSZ buf, ULONGLONG val, PSZ sep )
2989{
2990 if ( val < 1000 ) {
2991 sprintf( buf, "%u", val );
2992 return;
2993 }
2994 GroupNumber( buf, val / 1000, sep );
2995 sprintf( buf+strlen(buf), "%s%03u", sep, val % 1000 );
2996}
2997
2998#endif // #ifndef LEGACY_C_LOCALE
2999
3000
3001#ifdef NO_SHARED_SOURCE
3002
3003/****
3004 **** MOVED TO shfuncs.c
3005 ****/
3006
3007/* ------------------------------------------------------------------------- *
3008 * SaveResultString *
3009 * *
3010 * Writes new string contents to the specified RXSTRING, allocating any *
3011 * additional memory that may be required. *
3012 * *
3013 * ARGUMENTS: *
3014 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
3015 * PCH pchBytes : The string contents to write to prsResult or NULL *
3016 * ULONG ulBytes : The number of bytes in pchBytes to write 0..N. *
3017 * *
3018 * RETURNS: BOOL *
3019 * TRUE if prsResult was successfully updated. FALSE otherwise. *
3020 * ------------------------------------------------------------------------- */
3021BOOL SaveResultString( PRXSTRING prsResult, PCSZ pchBytes, ULONG ulBytes )
3022{
3023 ULONG ulRC;
3024 PCH pchNew;
3025
3026 // 2016-02-20 SHL Rework for easier usage
3027 if (!pchBytes)
3028 ulBytes = 0; // Sync for caller
3029 if ( ulBytes > 256 ) {
3030 // REXX provides 256 bytes by default; allocate more if necessary
3031 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
3032 if ( ulRC != 0 ) {
3033 WriteErrorCode( ulRC, "DosAllocMem");
3034 prsResult->strlength = 0; // 2016-02-20 SHL Force result to empty string
3035 return ( FALSE );
3036 }
3037 // 2015-06-03 SHL dropped DosFreeMem(prsResult->strptr);
3038 // 2015-06-03 SHL Pointer not allocated by DosAllocMem
3039 prsResult->strptr = pchNew;
3040 }
3041 if (ulBytes)
3042 memcpy( prsResult->strptr, pchBytes, ulBytes );
3043 prsResult->strlength = ulBytes;
3044
3045 return ( TRUE );
3046}
3047
3048
3049/* ------------------------------------------------------------------------- *
3050 * WriteStemElement *
3051 * *
3052 * Creates a stem element (compound variable) in the calling REXX program *
3053 * using the REXX shared variable pool interface. *
3054 * *
3055 * ARGUMENTS: *
3056 * PSZ pszStem : The name of the stem (before the '.') *
3057 * ULONG ulIndex : The number of the stem element (after the '.') *
3058 * PSZ pszValue : The value to write to the compound variable. *
3059 * *
3060 * RETURNS: BOOL *
3061 * TRUE on success, FALSE on failure. *
3062 * ------------------------------------------------------------------------- */
3063// 2016-02-20 SHL
3064BOOL WriteStemElement( PCSZ pszStem, ULONG ulIndex, PCSZ pszValue )
3065{
3066 SHVBLOCK shvVar; // REXX shared variable pool block
3067 ULONG ulRc,
3068 ulBytes;
3069 CHAR szCompoundName[ US_COMPOUND_MAXZ ];
3070
3071 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
3072 if ( pszValue == NULL ) {
3073 pszValue = "";
3074 ulBytes = 0;
3075 } else {
3076 // 2015-06-03 SHL Was using DosAllocMem and leaking memory
3077 // REXX API does not free this kind of buffer
3078 ulBytes = strlen(pszValue);
3079 }
3080 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
3081 shvVar.shvvalue.strptr = (PCH)pszValue;
3082 shvVar.shvvalue.strlength = ulBytes;
3083 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
3084 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
3085 shvVar.shvcode = RXSHV_SYSET;
3086 shvVar.shvnext = NULL;
3087 ulRc = RexxVariablePool( &shvVar );
3088 if ( ulRc > 1 ) {
3089 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
3090 return FALSE;
3091 }
3092 return TRUE;
3093
3094}
3095
3096
3097/* ------------------------------------------------------------------------- *
3098 * WriteErrorCode *
3099 * *
3100 * Writes an error code to a special variable in the calling REXX program *
3101 * using the REXX shared variable pool interface. This is used to return *
3102 * API error codes to the REXX program, since the REXX functions themselves *
3103 * normally return string values. *
3104 * *
3105 * ARGUMENTS: *
3106 * ULONG ulError : The error code returned by the failing API call. *
3107 * PSZ pszContext: A string describing the API call that failed. *
3108 * *
3109 * RETURNS: N/A *
3110 * ------------------------------------------------------------------------- */
3111void WriteErrorCode( ULONG ulError, PCSZ pszContext )
3112{
3113 SHVBLOCK shvVar; // REXX shared variable pool block
3114 ULONG ulRc;
3115 CHAR szErrorText[ US_ERRSTR_MAXZ ];
3116
3117 if ( pszContext == NULL )
3118 sprintf( szErrorText, "%u", ulError );
3119 else
3120 sprintf( szErrorText, "%u: %s", ulError, pszContext );
3121 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
3122 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
3123 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
3124 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
3125 shvVar.shvcode = RXSHV_SYSET;
3126 shvVar.shvnext = NULL;
3127 shvVar.shvret = 0; // 2016-02-26 SHL
3128 ulRc = RexxVariablePool( &shvVar );
3129 // 2016-02-26 SHL Correct if
3130 if ( ulRc & ~RXSHV_NEWV )
3131 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
3132}
3133
3134#endif // #ifdef NO_SHARED_SOURCE
3135
Note: See TracBrowser for help on using the repository browser.