source: rxutilex/trunk/rxutilex.c@ 20

Last change on this file since 20 was 20, checked in by Alex Taylor, 11 years ago

Sys2QueryProcess now accepts a PID of 0 for the current process. Version number increased to 0.1.0.

File size: 96.4 KB
Line 
1/******************************************************************************
2 * REXX Utility Functions - Extended (RXUTILEX.DLL) *
3 * (C) 2011, 2014 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
42#define INCL_WINATOM
43#define INCL_WINCLIPBOARD
44#define INCL_WINERRORS
45#define INCL_DOSERRORS
46#define INCL_DOSMISC
47#define INCL_DOSMODULEMGR
48#define INCL_DOSNMPIPES
49#define INCL_DOSPROCESS
50#define INCL_DOSPROFILE
51#ifndef OS2_INCLUDED
52 #include <os2.h>
53#endif
54#include <locale.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <time.h>
59#define INCL_RXSHV
60#define INCL_RXFUNC
61#include <rexxsaa.h>
62
63#pragma import( DosGetPrty, "DosGetPrty", "DOSCALL1", 9 )
64USHORT APIENTRY16 DosGetPrty( USHORT usScope, PUSHORT pusPriority, USHORT pid );
65
66#ifdef USE_DQPS
67#pragma import( DosQProcStatus, "DosQProcStatus", "DOSCALL1", 154 )
68USHORT APIENTRY16 DosQProcStatus( PVOID pBuf, USHORT cbBuf );
69#endif
70
71// CONSTANTS
72
73#define SZ_LIBRARY_NAME "RXUTILEX" // Name of this library
74#define SZ_ERROR_NAME "SYS2ERR" // REXX variable used to store error codes
75#define SZ_VERSION "0.1.0" // Current version of this library
76
77// Maximum string lengths...
78#define US_COMPOUND_MAXZ 250 // ...of a compound variable
79#define US_INTEGER_MAXZ 12 // ...of a 32-bit integer string
80#define US_LONGLONG_MAXZ 21 // ...of a 64-bit integer string
81#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // ...of a stem
82#define US_ERRSTR_MAXZ 250 // ...of an error string
83#define US_PIDSTR_MAXZ ( CCHMAXPATH + 100 ) // ...of a process information string
84#define US_TIMESTR_MAXZ 256 // ...of a formatted time string
85#define US_PIPESTATUS_MAXZ 128 // ...of a pipe status string
86
87#define UL_SSBUFSIZE 0xFFFF // Buffer size for the DosQuerySysState() data
88
89 // Time string formats
90#define FL_TIME_DEFAULT 0
91#define FL_TIME_ISO8601 1
92#define FL_TIME_LOCALE 2
93
94
95// List of functions to be registered by Sys2LoadFuncs
96static PSZ RxFunctionTbl[] = {
97 "Sys2DropFuncs",
98 "Sys2GetClipboardText",
99 "Sys2PutClipboardText",
100 "Sys2QueryProcess",
101 "Sys2QueryProcessList",
102 "Sys2KillProcess",
103 "Sys2QueryForegroundProcess",
104 "Sys2QueryPhysicalMemory",
105 "Sys2FormatTime",
106 "Sys2GetEpochTime",
107 "Sys2ReplaceModule",
108 "Sys2LocateDLL",
109 "Sys2CreateNamedPipe",
110 "Sys2ConnectNamedPipe",
111 "Sys2DisconnectNamedPipe",
112 "Sys2CheckNamedPipe",
113 "Sys2Open",
114 "Sys2Close",
115 "Sys2Seek",
116 "Sys2Read",
117 "Sys2Write",
118 "Sys2Version"
119};
120
121
122// FUNCTION DECLARATIONS
123
124// Exported REXX functions
125RexxFunctionHandler Sys2LoadFuncs;
126RexxFunctionHandler Sys2DropFuncs;
127RexxFunctionHandler Sys2Version;
128
129RexxFunctionHandler Sys2FormatTime;
130RexxFunctionHandler Sys2GetEpochTime;
131
132RexxFunctionHandler Sys2GetClipboardText;
133RexxFunctionHandler Sys2PutClipboardText;
134
135RexxFunctionHandler Sys2QueryProcess;
136RexxFunctionHandler Sys2QueryProcessList;
137RexxFunctionHandler Sys2KillProcess;
138RexxFunctionHandler Sys2QueryForegroundProcess;
139
140RexxFunctionHandler Sys2QueryPhysicalMemory;
141
142RexxFunctionHandler Sys2LocateDLL;
143RexxFunctionHandler Sys2ReplaceModule;
144
145// RexxFunctionHandler Sys2ReplaceObjectClass;
146
147RexxFunctionHandler Sys2CreateNamedPipe;
148RexxFunctionHandler Sys2ConnectNamedPipe;
149RexxFunctionHandler Sys2DisconnectNamedPipe;
150RexxFunctionHandler Sys2CheckNamedPipe;
151
152RexxFunctionHandler Sys2Open;
153RexxFunctionHandler Sys2Close;
154RexxFunctionHandler Sys2Seek;
155RexxFunctionHandler Sys2Read;
156RexxFunctionHandler Sys2Write;
157
158
159// Private internal functions
160ULONG GetProcess( PSZ pszProgram, PSZ pszFullName, PULONG pulPID, PULONG pulPPID, PULONG pulType, PUSHORT pusPriority, PULONG pulCPU );
161BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
162BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue );
163void WriteErrorCode( ULONG ulError, PSZ pszContext );
164
165
166// MACROS
167#define TIME_SECONDS( timeval ) ( timeval / 32 )
168#define TIME_HUNDREDTHS( timeval ) (( timeval % 32 ) * 100 / 32 )
169
170
171/* ------------------------------------------------------------------------- *
172 * Sys2LoadFuncs *
173 * *
174 * Register all Sys2* REXX functions (except this one, obviously). *
175 * *
176 * REXX ARGUMENTS: None *
177 * REXX RETURN VALUE: "" *
178 * ------------------------------------------------------------------------- */
179ULONG APIENTRY Sys2LoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
180{
181 int entries,
182 i;
183
184 // Reset the error indicator
185 WriteErrorCode( 0, NULL );
186
187 if ( argc > 0 ) return ( 40 );
188 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
189 for ( i = 0; i < entries; i++ )
190 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
191
192 MAKERXSTRING( *prsResult, "", 0 );
193 return ( 0 );
194}
195
196
197/* ------------------------------------------------------------------------- *
198 * Sys2DropFuncs *
199 * *
200 * Deregister all Sys2* REXX functions. *
201 * *
202 * REXX ARGUMENTS: None *
203 * REXX RETURN VALUE: "" *
204 * ------------------------------------------------------------------------- */
205ULONG APIENTRY Sys2DropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
206{
207 int entries,
208 i;
209
210 // Reset the error indicator
211 WriteErrorCode( 0, NULL );
212
213 if ( argc > 0 ) return ( 40 );
214 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
215 for ( i = 0; i < entries; i++ )
216 RexxDeregisterFunction( RxFunctionTbl[i] );
217
218 MAKERXSTRING( *prsResult, "", 0 );
219 return ( 0 );
220}
221
222
223/* ------------------------------------------------------------------------- *
224 * Sys2Version *
225 * *
226 * Returns the current library version. *
227 * *
228 * REXX ARGUMENTS: None *
229 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
230 * ------------------------------------------------------------------------- */
231ULONG APIENTRY Sys2Version( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
232{
233 CHAR szVersion[ 12 ];
234
235 // Reset the error indicator
236 WriteErrorCode( 0, NULL );
237
238 if ( argc > 0 ) return ( 40 );
239 sprintf( szVersion, "%s", SZ_VERSION );
240
241 MAKERXSTRING( *prsResult, szVersion, strlen(szVersion) );
242 return ( 0 );
243}
244
245
246/* ------------------------------------------------------------------------- *
247 * Sys2PutClipboardText *
248 * *
249 * Write a string to the clipboard in plain-text format. Specifying either *
250 * no value or an empty string in the first argument will simply clear the *
251 * clipboard of CF_TEXT data. *
252 * *
253 * REXX ARGUMENTS: *
254 * 1. String to be written to the clipboard (DEFAULT: "") *
255 * 2. Flag indicating whether other clipboard formats should be cleared: *
256 * Y = yes, call WinEmptyClipbrd() before writing text (DEFAULT) *
257 * N = no, leave (non-CF_TEXT) clipboard data untouched *
258 * *
259 * REXX RETURN VALUE: 1 on success, 0 on failure *
260 * ------------------------------------------------------------------------- */
261ULONG APIENTRY Sys2PutClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
262{
263 PSZ pszShareMem; // text in clipboard
264 ULONG ulRC = 0, // return code
265 ulBytes = 0, // size of input string
266 ulPType = 0; // process-type flag
267 BOOL fEmptyCB = TRUE, // call WinEmptyClipbrd() first?
268 fHabTerm = TRUE; // terminate HAB ourselves?
269 HAB hab; // anchor-block handle (for Win*)
270 HMQ hmq; // message-queue handle
271 PPIB ppib; // process information block
272 PTIB ptib; // thread information block
273
274
275 // Reset the error indicator
276 WriteErrorCode( 0, NULL );
277
278 // Make sure we have at least one valid argument (the input string)
279 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
280
281 // The second argument is optional, but must be correct if specified
282 if ( argc >= 2 ) {
283 // second argument: flag to clear clipboard (Y/N, but also accept 0/1)
284 if ( RXVALIDSTRING(argv[1]) ) {
285 strupr( argv[1].strptr );
286 if ( strcspn(argv[1].strptr, "YN01") > 0 ) return ( 40 );
287 switch ( argv[1].strptr[0] ) {
288 case 'N':
289 case '0': fEmptyCB = FALSE; break;
290 case 'Y':
291 case '1':
292 default : fEmptyCB = TRUE; break;
293 }
294 } else fEmptyCB = TRUE;
295 }
296
297 // Initialize the PM API
298 DosGetInfoBlocks( &ptib, &ppib );
299 ulPType = ppib->pib_ultype;
300 ppib->pib_ultype = 3;
301 hab = WinInitialize( 0 );
302 if ( !hab ) {
303 fHabTerm = FALSE;
304 hab = 1;
305 }
306
307 /* Try to create a message-queue if one doesn't exist. We don't need to
308 * check the result, because it could fail if a message queue already exists
309 * (in the calling process), which is also OK.
310 */
311 hmq = WinCreateMsgQueue( hab, 0);
312
313 // Place the string on the clipboard as CF_TEXT
314 ulRC = WinOpenClipbrd( hab );
315 if ( ulRC ) {
316
317 if ( fEmptyCB ) WinEmptyClipbrd( hab );
318
319 ulBytes = argv[0].strlength + 1;
320 ulRC = DosAllocSharedMem( (PVOID) &pszShareMem, NULL, ulBytes,
321 PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
322 if ( ulRC == 0 ) {
323 memset( pszShareMem, 0, ulBytes );
324 strncpy( pszShareMem, argv[0].strptr, ulBytes - 1 );
325 if ( ! WinSetClipbrdData( hab, (ULONG) pszShareMem, CF_TEXT, CFI_POINTER ))
326 WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinSetClipbrdData");
327 else
328 MAKERXSTRING( *prsResult, "", 0 );
329 } else {
330 WriteErrorCode( ulRC, "DosAllocSharedMem");
331 MAKERXSTRING( *prsResult, "", 0 );
332 }
333
334 WinCloseClipbrd( hab );
335 } else {
336 WriteErrorCode( ulRC, "WinOpenClipbrd");
337 MAKERXSTRING( *prsResult, "", 0 );
338 }
339
340 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
341 if ( fHabTerm ) WinTerminate( hab );
342 ppib->pib_ultype = ulPType;
343
344 return ( 0 );
345}
346
347
348/* ------------------------------------------------------------------------- *
349 * Sys2GetClipboardText *
350 * *
351 * Retrieve a plain-text string from the clipboard if one is available. *
352 * *
353 * REXX ARGUMENTS: *
354 * None. *
355 * *
356 * REXX RETURN VALUE: The retrieved clipboard string *
357 * ------------------------------------------------------------------------- */
358ULONG APIENTRY Sys2GetClipboardText( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
359{
360 PSZ pszClipText, // pointer to clipboard data
361 pszLocalText; // our copy of the data (to return)
362 ULONG ulRC = 0, // return code
363 ulBytes = 0, // size in bytes of output string
364 ulPType = 0; // process-type flag
365 BOOL fHabTerm = TRUE; // terminate HAB ourselves?
366 HAB hab; // anchor-block handle (for Win*)
367 HMQ hmq; // message-queue handle
368 PPIB ppib; // process information block
369 PTIB ptib; // thread information block
370
371
372 // Reset the error indicator
373 WriteErrorCode( 0, NULL );
374
375 // Initialize the PM API
376 DosGetInfoBlocks( &ptib, &ppib );
377 ulPType = ppib->pib_ultype;
378 ppib->pib_ultype = 3;
379 hab = WinInitialize( 0 );
380 if ( !hab ) {
381 fHabTerm = FALSE;
382 hab = 1;
383 }
384
385 /* Note: A message-queue must exist before we can access the clipboard. We
386 * don't actually use the returned value. In fact, we don't even
387 * verify it, because it could be NULLHANDLE if this function was
388 * called from a PM process (e.g. VX-REXX) - in which case, a message
389 * queue should already exist, and we can proceed anyway.
390 */
391 hmq = WinCreateMsgQueue( hab, 0 );
392
393 // Open the clipboard
394 ulRC = WinOpenClipbrd( hab );
395 if ( ulRC ) {
396
397 // Read plain text from the clipboard, if available
398 if (( pszClipText = (PSZ) WinQueryClipbrdData( hab, CF_TEXT )) != NULL ) {
399
400 ulBytes = strlen( pszClipText ) + 1;
401 if (( pszLocalText = (PSZ) malloc( ulBytes )) != NULL ) {
402 memset( pszLocalText, 0, ulBytes );
403 strncpy( pszLocalText, pszClipText, ulBytes - 1 );
404 if ( ! SaveResultString( prsResult, pszLocalText, ulBytes - 1 )) {
405 MAKERXSTRING( *prsResult, "", 0 );
406 }
407 free( pszLocalText );
408 } else {
409 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
410 MAKERXSTRING( *prsResult, "", 0 );
411 }
412
413 } else {
414 // Either no text exists, or clipboard is not readable
415 MAKERXSTRING( *prsResult, "", 0 );
416 }
417
418 WinCloseClipbrd( hab );
419 } else {
420 WriteErrorCode( ulRC, "WinOpenClipbrd");
421 MAKERXSTRING( *prsResult, "", 0 );
422 }
423
424 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
425 if ( fHabTerm ) WinTerminate( hab );
426
427 ppib->pib_ultype = ulPType;
428
429 return ( 0 );
430}
431
432
433/* ------------------------------------------------------------------------- *
434 * Sys2QueryProcess *
435 * *
436 * Queries information about the specified process. *
437 * *
438 * REXX ARGUMENTS: *
439 * 1. The process identifier (program name or process ID) (REQUIRED) *
440 * 2. Flag indicicating the identifier type: *
441 * 'P': decimal process ID *
442 * 'H': hexadecimal process ID *
443 * 'N': executable program name (with or without extension) (DEFAULT) *
444 * *
445 * REXX RETURN VALUE: *
446 * A string of the format *
447 * pid parent-pid process-type priority cpu-time executable-name *
448 * "priority" is in hexadecimal notation, all other numbers are decimal. *
449 * "" is returned if the process was not found or if an internal error *
450 * occurred. *
451 * ------------------------------------------------------------------------- */
452ULONG APIENTRY Sys2QueryProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
453{
454 PSZ pszProcName; // Requested process name
455 UCHAR szFullName[ CCHMAXPATH ] = {0}, // Fully-qualified name
456 szReturn[ US_PIDSTR_MAXZ ] = {0}; // Buffer for return value
457 ULONG ulPID = 0, // Process ID
458 ulPPID = 0, // Parent process ID
459 ulType = 0, // Process type
460 ulTime = 0; // Process CPU time
461 USHORT usPrty = 0; // Process priority
462 APIRET rc; // API return code
463
464
465 // Reset the error indicator
466 WriteErrorCode( 0, NULL );
467
468 // Make sure we have at least one valid argument (the input string)
469 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
470
471 // Parse the ID type flag
472 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
473 strupr( argv[1].strptr );
474 if (strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 );
475 switch ( argv[1].strptr[0] ) {
476
477 case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 );
478 pszProcName = NULL;
479 break;
480
481 case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 );
482 pszProcName = NULL;
483 break;
484
485 default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
486 if ( pszProcName == NULL ) {
487 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
488 MAKERXSTRING( *prsResult, "0", 1 );
489 return ( 0 );
490 }
491 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
492 break;
493 }
494 } else {
495 pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
496 if ( pszProcName == NULL ) {
497 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
498 MAKERXSTRING( *prsResult, "0", 1 );
499 return ( 0 );
500 }
501 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
502 }
503
504 // See if the requested process is running and get its PID/PPID
505 rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime );
506 if (( rc != NO_ERROR ) || ( ulPID == 0 )) {
507 MAKERXSTRING( *prsResult, "", 0 );
508 return ( 0 );
509 }
510
511 sprintf( szReturn, "%u %u %u %04X %02u:%02u.%02u %s",
512 ulPID, ulPPID, ulType, usPrty, TIME_SECONDS( ulTime ) / 60,
513 TIME_SECONDS( ulTime ) % 60, TIME_HUNDREDTHS( ulTime ), szFullName );
514
515 MAKERXSTRING( *prsResult, szReturn, strlen(szReturn) );
516
517 return ( 0 );
518}
519
520
521/* ------------------------------------------------------------------------- *
522 * Sys2KillProcess *
523 * *
524 * Terminate the (first) running process with the specified executable name *
525 * or process-ID. *
526 * *
527 * REXX ARGUMENTS: *
528 * 1. The process identifier (program name or process ID) (REQUIRED) *
529 * 2. Flag indicicating the identifier type: *
530 * 'P': decimal process ID *
531 * 'H': hexadecimal process ID *
532 * 'N': executable program name (with or without extension) (DEFAULT) *
533 * *
534 * REXX RETURN VALUE: 1 on success or 0 on failure. *
535 * ------------------------------------------------------------------------- */
536ULONG APIENTRY Sys2KillProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
537{
538 PSZ pszProcName; // Requested process name
539 UCHAR szFullName[ CCHMAXPATH ] = {0}; // Fully-qualified name
540 ULONG ulPID = 0, // Process ID
541 ulPPID = 0, // Parent process ID (not used)
542 ulType = 0, // Process type (not used)
543 ulTime = 0; // Process CPU time (not used)
544 USHORT usPrty = 0; // Process priority (not used)
545 APIRET rc; // API return code
546
547
548 // Reset the error indicator
549 WriteErrorCode( 0, NULL );
550
551 // Make sure we have at least one valid argument (the input string)
552 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
553
554 // Parse the ID type flag
555 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
556 strupr( argv[1].strptr );
557 if (strcspn(argv[1].strptr, "HNP") > 0 ) return ( 40 );
558 switch ( argv[1].strptr[0] ) {
559
560 case 'H': if (( sscanf( argv[0].strptr, "%X", &ulPID )) != 1 ) return ( 40 );
561 pszProcName = NULL;
562 break;
563
564 case 'P': if (( sscanf( argv[0].strptr, "%u", &ulPID )) != 1 ) return ( 40 );
565 pszProcName = NULL;
566 break;
567
568 default : pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
569 if ( pszProcName == NULL ) {
570 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
571 MAKERXSTRING( *prsResult, "0", 1 );
572 return ( 0 );
573 }
574 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
575 break;
576 }
577 } else {
578 pszProcName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
579 if ( pszProcName == NULL ) {
580 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
581 MAKERXSTRING( *prsResult, "0", 1 );
582 return ( 0 );
583 }
584 strncpy( pszProcName, argv[0].strptr, RXSTRLEN(argv[0]) );
585 }
586
587 if ( pszProcName != NULL ) {
588 // Get the process PID
589 rc = GetProcess( pszProcName, szFullName, &ulPID, &ulPPID, &ulType, &usPrty, &ulTime );
590 if (( rc != NO_ERROR ) || ( ulPID == 0 )) {
591 MAKERXSTRING( *prsResult, "0", 1 );
592 return ( 0 );
593 }
594 }
595
596 // Now attempt to kill the process using DosKillProcess()
597 rc = DosKillProcess( 1, ulPID );
598 if ( rc != NO_ERROR ) {
599 WriteErrorCode( rc, "DosKillProcess");
600 MAKERXSTRING( *prsResult, "0", 1 );
601 return ( 0 );
602 }
603
604 MAKERXSTRING( *prsResult, "1", 1 );
605 return ( 0 );
606}
607
608
609/* ------------------------------------------------------------------------- *
610 * Sys2QueryProcessList *
611 * *
612 * Gets the process ID of the specified executable, if it is running. *
613 * The results will be returned in a stem variable, where stem.0 contains *
614 * number of items, and each stem item is a string of the form: *
615 * pid parent-pid process-type priority cpu-time executable-name *
616 * "priority" is in hexadecimal notation, all other numbers are decimal. *
617 * *
618 * Notes: *
619 * - "process-type" will be one of: *
620 * 0 Full screen protect-mode session *
621 * 1 Requires real mode. Dos emulation. *
622 * 2 VIO windowable protect-mode session *
623 * 3 Presentation Manager protect-mode session *
624 * 4 Detached protect-mode process. *
625 * - If "priority" is 0 then the priority class could not be determined. *
626 * - If "executable-name" is "--" then the name could not be identified. *
627 * *
628 * REXX ARGUMENTS: *
629 * 1. The name of the stem in which to return the results (REQUIRED) *
630 * *
631 * REXX RETURN VALUE: Number of processes found, or "" in case of error. *
632 * ------------------------------------------------------------------------- */
633ULONG Sys2QueryProcessList( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
634{
635#ifdef USE_DQPS
636 QSPTRREC *pBuf; // Data returned by DosQProcStatus()
637#else
638 QSGREC **pBuf; // Data returned by DosQuerySysState()
639#endif
640 QSPREC *pPrec; // Pointer to process information block
641 QSTREC *pTrec; // Pointer to thread information block
642 CHAR szStem[ US_STEM_MAXZ ], // Buffers used for building strings ...
643 szNumber[ US_INTEGER_MAXZ ], // ...
644 szName[ CCHMAXPATH ], // Fully-qualified name of process
645 szPInfo[ US_PIDSTR_MAXZ ]; // Stem item string
646 ULONG ulCount, // Number of processes
647 ulCPU; // Process CPU time
648 USHORT usPriority, // Process priority class
649 i; // Loop counter
650 APIRET rc; // Return code
651
652
653 // Reset the error indicator
654 WriteErrorCode( 0, NULL );
655
656 // Do some validity checking on the arguments
657 if (( argc != 1 ) || // Make sure we have exactly one argument...
658 ( ! RXVALIDSTRING(argv[0]) ) || // ...which is a valid REXX string...
659 ( RXSTRLEN(argv[0]) > US_STEM_MAXZ )) // ...and isn't too long.
660 return ( 40 );
661
662 // Generate the stem variable name from the argument (stripping any final dot)
663 if ( argv[0].strptr[ argv[0].strlength-1 ] == '.') argv[0].strlength--;
664 strncpy( szStem, argv[0].strptr, RXSTRLEN(argv[0]) );
665 szStem[ RXSTRLEN(argv[0]) ] = '\0';
666
667#ifdef USE_DQPS
668 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE );
669#else
670 pBuf = (QSGREC **) malloc( UL_SSBUFSIZE );
671#endif
672
673 if ( pBuf == NULL ) {
674 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
675 MAKERXSTRING( *prsResult, "", 0 );
676 return ( 0 );
677 }
678
679#ifdef USE_DQPS
680 // Get running process information using DosQProcStatus()
681 rc = DosQProcStatus( pBuf, UL_SSBUFSIZE );
682 if ( rc != NO_ERROR ) {
683 WriteErrorCode( rc, "DosQProcStatus");
684 MAKERXSTRING( *prsResult, "", 0 );
685 return ( 0 );
686 }
687 pPrec = pBuf->pProcRec;
688#else
689 // Get running process information using DosQuerySysState()
690 rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE );
691 if ( rc != NO_ERROR ) {
692 WriteErrorCode( rc, "DosQuerySysState");
693 MAKERXSTRING( *prsResult, "", 0 );
694 return ( 0 );
695 }
696 pPrec = (QSPREC *) ( (PBYTE) (*pBuf) + sizeof(QSGREC) );
697#endif
698
699 // Now get the list of processes
700 ulCount = 0;
701 while ( pPrec->RecType == 1 ) {
702 ulCount++;
703
704 // Get the program name of each process (including path)
705 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
706 sprintf( szName, "--");
707 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
708 usPriority = 0;
709
710 // Get the CPU time of the process by querying each of its threads
711 ulCPU = 0;
712 pTrec = pPrec->pThrdRec;
713 for ( i = 0; i < pPrec->cTCB; i++ ) {
714 ulCPU += ( pTrec->systime + pTrec->usertime );
715 pTrec++;
716 }
717
718 // Now generate the stem item with all of this information
719 sprintf( szPInfo, "%u %u %u %04X %02u:%02u.%02u %s",
720 pPrec->pid, // PID
721 pPrec->ppid, // Parent PID
722 pPrec->type, // Process type
723 usPriority, // Priority class
724 TIME_SECONDS( ulCPU ) / 60, // CPU time (hours)
725 TIME_SECONDS( ulCPU ) % 60, // CPU time (minutes)
726 TIME_HUNDREDTHS( ulCPU ), // CPU time (seconds)
727 szName ); // Executable name & path
728 WriteStemElement( szStem, ulCount, szPInfo );
729
730 pPrec = (QSPREC *) ( (PBYTE) (pPrec->pThrdRec) + ( pPrec->cTCB * sizeof(QSTREC) ) );
731 }
732
733 // Create the "0" stem element with the number of processes found
734 sprintf( szNumber, "%d", ulCount );
735 WriteStemElement( szStem, 0, szNumber );
736
737 // And also return the number of processes as the REXX return string
738 MAKERXSTRING( *prsResult, szNumber, strlen(szNumber) );
739
740 free( pBuf );
741 return ( 0 );
742}
743
744
745/* ------------------------------------------------------------------------- *
746 * Sys2QueryPhysicalMemory *
747 * *
748 * Queries the amount of physical memory (RAM) installed in the system. *
749 * *
750 * REXX ARGUMENTS: None *
751 * *
752 * REXX RETURN VALUE: *
753 * Integer representing the amount of installed memory, in KiB, or 0 if an *
754 * error occurred. *
755 * ------------------------------------------------------------------------- */
756ULONG APIENTRY Sys2QueryPhysicalMemory( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
757{
758 CHAR szMemSize[ US_INTEGER_MAXZ ];
759 ULONG ulMemBytes = 0,
760 ulMemKBytes = 0;
761 APIRET rc = 0;
762
763 // Reset the error indicator
764 WriteErrorCode( 0, NULL );
765
766 // Make sure we have no arguments
767 if ( argc > 0 ) return ( 40 );
768
769 // Query installed memory in bytes
770 rc = DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
771 &ulMemBytes, sizeof(ulMemBytes) );
772 if ( rc != NO_ERROR ) {
773 WriteErrorCode( rc, "DosQuerySysInfo");
774 MAKERXSTRING( *prsResult, "0", 1 );
775 return ( 0 );
776 }
777
778 // Convert to binary kilobytes (any remainder is discarded)
779 ulMemKBytes = ulMemBytes / 1024;
780 sprintf( szMemSize, "%u", ulMemKBytes );
781
782 // Return the memory size as the REXX return string
783 MAKERXSTRING( *prsResult, szMemSize, strlen(szMemSize) );
784
785 return ( 0 );
786}
787
788
789/* ------------------------------------------------------------------------- *
790 * Sys2QueryForegroundProcess *
791 * *
792 * Queries the PID of the current foreground process. *
793 * *
794 * REXX ARGUMENTS: None *
795 * *
796 * REXX RETURN VALUE: *
797 * Integer representing the process ID (in decimal), or 0 if an error *
798 * occurred. *
799 * ------------------------------------------------------------------------- */
800ULONG APIENTRY Sys2QueryForegroundProcess( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
801{
802 CHAR szPID[ US_INTEGER_MAXZ ];
803 ULONG ulPID = 0;
804 APIRET rc = 0;
805
806 // Reset the error indicator
807 WriteErrorCode( 0, NULL );
808
809 // Make sure we have no arguments
810 if ( argc > 0 ) return ( 40 );
811
812 // Query installed memory in bytes
813 rc = DosQuerySysInfo( QSV_FOREGROUND_PROCESS,
814 QSV_FOREGROUND_PROCESS,
815 &ulPID, sizeof(ulPID) );
816 if ( rc != NO_ERROR ) {
817 WriteErrorCode( rc, "DosQuerySysInfo");
818 MAKERXSTRING( *prsResult, "0", 1 );
819 return ( 0 );
820 }
821 sprintf( szPID, "%u", ulPID );
822
823 // Return the PID as the REXX return string
824 MAKERXSTRING( *prsResult, szPID, strlen(szPID) );
825
826 return ( 0 );
827}
828
829
830/* ------------------------------------------------------------------------- *
831 * Sys2ReplaceModule *
832 * *
833 * Unlocks and optionally replaces an in-use (locked) DLL or EXE. *
834 * *
835 * REXX ARGUMENTS: *
836 * 1. The filespec of the module to be replaced. (REQUIRED) *
837 * 2. The filespec of the new module to replace it with. (DEFAULT: none) *
838 * 3. The filespec of the backup file to be created. (DEFAULT: none) *
839 * *
840 * REXX RETURN VALUE: *
841 * 1 on success, or 0 if an error occurred. *
842 * ------------------------------------------------------------------------- */
843ULONG APIENTRY Sys2ReplaceModule( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
844{
845 PSZ pszOldModule = NULL,
846 pszNewModule = NULL,
847 pszBackup = NULL;
848 APIRET rc = 0;
849
850 // Reset the error indicator
851 WriteErrorCode( 0, NULL );
852
853 // Make sure we have at least one valid argument (the module name)
854 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
855 pszOldModule = calloc( argv[0].strlength + 1, sizeof(UCHAR) );
856 if ( pszOldModule == NULL ) {
857 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
858 MAKERXSTRING( *prsResult, "0", 1 );
859 return ( 0 );
860 }
861 strncpy( pszOldModule, argv[0].strptr, argv[0].strlength );
862
863 // Second argument: new module name (optional, but must be correct if specified)
864 if ( argc >= 2 ) {
865 if ( RXVALIDSTRING(argv[1]) ) {
866 pszNewModule = calloc( argv[1].strlength + 1, sizeof(char) );
867 if ( pszNewModule == NULL ) {
868 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
869 MAKERXSTRING( *prsResult, "0", 1 );
870 return ( 0 );
871 }
872 strncpy( pszNewModule, argv[1].strptr, argv[1].strlength );
873 } else return ( 40 );
874 }
875
876 // Third argument: backup filename (optional, but must be correct if specified)
877 if ( argc >= 3 ) {
878 if ( RXVALIDSTRING(argv[2]) ) {
879 pszBackup = calloc( argv[2].strlength + 1, sizeof(char) );
880 if ( pszBackup == NULL ) {
881 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
882 MAKERXSTRING( *prsResult, "0", 1 );
883 return ( 0 );
884 }
885 strncpy( pszBackup, argv[2].strptr, argv[2].strlength );
886 } else return ( 40 );
887 }
888
889 // Now replace the module using DosReplaceModule
890 rc = DosReplaceModule( pszOldModule, pszNewModule, pszBackup );
891 if ( rc != NO_ERROR ) {
892 WriteErrorCode( rc, "DosReplaceModule");
893 MAKERXSTRING( *prsResult, "0", 1 );
894 return ( 0 );
895 }
896
897 // Return 1 on success
898 MAKERXSTRING( *prsResult, "1", 1 );
899
900 return ( 0 );
901}
902
903
904/* ------------------------------------------------------------------------- *
905 * Sys2FormatTime *
906 * *
907 * Convert a number of seconds from the epoch (1970-01-01 0:00:00 UTC) into *
908 * a formatted date and time string. *
909 * *
910 * REXX ARGUMENTS: *
911 * 1. Number of seconds (a positive integer) to be converted. (REQUIRED) *
912 * 2. Format type, one of: *
913 * D = return in the form 'yyyy-mm-dd hh:mm:ss (w)' where w *
914 * represents the weekday (0-6 where 0=Sunday) (DEFAULT) *
915 * I = return in ISO8601 combined form 'yyyy-mm-ddThh:mm:ss[Z]' *
916 * L = return in the form 'day month year (weekday) time' where month *
917 * and weekday are language-dependent abbreviations *
918 * Note: With D and I, time is returned in 24-hour format; L may vary. *
919 * 3. TZ conversion flag (indicates whether to convert to UTC from local *
920 * time), one of: *
921 * U = return in Coordinated Universal Time *
922 * L = convert to local time using the current TZ (DEFAULT) *
923 * *
924 * REXX RETURN VALUE: The formatted time string, or '' on error. *
925 * ------------------------------------------------------------------------- */
926ULONG APIENTRY Sys2FormatTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
927{
928 UCHAR szFormat[ US_TIMESTR_MAXZ ] = {0}, // strftime() format specifier
929 szTime[ US_TIMESTR_MAXZ ] = {0}; // Formatted time string
930 BYTE flFormat = FL_TIME_DEFAULT; // Time format flag
931 BOOL fUTC = FALSE; // UTC/local conversion flag
932 PSZ pszTZ, // Pointer to TZ environment var
933 pszSetTZ;
934 int iEpoch; // Input epoch time
935 time_t ttSeconds; // Input timestamp (seconds)
936 struct tm *timeptr; // Timestamp structure
937 size_t stRC; // return code from strftime()
938
939 // Reset the error indicator
940 WriteErrorCode( 0, NULL );
941
942 // All arguments are optional but must be correct if specified
943
944 if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) {
945 // first argument: epoch time value
946 if (( sscanf( argv[0].strptr, "%d", &iEpoch )) != 1 ) return ( 40 );
947 ttSeconds = (time_t) iEpoch;
948 }
949
950 if ( argc >= 2 ) {
951 // second argument: format flag
952 if ( RXVALIDSTRING(argv[1]) ) {
953 strupr( argv[1].strptr );
954 if ( strcspn(argv[1].strptr, "DIL") > 0 ) return ( 40 );
955 switch ( argv[1].strptr[0] ) {
956 case 'I': flFormat = FL_TIME_ISO8601; break;
957 case 'L': flFormat = FL_TIME_LOCALE; break;
958 default : flFormat = FL_TIME_DEFAULT; break;
959 }
960 }
961 }
962
963 if ( argc >= 3 ) {
964 // third argument: conversion flag
965 if ( RXVALIDSTRING(argv[2]) ) {
966 strupr( argv[2].strptr );
967 if ( strcspn(argv[2].strptr, "UL") > 0 ) return ( 40 );
968 switch ( argv[2].strptr[0] ) {
969 case 'U': fUTC = TRUE; break;
970 default : fUTC = FALSE; break;
971 }
972 }
973 }
974
975 /* These next 4 lines really shouldn't be necessary, but without them
976 * getenv() and (apparently) tzset() may see the value of TZ as NULL
977 * if the environment variable was changed in the REXX script.
978 */
979 DosScanEnv("TZ", &pszTZ );
980 pszSetTZ = (PSZ) malloc( strlen( pszTZ ) + 5 );
981 sprintf( pszSetTZ, "TZ=%s", pszTZ );
982 putenv( pszSetTZ );
983
984 // Use the locale and timezone settings from the environment
985 tzset();
986 setlocale( LC_TIME, "");
987
988 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) {
989 ttSeconds = time( NULL );
990 if ( ttSeconds == -1 ) {
991 WriteErrorCode( ttSeconds, "time");
992 MAKERXSTRING( *prsResult, "", 0 );
993 return 0;
994 }
995 }
996
997 if ( fUTC ) {
998 timeptr = gmtime( &ttSeconds );
999 if ( !timeptr ) {
1000 WriteErrorCode( 1, "gmtime");
1001 MAKERXSTRING( *prsResult, "0", 1 );
1002 return 0;
1003 }
1004 }
1005 else {
1006 timeptr = localtime( &ttSeconds );
1007 if ( !timeptr ) {
1008 WriteErrorCode( 1, "localtime");
1009 MAKERXSTRING( *prsResult, "0", 1 );
1010 return 0;
1011 }
1012 }
1013
1014 switch ( flFormat ) {
1015 default:
1016 case FL_TIME_DEFAULT:
1017 sprintf( szFormat, "%%Y-%%m-%%d %%T (%%w)");
1018 break;
1019
1020 case FL_TIME_ISO8601:
1021 sprintf( szFormat, "%%Y-%%m-%%dT%%T");
1022 if ( fUTC ) strcat( szFormat, "Z");
1023 break;
1024
1025 case FL_TIME_LOCALE:
1026 sprintf( szFormat, "%%e %%b %%Y (%%a) %%X");
1027 break;
1028 }
1029
1030 stRC = strftime( szTime, US_TIMESTR_MAXZ-1, szFormat, timeptr );
1031 if ( stRC == NO_ERROR ) {
1032 WriteErrorCode( stRC, "strftime");
1033 MAKERXSTRING( *prsResult, "", 0 );
1034 return ( 0 );
1035 }
1036
1037 // Return the formatted time string
1038 MAKERXSTRING( *prsResult, szTime, strlen(szTime) );
1039
1040 free( pszSetTZ );
1041 return ( 0 );
1042}
1043
1044
1045/* ------------------------------------------------------------------------- *
1046 * Sys2GetEpochTime *
1047 * *
1048 * Convert formatted date and time into a number of seconds (UTC) from the *
1049 * epoch (defined as 1970-01-01 0:00:00). The input time is assumed to *
1050 * refer to the current timezone as defined in the TZ environment variable. *
1051 * *
1052 * If no parameters are specified, the current system time is used. If at *
1053 * least one parameter is specified, then any missing parameter is assumed *
1054 * to be its minimum possible value. *
1055 * *
1056 * Due to limitations in time_t, dates later than 2037 are not supported; *
1057 * the IBM library seems to convert them all to January 1 1970 00:00:00 UTC. *
1058 * *
1059 * REXX ARGUMENTS: *
1060 * 1. The year (0-99 or 1970+) (value <70 is assumed to be 20xx) *
1061 * 2. The month (1-12) *
1062 * 3. The day (1-31) *
1063 * 4. Hours (0-23) *
1064 * 5. Minutes (0-59) *
1065 * 6. Seconds (0-61) *
1066 * *
1067 * REXX RETURN VALUE: The number of seconds since the epoch, or 0 on error. *
1068 * ------------------------------------------------------------------------- */
1069ULONG APIENTRY Sys2GetEpochTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1070{
1071 ULONG ulYear = 1970, // Input year
1072 ulMonth = 1, // Input month
1073 ulDay = 1, // Input day
1074 ulHour = 0, // Input hours
1075 ulMin = 0, // Input minutes
1076 ulSec = 0; // Input seconds
1077 BOOL fYear = FALSE, // Year parameter specified?
1078 fMonth = FALSE, // Month parameter specified?
1079 fDay = FALSE, // Day parameter specified?
1080 fHour = FALSE, // Hours parameter specified?
1081 fMin = FALSE, // Minutes parameter specified?
1082 fSec = FALSE; // Seconds parameter specified?
1083 //SHORT sDST = 0; // Input time is DST?
1084 time_t timeval; // Calculated epoch time
1085 struct tm tsTime = {0}; // Time structure for mktime()
1086 UCHAR szEpochTime[ US_INTEGER_MAXZ ]; // Output string
1087 PSZ pszTZ,
1088 pszSetTZ;
1089
1090
1091 // Reset the error indicator
1092 WriteErrorCode( 0, NULL );
1093
1094 // Parse the various time items
1095 if ( argc >= 1 && RXVALIDSTRING(argv[0]) ) {
1096 if (( sscanf( argv[0].strptr, "%u", &ulYear )) != 1 ) return ( 40 );
1097 if ( ulYear < 100 ) {
1098 ulYear += (ulYear < 70) ? 2000 : 1900;
1099 }
1100 if ( ulYear < 1970 ) return ( 40 );
1101 fYear = TRUE;
1102 }
1103 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1104 if (( sscanf( argv[1].strptr, "%u", &ulMonth )) != 1 ) return ( 40 );
1105 if ( ulMonth < 1 || ulMonth > 12 ) return ( 40 );
1106 fMonth = TRUE;
1107 }
1108 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
1109 if (( sscanf( argv[2].strptr, "%u", &ulDay )) != 1 ) return ( 40 );
1110 if ( ulDay < 1 || ulDay > 31 ) return ( 40 );
1111 fDay = TRUE;
1112 }
1113 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
1114 if (( sscanf( argv[3].strptr, "%u", &ulHour )) != 1 ) return ( 40 );
1115 if ( ulHour > 23 ) return ( 40 );
1116 fHour = TRUE;
1117 }
1118 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
1119 if (( sscanf( argv[4].strptr, "%u", &ulMin )) != 1 ) return ( 40 );
1120 if ( ulMin > 59 ) return ( 40 );
1121 fMin = TRUE;
1122 }
1123 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
1124 if (( sscanf( argv[5].strptr, "%u", &ulSec )) != 1 ) return ( 40 );
1125 if ( ulSec > 61 ) return ( 40 );
1126 fSec = TRUE;
1127 }
1128 if ( argc >= 7 ) return ( 40 );
1129/*
1130 // Parse the conversion flag
1131 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1132 strupr( argv[6].strptr );
1133 if ( strcspn(argv[6].strptr, "SD") > 0 ) return ( 40 );
1134 switch ( argv[6].strptr[0] ) {
1135 case 'S': sDST = 0; break;
1136 case 'D': sDST = 1; break;
1137 default : sDST = -1; break;
1138 }
1139 }
1140*/
1141
1142 /* These next 4 lines really shouldn't be necessary, but without them
1143 * getenv() and (apparently) tzset() may see the value of TZ as NULL
1144 * if the environment variable was changed in the REXX script.
1145 */
1146 DosScanEnv("TZ", &pszTZ );
1147 pszSetTZ = (PSZ) malloc( strlen( pszTZ ) + 5 );
1148 sprintf( pszSetTZ, "TZ=%s", pszTZ );
1149 putenv( pszSetTZ );
1150
1151// This seems to conflict with time() under some shells -AT
1152 tzset();
1153
1154 // Use the locale settings from the environment
1155 setlocale( LC_TIME, "");
1156
1157 if ( !fYear && !fMonth && !fDay && !fHour && !fMin && !fSec ) {
1158 timeval = time( NULL );
1159 if ( timeval == -1 ) {
1160 WriteErrorCode( timeval, "time");
1161 MAKERXSTRING( *prsResult, "0", 1 );
1162 free( pszSetTZ );
1163 return 0;
1164 }
1165 }
1166 else {
1167//printf("TZ=%s\n", getenv("TZ"));
1168 tsTime.tm_sec = ulSec;
1169 tsTime.tm_min = ulMin;
1170 tsTime.tm_hour = ulHour;
1171 tsTime.tm_mday = ulDay;
1172 tsTime.tm_mon = ulMonth - 1;
1173 tsTime.tm_year = ulYear - 1900;
1174 tsTime.tm_isdst = -1;
1175 timeval = mktime( &tsTime );
1176 if ( timeval == -1 ) {
1177 WriteErrorCode( timeval, "mktime");
1178 MAKERXSTRING( *prsResult, "0", 1 );
1179 free( pszSetTZ );
1180 return 0;
1181 }
1182 }
1183
1184 // Return the calculated time value
1185#if __IBMC__ >= 360 || __IBMCPP__ >= 360
1186 sprintf( szEpochTime, "%.0f", timeval );
1187#else
1188 sprintf( szEpochTime, "%d", timeval );
1189#endif
1190 MAKERXSTRING( *prsResult, szEpochTime, strlen(szEpochTime) );
1191
1192 free( pszSetTZ );
1193 return ( 0 );
1194}
1195
1196
1197/* ------------------------------------------------------------------------- *
1198 * Sys2LocateDLL *
1199 * *
1200 * Search for an installed or loaded DLL by module name. *
1201 * Code derived from 'whichdll' by Alessandro Cantatore (public domain). *
1202 * *
1203 * REXX ARGUMENTS: *
1204 * 1. The name of the DLL to search for. (REQUIRED) *
1205 * *
1206 * REXX RETURN VALUE: *
1207 * The fully-qualified path of the DLL, if found (or '' if not found). *
1208 * ------------------------------------------------------------------------- */
1209ULONG APIENTRY Sys2LocateDLL( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1210{
1211 HMODULE hmod;
1212 CHAR achModuleName[ CCHMAXPATH ];
1213 BOOL bUnload = FALSE;
1214 APIRET rc;
1215
1216 // Reset the error indicator
1217 WriteErrorCode( 0, NULL );
1218
1219 // Parse the various time items
1220 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1221
1222 // See if the DLL is already loaded
1223 rc = DosQueryModuleHandle( argv[0].strptr, &hmod );
1224 if ( rc ) {
1225 // Guess not; try to load it now
1226 rc = DosLoadModule( NULL, 0, argv[0].strptr, &hmod );
1227 if ( rc ) {
1228 WriteErrorCode( rc, "DosLoadModule");
1229 MAKERXSTRING( *prsResult, "", 0 );
1230 return 0;
1231 }
1232 bUnload = TRUE;
1233 }
1234
1235 // Get the full path name of the DLL
1236 rc = DosQueryModuleName( hmod, CCHMAXPATH, achModuleName );
1237 if ( rc ) {
1238 WriteErrorCode( rc, "DosQueryModuleName");
1239 MAKERXSTRING( *prsResult, "", 0 );
1240 if ( bUnload ) DosFreeModule( hmod );
1241 return 0;
1242 }
1243
1244 // Free the module if we loaded it ourselves
1245 if ( bUnload ) DosFreeModule( hmod );
1246
1247 // Return the full path name
1248 if ( ! SaveResultString( prsResult, achModuleName, strlen( achModuleName ))) {
1249 MAKERXSTRING( *prsResult, "", 0 );
1250 }
1251
1252 return 0;
1253}
1254
1255
1256/* ------------------------------------------------------------------------- *
1257 * Sys2CreateNamedPipe *
1258 * *
1259 * Create a named pipe with the specified name and parameters. Only byte *
1260 * mode is supported; message mode is not. *
1261 * *
1262 * REXX ARGUMENTS: *
1263 * 1. The name of the pipe, in the form "\PIPE\something". (REQUIRED) *
1264 * 2. The size of the outbound buffer, in bytes. (REQUIRED) *
1265 * 3. The size of the inbound buffer, in bytes. (REQUIRED) *
1266 * 4. The pipe's timeout value, in milliseconds. (DEFAULT: 3000) *
1267 * 5. The number of simultaneous instances of this pipe which are allowed. *
1268 * Must be between 1 and 254, or 0 indicating no limit. (DEFAULT: 1) *
1269 * 6. Pipe blocking mode, one of: *
1270 * W = WAIT mode, read and write block waiting for data. (DEFAULT) *
1271 * N = NOWAIT mode, read and write return immediately. *
1272 * 7. Pipe mode, one of: *
1273 * I = Inbound pipe (DEFAULT) *
1274 * O = Outbound pipe *
1275 * D = Duplex (inbound/outbound) pipe *
1276 * 8. Privacy/inheritance flag, one of: *
1277 * 0 = The pipe handle is inherited by child processes. (DEFAULT) *
1278 * 1 = The pipe handle is private to the current process. *
1279 * 9. Write-through flag, one of: *
1280 * 0 = Allow delayed writes (write-behind) to remote pipes. (DEFAULT) *
1281 * 1 = Force immediate writes (write-through) to remote pipes. *
1282 * *
1283 * REXX RETURN VALUE: *
1284 * A four-byte pipe handle. *
1285 * ------------------------------------------------------------------------- */
1286ULONG APIENTRY Sys2CreateNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1287{
1288 HPIPE hp;
1289 PSZ pszNPName;
1290 LONG iLimit;
1291 ULONG ulBufOut,
1292 ulBufIn,
1293 ulTimeout = 3000,
1294 flOpen = 0,
1295 flPipe = 1;
1296 CHAR achHandle[ 9 ];
1297 APIRET rc;
1298
1299 // Reset the error indicator
1300 WriteErrorCode( 0, NULL );
1301
1302 // Make sure we have at least three valid arguments (pipe name and sizes)
1303 if ( argc < 3 || ( !RXVALIDSTRING(argv[0]) ) ||
1304 ( !RXVALIDSTRING(argv[1]) ) || ( !RXVALIDSTRING(argv[2]) ))
1305 return ( 40 );
1306
1307 // (Validate the first argument last to simplify error processing)
1308
1309 // Second argument: pipe outbound buffer size
1310 if (( sscanf( argv[1].strptr, "%u", &ulBufOut )) != 1 ) return ( 40 );
1311
1312 // Third argument: pipe outbound buffer size
1313 if (( sscanf( argv[2].strptr, "%u", &ulBufIn )) != 1 ) return ( 40 );
1314
1315 // Fourth argument: pipe timeout value
1316 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
1317 if (( sscanf( argv[3].strptr, "%u", &ulTimeout )) != 1 ) return ( 40 );
1318 }
1319
1320 // Fifth argument: instances limit
1321 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
1322 if (( sscanf( argv[4].strptr, "%d", &iLimit )) != 1 ) return ( 40 );
1323 if (( iLimit > 1 ) && ( iLimit < 255 ))
1324 flPipe = iLimit;
1325 else if ( !iLimit || ( iLimit == -1 ))
1326 flPipe = NP_UNLIMITED_INSTANCES;
1327 else
1328 return ( 40 );
1329 }
1330
1331 // Sixth argument: blocking mode
1332 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
1333 strupr( argv[5].strptr );
1334 if ( argv[5].strptr[0] == 'N' )
1335 flPipe |= NP_NOWAIT;
1336 else if ( argv[5].strptr[0] != 'W' )
1337 return ( 40 );
1338 }
1339
1340 // Seventh argument: pipe mode (direction)
1341 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1342 strupr( argv[6].strptr );
1343 if (strcspn(argv[6].strptr, "IOD") > 0 ) return ( 40 );
1344 switch ( argv[6].strptr[0] ) {
1345 case 'O': flOpen |= NP_ACCESS_OUTBOUND; break;
1346 case 'D': flOpen |= NP_ACCESS_DUPLEX; break;
1347 default : break; // default is 0
1348 }
1349 }
1350
1351 // Eighth argument: inheritance mode
1352 if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) {
1353 strupr( argv[7].strptr );
1354 if ( argv[7].strptr[0] == '1' )
1355 flOpen |= NP_NOINHERIT;
1356 else if ( argv[7].strptr[0] != '0' )
1357 return ( 40 );
1358 }
1359
1360 // Ninth argument: write mode
1361 if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) {
1362 strupr( argv[8].strptr );
1363 if ( argv[8].strptr[0] == '1' )
1364 flOpen |= NP_NOWRITEBEHIND;
1365 else if ( argv[8].strptr[0] != '0' )
1366 return ( 40 );
1367 }
1368
1369 // Now the first argument: pipe name
1370 pszNPName = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
1371 if ( pszNPName == NULL ) {
1372 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1373 MAKERXSTRING( *prsResult, "0", 1 );
1374 return ( 0 );
1375 }
1376 strncpy( pszNPName, argv[0].strptr, RXSTRLEN(argv[0]) );
1377
1378 // All good, now create the pipe
1379 rc = DosCreateNPipe( pszNPName, &hp, flOpen, flPipe, ulBufOut, ulBufIn, ulTimeout );
1380 if (rc) {
1381 WriteErrorCode( rc, "DosCreateNPipe");
1382 MAKERXSTRING( *prsResult, "", 0 );
1383 return 0;
1384 }
1385
1386 // Return the handle as the REXX result string
1387 sprintf( achHandle, "%8X", hp );
1388 MAKERXSTRING( *prsResult, achHandle, strlen( achHandle ));
1389
1390 free( pszNPName );
1391 return ( 0 );
1392}
1393
1394
1395/* ------------------------------------------------------------------------- *
1396 * Sys2ConnectNamedPipe *
1397 * *
1398 * Start 'listening' by allowing clients to connect to a previously-created *
1399 * named pipe. *
1400 * *
1401 * REXX ARGUMENTS: *
1402 * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) *
1403 * *
1404 * REXX RETURN VALUE: *
1405 * 1 on success, or 0 if an error occurred. *
1406 * ------------------------------------------------------------------------- */
1407ULONG APIENTRY Sys2ConnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1408{
1409 HPIPE hp;
1410 APIRET rc;
1411
1412 // Reset the error indicator
1413 WriteErrorCode( 0, NULL );
1414
1415 // Parse the handle
1416 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1417 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1418
1419 // Connect the pipe
1420 rc = DosConnectNPipe( hp );
1421 if ( rc != NO_ERROR ) {
1422 WriteErrorCode( rc, "DosConnectNPipe");
1423 MAKERXSTRING( *prsResult, "0", 1 );
1424 return ( 0 );
1425 }
1426
1427 // Return 1 on success
1428 MAKERXSTRING( *prsResult, "1", 1 );
1429 return ( 0 );
1430}
1431
1432
1433/* ------------------------------------------------------------------------- *
1434 * Sys2DisconnectNamedPipe *
1435 * *
1436 * Unlocks a named pipe after a client has closed its connection. *
1437 * *
1438 * REXX ARGUMENTS: *
1439 * 1. The pipe handle, as returned by Sys2CreateNamedPipe. (REQUIRED) *
1440 * *
1441 * REXX RETURN VALUE: *
1442 * 1 on success, or 0 if an error occurred. *
1443 * ------------------------------------------------------------------------- */
1444ULONG APIENTRY Sys2DisconnectNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1445{
1446 HPIPE hp;
1447 APIRET rc;
1448
1449 // Reset the error indicator
1450 WriteErrorCode( 0, NULL );
1451
1452 // Parse the handle
1453 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1454 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1455
1456 // Connect the pipe
1457 rc = DosDisConnectNPipe( hp );
1458 if ( rc != NO_ERROR ) {
1459 WriteErrorCode( rc, "DosDisConnectNPipe");
1460 MAKERXSTRING( *prsResult, "0", 1 );
1461 return ( 0 );
1462 }
1463
1464 // Return 1 on success
1465 MAKERXSTRING( *prsResult, "1", 1 );
1466 return ( 0 );
1467}
1468
1469
1470/* ------------------------------------------------------------------------- *
1471 * Sys2CheckNamedPipe *
1472 * *
1473 * Check the status of a named pipe. *
1474 * *
1475 * REXX ARGUMENTS: *
1476 * 1. The pipe handle (from Sys2CreateNamedPipe or DosOpen). (REQUIRED) *
1477 * *
1478 * REXX RETURN VALUE: *
1479 * String of the format "bytes status", where bytes is the number of bytes *
1480 * currently waiting in the pipe, and status is one of: DISCONNECTED, *
1481 * LISTENING, CONNECTED, or CLOSING. *
1482 * ------------------------------------------------------------------------- */
1483ULONG APIENTRY Sys2CheckNamedPipe( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1484{
1485 HPIPE hp;
1486 ULONG cbActual, ulState;
1487 AVAILDATA avd;
1488 CHAR szStatus[ US_PIPESTATUS_MAXZ ];
1489 APIRET rc;
1490
1491 // Reset the error indicator
1492 WriteErrorCode( 0, NULL );
1493
1494 // Parse the handle
1495 if ( !(argc == 1 && RXVALIDSTRING(argv[0])) ) return ( 40 );
1496 if (( sscanf( argv[0].strptr, "%8X", &hp )) != 1 ) return ( 40 );
1497
1498 rc = DosPeekNPipe( hp, NULL, 0, &cbActual, &avd, &ulState );
1499 if ( rc != NO_ERROR ) {
1500 WriteErrorCode( rc, "DosPeekNPipe");
1501 MAKERXSTRING( *prsResult, "", 0 );
1502 return ( 0 );
1503 }
1504 sprintf( szStatus, "%u ", avd.cbpipe );
1505 switch ( ulState ) {
1506 case NP_STATE_DISCONNECTED: strncat( szStatus, "DISCONNECTED", US_PIPESTATUS_MAXZ-1 ); break;
1507 case NP_STATE_LISTENING: strncat( szStatus, "LISTENING", US_PIPESTATUS_MAXZ-1 ); break;
1508 case NP_STATE_CONNECTED: strncat( szStatus, "CONNECTED", US_PIPESTATUS_MAXZ-1 ); break;
1509 case NP_STATE_CLOSING: strncat( szStatus, "CLOSING", US_PIPESTATUS_MAXZ-1 ); break;
1510 default: strncat( szStatus, "UNKNOWN", US_PIPESTATUS_MAXZ-1 ); break;
1511 }
1512
1513 if ( ! SaveResultString( prsResult, szStatus, strlen( szStatus ))) {
1514 MAKERXSTRING( *prsResult, "", 0 );
1515 }
1516 return ( 0 );
1517}
1518
1519
1520/* ------------------------------------------------------------------------- *
1521 * Sys2Open *
1522 * *
1523 * Wrapper to DosOpenL: open a file or stream (with >2GB support). *
1524 * Direct-DASD mode is not supported by this function, nor is setting the *
1525 * initial extended attributes. *
1526 * *
1527 * REXX ARGUMENTS: *
1528 * 1. Name of file or stream to open. (REQUIRED) *
1529 * 2. Open action flags, must be either "O" (open if exists), "R" (replace *
1530 * if exists), or nothing (fail if exists), optionally followed by "C" *
1531 * (create if file does not exist). If "C" is not specified, the *
1532 * operation will fail if the file does not exist. Note that a value *
1533 * of "" alone will therefore fail automatically. (DEFAULT: "O") *
1534 * In summary, the possible combinations are: *
1535 * O = Open only (if file exists, open it; if not, fail) *
1536 * OC= Open/create (if file exists, open it; if not, create it) *
1537 * R = Replace only (if file exists, replace it; if not, fail) *
1538 * RC= Replace/create (if file exists, replace it; if not, create it) *
1539 * C = Create only (if file exists, fail; if not, create it) *
1540 * (empty) = No-op (if file exists, fail; if not, fail) *
1541 * 3. Access mode flags, one or both of: (DEFAULT: "RW") *
1542 * R = Open file with read access. *
1543 * W = Open file with write access. *
1544 * 4. Sharing mode flags, any combination of: (DEFAULT: "W") *
1545 * R = Deny read access to other processes *
1546 * W = Deny write access to other processes *
1547 * 5. Deny legacy DosOpen access, one of: *
1548 * 0 = Allow DosOpen to access the file (DEFAULT) *
1549 * 1 = Deny access using the DosOpen API *
1550 * 6. Privacy/inheritance flag, one of: *
1551 * 0 = The file handle is inherited by child processes. (DEFAULT) *
1552 * 1 = The file handle is private to the current process. *
1553 * 7. Initial file attributes when creating a file: (DEFAULT: "") *
1554 * A = Archive attribute set *
1555 * D = Directory attribute set *
1556 * S = System attribute set *
1557 * H = Hidden attribute set *
1558 * R = Read-only attribute set *
1559 * 8. Initial file size when creating or replacing a file; ignored if *
1560 * access mode is read-only. (DEFAULT: 0) *
1561 * 9. I/O mode flags, any or all of: (DEFAULT: "") *
1562 * T = Write-through mode (default is normal write) *
1563 * N = No-cache mode (default is to use filesystem cache) *
1564 * S = Sequential access *
1565 * R = Random access *
1566 * * S and R can combine as follows: *
1567 * Neither: No locality known (default) *
1568 * S only: Mainly sequential access *
1569 * R only: Mainly random access *
1570 * Both: Random/sequential (i.e. random with some locality) *
1571 * *
1572 * REXX RETURN VALUE: *
1573 * File handle, or "" in case of error. *
1574 * ------------------------------------------------------------------------- */
1575ULONG APIENTRY Sys2Open( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1576{
1577 PSZ pszFile;
1578 HFILE hf;
1579 ULONG fsAction = 0,
1580 fsMode = 0,
1581 ulResult = 0,
1582 ulAttr = FILE_NORMAL;
1583 LONGLONG llSize = {0};
1584 CHAR achHandle[ 9 ];
1585 APIRET rc;
1586
1587
1588 // Reset the error indicator
1589 WriteErrorCode( 0, NULL );
1590
1591 // Make sure we have at least one valid argument (the file name)
1592 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) ))
1593 return ( 40 );
1594
1595 // (Validate the first argument last to simplify error processing)
1596
1597 // Second argument: open action
1598 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1599 strupr( argv[1].strptr );
1600 if ( strcspn(argv[1].strptr, "OCR") > 0 ) return ( 40 );
1601 if ( strchr(argv[1].strptr, 'O'))
1602 fsAction |= OPEN_ACTION_OPEN_IF_EXISTS;
1603 else if ( strchr(argv[1].strptr, 'R'))
1604 fsAction |= OPEN_ACTION_REPLACE_IF_EXISTS;
1605 if ( strchr(argv[1].strptr, 'C'))
1606 fsAction |= OPEN_ACTION_CREATE_IF_NEW;
1607 }
1608 else
1609 fsAction = OPEN_ACTION_OPEN_IF_EXISTS;
1610
1611 // Third argument: access mode
1612 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
1613 strupr( argv[2].strptr );
1614 if ( strcspn(argv[2].strptr, "RW") > 0 ) return ( 40 );
1615 if ( strchr(argv[2].strptr, 'R')) {
1616 if (strchr(argv[2].strptr, 'W'))
1617 fsMode = OPEN_ACCESS_READWRITE;
1618 else
1619 fsMode = OPEN_ACCESS_READONLY;
1620 }
1621 else if (strchr(argv[2].strptr, 'W'))
1622 fsMode = OPEN_ACCESS_WRITEONLY;
1623 else
1624 return ( 40 );
1625 }
1626 else
1627 fsMode = OPEN_ACCESS_READWRITE;
1628
1629 // Fourth argument: sharing mode
1630 if ( argc >= 4 && RXVALIDSTRING(argv[3]) ) {
1631 strupr( argv[3].strptr );
1632 if ( strcspn(argv[3].strptr, "RW") > 0 ) return ( 40 );
1633 if ( strchr(argv[3].strptr, 'R')) {
1634 if (strchr(argv[3].strptr, 'W'))
1635 fsMode |= OPEN_SHARE_DENYREADWRITE;
1636 else
1637 fsMode |= OPEN_SHARE_DENYREAD;
1638 }
1639 else if (strchr(argv[3].strptr, 'W'))
1640 fsMode |= OPEN_SHARE_DENYWRITE;
1641 else
1642 fsMode |= OPEN_SHARE_DENYNONE;
1643 }
1644 else
1645 fsMode |= OPEN_SHARE_DENYWRITE;
1646
1647 // Fifth argument: deny legacy mode
1648 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
1649 strupr( argv[4].strptr );
1650 if ( argv[4].strptr[0] == '1' )
1651 fsMode |= OPEN_SHARE_DENYLEGACY;
1652 else if ( argv[4].strptr[0] != '0' )
1653 return ( 40 );
1654 }
1655
1656 // Sixth argument: inheritance mode
1657 if ( argc >= 6 && RXVALIDSTRING(argv[5]) ) {
1658 strupr( argv[5].strptr );
1659 if ( argv[5].strptr[0] == '1' )
1660 fsMode |= OPEN_FLAGS_NOINHERIT;
1661 else if ( argv[5].strptr[0] != '0' )
1662 return ( 40 );
1663 }
1664
1665 // Seventh argument: attributes
1666 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1667 strupr( argv[6].strptr );
1668 if (strcspn(argv[6].strptr, "ADSHR") > 0 ) return ( 40 );
1669 if ( strchr(argv[6].strptr, 'A')) ulAttr |= FILE_ARCHIVED;
1670 if ( strchr(argv[6].strptr, 'D')) ulAttr |= FILE_DIRECTORY;
1671 if ( strchr(argv[6].strptr, 'S')) ulAttr |= FILE_SYSTEM;
1672 if ( strchr(argv[6].strptr, 'H')) ulAttr |= FILE_HIDDEN;
1673 if ( strchr(argv[6].strptr, 'R')) ulAttr |= FILE_READONLY;
1674 }
1675
1676 // Eighth argument: initial size
1677 if ( argc >= 8 && RXVALIDSTRING(argv[7]) ) {
1678 if (( sscanf( argv[7].strptr, "%lld", &llSize )) != 1 ) return ( 40 );
1679 }
1680
1681 // Ninth argument: I/O mode flags
1682 if ( argc >= 9 && RXVALIDSTRING(argv[8]) ) {
1683 strupr( argv[8].strptr );
1684 if (strcspn(argv[8].strptr, "TNSR") > 0 ) return ( 40 );
1685 if ( strchr(argv[8].strptr, 'T')) fsMode |= OPEN_FLAGS_WRITE_THROUGH;
1686 if ( strchr(argv[8].strptr, 'N')) fsMode |= OPEN_FLAGS_NO_CACHE;
1687 if ( strchr(argv[8].strptr, 'S')) fsMode |= OPEN_FLAGS_SEQUENTIAL;
1688 if ( strchr(argv[8].strptr, 'R')) fsMode |= OPEN_FLAGS_RANDOM;
1689 }
1690
1691 // Now the first argument: file name
1692 pszFile = (PSZ) calloc( RXSTRLEN(argv[0]) + 1, sizeof(UCHAR) );
1693 if ( pszFile == NULL ) {
1694 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "calloc");
1695 MAKERXSTRING( *prsResult, "0", 1 );
1696 return ( 0 );
1697 }
1698 strncpy( pszFile, argv[0].strptr, RXSTRLEN(argv[0]) );
1699
1700 // Try and open the file
1701 rc = DosOpenL( pszFile, &hf, &ulResult, llSize, ulAttr, fsAction, fsMode, NULL );
1702 if (rc) {
1703 WriteErrorCode( rc, "DosOpenL");
1704 MAKERXSTRING( *prsResult, "", 0 );
1705 free( pszFile );
1706 return ( 0 );
1707 }
1708
1709 // Return the handle as the REXX result string
1710 sprintf( achHandle, "%8X", hf );
1711 MAKERXSTRING( *prsResult, achHandle, strlen( achHandle ));
1712
1713 free( pszFile );
1714 return ( 0 );
1715}
1716
1717
1718/* ------------------------------------------------------------------------- *
1719 * Sys2Close *
1720 * *
1721 * Wrapper to DosClose: close a file/stream. *
1722 * *
1723 * REXX ARGUMENTS: *
1724 * 1. File handle (returned by Sys2Open) (REQUIRED) *
1725 * *
1726 * REXX RETURN VALUE: *
1727 * 1 on success, or 0 if an error occurred. *
1728 * ------------------------------------------------------------------------- */
1729ULONG APIENTRY Sys2Close( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1730{
1731 HFILE hf;
1732 APIRET rc;
1733
1734 // Reset the error indicator
1735 WriteErrorCode( 0, NULL );
1736
1737 // Make sure we have exactly one valid argument (the file handle)
1738 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) ))
1739 return ( 40 );
1740 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
1741
1742 // Close the file
1743 rc = DosClose( hf );
1744 if ( rc != NO_ERROR ) {
1745 WriteErrorCode( rc, "DosClose");
1746 MAKERXSTRING( *prsResult, "0", 1 );
1747 }
1748 else {
1749 MAKERXSTRING( *prsResult, "1", 1 );
1750 }
1751
1752 return ( 0 );
1753}
1754
1755
1756/* ------------------------------------------------------------------------- *
1757 * Sys2Seek *
1758 * *
1759 * Wrapper to DosSetFilePtrL: move the read/write pointer to the specified *
1760 * location in a stream. *
1761 * *
1762 * REXX ARGUMENTS: *
1763 * 1. File handle (returned by Sys2Open) (REQUIRED) *
1764 * 2. The signed distance in bytes to move (REQUIRED) *
1765 * 3. Move method, one of: *
1766 * B = Beginning of file *
1767 * C = Current position (DEFAULT) *
1768 * E = End of file *
1769 * *
1770 * REXX RETURN VALUE: *
1771 * The new file position, in bytes. *
1772 * ------------------------------------------------------------------------- */
1773ULONG APIENTRY Sys2Seek( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1774{
1775 HFILE hf;
1776 LONGLONG llPos,
1777 llActual;
1778 ULONG ulMethod = FILE_CURRENT;
1779 CHAR achActual[ US_LONGLONG_MAXZ ];
1780 APIRET rc;
1781
1782 // Reset the error indicator
1783 WriteErrorCode( 0, NULL );
1784
1785 // Make sure we have at least two valid arguments
1786 if ( argc < 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
1787 return ( 40 );
1788
1789 // First argument: file handle
1790 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
1791
1792 // Second argument: requested offset
1793 if (( sscanf( argv[1].strptr, "%lld", &llPos )) != 1 ) return ( 40 );
1794
1795 // Third argument: starting position
1796 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
1797 strupr( argv[2].strptr );
1798 if ( strcspn(argv[2].strptr, "BCE") > 0 ) return ( 40 );
1799 switch ( argv[2].strptr[0] ) {
1800 case 'B': ulMethod = FILE_BEGIN; break;
1801 case 'E': ulMethod = FILE_END; break;
1802 default : ulMethod = FILE_CURRENT; break;
1803 }
1804 }
1805
1806 rc = DosSetFilePtrL( hf, llPos, ulMethod, &llActual );
1807 if ( rc != NO_ERROR ) {
1808 WriteErrorCode( rc, "DosSetFilePtrL");
1809 MAKERXSTRING( *prsResult, "", 0 );
1810 return ( 0 );
1811 }
1812
1813 // Return the new position as the REXX result string
1814 sprintf( achActual, "%lld", llActual );
1815 MAKERXSTRING( *prsResult, achActual, strlen( achActual ));
1816
1817 return ( 0 );
1818}
1819
1820
1821/* ------------------------------------------------------------------------- *
1822 * Sys2Read *
1823 * *
1824 * Wrapper to DosRead: read bytes from a previously-opened stream. *
1825 * *
1826 * REXX ARGUMENTS: *
1827 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
1828 * 2. Number of bytes to read (REQUIRED) *
1829 * *
1830 * REXX RETURN VALUE: *
1831 * String containing the bytes read, or "" in case of error. *
1832 * ------------------------------------------------------------------------- */
1833ULONG APIENTRY Sys2Read( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1834{
1835 HFILE hf;
1836 ULONG cb,
1837 cbActual;
1838 PSZ pszData;
1839 APIRET rc;
1840
1841 // Reset the error indicator
1842 WriteErrorCode( 0, NULL );
1843
1844 // Make sure we have two valid arguments
1845 if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
1846 return ( 40 );
1847
1848 // First argument: handle
1849 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
1850
1851 // Second argument: number of bytes to read
1852 if (( sscanf( argv[1].strptr, "%u", &cb )) != 1 ) return ( 40 );
1853 if ( cb < 1 ) return ( 40 );
1854 pszData = (PSZ) malloc( cb );
1855
1856 rc = DosRead( hf, pszData, cb, &cbActual );
1857 if ( rc || !cbActual ) {
1858 WriteErrorCode( rc, "DosRead");
1859 MAKERXSTRING( *prsResult, "", 0 );
1860 goto cleanup;
1861 }
1862 if ( ! SaveResultString( prsResult, pszData, cbActual )) {
1863 MAKERXSTRING( *prsResult, "", 0 );
1864 }
1865
1866cleanup:
1867 free( pszData );
1868 return ( 0 );
1869}
1870
1871
1872/* ------------------------------------------------------------------------- *
1873 * Sys2Write *
1874 * *
1875 * Wrapper to DosWrite: write bytes to a previously-opened stream. *
1876 * *
1877 * REXX ARGUMENTS: *
1878 * 1. File handle (returned by Sys2Open or Sys2CreateNamedPipe) (REQUIRED) *
1879 * 2. Data to be written (REQUIRED) *
1880 * *
1881 * REXX RETURN VALUE: *
1882 * Number of bytes written. *
1883 * ------------------------------------------------------------------------- */
1884ULONG APIENTRY Sys2Write( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1885{
1886 HFILE hf;
1887 ULONG cbActual;
1888 CHAR szActual[ US_INTEGER_MAXZ ];
1889 APIRET rc;
1890
1891 // Reset the error indicator
1892 WriteErrorCode( 0, NULL );
1893
1894 // Make sure we have two valid arguments
1895 if ( argc != 2 || ( !RXVALIDSTRING(argv[0]) ) || ( !RXVALIDSTRING(argv[1]) ))
1896 return ( 40 );
1897
1898 // First argument: handle
1899 if (( sscanf( argv[0].strptr, "%8X", &hf )) != 1 ) return ( 40 );
1900
1901 // (Second argument can be left in standard RXSTRING form)
1902
1903 rc = DosWrite( hf, argv[1].strptr, argv[1].strlength, &cbActual );
1904 if ( rc != NO_ERROR ) {
1905 WriteErrorCode( rc, "DosWrite");
1906 MAKERXSTRING( *prsResult, "0", 1 );
1907 return ( 0 );
1908 }
1909
1910 sprintf( szActual, "%d", cbActual );
1911 MAKERXSTRING( *prsResult, szActual, strlen( szActual ));
1912 return ( 0 );
1913}
1914
1915
1916// -------------------------------------------------------------------------
1917// INTERNAL FUNCTIONS
1918// -------------------------------------------------------------------------
1919
1920
1921/* ------------------------------------------------------------------------- *
1922 * GetProcess *
1923 * *
1924 * Gets information about the specified process (if found). If pszProgram *
1925 * is NULL, the search is done on the process ID in pulPID; otherwise, the *
1926 * search is done on the executable name in pszProgram (which may or may not *
1927 * include the extension). *
1928 * *
1929 * ARGUMENTS: *
1930 * PSZ pszProgram : The requested executable (process name). (I) *
1931 * PSZ pszFullName: The returned fully-qualified process name. (O) *
1932 * PULONG pulPID : The process ID. (IO) *
1933 * PULONG pulPPID : The returned process parent ID. (O) *
1934 * PULONG pulType : The returned process type. (O) *
1935 * PUSHORT pusPriority: The returned process priority. (O) *
1936 * PULONG pulCPU : The returned process CPU time. (O) *
1937 * *
1938 * RETURNS: ULONG *
1939 * 0 on success, or a non-zero API return code in the case of an error. *
1940 * ------------------------------------------------------------------------- */
1941ULONG GetProcess( PSZ pszProgram,
1942 PSZ pszFullName,
1943 PULONG pulPID,
1944 PULONG pulPPID,
1945 PULONG pulType,
1946 PUSHORT pusPriority,
1947 PULONG pulCPU )
1948{
1949#ifdef USE_DQPS
1950 QSPTRREC *pBuf; // Data returned by DosQProcStatus()
1951#else
1952 QSGREC **pBuf; // Data returned by DosQuerySysState()
1953#endif
1954 QSPREC *pPrec; // Pointer to process information block
1955 QSTREC *pTrec; // Pointer to thread information block
1956 CHAR szName[ CCHMAXPATH ] = {0}, // Fully-qualified name of process
1957 szNoExt[ CCHMAXPATH ] = {0}; // Program name without extension
1958 PPIB ppib; // pointer to current process info block
1959 PSZ pszCurrent, // Program name of a queried process
1960 c; // Pointer to substring
1961 ULONG ulCPU; // Process CPU time
1962 USHORT usPriority, // Process priority class
1963 i; // index
1964 BOOL fMatch = FALSE; // The current process is a match?
1965 APIRET rc; // Return code
1966
1967
1968 // Use current process when PID is 0 and program name is not specified
1969 if (( pszProgram == NULL ) && ( *pulPID == 0 )) {
1970 rc = DosGetInfoBlocks( NULL, &ppib );
1971 if ( rc != NO_ERROR ) {
1972 WriteErrorCode( rc, "DosGetInfoBlocks");
1973 return ( rc );
1974 }
1975 *pulPID = ppib->pib_ulpid;
1976 }
1977
1978#ifdef USE_DQPS
1979 pBuf = (QSPTRREC *) malloc( UL_SSBUFSIZE );
1980#else
1981 pBuf = (QSGREC **) malloc( UL_SSBUFSIZE );
1982#endif
1983
1984 if ( pBuf == NULL ) {
1985 WriteErrorCode( ERROR_NOT_ENOUGH_MEMORY, "malloc");
1986 return ( ERROR_NOT_ENOUGH_MEMORY );
1987 }
1988
1989#ifdef USE_DQPS
1990 // Get running process information using DosQProcStatus()
1991 rc = DosQProcStatus( pBuf, UL_SSBUFSIZE );
1992 if ( rc != NO_ERROR ) {
1993 WriteErrorCode( rc, "DosQProcStatus");
1994 return ( rc );
1995 }
1996 pPrec = pBuf->pProcRec;
1997#else
1998 // Get running process information using DosQuerySysState()
1999 rc = DosQuerySysState( QS_PROCESS, 0L, 0L, 0L, pBuf, UL_SSBUFSIZE );
2000 if ( rc != NO_ERROR ) {
2001 WriteErrorCode( rc, "DosQuerySysState");
2002 return ( rc );
2003 }
2004 pPrec = (QSPREC *) ( (PBYTE) (*pBuf) + sizeof(QSGREC) );
2005#endif
2006
2007 *pulPPID = 0;
2008 *pulType = 0;
2009 *pusPriority = 0;
2010 *pulCPU = 0;
2011 if ( pszProgram != NULL ) *pulPID = 0;
2012 else if ( *pulPID == 0 ) return 0;
2013
2014 // Now look for the specified process
2015 while (( pPrec->RecType == 1 ) && ( !fMatch )) {
2016
2017 if ( pszProgram == NULL ) {
2018 if ( pPrec->pid == *pulPID ) {
2019 fMatch = TRUE;
2020 // Get the program name
2021 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
2022 sprintf( pszFullName, "--");
2023 else
2024 strcpy( pszFullName, szName );
2025
2026 // Get the process priority
2027 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
2028 usPriority = 0;
2029
2030 // Get the CPU time of the process by querying each of its threads
2031 ulCPU = 0;
2032 pTrec = pPrec->pThrdRec;
2033 for ( i = 0; i < pPrec->cTCB; i++ ) {
2034 ulCPU += ( pTrec->systime + pTrec->usertime );
2035 pTrec++;
2036 }
2037
2038 *pulPPID = pPrec->ppid;
2039 *pulType = pPrec->type;
2040 *pusPriority = usPriority;
2041 *pulCPU = ulCPU;
2042 }
2043 }
2044 else {
2045 // Get the program name (without the path)
2046 if (( rc = DosQueryModuleName( pPrec->hMte, CCHMAXPATH-1, szName )) != NO_ERROR )
2047 sprintf( pszCurrent, "--");
2048 else
2049 pszCurrent = strrchr( szName, '\\') + 1;
2050
2051 // Create a copy without the extension
2052 strcpy( szNoExt, pszCurrent );
2053 if (( c = strrchr( szNoExt, '.')) != NULL ) memset( c, 0, strlen(c) );
2054 if (( pszCurrent != NULL ) &&
2055 (( stricmp(pszCurrent, pszProgram) == 0 ) || ( stricmp(szNoExt, pszProgram) == 0 )))
2056 {
2057 fMatch = TRUE;
2058
2059 // Get the process priority
2060 if (( rc = DosGetPrty( PRTYS_PROCESS, &usPriority, pPrec->pid )) != NO_ERROR )
2061 usPriority = 0;
2062
2063 // Get the CPU time of the process by querying each of its threads
2064 ulCPU = 0;
2065 pTrec = pPrec->pThrdRec;
2066 for ( i = 0; i < pPrec->cTCB; i++ ) {
2067 ulCPU += ( pTrec->systime + pTrec->usertime );
2068 pTrec++;
2069 }
2070
2071 *pulPID = pPrec->pid;
2072 *pulPPID = pPrec->ppid;
2073 *pulType = pPrec->type;
2074 *pusPriority = usPriority;
2075 *pulCPU = ulCPU;
2076 strcpy( pszFullName, szName );
2077 }
2078 }
2079 pPrec = (QSPREC *) ( (PBYTE) (pPrec->pThrdRec) + ( pPrec->cTCB * sizeof(QSTREC) ) );
2080 }
2081 if ( !fMatch ) *pulPID = 0;
2082
2083 free( pBuf );
2084 return ( 0 );
2085}
2086
2087
2088/* ------------------------------------------------------------------------- *
2089 * SaveResultString *
2090 * *
2091 * Writes new string contents to the specified RXSTRING, allocating any *
2092 * additional memory that may be required. If the string to be written has *
2093 * zero length, nothing is done. *
2094 * *
2095 * This function should be used in place of MAKERXSTRING if there is a *
2096 * possibility that the string contents could be longer than 256 characters. *
2097 * *
2098 * ARGUMENTS: *
2099 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
2100 * PCH pchBytes : The string contents to write to prsResult. *
2101 * ULONG ulBytes : The number of bytes in pchBytes to write. *
2102 * *
2103 * RETURNS: BOOL *
2104 * TRUE if prsResult was successfully updated. FALSE otherwise. *
2105 * ------------------------------------------------------------------------- */
2106BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
2107{
2108 ULONG ulRC;
2109 PCH pchNew;
2110
2111 if ( ulBytes == 0 ) return ( FALSE );
2112 if ( ulBytes > 256 ) {
2113 // REXX provides 256 bytes by default; allocate more if necessary
2114 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
2115 if ( ulRC != 0 ) {
2116 WriteErrorCode( ulRC, "DosAllocMem");
2117 return ( FALSE );
2118 }
2119 DosFreeMem( prsResult->strptr );
2120 prsResult->strptr = pchNew;
2121 }
2122 memcpy( prsResult->strptr, pchBytes, ulBytes );
2123 prsResult->strlength = ulBytes;
2124
2125 return ( TRUE );
2126}
2127
2128
2129/* ------------------------------------------------------------------------- *
2130 * WriteStemElement *
2131 * *
2132 * Creates a stem element (compound variable) in the calling REXX program *
2133 * using the REXX shared variable pool interface. *
2134 * *
2135 * ARGUMENTS: *
2136 * PSZ pszStem : The name of the stem (before the '.') *
2137 * ULONG ulIndex : The number of the stem element (after the '.') *
2138 * PSZ pszValue : The value to write to the compound variable. *
2139 * *
2140 * RETURNS: BOOL *
2141 * TRUE on success, FALSE on failure. *
2142 * ------------------------------------------------------------------------- */
2143BOOL WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
2144{
2145 SHVBLOCK shvVar; // REXX shared variable pool block
2146 ULONG ulRc,
2147 ulBytes;
2148 CHAR szCompoundName[ US_COMPOUND_MAXZ ],
2149 *pchValue;
2150
2151 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
2152 if ( pszValue == NULL ) {
2153 pchValue = "";
2154 ulBytes = 0;
2155 } else {
2156 ulBytes = strlen( pszValue );
2157 ulRc = DosAllocMem( (PVOID) &pchValue, ulBytes + 1, PAG_WRITE | PAG_COMMIT );
2158 if ( ulRc != 0 ) {
2159 WriteErrorCode( ulRc, "DosAllocMem");
2160 return FALSE;
2161 }
2162 memcpy( pchValue, pszValue, ulBytes );
2163 }
2164 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
2165 shvVar.shvvalue.strptr = pchValue;
2166 shvVar.shvvalue.strlength = ulBytes;
2167 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2168 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2169 shvVar.shvcode = RXSHV_SYSET;
2170 shvVar.shvnext = NULL;
2171 ulRc = RexxVariablePool( &shvVar );
2172 if ( ulRc > 1 ) {
2173 WriteErrorCode( shvVar.shvret, "RexxVariablePool (SHVBLOCK.shvret)");
2174 return FALSE;
2175 }
2176 return TRUE;
2177
2178}
2179
2180
2181/* ------------------------------------------------------------------------- *
2182 * WriteErrorCode *
2183 * *
2184 * Writes an error code to a special variable in the calling REXX program *
2185 * using the REXX shared variable pool interface. This is used to return *
2186 * API error codes to the REXX program, since the REXX functions themselves *
2187 * normally return string values. *
2188 * *
2189 * ARGUMENTS: *
2190 * ULONG ulError : The error code returned by the failing API call. *
2191 * PSZ pszContext: A string describing the API call that failed. *
2192 * *
2193 * RETURNS: N/A *
2194 * ------------------------------------------------------------------------- */
2195void WriteErrorCode( ULONG ulError, PSZ pszContext )
2196{
2197 SHVBLOCK shvVar; // REXX shared variable pool block
2198 ULONG ulRc;
2199 CHAR szErrorText[ US_ERRSTR_MAXZ ];
2200
2201 if ( pszContext == NULL )
2202 sprintf( szErrorText, "%u", ulError );
2203 else
2204 sprintf( szErrorText, "%u: %s", ulError, pszContext );
2205 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
2206 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
2207 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2208 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2209 shvVar.shvcode = RXSHV_SYSET;
2210 shvVar.shvnext = NULL;
2211 ulRc = RexxVariablePool( &shvVar );
2212 if ( ulRc > 1 )
2213 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
2214}
2215
2216
Note: See TracBrowser for help on using the repository browser.