windows/rexxutil.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2017 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Windows Support rexxutil.c */
40 /* */
41 /* Windows system utility functions */
42 /* */
43 /******************************************************************************/
44 /**********************************************************************
45 * *
46 * This program extends the REXX language by providing many *
47 * REXX external functions. *
48 * These functions are: *
49 * SysCls -- Clear the screen in an OS/2 fullscreen *
50 * or windowed command prompt session. *
51 * SysCurPos -- Set and/or Query the cursor position *
52 * in an OS/2 fullscreen or windowed *
53 * command prompt session. *
54 * SysCurState -- Make the cursor visible or invisible *
55 * in an OS/2 fullscreen or windowed *
56 * command prompt session. *
57 * SysDriveInfo -- Returns information about a specific *
58 * drive. *
59 * SysDriveMap -- Returns a list of the drives on the *
60 * machine *
61 * SysDropFuncs -- Makes all functions in this package *
62 * unknown to REXX. *
63 * SysFileDelete -- Deletes a file *
64 * SysFileSearch -- Searches for a file matching a given *
65 * filespec. *
66 * SysFileTree -- Searches for files matching a given *
67 * filespec, including files in *
68 * subdirectories. *
69 * SysGetKey -- Returns one by of data indicating the *
70 * key which was pressed, *
71 * in an OS/2 fullscreen or windowed *
72 * command prompt session. *
73 * SysIni -- Reads and/or updates entries in .INI *
74 * files. *
75 * SysLoadFuncs -- Makes all functions in this package *
76 * known to REXX so REXX programs may *
77 * call them. *
78 * SysMkDir -- Creates a directory *
79 * SysWinVer -- Returns the Win OS and Version number *
80 * SysVersion -- Returns the OS and Version number *
81 * SysRmDir -- Removes a directory *
82 * SysSearchPath -- Searches throught a specified path *
83 * for a file. *
84 * SysSleep -- Suspends execution for a number of *
85 * seconds and milliseconds. *
86 * SysTempFilename -- Creates a unique filename *
87 * SysTextScreenRead -- Reads characters from the screen, *
88 * in an OS/2 fullscreen or windowed *
89 * command prompt session. *
90 * SysTextScreenSize -- Returns the size of the window in *
91 * rows and columns, *
92 * in an OS/2 fullscreen or windowed *
93 * command prompt session. *
94 * SysWaitNamedPipe -- Wait on a named pipe. *
95 * SysRegisterObjectClass -- Register a new object class *
96 * SysDeregisterObjectClass -- Remove class registration *
97 * SysQueryClassList -- Get list of registered classes *
98 * SysCreateObject -- Create an object instance *
99 * SysDestroyObject -- Delete an object instance *
100 * SysSetObjectData -- Change object settings data *
101 * SysBootDrive -- Return the windows boot drive *
102 * SysSystemDirectory -- Return the Windows system directory *
103 * SysQueryEAList -- Return list of file EA names *
104 * SysWildCard -- Perform file wild card editting *
105 * SysFileSystemType -- Return drive file system type *
106 * SysVolumeLabel -- Return the drive label *
107 * SysAddFileHandle -- Add file handles to a process *
108 * SysSetFileHandle -- Set file handles for a process *
109 * SysCreateMutexSem -- Create a Mutex semaphore *
110 * SysOpenMutexSem -- Open a Mutex semaphore *
111 * SysCloseMutexSem -- Close a Mutex semaphore *
112 * SysRequestMutexSem -- Request a Mutex semaphore *
113 * SysReleaseMutexSem -- Release a Mutex semaphore *
114 * SysCreateEventSem -- Create an Event semaphore *
115 * SysOpenEventSem -- Open an Event semaphore *
116 * SysCloseEventSem -- Close an Event semaphore *
117 * SysPostEventSem -- Post an Event semaphore *
118 * SysPulseEventSem -- Post and reset an Event semaphore *
119 * SysResetEventSem -- Reset an Event semaphore *
120 * SysWaitEventSem -- Wait on an Event semaphore *
121 * SysProcessType -- Return type of process *
122 * SysSetPriority -- Set current thread priority *
123 * SysGetCollate -- Get country/codepage collating sequence*
124 * SysNationalLanguageCompare -- NLS strict compare *
125 * SysMapCase -- NLS uppercasing *
126 * SysSetProcessCodePage -- Set current code page *
127 * SysQueryProcessCodePage -- Get current code page *
128 * SysAddRexxMacro -- Load program into macro space *
129 * SysDropRexxMacro -- Drop program from macro space *
130 * SysReorderRexxMacro -- Reorder program in macro space *
131 * SysQueryRexxMacro -- Query ordering of macro space program *
132 * SysClearRexxMacroSpace -- Remove all programs from macro space*
133 * SysLoadRexxMacroSpace -- Load a Rexx macro space *
134 * SysSaveRexxMacroSpace -- Save a Rexx macro space *
135 * SysShutDownSystem -- Shutdown the system *
136 * SysSwitchSession -- Switch to a named session *
137 * SysDropLibrary -- Drop a function package *
138 * SysQueryProcess -- Get information on current proc/thread *
139 * SysDumpVariables -- Dump current variables to a file *
140 * SysSetFileDateTime -- Set the last modified date of a file *
141 * SysGetFileDateTime -- Get the last modified date of a file *
142 * SysStemSort -- sort a stem array *
143 * SysStemDelete -- delete items in a stem array *
144 * SysStemInsert -- insert items into a stem array *
145 * SysStemCopy -- copy items from one stem array to other*
146 * SysUtilVersion -- query version of REXXUTIL.DLL *
147 * SysWinFileEncrypt -- Encrypt file on a W2K-NTFS *
148 * SysWinFileDecrypt -- Decrypt file on a W2K-NTFS *
149 * SysGetErrortext -- Retrieve textual desc. of error number *
150 * SysWinGetDefaultPrinter -- retrieve default printer *
151 * SysWinGetPrinters -- Obtain list of local printers *
152 * SysWinSetDefaultPrinter -- set the local default printer *
153 * SysFileCopy -- Copy files on the file system *
154 * SysFileMove -- Move / Rename files or directories *
155 * SysIsFile -- Check for the existance of a file *
156 * SysIsFileDirectory -- Check for the existance of a directory *
157 * SysIsFileLink -- Check for the existance of a link *
158 * SysIsFileCompressed -- Check for a file to be compressed *
159 * SysIsFileEncrypted -- Check for a file to be encrypted *
160 * SysIsFileNotContentIndexed -- Check if a file should be indexed *
161 * SysIsFileOffline -- Check if a file is offline *
162 * SysIsFileSparse -- Check if a file is sparse *
163 * SysIsFileTemporary -- Check if a file is temporary *
164 * *
165 **********************************************************************/
166 
167 /* Include files */
168 
169 #include "oorexxapi.h"
170 #include <memory.h>
171 #include <fcntl.h>
172 #include <ctype.h>
173 #include <string.h>
174 #include <stdlib.h>
175 #include <stdio.h>
176 #include <conio.h>
177 #include <limits.h>
178 #include <shlwapi.h>
179 #include <math.h> // isnan(), HUGE_VAL
180 #include <algorithm>
181 
182 #define OM_WAKEUP (WM_USER+10)
183 VOID CALLBACK SleepTimerProc( HWND, UINT, UINT, DWORD);
184 
185 /*********************************************************************/
186 /* Various definitions used by various functions. */
187 /*********************************************************************/
188 
189 #define MAX_LABEL 13 /* max label length (sdrvinfo)*/
190 #define MAX_DIGITS 9 /* max digits in numeric arg */
191 #define MAX 264 /* temporary buffer length */
192 #define IBUF_LEN 4096 /* Input buffer length */
193 #define MAX_READ 0x10000 /* full segment of buffer */
194 #define CH_EOF 0x1A /* end of file marker */
195 #define CH_CR '\r' /* carriage return character */
196 #define CH_NL '\n' /* new line character */
197 #define AllocFlag PAG_COMMIT | PAG_WRITE /* for DosAllocMem */
198 #define RNDFACTOR 1664525L
199 #define MAX_ENVVAR 1024
200 #define MAX_LINE_LEN 4096 /* max line length */
201 #define MAX_CREATEPROCESS_CMDLINE (32 * 1024)
202 
203 /*********************************************************************/
204 /* Defines used by SysDriveMap */
205 /*********************************************************************/
206 
207 #define USED 0
208 #define FREE 1
209 #define CDROM 2
210 #define REMOTE 3
211 #define LOCAL 4
212 #define RAMDISK 5
213 #define REMOVABLE 6
214 
215 /*********************************************************************/
216 /* Defines uses by SysTree */
217 /*********************************************************************/
218 
219 #define RECURSE 0x0002
220 #define DO_DIRS 0x0004
221 #define DO_FILES 0x0008
222 #define NAME_ONLY 0x0010
223 #define EDITABLE_TIME 0x0020
224 #define LONG_TIME 0x0040 /* long time format for SysFileTree */
225 #define CASELESS 0x0080
226 #define RXIGNORE 2 /* Ignore attributes entirely */
227 #define AllAtts FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | \
228 FILE_SYSTEM | FILE_DIRECTORY | FILE_ARCHIVED
229 #define AllFiles FILE_NORMAL | FILE_READONLY | FILE_HIDDEN | \
230 FILE_SYSTEM | FILE_ARCHIVED
231 #define AllDirs FILE_READONLY | FILE_HIDDEN | \
232 FILE_SYSTEM | FILE_ARCHIVED | MUST_HAVE_DIRECTORY | FILE_DIRECTORY
233 
234 /*********************************************************************/
235 /* Defines used by SysStemSort -- must match values in okstem.hpp */
236 /*********************************************************************/
237 #define SORT_CASESENSITIVE 0
238 #define SORT_CASEIGNORE 1
239 
240 #define SORT_ASCENDING 0
241 #define SORT_DECENDING 1
242 
243 /*********************************************************************/
244 /* Define used for Unicode translation. Not present in early Windows */
245 /* SDKs. */
246 /*********************************************************************/
247 #ifndef WC_ERR_INVALID_CHARS
248 #define WC_ERR_INVALID_CHARS 0x00000080
249 #endif
250 
251 // Defines for various SysFileTree buffer.
252 #define FNAMESPEC_BUF_EXTRA 8
253 #define FNAMESPEC_BUF_LEN MAX_PATH + FNAMESPEC_BUF_EXTRA
254 #define FOUNDFILE_BUF_LEN MAX_PATH
255 #define FILETIME_BUF_LEN 64
256 #define FILEATTR_BUF_LEN 16
257 #define FOUNDFILELINE_BUF_LEN FOUNDFILE_BUF_LEN + FILETIME_BUF_LEN + FILEATTR_BUF_LEN
258 
259 
260 /*********************************************************************/
261 /* Structures used throughout REXXUTIL.C */
262 /*********************************************************************/
263 
264 /*********************************************************************/
265 /* RxTree Structure used by GetLine, OpenFile and CloseFile */
266 /*********************************************************************/
267 typedef struct _GetFileData {
268  char * buffer; /* file read buffer */
269  size_t size; /* file size */
270  size_t data; /* data left in buffer */
271  size_t residual; /* size left to read */
272  char * scan; /* current scan position */
273  HANDLE handle; /* file handle */
275 
276 
277 /*
278  * Data structure for SysFileTree.
279  *
280  * Note that in Windows the MAX_PATH define includes the terminating null.
281  */
282 typedef struct RxTreeData {
283  size_t count; // Number of found file lines
284  RexxStemObject files; // Stem that holds results.
285  char fNameSpec[FNAMESPEC_BUF_LEN]; // File name portion of the search for file spec, may contain glob characters.
286  char foundFile[FOUNDFILE_BUF_LEN]; // Full path name of found file
287  char fileTime[FILETIME_BUF_LEN]; // Time and size of found file
288  char fileAttr[FILEATTR_BUF_LEN]; // File attribute string of found file
289  char foundFileLine[FOUNDFILELINE_BUF_LEN]; // Buffer for found file line, includes foundFile, fileTime, and fileAttr
290  char *dFNameSpec; // Starts out pointing at fNameSpec
291  size_t nFNameSpec; // CouNt of bytes in dFNameSpec buffer
293 
294 /*********************************************************************/
295 /* RxStemData */
296 /* Structure which describes as generic */
297 /* stem variable. */
298 /*********************************************************************/
299 
300 typedef struct RxStemData {
301  SHVBLOCK shvb; /* Request block for RxVar */
302  char ibuf[IBUF_LEN]; /* Input buffer */
303  char varname[MAX]; /* Buffer for the variable */
304  /* name */
305  char stemname[MAX]; /* Buffer for the variable */
306  /* name */
307  size_t stemlen; /* Length of stem. */
308  size_t vlen; /* Length of variable value */
309  size_t j; /* Temp counter */
310  size_t tlong; /* Temp counter */
311  size_t count; /* Number of elements */
312  /* processed */
314 
315 
316 /*********************************************************************/
317 /* Saved character status */
318 /*********************************************************************/
319 static int ExtendedFlag = 0; /* extended character saved */
320 static char ExtendedChar; /* saved extended character */
321 
322 /*********************************************************************/
323 /* function pointer for GetDiskFreespaceEx for SysDriveInfo */
324 /*********************************************************************/
325 typedef BOOL (WINAPI *P_GDFSE)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER,
326  PULARGE_INTEGER);
328 
329 /*********************************************************************/
330 /* Numeric Error Return Strings */
331 /*********************************************************************/
332 
333 #define NO_UTIL_ERROR "0" /* No error whatsoever */
334 #define ERROR_NOMEM "2" /* Insufficient memory */
335 #define ERROR_FILEOPEN "3" /* Error opening text file */
336 
337 /*********************************************************************/
338 /* Alpha Numeric Return Strings */
339 /*********************************************************************/
340 
341 #define ERROR_RETSTR "ERROR:"
342 
343 /*********************************************************************/
344 /* Numeric Return calls */
345 /*********************************************************************/
346 
347 #define INVALID_ROUTINE 40 /* Raise Rexx error */
348 #define VALID_ROUTINE 0 /* Successful completion */
349 
350 /*********************************************************************/
351 /* Some useful macros */
352 /*********************************************************************/
353 
354 #define BUILDRXSTRING(t, s) { \
355  strcpy((t)->strptr,(s));\
356  (t)->strlength = strlen((s)); \
357 }
358 
359 #define RETVAL(retc) { \
360  retstr->strlength = strlen(itoa(retc, retstr->strptr,10)); \
361  return VALID_ROUTINE; \
362 }
363 
364 /*********************************************************************/
365 /**************** REXXUTIL Supporting Functions ********************/
366 /**************** REXXUTIL Supporting Functions ********************/
367 /**************** REXXUTIL Supporting Functions ********************/
368 /*********************************************************************/
369 
371 {
372  c->RaiseException1(Rexx_Error_System_service_user_defined, c->String("failed to allocate memory"));
373 }
374 
375 /**
376  * <routineName> argument <argPos> must not be a null string
377  *
378  * SysFileTree argument 2 must not be a null string
379  *
380  * @param c Threade context we are operating in.
381  * @param fName Routine name.
382  * @param pos Argument position.
383  */
384 void inline nullStringException(RexxThreadContext *c, CSTRING fName, size_t pos)
385 {
386  c->RaiseException3(Rexx_Error_Incorrect_call_null, c->String(fName), c->String("positional"), c->StringSize(pos));
387 }
388 
389 inline void safeLocalFree(void *p)
390 {
391  if (p != NULL)
392  {
393  LocalFree(p);
394  }
395 }
396 
397 /**
398  * Raises an exception for an unrecoverable system API failure.
399  *
400  * @param c Call context we are operating in.
401  * @param api System API name.
402  * @param rc Return code from calling the API.
403  */
405 {
406  char buf[256] = {0};
407  _snprintf(buf, sizeof(buf),
408  "system API %s() failed; rc: %d last error code: %d", api, rc, GetLastError());
409 
410  c->RaiseException1(Rexx_Error_System_service_user_defined, c->String(buf));
411 }
412 
413 /**
414  * Tests if the the current operating system version meets the specified
415  * requirements. Really a front end to VerifyVersionInfo(). See MSDN docs for
416  * type and condition flags.
417  *
418  * @param major OS major number.
419  * @param minor OS minor number.
420  * @param sp Service pack level.
421  * @param type Further refines the test. See MSDN for all the flags, but
422  * for example there is VER_NT_WORKSTATION to differentiate
423  * between NT desktop and NT server.
424  * @param condition The test condition. Typical flags would be VER_EQUAL or
425  * VER_GREATER_EQUAL.
426  *
427  * @return True if the condition is met by the current operating system, or
428  * false if not.
429  */
430 static bool isWindowsVersion(DWORD major, DWORD minor, unsigned int sp, unsigned int type, unsigned int condition)
431 {
432  OSVERSIONINFOEX ver;
433  DWORDLONG mask = 0;
434  DWORD testForMask = VER_MAJORVERSION | VER_MINORVERSION;
435 
436  ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
437 
438  ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
439  ver.dwMajorVersion = major;
440  ver.dwMinorVersion = minor;
441 
442  VER_SET_CONDITION(mask, VER_MAJORVERSION, condition);
443  VER_SET_CONDITION(mask, VER_MINORVERSION, condition);
444 
445  if ( condition != VER_EQUAL )
446  {
447  ver.wServicePackMajor = sp;
448  testForMask |= VER_SERVICEPACKMAJOR;
449  VER_SET_CONDITION(mask, VER_SERVICEPACKMAJOR, condition);
450  }
451 
452  if ( type != 0 )
453  {
454  ver.wProductType = type;
455  testForMask |= VER_PRODUCT_TYPE;
456  VER_SET_CONDITION(mask, VER_PRODUCT_TYPE, condition);
457  }
458 
459  if ( VerifyVersionInfo(&ver, testForMask, mask) )
460  {
461  return true;
462  }
463  else
464  {
465  return false;
466  }
467 }
468 
469 /********************************************************************
470 * Function: string2size_t(string, number) *
471 * *
472 * Purpose: Validates and converts an ASCII-Z string from string *
473 * form to an unsigned long. Returns false if the number *
474 * is not valid, true if the number was successfully *
475 * converted. *
476 * *
477 * RC: true - Good number converted *
478 * false - Invalid number supplied. *
479 *********************************************************************/
481  const char *string, /* string to convert */
482  size_t *number) /* converted number */
483 {
484  size_t accumulator; /* converted number */
485  size_t length; /* length of number */
486 
487  length = strlen(string); /* get length of string */
488  if (length == 0 || /* if null string */
489  length > MAX_DIGITS + 1) /* or too long */
490  {
491  return false; /* not valid */
492  }
493 
494  accumulator = 0; /* start with zero */
495 
496  while (length) /* while more digits */
497  {
498  if (!isdigit(*string)) /* not a digit? */
499  {
500  return false; /* tell caller */
501  }
502  /* add to accumulator */
503  accumulator = accumulator * 10 + (*string - '0');
504  length--; /* reduce length */
505  string++; /* step pointer */
506  }
507  *number = accumulator; /* return the value */
508  return true; /* good number */
509 }
510 
511 inline bool isAtLeastVista(void)
512 {
513  return isWindowsVersion(6, 0, 0, 0, VER_GREATER_EQUAL);
514 }
515 
516 /*********************************************************************/
517 /* */
518 /* Subroutine Name: memupper */
519 /* */
520 /* Descriptive Name: uppercase a memory location */
521 /* */
522 /* Entry Point: memupper */
523 /* */
524 /* Input: memory to upper case */
525 /* length of memory location */
526 /* */
527 /*********************************************************************/
528 
529 void memupper(
530  char *location, /* location to uppercase */
531  size_t length) /* length to uppercase */
532 {
533  for (; length--; location++) /* loop for entire string */
534  /* uppercase in place */
535  *location = toupper(*location);
536 }
537 
538 bool ReadNextBuffer( GetFileData *filedata );
539 
540 /********************************************************************
541 * Function: OpenFile(file, filedata) *
542 * *
543 * Purpose: Prepares a file for reading. *
544 * *
545 * RC: 0 - file was opened successfully *
546 * 1 - file open error occurred *
547 *********************************************************************/
548 
550  const char *file, /* file name */
551  GetFileData *filedata ) /* global file information */
552 {
553  DWORD dwSize; /* file status information */
554 
555  /* try to open the file */
556  if ((filedata->handle = CreateFile(file, GENERIC_READ,
557  FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
558  FILE_FLAG_WRITE_THROUGH, 0))
559  == INVALID_HANDLE_VALUE)
560  return true; /* return failure */
561 
562  /* retrieve the file size */
563  dwSize = GetFileSize(filedata->handle, NULL);
564  /* if GetFileSize failed or */
565  /* size=0 */
566  if (dwSize == 0xffffffff || !dwSize) {
567  CloseHandle(filedata->handle); /* close the file */
568  return true; /* and quit */
569  }
570  if (dwSize <= MAX_READ) { /* less than a single buffer */
571  DWORD bytesRead;
572  /* allocate buffer for file */
573  if (!(filedata->buffer = (char *)GlobalAlloc(GMEM_ZEROINIT |
574  GMEM_FIXED, dwSize))) {
575  CloseHandle(filedata->handle); /* close the file */
576  return true;
577  }
578  filedata->size = dwSize; /* save file size */
579  filedata->residual = 0; /* no left over information */
580  /* read the file in */
581  if (!ReadFile(filedata->handle, filedata->buffer, dwSize, &bytesRead, NULL)) {
582  GlobalFree(filedata->buffer); /* free the buffer */
583  CloseHandle(filedata->handle); /* close the file */
584  return true;
585  }
586 
587  // Set scan to beginning of buffer, and data to number of bytes we have.
588  filedata->scan = filedata->buffer;
589  filedata->data = bytesRead;
590  }
591  else { /* need to read partial */
592  /* allocate buffer for read */
593  if (!(filedata->buffer = (char *)GlobalAlloc(GMEM_ZEROINIT |
594  GMEM_FIXED, MAX_READ))) {
595  CloseHandle(filedata->handle); /* close the file */
596  return true;
597  }
598 
599  filedata->size = dwSize; /* save file size */
600  /* and set remainder */
601  filedata->residual = filedata->size;
602  /* read the file in */
603  if (ReadNextBuffer(filedata)) {
604  GlobalFree(filedata->buffer); /* free the buffer */
605  CloseHandle(filedata->handle); /* close the file */
606  return true;
607  }
608  }
609  return false; /* file is opened */
610 }
611 
612 /********************************************************************
613 * Function: CloseFile(filedata) *
614 * *
615 * Purpose: Close a file *
616 *********************************************************************/
618  GetFileData *filedata ) /* global file information */
619 {
620  CloseHandle(filedata->handle); /* close the file */
621  GlobalFree(filedata->buffer); /* free the buffer */
622 }
623 
624 /**
625  * Reads the next buffer of data.
626  *
627  * @param filedata Global file information.
628  *
629  * @return 0, buffer was read. 1, an error occurred reading buffer.
630  */
632 {
633  size_t size;
634  DWORD bytesRead;
635 
636  /* get size of this read */
637  size = std::min(size_t(MAX_READ), filedata->residual);
638 
639  /* read the file in */
640  if ( !ReadFile(filedata->handle, filedata->buffer, (DWORD)size, &bytesRead, NULL) )
641  {
642  return 1;
643  }
644  filedata->data = bytesRead;
645 
646  if ( filedata->data != size )
647  {
648  // Read less than requested, no residual.
649  filedata->residual = 0; /* no residual */
650  }
651  else
652  {
653  // Residual is remainder.
654  filedata->residual = filedata->residual - size;
655  }
656 
657  /* don't check for EOF but read to real end of file */
658  // /* look for a EOF mark */
659  //endptr = memchr(filedata->buffer, CH_EOF, filedata->data);
660  //
661  //if (endptr) { /* found an EOF mark */
662  // /* set new length */
663  // filedata->data = (ULONG)(endptr - filedata->buffer);
664  // filedata->residual = 0; /* no residual */
665  //}
666 
667  // Set position to beginning.
668  filedata->scan = filedata->buffer;
669  return 0;
670 }
671 
672 /********************************************************************
673 * Function: GetLine(line, size, filedata) *
674 * *
675 * Purpose: Reads a line of data using buffered reads. At end of *
676 * file, zero is returned to indicate nothing left. *
677 * *
678 * RC: true - line was read successfully *
679 * false - end of file was reached *
680 *********************************************************************/
681 
682 bool GetLine(
683  char *line, /* returned line */
684  size_t size, /* size of line buffer */
685  GetFileData *filedata ) /* file handle */
686 {
687  char *scan; /* current scan pointer */
688  size_t length; /* line length */
689  size_t copylength; /* copied length */
690 
691 
692  if (!(filedata->data)) { /* if out of current buffer */
693  if (filedata->residual) { /* may be another buffer */
694  ReadNextBuffer(filedata); /* try to read one */
695  if (!filedata->data) /* nothing more? */
696  return true; /* all done */
697  }
698  else
699  return true; /* return EOF condition */
700  }
701  /* look for a carriage return */
702  scan = (char *)memchr(filedata->scan, CH_NL, filedata->data);
703  if (scan) { /* found one */
704  /* calculate the length */
705  length = scan - filedata->scan;
706  copylength = std::min(length, size); /* get length to copy */
707  /* copy over the data */
708  memcpy(line, filedata->scan, copylength);
709  line[copylength] = '\0'; /* make into ASCIIZ string */
710 
711  /* we don't want the CR character in the result string*/
712  if ( line[copylength - 1] == CH_CR ) {
713  line[copylength - 1] = '\0';
714  } /* endif */
715 
716  filedata->data -= length + 1; /* reduce the length */
717  filedata->scan = scan + 1; /* set new scan point */
718 
719  if (!filedata->data) { /* all used up */
720  if (filedata->residual) /* more to read */
721  ReadNextBuffer(filedata); /* read the next buffer */
722  }
723  return false; /* this worked ok */
724  }
725  else /* ran off the end */
726  {
727  /* now we have scanned the whole buffer, but didn't find LF. */
728  /* we have two situation that can appear: */
729  /* 1.) size > filedata->data ==> there is still room in the working */
730  /* buffer, we can see whether we have scanned the whole file */
731  /* --> ReadNextBuffer, or this was it, and we return */
732  /* 2.) size < filedata->buffer ==> we have scanned to the end of the */
733  /* buffer, more than what would fit into it, but still we */
734  /* haven't had a hit. So copy all elements into the buffer */
735  /* read the next buffer, GetLine to get the next LF */
736  /* and return what was put into buffer. Be ALWAYS AWARE that */
737  /* that buffer limits to 2047 bytes, and that we only return up */
738  /* to 2047 bytes of a line. The rest of the line is not returned */
739  /* and not checked for search argument. Nevertheless, this */
740  /* garantees, that the line counter (argument 'N') corresponds */
741  /* with the input file */
742 
743  /* get length to copy */
744  if (size > filedata->data)
745  {
746  copylength = filedata->data; /* copy the rest into linebuffer */
747  /* copy over the data */
748  memcpy(line, filedata->scan, copylength);
749  line[copylength] = '\0'; /* make into ASCIIZ string */
750 
751  /* all data should be read, filedata->data must be zero */
752  filedata->data -= copylength;
753  /* scan should be at the end */
754  filedata->scan += copylength; /* set new scan point */
755 
756  /* if no more data to read in the file, return OK */
757  if (!filedata->residual)
758  return false;
759  else
760  return GetLine(line + copylength, size - copylength, filedata);
761  }
762  else /* the line is full, scan until LF found but no copy */
763  {
764  copylength = std::min(size, filedata->data);
765  /* copy over the data */
766  memcpy(line, filedata->scan, copylength);
767  line[copylength] = '\0'; /* make into ASCIIZ string */
768 
769  filedata->data = 0; /* no data in buffer */
770  filedata->scan += filedata->data; /* set scan point to end */
771 
772  if (filedata->residual) /* more to read */
773  {
774  ReadNextBuffer(filedata); /* read the next buffer */
775  return GetLine(line + copylength, 0, filedata);
776  }
777  else
778  return false;
779  }
780  }
781 }
782 
783 /********************************************************************
784 * Function: SetFileMode(file, attributes) *
785 * *
786 * Purpose: Change file attribute bits *
787 * without PM. *
788 * *
789 * RC: 0 - File attributes successfully changed *
790 * 1 - Unable to change attributes *
791 *********************************************************************/
793  const char *file, /* file name */
794  size_t attr ) /* new file attributes */
795 {
796 
797  DWORD dwfileattrib; /* file attributes */
798 
799  /* get the file status */
800  if ((dwfileattrib = GetFileAttributes(file)) != 0xffffffff) {
801  /* if worked */
802  /* set the attributes */
803  if ((dwfileattrib = SetFileAttributes(file, (DWORD)attr)) != 0)
804  return false; /* give back success flag */
805  else
806  return true;
807  } else
808  return true;
809 }
810 
811 /********************************************************************
812 * Function: mystrstr(haystack, needle, hlen, nlen, sensitive) *
813 * *
814 * Purpose: Determines if the string 'needle' is in the *
815 * string 'haystack' by returning it's position or *
816 * a NULL if not found. The length of haystack and *
817 * needle are given by 'hlen' and 'nlen' respectively. *
818 * *
819 * If 'sensitive' is true, then the search is case *
820 * sensitive, else it is case insensitive. *
821 * *
822 * RC: num - The pos where needle was found. *
823 * NULL - needle not found. *
824 * *
825 * Used By: SysFileSearch() *
826 *********************************************************************/
827 
828 char *mystrstr(
829  const char *haystack,
830  const char *needle,
831  size_t hlen,
832  size_t nlen,
833  bool sensitive)
834 {
835 // TODO: This can be made a LOT more efficient
836 
837  char line[MAX_LINE_LEN];
838  char target[MAX_LINE_LEN];
839  size_t p;
840  /* Copy line - Change nulls to spaces and uppercase if needed */
841 
842  for (p = 0; p < hlen; p++)
843  {
844  if (haystack[p] == '\0')
845  line[p] = ' ';
846  else if (sensitive)
847  line[p] = haystack[p];
848  else line[p] = (char)toupper(haystack[p]);
849  }
850  line[p] = '\0';
851 
852  /* Copy target - Change nulls to spaces and uppercase if needed */
853 
854  for (p = 0; p < nlen; p++) {
855 
856  if (needle[p] == '\0')
857  target[p] = ' ';
858  else if (sensitive)
859  target[p] = needle[p];
860  else target[p] = (char)toupper(needle[p]);
861  }
862  target[p] = '\0';
863 
864  return strstr(line, target);
865 }
866 
867 const char *mystrstr(const char *haystack, const char *needle)
868 {
869  size_t hlen = strlen(haystack);
870  size_t nlen = strlen(haystack);
871 
872  char *haystackCopy = strdup(haystack);
873  for (size_t i = 0; i < hlen; i++)
874  {
875  haystackCopy[i] = toupper(haystackCopy[i]);
876  }
877 
878  const char *result = strstr(haystackCopy, needle);
879  free(haystackCopy);
880  return result;
881 }
882 
883 /****************************************************************
884 * Function: GetUniqueFileName(Template, Filler, file) *
885 * *
886 * Purpose: This function returns a unique temporary file name *
887 * given a template and a filler character. *
888 * *
889 * Params: CHAR* Template - The template. Must contain at *
890 * least one or more filler chars. *
891 * *
892 * Example: "C:\TEMP\FILE????.DAT *
893 * *
894 * CHAR Filler - The character in the Template to *
895 * be replaced with numbers. For *
896 * the above example, the filler char *
897 * would be '?'. *
898 * CHAR* file - file name produced (output) *
899 * *
900 * Used By: RxTempFileName() *
901 ****************************************************************/
902 
904  CHAR *Template,
905  CHAR Filler,
906  CHAR *file)
907 {
908 
909  CHAR numstr[6];
910  bool Unique = false;
911 
912  ULONG x, /* loop index */
913  i, /* */
914  j = 0, /* number of filler chars */
915  /* found */
916  num, /* temporary random number */
917  start, /* first random number */
918  max = 1; /* maximum random number */
919 
920  INT seed; /* to get current time */
921  WIN32_FIND_DATA wfdFinfo; /* Windows Find data struct */
922  /* Structure */
923  SYSTEMTIME DT; /* The date and time structure*/
924  UINT fuErrorMode; /* holds current file err mode*/
925  HANDLE hSearch; /* handle of file if found */
926 
927  /** Determine number of filler characters * */
928 
929  for (x = 0; Template[x] != 0; x++)
930 
931  if (Template[x] == Filler) {
932  max = max *10;
933  j++;
934  }
935 
936  /** Return NULL string if less than 1 or greater than 4 * */
937 
938  if (j == 0 || j > 5) {
939  Unique = true;
940  strcpy(file, "");
941  return;
942  }
943 
944  /** Get a random number in the appropriate range */
945 
946  /* Get the time */
947  GetSystemTime(&DT); /* via Windows */
948 
949  seed = DT.wHour*60 + DT.wMinute; /* convert to hundreths */
950  seed = seed*60 + DT.wSecond;
951  seed = seed*100 + ( DT.wMilliseconds / (UINT)10 );
952  seed = seed * RNDFACTOR + 1;
953  num = (ULONG)seed % max;
954  start = num;
955 
956  /** Do until a unique name is found */
957 
958  while (!Unique) {
959 
960  /** Generate string which represents the number */
961 
962  switch (j) {
963  case 1 :
964  wsprintf(numstr, "%01u", num);
965  break;
966  case 2 :
967  wsprintf(numstr, "%02u", num);
968  break;
969  case 3 :
970  wsprintf(numstr, "%03u", num);
971  break;
972  case 4 :
973  wsprintf(numstr, "%04u", num);
974  break;
975  case 5 :
976  wsprintf(numstr, "%05u", num);
977  break;
978  }
979 
980  /** Subsitute filler characters with numeric string */
981 
982  i = 0;
983 
984  for (x = 0; Template[x] != 0; x++)
985 
986  if (Template[x] == Filler)
987  file[x] = numstr[i++];
988 
989  else
990  file[x] = Template[x];
991  file[x] = '\0';
992 
993  /** See if the file exists */
994  /* Disable Hard-Error popups */
995  fuErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);
996  hSearch = FindFirstFile(file, &wfdFinfo);
997 
998  if (hSearch == INVALID_HANDLE_VALUE)/* file not found? */
999  Unique = true; /* got one */
1000 
1001  FindClose(hSearch);
1002  SetErrorMode(fuErrorMode); /* Enable previous setting */
1003 
1004  /** Make sure we are not wasting our time */
1005 
1006  num = (num+1)%max;
1007 
1008  if (num == start && !Unique) {
1009  Unique = true;
1010  strcpy(file, "");
1011  }
1012  }
1013 }
1014 
1015 /**********************************************************************
1016 *** <<<<<< REXXUTIL Functions Follow >>>>>>> ***
1017 *** <<<<<< REXXUTIL Functions Follow >>>>>>> ***
1018 *** <<<<<< REXXUTIL Functions Follow >>>>>>> ***
1019 *** <<<<<< REXXUTIL Functions Follow >>>>>>> ***
1020 **********************************************************************/
1021 /**********************************************************************
1022 * Function: SysCls *
1023 * *
1024 * Syntax: call SysCls *
1025 * *
1026 * Return: NO_UTIL_ERROR - Successful. *
1027 **********************************************************************/
1028 
1029 size_t RexxEntry SysCls(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1030 {
1031  HANDLE hStdout; /* Handle to Standard Out */
1032  DWORD dummy;
1033  COORD Home = {0, 0}; /* Home coordinates on console*/
1034  CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */
1035 
1036  if (numargs) /* arguments specified? */
1037  return INVALID_ROUTINE; /* raise the error */
1038 
1039  hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
1040  /* if in character mode */
1041  if (GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) {
1042  FillConsoleOutputCharacter( hStdout, ' ',
1043  csbiInfo.dwSize.X * csbiInfo.dwSize.Y,
1044  Home, &dummy );
1045  SetConsoleCursorPosition(hStdout, Home);
1046 
1047  }
1048  BUILDRXSTRING(retstr, NO_UTIL_ERROR);/* pass back result */
1049  return VALID_ROUTINE; /* no error on call */
1050 }
1051 
1052 /*************************************************************************
1053 * Function: SysCurPos - positions cursor in DOS window *
1054 * *
1055 * Syntax: call SysCurPos [row, col] *
1056 * *
1057 * Params: row - row to place cursor on *
1058 * col - column to place cursor on *
1059 * *
1060 * Return: row, col *
1061 *************************************************************************/
1062 
1063 RexxRoutine2(RexxStringObject, SysCurPos, OPTIONAL_stringsize_t, inrow, OPTIONAL_stringsize_t, incol)
1064 {
1065  CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */
1066 
1067 
1068  if ((argumentExists(1) && argumentOmitted(2)) || (argumentExists(2) && argumentOmitted(1)))
1069  {
1070  context->InvalidRoutine();
1071  return NULL;
1072  }
1073  /* get handle to stdout */
1074  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
1075 
1076  /* get current position, and */
1077  /* continue only if in */
1078  /* character mode */
1079  if (GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
1080  {
1081  char buffer[256];
1082 
1083  sprintf(buffer, "%d %d", csbiInfo.dwCursorPosition.Y, csbiInfo.dwCursorPosition.X);
1084 
1085  if (argumentExists(2))
1086  {
1087  COORD newHome; /* Position to move cursor */
1088  newHome.Y = (short)inrow; /* convert to short form */
1089  newHome.X = (short)incol; /* convert to short form */
1090  /* Set the cursor position */
1091  SetConsoleCursorPosition(hStdout, newHome);
1092  }
1093 
1094  return context->NewStringFromAsciiz(buffer);
1095  }
1096 
1097  return context->NullString();
1098 }
1099 
1100 /*************************************************************************
1101 * Function: SysCurState *
1102 * *
1103 * Syntax: call SysCurState state *
1104 * *
1105 * Params: state - Either 'ON' or 'OFF'. *
1106 * *
1107 * Return: NO_UTIL_ERROR - Successful. *
1108 *************************************************************************/
1109 
1110 RexxRoutine1(int, SysCurState, CSTRING, option)
1111 {
1112  CONSOLE_CURSOR_INFO CursorInfo; /* info about cursor */
1113  /* get handle to stdout */
1114  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
1115  /* Get the cursor info */
1116  GetConsoleCursorInfo(hStdout,&CursorInfo);
1117 
1118  /* Get state and validate */
1119  if (stricmp(option, "ON") == 0)
1120  {
1121  CursorInfo.bVisible = true;
1122  }
1123  else if (stricmp(option, "OFF") == 0)
1124  {
1125  CursorInfo.bVisible = false;
1126  }
1127  else
1128  {
1129  // this is an error, raise the condition and return
1130  context->InvalidRoutine();
1131  return 0;
1132  }
1133  /* Set the cursor info */
1134  SetConsoleCursorInfo(hStdout,&CursorInfo);
1135  return 0; /* no error on call */
1136 }
1137 
1138 
1139 /*************************************************************************
1140 * Function: SysDriveInfo *
1141 * *
1142 * Syntax: call SysDriveInfo drive *
1143 * *
1144 * Params: drive - 'C', 'D', 'E', etc. *
1145 * *
1146 * Return: disk free total label *
1147 *************************************************************************/
1148 
1149 size_t RexxEntry SysDriveInfo(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1150 {
1151  CHAR chFileSysType[MAX_PATH], /* File system name */
1152  chVolumeName[MAX_PATH], /* volume label */
1153  chDriveLetter[4]; /* drive_letter + : + \ + \0 */
1154  BOOL bGVIrc; /* rc from GVI */
1155 
1156  /* GetDiskFreeSpace calculations */
1157  DWORD dwSectorsPerCluster, dwBytesPerSector;
1158  DWORD dwFreeClusters, dwClusters;
1159  BOOL bGDFSrc; /* GDFS rc */
1160  UINT errorMode;
1161 
1162  DWORD dwgle;
1163  unsigned __int64 i64FreeBytesToCaller,
1164  i64TotalBytes,
1165  i64FreeBytes;
1166 
1167  /* validate arguments */
1168  if (numargs != 1 ||
1169  args[0].strlength > 2 || /* no more than 2 chars */
1170  args[0].strlength == 0) /* at least 1 */
1171  return INVALID_ROUTINE;
1172 
1173  const char *arg = args[0].strptr; /* get argument pointer */
1174  /* drive letter? */
1175  if (strlen(arg) == 2 && /* if second letter isn't : bye */
1176  arg[1] != ':')
1177  return INVALID_ROUTINE;
1178 
1179  if (arg[0] < 'A' || /* is it in range? */
1180  arg[0] > 'z')
1181  return INVALID_ROUTINE;
1182 
1183  if (strlen(arg) == 1){ /* need to add a : if only the*/
1184  chDriveLetter[0]=arg[0]; /* letter was passed in */
1185  chDriveLetter[1]=':';
1186  chDriveLetter[2]='\\'; /* need to add \ because of */
1187  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
1188  }
1189  else /* have <letter>: , just copy over */
1190  {
1191  strcpy(chDriveLetter, args[0].strptr);
1192  chDriveLetter[2]='\\'; /* need to add \ because of */
1193  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
1194  }
1195 
1196  /* try to load GetDiskFreeSpaceEx function pointer */
1197  if ( !pGetDiskFreeSpaceEx )
1198  {
1199  pGetDiskFreeSpaceEx = (P_GDFSE) GetProcAddress(GetModuleHandle("kernel32.dll"),
1200  "GetDiskFreeSpaceExA");
1201  } /* endif */
1202 
1203  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1204  /* get the volume name and file system type */
1205  bGVIrc=GetVolumeInformation(chDriveLetter,
1206  chVolumeName,
1207  (DWORD)MAX_PATH,
1208  NULL,
1209  NULL,
1210  NULL,
1211  chFileSysType,
1212  (DWORD)MAX_PATH);
1213 
1214  dwgle=GetLastError();
1215 
1216  /* use appropriate function */
1217  if ( pGetDiskFreeSpaceEx )
1218  {
1219  bGDFSrc = pGetDiskFreeSpaceEx(chDriveLetter,
1220  (PULARGE_INTEGER) &i64FreeBytesToCaller,
1221  (PULARGE_INTEGER) &i64TotalBytes,
1222  (PULARGE_INTEGER) &i64FreeBytes);
1223  }
1224  else
1225  {
1226  /* get the disk free space information */
1227  bGDFSrc=GetDiskFreeSpace( chDriveLetter,
1228  &dwSectorsPerCluster,
1229  &dwBytesPerSector,
1230  &dwFreeClusters,
1231  &dwClusters);
1232 
1233  /* force 64 bit maths */
1234  i64TotalBytes = (__int64)dwClusters * dwSectorsPerCluster * dwBytesPerSector;
1235  i64FreeBytes = (__int64)dwFreeClusters * dwSectorsPerCluster * dwBytesPerSector;
1236  } /* endif */
1237 
1238  dwgle=GetLastError();
1239  SetErrorMode(errorMode);
1240 
1241  if (bGVIrc && bGDFSrc) {
1242 
1243  /* use simplified display routine with 64 bit types */
1244  sprintf(retstr->strptr, // drive free total label
1245  "%c%c %-12I64u %-12I64u %-13s",
1246  chDriveLetter[0], chDriveLetter[1],
1247  i64FreeBytes, i64TotalBytes, chVolumeName);
1248  /* create return string */
1249  retstr->strlength = strlen(retstr->strptr);
1250  }
1251  else
1252  retstr->strlength = 0; /* return null string */
1253 
1254  return VALID_ROUTINE; /* no error on call */
1255 }
1256 
1257 /*************************************************************************
1258 * Function: SysDriveMap *
1259 * *
1260 * Syntax: call SysDriveMap [drive] [,mode] *
1261 * *
1262 * Params: drive - 'C', 'D', 'E', etc. The drive to start the search *
1263 * with. *
1264 * mode - Any of the following: 'FREE', 'USED', 'DETACHED', *
1265 * 'LOCAL', 'REMOTE' *
1266 * *
1267 * Return: 'A: B: C: D: ...' *
1268 *************************************************************************/
1269 
1270 size_t RexxEntry SysDriveMap(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1271 {
1272 
1273  CHAR temp[MAX]; /* Entire drive map built here*/
1274 
1275  CHAR tmpstr[MAX]; /* Single drive entries built */
1276  /* here */
1277  CHAR DeviceName[4]; /* Device name or drive letter*/
1278  /* string */
1279  DWORD DriveMap; /* Drive map */
1280  ULONG Ordinal; /* Ordinal of entry in name */
1281  /* list */
1282  /* required */
1283  ULONG dnum; /* Disk num variable */
1284  ULONG start = 3; /* Initial disk num */
1285  ULONG Mode = USED; /* Query mode USED, FREE, */
1286  /* LOCAL, etc */
1287  LONG rc; /* OS/2 return codes */
1288  UINT errorMode;
1289 
1290  Ordinal = (ULONG )0;
1291 
1292  temp[0] = '\0';
1293 
1294  if (numargs > 2) /* validate arguments */
1295  return INVALID_ROUTINE;
1296  /* check starting drive letter*/
1297  if (numargs >= 1 && args[0].strptr) {
1298 
1299  if ((strlen(args[0].strptr) == 2 &&
1300  args[0].strptr[1] != ':') ||
1301  strlen(args[0].strptr) > 2 ||
1302  strlen(args[0].strptr) == 0)
1303  return INVALID_ROUTINE;
1304  start = toupper(args[0].strptr[0])-'A'+1;
1305  }
1306  if (start < 1 || /* is it in range? */
1307  start > 26)
1308  return INVALID_ROUTINE;
1309  /* check the mode */
1310  if (numargs == 2 && args[1].strlength != 0) {
1311 
1312  if (!stricmp(args[1].strptr, "FREE"))
1313  Mode = FREE;
1314  else if (!stricmp(args[1].strptr, "USED"))
1315  Mode = USED;
1316  else if (!stricmp(args[1].strptr, "RAMDISK"))
1317  Mode = RAMDISK;
1318  else if (!stricmp(args[1].strptr, "REMOTE"))
1319  Mode = REMOTE;
1320  else if (!stricmp(args[1].strptr, "LOCAL"))
1321  Mode = LOCAL;
1322  else if (!stricmp(args[1].strptr, "REMOVABLE"))
1323  Mode = REMOVABLE;
1324  else if (!stricmp(args[1].strptr, "CDROM"))
1325  Mode = CDROM;
1326  else
1327  return INVALID_ROUTINE;
1328  }
1329  /* perform the query */
1330  DriveMap = GetLogicalDrives();
1331  DriveMap>>=start-1; /* Shift to the first drive */
1332  temp[0] = '\0'; /* Clear temporary buffer */
1333 
1334  for (dnum = start; dnum <= 26; dnum++) {
1335 
1336  /* Hey, we have a free drive */
1337  if (!(DriveMap&(DWORD)1) && Mode == FREE) {
1338  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1339  strcat(temp, tmpstr);
1340  }
1341  /* Hey, we have a used drive */
1342  else if ((DriveMap&(DWORD)1) && Mode == USED) {
1343  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1344  strcat(temp, tmpstr);
1345  }
1346 
1347  else if (DriveMap&(DWORD)1) { /* Check specific drive info */
1348  sprintf(DeviceName, "%c:\\", dnum+'A'-1);
1349 
1350  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1351  rc = (LONG)GetDriveType(DeviceName);
1352  SetErrorMode(errorMode);
1353  #ifdef UNDELETE
1354  DataBufferLen = sizeof DataBuffer;
1355  DosQueryFSAttach(DeviceName, Ordinal, FSAInfoLevel,
1356  &DataBuffer, &DataBufferLen);
1357  rc = DosQueryFSInfo(dnum, 2, buf, sizeof(buf));
1358  #endif
1359 
1360  if (rc == DRIVE_REMOVABLE && Mode == REMOVABLE) {
1361  /* Hey, we have a removable */
1362  /* drive */
1363  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1364  strcat(temp, tmpstr);
1365  }
1366 
1367  else if (rc == DRIVE_CDROM && Mode == CDROM) {
1368  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1369  strcat(temp, tmpstr);
1370  }
1371 
1372  else if (rc == DRIVE_FIXED && Mode == LOCAL) {
1373  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1374  strcat(temp, tmpstr);
1375  }
1376 
1377  else if (rc == DRIVE_REMOTE && Mode == REMOTE) {
1378  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1379  strcat(temp, tmpstr);
1380  }
1381 
1382  else if (rc == DRIVE_RAMDISK && Mode == RAMDISK) {
1383  sprintf(tmpstr, "%c: ", dnum+'A'-1);
1384  strcat(temp, tmpstr);
1385  }
1386  }
1387  DriveMap>>=1; /* Shift to the next drive */
1388  }
1389 
1390  BUILDRXSTRING(retstr, temp); /* pass back result */
1391  if (retstr->strlength) /* if not a null string */
1392  retstr->strlength--; /* Get rid of last space */
1393  return VALID_ROUTINE; /* no error on call */
1394 }
1395 
1396 
1397 /*************************************************************************
1398 * Function: SysDropFuncs *
1399 * *
1400 * Syntax: call SysDropFuncs *
1401 * *
1402 * Return: NO_UTIL_ERROR - Successful. *
1403 *************************************************************************/
1404 
1405 size_t RexxEntry SysDropFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1406 {
1407  // this is a NOP now
1408  retstr->strlength = 0; /* set return value */
1409  return VALID_ROUTINE;
1410 }
1411 
1412 /*************************************************************************
1413 * Function: SysFileDelete *
1414 * *
1415 * Syntax: call SysFileDelete file *
1416 * *
1417 * Params: file - file to be deleted. *
1418 * *
1419 * Return: Return code from DosDelete() function. *
1420 *************************************************************************/
1421 
1423 {
1424  return !DeleteFile(name) ? GetLastError() : 0;
1425 }
1426 
1427 
1428 /*************************************************************************
1429 * Function: SysFileSearch *
1430 * *
1431 * Syntax: call SysFileSearch target, file, stem [, options] *
1432 * *
1433 * Params: target - String to search for. *
1434 * file - Filespec to search. *
1435 * stem - Stem variable name to place results in. *
1436 * options - Any combo of the following: *
1437 * 'C' - Case sensitive search (non-default). *
1438 * 'N' - Preceed each found string in result stem *
1439 * with it line number in file (non-default).*
1440 * *
1441 * Return: NO_UTIL_ERROR - Successful. *
1442 * ERROR_NOMEM - Out of memory. *
1443 *************************************************************************/
1444 
1445 size_t RexxEntry SysFileSearch(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1446 {
1447 
1448  CHAR line[MAX_LINE_LEN]; /* Line read from file */
1449  char *ptr; /* Pointer to char str found */
1450  ULONG num = 0; /* Line number */
1451  size_t len; /* Length of string */
1452  size_t len2; /* Length of string */
1453  ULONG rc = 0; /* Return code of this func */
1454  bool linenums = false; /* Set true for linenums in */
1455  /* output */
1456  bool sensitive = false; /* Set true for case-sens */
1457  /* search */
1458  RXSTEMDATA ldp; /* stem data */
1459  char *buffer_pointer; /* current buffer pointer */
1460  GetFileData filedata; /* file read information */
1461 
1462  BUILDRXSTRING(retstr, NO_UTIL_ERROR);/* pass back result */
1463  /* validate arguments */
1464  if (numargs < 3 || numargs > 4 ||
1465  !RXVALIDSTRING(args[0]) ||
1466  !RXVALIDSTRING(args[1]) ||
1467  !RXVALIDSTRING(args[2]))
1468  return INVALID_ROUTINE; /* raise an error */
1469 
1470  buffer_pointer = NULL; /* nothing in buffer */
1471 
1472  const char *target = args[0].strptr; /* get target pointer */
1473  const char *file = args[1].strptr; /* get file name */
1474 
1475  if (numargs == 4) { /* process options */
1476  const char *opts = args[3].strptr; /* point to the options */
1477  if (strstr(opts, "N") || strstr(opts, "n"))
1478  linenums = true;
1479 
1480  if (strstr(opts, "C") || strstr(opts, "c"))
1481  sensitive = true;
1482  }
1483 
1484  /* Initialize data area */
1485  ldp.count = 0;
1486  strcpy(ldp.varname, args[2].strptr);
1487  ldp.stemlen = args[2].strlength;
1488  /* uppercase the name */
1489  memupper(ldp.varname, strlen(ldp.varname));
1490 
1491  if (ldp.varname[ldp.stemlen-1] != '.')
1492  ldp.varname[ldp.stemlen++] = '.';
1493 
1494  if (MyOpenFile(file, &filedata)) { /* open the file */
1495  BUILDRXSTRING(retstr, ERROR_FILEOPEN);
1496  return VALID_ROUTINE; /* finished */
1497  }
1498  /* do the search...found lines*/
1499  /* are saved in stem vars */
1500  while (!GetLine(line, MAX_LINE_LEN - 1, &filedata)) {
1501  len = strlen(line);
1502  num++;
1503  ptr = mystrstr(line, target, len, args[0].strlength, sensitive);
1504 
1505  if (ptr != NULL) {
1506 
1507  if (linenums) {
1508  wsprintf(ldp.ibuf, "%d ", num);
1509  len2 = strlen(ldp.ibuf);
1510  memcpy(ldp.ibuf+len2, line, std::min(len, IBUF_LEN-len2));
1511  ldp.vlen = std::min(size_t(IBUF_LEN), len+len2);
1512  }
1513  else {
1514  memcpy(ldp.ibuf, line, len);
1515  ldp.vlen = len;
1516  }
1517  ldp.count++;
1518  ltoa((long)ldp.count, ldp.varname+ldp.stemlen, 10);
1519 
1520  if (ldp.ibuf[ldp.vlen-1] == '\n')
1521  ldp.vlen--;
1522  ldp.shvb.shvnext = NULL;
1523  ldp.shvb.shvname.strptr = ldp.varname;
1524  ldp.shvb.shvname.strlength = strlen(ldp.varname);
1525  ldp.shvb.shvnamelen = ldp.shvb.shvname.strlength;
1526  ldp.shvb.shvvalue.strptr = ldp.ibuf;
1527  ldp.shvb.shvvalue.strlength = ldp.vlen;
1528  ldp.shvb.shvvaluelen = ldp.vlen;
1529  ldp.shvb.shvcode = RXSHV_SET;
1530  ldp.shvb.shvret = 0;
1531  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN) {
1532  CloseFile(&filedata); /* close the file */
1533  return INVALID_ROUTINE; /* error on non-zero */
1534  }
1535  }
1536  }
1537 
1538  CloseFile(&filedata); /* Close that file */
1539  /* set stem.0 to lines read */
1540  ltoa((long)ldp.count, ldp.ibuf, 10);
1541  ldp.varname[ldp.stemlen] = '0';
1542  ldp.varname[ldp.stemlen+1] = 0;
1543  ldp.shvb.shvnext = NULL;
1544  ldp.shvb.shvname.strptr = ldp.varname;
1545  ldp.shvb.shvname.strlength = ldp.stemlen+1;
1546  ldp.shvb.shvnamelen = ldp.stemlen+1;
1547  ldp.shvb.shvvalue.strptr = ldp.ibuf;
1548  ldp.shvb.shvvalue.strlength = strlen(ldp.ibuf);
1550  ldp.shvb.shvcode = RXSHV_SET;
1551  ldp.shvb.shvret = 0;
1552  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN)
1553  return INVALID_ROUTINE; /* error on non-zero */
1554 
1555  return VALID_ROUTINE; /* no error on call */
1556 }
1557 
1558 
1559 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
1560  * *
1561  * SysFileTree() implmentation and helper functions. *
1562  * *
1563 \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1564 
1565 /**
1566  * This is a SysFileTree specific function.
1567  *
1568  * @param c
1569  * @param pos
1570  * @param actual
1571  */
1572 static void badSFTOptsException(RexxThreadContext *c, size_t pos, CSTRING actual)
1573 {
1574  char buf[256] = {0};
1575  _snprintf(buf, sizeof(buf),
1576  "SysFileTree argument %zd must be a combination of F, D, B, S, T, L, I, or O; found \"%s\"",
1577  pos, actual);
1578 
1579  c->RaiseException1(Rexx_Error_Incorrect_call_user_defined, c->String(buf));
1580 }
1581 
1582 /**
1583  * This is a SysFile specific function.
1584  *
1585  * @param c
1586  * @param pos
1587  * @param actual
1588  */
1589 static void badMaskException(RexxThreadContext *c, size_t pos, CSTRING actual)
1590 {
1591  char buf[256] = {0};
1592  _snprintf(buf, sizeof(buf),
1593  "SysFileTree argument %zd must be 5 characters or less in length containing only '+', '-', or '*'; found \"%s\"",
1594  pos, actual);
1595 
1596  c->RaiseException1(Rexx_Error_Incorrect_call_user_defined, c->String(buf));
1597 }
1598 
1599 /**
1600  * Returns a value that is greater than 'need' by doubling 'have' until that
1601  * value is reached.
1602  */
1603 inline size_t neededSize(size_t need, size_t have)
1604 {
1605  while ( have < need )
1606  {
1607  have *= 2;
1608  }
1609  return have;
1610 }
1611 
1612 /**
1613  * Allocates a buffer that is at least twice as big as the buffer passed in.
1614  *
1615  * @param c Call context we are operating in.
1616  * @param dBuf Pointer to the buffer to reallocate
1617  * @param nBuf Size of current dBuf buffer. Will be updated on return
1618  * to size of newly allocated buffer.
1619  * @param needed Minimum size needed.
1620  * @param nStaticBuffer Size of original static buffer.
1621  *
1622  * @return True on success, false on memory allocation failure.
1623  *
1624  * @remarks NOTE: that the pointer to the buffer to reallocate, may, or may
1625  * not, be a pointer to a static buffer. We must NOT try to free a
1626  * static buffer and we MUST free a non-static buffer.
1627  */
1628 static bool getBiggerBuffer(RexxCallContext *c, char **dBuf, size_t *nBuf, size_t needed, size_t nStaticBuffer)
1629 {
1630  if ( *nBuf != nStaticBuffer )
1631  {
1632  LocalFree(*dBuf);
1633  }
1634 
1635  *nBuf = neededSize(needed, *nBuf);
1636  *dBuf = (char *)LocalAlloc(LPTR, *nBuf * sizeof(char));
1637 
1638  if ( *dBuf == NULL )
1639  {
1641  return false;
1642  }
1643 
1644  return true;
1645 }
1646 
1647 /**
1648  * Checks that attr is the same as that specified by the mask.
1649  *
1650  * @param mask
1651  * @param attr
1652  * @param options
1653  *
1654  * @return True for a match, otherwise false.
1655  */
1656 static bool sameAttr(int32_t *mask, uint32_t attr, uint32_t options)
1657 {
1658  if ( (options & DO_DIRS) && ! (options & DO_FILES) && ! (attr & FILE_ATTRIBUTE_DIRECTORY) )
1659  {
1660  return false;
1661  }
1662  if ( ! (options & DO_DIRS) && (options & DO_FILES) && (attr & FILE_ATTRIBUTE_DIRECTORY) )
1663  {
1664  return false;
1665  }
1666  if ( mask[0] == RXIGNORE )
1667  {
1668  return true;
1669  }
1670 
1671  if ( mask[0] < 0 && attr & FILE_ATTRIBUTE_ARCHIVE )
1672  {
1673  return false;
1674  }
1675  if ( mask[0] > 0 && ! (attr & FILE_ATTRIBUTE_ARCHIVE) )
1676  {
1677  return false;
1678  }
1679  if ( mask[1] < 0 && attr & FILE_ATTRIBUTE_DIRECTORY )
1680  {
1681  return false;
1682  }
1683  if ( mask[1] > 0 && ! (attr & FILE_ATTRIBUTE_DIRECTORY) )
1684  {
1685  return false;
1686  }
1687  if ( mask[2] < 0 && attr & FILE_ATTRIBUTE_HIDDEN )
1688  {
1689  return false;
1690  }
1691  if (mask[2] > 0 && ! (attr & FILE_ATTRIBUTE_HIDDEN) )
1692  {
1693  return false;
1694  }
1695  if (mask[3] < 0 && attr & FILE_ATTRIBUTE_READONLY )
1696  {
1697  return false;
1698  }
1699  if (mask[3] > 0 && ! (attr & FILE_ATTRIBUTE_READONLY) )
1700  {
1701  return false;
1702  }
1703  if (mask[4] < 0 && attr & FILE_ATTRIBUTE_SYSTEM )
1704  {
1705  return false;
1706  }
1707  if (mask[4] > 0 && ! (attr & FILE_ATTRIBUTE_SYSTEM) )
1708  {
1709  return false;
1710  }
1711 
1712  return true;
1713 }
1714 
1715 /**
1716  * Returns a new file attribute value given a mask of attributes to be changed
1717  * and the current attribute value.
1718  *
1719  * @param mask
1720  * @param attr
1721  *
1722  * @return New attribute value.
1723  */
1724 static uint32_t newAttr(int32_t *mask, uint32_t attr)
1725 {
1726  if ( mask[0] == RXIGNORE )
1727  {
1728  return attr;
1729  }
1730 
1731  if ( mask[0] < 0 )
1732  {
1733  attr &= ~FILE_ATTRIBUTE_ARCHIVE; // Clear
1734  }
1735  if ( mask[0] > 0 )
1736  {
1737  attr |= FILE_ATTRIBUTE_ARCHIVE; // Set
1738  }
1739  if ( mask[1] < 0 )
1740  {
1741  attr &= ~FILE_ATTRIBUTE_DIRECTORY; // Clear
1742  }
1743  if ( mask[1] > 0 )
1744  {
1745  attr |= FILE_ATTRIBUTE_DIRECTORY; // Set
1746  }
1747  if ( mask[2] < 0 )
1748  {
1749  attr &= ~FILE_ATTRIBUTE_HIDDEN; // Clear
1750  }
1751  if ( mask[2] > 0 )
1752  {
1753  attr |= FILE_ATTRIBUTE_HIDDEN; // Set
1754  }
1755  if ( mask[3] < 0 )
1756  {
1757  attr &= ~FILE_ATTRIBUTE_READONLY; // Clear
1758  }
1759  if ( mask[3] > 0 )
1760  {
1761  attr |= FILE_ATTRIBUTE_READONLY; // Set
1762  }
1763  if ( mask[4] < 0 )
1764  {
1765  attr &= ~FILE_ATTRIBUTE_SYSTEM; // Clear
1766  }
1767  if ( mask[4] > 0 )
1768  {
1769  attr |= FILE_ATTRIBUTE_SYSTEM; // Set
1770  }
1771 
1772  return attr;
1773 }
1774 
1775 /**
1776  * Changes the file attributes of the specified file to those specified by attr.
1777  *
1778  * @param file File to change the attributes of.
1779  *
1780  * @param attr New file attributes.
1781  *
1782  * @return True on success, false on error.
1783  *
1784  * @remarks Note that this function was named SetFileMode() in the old IBM
1785  * code.
1786  */
1787 static bool setAttr(const char *file, uint32_t attr)
1788 {
1789  if ( SetFileAttributes(file, attr) == 0 )
1790  {
1791  return false;
1792  }
1793  return true;
1794 }
1795 
1796 
1797 /**
1798  * This function is used by SysFileTree only.
1799  *
1800  * Formats the line for a found file and adds it to the stem containing all the
1801  * found files.
1802  *
1803  * @param c
1804  * @parm path
1805  * @param treeData
1806  * @param newMask
1807  * @param options
1808  * @param wfd
1809  *
1810  * @return True on success, false on error.
1811  *
1812  * @remarks We try to use the static buffers in treeData, but if they are not
1813  * big enough, we allocate memory. If we do allocate memory, we have to free
1814  * it of course. We can determine if the memory needs to be freed by checking
1815  * that either nFoundFile, or nFoundFileLine, are the same size as they are
1816  * originally set to, or not.
1817  *
1818  * If the file search is a very deep recursion in the host file system, a very
1819  * large number of String objects may be created in the single Call context of
1820  * SysFileTree. A reference to each created object is saved in a hash table to
1821  * protect it from garbage collection, which can lead to a very large hash
1822  * table. To prevent the creation of a very large hash table, we create a temp
1823  * object, pass that object to the interpreter, and then tell the interpreter
1824  * the object no longer needs to be protected in this call context.
1825  */
1826 static bool formatFile(RexxCallContext *c, char *path, RXTREEDATA *treeData, int32_t *newMask,
1827  uint32_t options, WIN32_FIND_DATA *wfd)
1828 {
1829  SYSTEMTIME systime;
1830  FILETIME ftLocal;
1831 
1832  char *dFoundFile = treeData->foundFile;
1833  size_t nFoundFile = sizeof(treeData->foundFile);
1834 
1835  int len = _snprintf(dFoundFile, nFoundFile, "%s%s", path, wfd->cFileName);
1836  if ( len < 0 || len == nFoundFile )
1837  {
1838  nFoundFile = strlen(path) + strlen(wfd->cFileName) + 1;
1839  dFoundFile = (char *)LocalAlloc(LPTR, nFoundFile);
1840  if ( dFoundFile == NULL )
1841  {
1843  return false;
1844  }
1845 
1846  // Buffer is sure to be big enough now, we we don't check the return.
1847  _snprintf(dFoundFile, nFoundFile, "%s%s", path, wfd->cFileName);
1848  }
1849 
1850  if ( options & NAME_ONLY )
1851  {
1852  RexxStringObject t = c->String(dFoundFile);
1853 
1854  // Add the file name to the stem and be done with it.
1855  treeData->count++;
1856  c->SetStemArrayElement(treeData->files, treeData->count, t);
1857  c->ReleaseLocalReference(t);
1858 
1859  if ( nFoundFile != sizeof(treeData->foundFile) )
1860  {
1861  LocalFree(dFoundFile);
1862  }
1863  return true;
1864  }
1865 
1866  // The file attributes need to be changed before we format the found file
1867  // line.
1868 
1869  uint32_t changedAttr = newAttr(newMask, wfd->dwFileAttributes);
1870  if ( changedAttr != wfd->dwFileAttributes )
1871  {
1872  // try to set the attributes, but if it fails, just use the exsiting.
1873  if ( ! setAttr(treeData->foundFile, changedAttr & ~FILE_ATTRIBUTE_DIRECTORY) )
1874  {
1875  changedAttr = wfd->dwFileAttributes;
1876  }
1877  }
1878 
1879  // Convert UTC to local file time, and then to system format.
1880  FileTimeToLocalFileTime(&wfd->ftLastWriteTime, &ftLocal);
1881  FileTimeToSystemTime(&ftLocal, &systime);
1882 
1883  // The fileTime buffer is 64 bytes, and the fileAtt buffer is 16 bytes.
1884  // Since we can count the characters put into the buffer here, there is
1885  // no need to check for buffer overflow.
1886 
1887  if ( options & LONG_TIME )
1888  {
1889  sprintf(treeData->fileTime, "%4d-%02d-%02d %02d:%02d:%02d %10lu ",
1890  systime.wYear,
1891  systime.wMonth,
1892  systime.wDay,
1893  systime.wHour,
1894  systime.wMinute,
1895  systime.wSecond,
1896  wfd->nFileSizeLow);
1897  }
1898  else
1899  {
1900  if ( options & EDITABLE_TIME )
1901  {
1902  sprintf(treeData->fileTime, "%02d/%02d/%02d/%02d/%02d %10lu ",
1903  (systime.wYear + 100) % 100,
1904  systime.wMonth,
1905  systime.wDay,
1906  systime.wHour,
1907  systime.wMinute,
1908  wfd->nFileSizeLow);
1909  }
1910  else
1911  {
1912  sprintf(treeData->fileTime, "%2d/%02d/%02d %2d:%02d%c %10lu ",
1913  systime.wMonth,
1914  systime.wDay,
1915  (systime.wYear + 100) % 100,
1916  (systime.wHour < 13 && systime.wHour != 0 ?
1917  systime.wHour : (abs(systime.wHour - (SHORT)12))),
1918  systime.wMinute,
1919  (systime.wHour < 12 || systime.wHour == 24) ? 'a' : 'p',
1920  wfd->nFileSizeLow);
1921  }
1922  }
1923 
1924  sprintf(treeData->fileAttr, "%c%c%c%c%c ",
1925  (changedAttr & FILE_ATTRIBUTE_ARCHIVE) ? 'A' : '-',
1926  (changedAttr & FILE_ATTRIBUTE_DIRECTORY) ? 'D' : '-',
1927  (changedAttr & FILE_ATTRIBUTE_HIDDEN) ? 'H' : '-',
1928  (changedAttr & FILE_ATTRIBUTE_READONLY) ? 'R' : '-',
1929  (changedAttr & FILE_ATTRIBUTE_SYSTEM) ? 'S' : '-');
1930 
1931  // Now format the complete line, allocating memory if we have to.
1932 
1933  char *dFoundFileLine = treeData->foundFileLine;
1934  size_t nFoundFileLine = sizeof(treeData->foundFileLine);
1935 
1936  len = _snprintf(dFoundFileLine, nFoundFileLine, "%s%s%s",
1937  treeData->fileTime, treeData->fileAttr, dFoundFile);
1938  if ( len < 0 || len == nFoundFileLine )
1939  {
1940  nFoundFileLine = strlen(treeData->fileTime) + strlen(treeData->fileAttr) + nFoundFile + 1;
1941  dFoundFileLine = (char *)LocalAlloc(LPTR, nFoundFileLine);
1942 
1943  if ( dFoundFileLine == NULL )
1944  {
1946  if ( nFoundFile != sizeof(treeData->foundFile) )
1947  {
1948  LocalFree(dFoundFile);
1949  }
1950  return false;
1951  }
1952  // Buffer is sure to be big enough now so we don't check return.
1953  _snprintf(dFoundFileLine, nFoundFileLine, "%s%s%s", treeData->fileTime, treeData->fileAttr, dFoundFile);
1954  }
1955 
1956  // Place found file line in the stem.
1957  RexxStringObject t = c->String(dFoundFileLine);
1958 
1959  treeData->count++;
1960  c->SetStemArrayElement(treeData->files, treeData->count, t);
1961  c->ReleaseLocalReference(t);
1962 
1963  if ( nFoundFile != sizeof(treeData->foundFile) )
1964  {
1965  LocalFree(dFoundFile);
1966  }
1967  if ( nFoundFileLine != sizeof(treeData->foundFileLine) )
1968  {
1969  LocalFree(dFoundFileLine);
1970  }
1971 
1972  return true;
1973 }
1974 
1975 /**
1976  * Finds all files matching a file specification, formats a file name line and
1977  * adds the formatted line to a stem. Much of the data to complete this
1978  * operation is contained in the treeData struct.
1979  *
1980  * This is a recursive function that may search through subdirectories if the
1981  * recurse option is used.
1982  *
1983  * @param c Call context we are operating in.
1984  *
1985  * @param path Current directory we are searching.
1986  *
1987  * @param treeData Struct containing data pertaining to the search, such as
1988  * the file specification we are searching for, the stem to
1989  * put the results in, etc..
1990  *
1991  * @param targetMask An array of integers which describe the source attribute
1992  * mask. Only files with attributes matching this mask will
1993  * be found.
1994  *
1995  * @param newMask An array of integers which describe the target attribute
1996  * mask. Attributes of all found files will be changed / set
1997  * to the values specified by this mask.
1998  * @param options
1999  *
2000  * @return uint32_t
2001  *
2002  * @remarks For both targetMask and newMask, each index of the mask corresponds
2003  * to a different file attribute. Each index and its associated
2004  * attribute are as follows:
2005  *
2006  * mask[0] = FILE_ARCHIVED
2007  * mask[1] = FILE_DIRECTORY
2008  * mask[2] = FILE_HIDDEN
2009  * mask[3] = FILE_READONLY
2010  * mask[4] = FILE_SYSTEM
2011  *
2012  * A negative value at a given index indicates that the attribute bit
2013  * of the file is not set. A positive number indicates that the
2014  * attribute should be set. A value of 0 indicates a "Don't Care"
2015  * setting.
2016  *
2017  * A close reading of MSDN seems to indicate that as long as we are
2018  * compiled for ANSI, which we are, that MAX_PATH is sufficiently
2019  * large. But, we will code for the possibility that it is not large
2020  * enough, by mallocing dynamic memory if _snprintf indicates a
2021  * failure.
2022  *
2023  * We point dTmpFileName at the static buffer and nTmpFileName is set
2024  * to the size of the buffer. If we have to allocate memory,
2025  * nTmpFileName will be set to the size we allocate and if
2026  * nTmpFileName does not equal what it is originally set to, we know
2027  * we have to free the allocated memory.
2028  */
2029 static bool recursiveFindFile(RexxCallContext *c, char *path, RXTREEDATA *treeData,
2030  int32_t *targetMask, int32_t *newMask, uint32_t options)
2031 {
2032  WIN32_FIND_DATA wfd;
2033  HANDLE fHandle;
2034  char tmpFileName[FNAMESPEC_BUF_LEN];
2035  char *dTmpFileName = tmpFileName; // Dynamic memory for tmpFileName, static memory to begin with.
2036  size_t nTmpFileName = FNAMESPEC_BUF_LEN; // CouNt of bytes in dTmpFileName.
2037  int32_t len;
2038  bool result = true;
2039 
2040  len = _snprintf(dTmpFileName, nTmpFileName, "%s%s", path, treeData->dFNameSpec);
2041  if ( len < 0 || len == nTmpFileName )
2042  {
2043  nTmpFileName = strlen(path) + strlen(treeData->dFNameSpec) + 1;
2044  dTmpFileName = (char *)LocalAlloc(LPTR, nTmpFileName);
2045  if ( dTmpFileName == NULL )
2046  {
2048  result = false;
2049  goto done_out;
2050  }
2051  // buffer is sure to be big enough now, so we don't check the return.
2052  _snprintf(dTmpFileName, nTmpFileName, "%s%s", path, treeData->dFNameSpec);
2053  }
2054 
2055  fHandle = FindFirstFile(dTmpFileName, &wfd);
2056  if ( fHandle != INVALID_HANDLE_VALUE )
2057  {
2058  do
2059  {
2060  // Skip dot directories
2061  if ( strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0 )
2062  {
2063  continue;
2064  }
2065 
2066  if ( sameAttr(targetMask, wfd.dwFileAttributes, options) )
2067  {
2068  if ( ! formatFile(c, path, treeData, newMask, options, &wfd) )
2069  {
2070  FindClose(fHandle);
2071  result = false;
2072  goto done_out;
2073  }
2074  }
2075  } while ( FindNextFile(fHandle, &wfd) );
2076 
2077  FindClose(fHandle);
2078  }
2079 
2080  if ( options & RECURSE )
2081  {
2082  // Build new target spec. Above, path + fileSpec fit into tmpFileName,
2083  // fileSpec is always longer than 1 character, so we are okay here.
2084  sprintf(dTmpFileName, "%s*", path);
2085 
2086  fHandle = FindFirstFile(dTmpFileName, &wfd);
2087  if ( fHandle != INVALID_HANDLE_VALUE )
2088  {
2089  do
2090  {
2091  // Skip non-directories and dot directories.
2092  if ( ! (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
2093  strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0 )
2094  {
2095  continue;
2096  }
2097 
2098  // Build the new directory file name.
2099  len = _snprintf(dTmpFileName, nTmpFileName, "%s%s\\", path, wfd.cFileName);
2100  if ( len < 0 || len == nTmpFileName )
2101  {
2102  // We may need to free dTmpFileName if it is now allocated
2103  // memory.
2104  if ( nTmpFileName != FNAMESPEC_BUF_LEN )
2105  {
2106  LocalFree(dTmpFileName);
2107  }
2108 
2109  nTmpFileName = strlen(path) + strlen(wfd.cFileName) + 2;
2110  dTmpFileName = (char *)LocalAlloc(LPTR, nTmpFileName);
2111  if ( dTmpFileName == NULL )
2112  {
2114  FindClose(fHandle);
2115  result = false;
2116  goto done_out;
2117  }
2118  // buffer is sure to be big enough now, so we don't check the
2119  // return.
2120  _snprintf(dTmpFileName, nTmpFileName, "%s%s\\", path, wfd.cFileName);
2121  }
2122 
2123  // Search the next level.
2124  if ( ! recursiveFindFile(c, tmpFileName, treeData, targetMask, newMask, options) )
2125  {
2126  FindClose(fHandle);
2127  result = false;
2128  goto done_out;
2129  }
2130  }
2131  while (FindNextFile(fHandle, &wfd));
2132 
2133  FindClose(fHandle);
2134  }
2135  }
2136 
2137 done_out:
2138 
2139  if ( nTmpFileName != FNAMESPEC_BUF_LEN )
2140  {
2141  safeLocalFree(dTmpFileName);
2142  }
2143  return result;
2144 }
2145 
2146 /**
2147  * This is a SysFileTree() specific function. It is only called, indirectly
2148  * through getPath(), from SysFileTree().
2149  *
2150  * This function mimics the old IBM code.
2151  *
2152  * Leading spaces are stripped, in some cases. A file specification of "." is
2153  * changed to "*.*" and a file specification of ".." is changed to "..\*.*"
2154  *
2155  * Leading spaces in fSpec are stripped IFF the first character(s) after the
2156  * leading spaces:
2157  *
2158  * is '\' or '/'
2159  * or
2160  * is '.\' or './'
2161  * or
2162  * is '..\' or '../'
2163  * or
2164  * is z: (i.e., a drive letter)
2165  *
2166  * @param fSpec The SysFileTree search specification
2167  *
2168  * @return A pointer to fSpec, possibly adjust to point to the first non-space
2169  * character in the string.
2170  *
2171  * @side effects: fSpec may be changed from "." to "*.*" or may be changed from
2172  * ".." to "..\*.*"
2173  *
2174  * @assumes: The buffer for fSpec is large enough for the possible changes.
2175  */
2176 static char *adjustFSpec(char *fSpec)
2177 {
2178  size_t i = 0;
2179 
2180  // Skip leading blanks.
2181  while ( fSpec[i] == ' ' )
2182  {
2183  i++;
2184  }
2185 
2186  if ( i > 0 )
2187  {
2188  size_t len = strlen(fSpec);
2189 
2190  // This series of if statements could be combined in to on huge if, but
2191  // this is easier to comprehend:
2192  if ( fSpec[i] == '\\' || fSpec[i] == '/' ) // The "\" case
2193  {
2194  fSpec = &fSpec[i];
2195  }
2196  else if ( fSpec[i] == '.' )
2197  {
2198  if ( i + 1 < len )
2199  {
2200  if ( fSpec[i + 1] == '\\' || fSpec[i + 1] == '/' ) // The ".\" case
2201  {
2202  fSpec = &fSpec[i];
2203  }
2204  else if ( i + 2 < len )
2205  {
2206  if ( fSpec[i + 1] == '.' &&
2207  (fSpec[i + 2] == '\\' || fSpec[i + 2] == '/') ) // The "..\" case
2208  {
2209  fSpec = &fSpec[i];
2210  }
2211  }
2212  }
2213  }
2214  else if ( i + 1 < len && fSpec[i + 1] == ':' ) // The "z:' case
2215  {
2216  fSpec = &fSpec[i];
2217  }
2218  }
2219 
2220  if ( strcmp(fSpec, ".") == 0 )
2221  {
2222  // If fSpec is exactly "." then change it to "*.*"
2223  strcpy(fSpec, "*.*");
2224  }
2225  else if ( strcmp(fSpec, "..") == 0 )
2226  {
2227  // Else if fSpec is exactly ".." then change it to "..\*.*"
2228  strcpy(fSpec, "..\\*.*");
2229  }
2230 
2231  return fSpec;
2232 }
2233 
2234 static bool safeGetCurrentDirectory(RexxCallContext *c, char **pPath, size_t *pPathLen)
2235 {
2236  size_t pathLen = *pPathLen;
2237  char *path = *pPath;
2238 
2239  // Get the current directory. First check that the path buffer is large
2240  // enough.
2241  uint32_t ret = GetCurrentDirectory(0, 0);
2242  if ( ret == 0 )
2243  {
2244  systemServiceExceptionCode(c->threadContext, "GetCurrentDirectory", GetLastError());
2245  return false;
2246  }
2247 
2248  // we might need to add a trailing backslash here, so make sure we leave enough room
2249  ret += FNAMESPEC_BUF_EXTRA;
2250 
2251  path = (char *)LocalAlloc(LPTR, ret);
2252  if ( path == NULL )
2253  {
2255  return false;
2256  }
2257 
2258  // Fix up our input path / pathLen variables now. The input path buffer
2259  // is allocated memory, so we need to free it.
2260  LocalFree(*pPath);
2261  *pPath = path;
2262  *pPathLen = ret;
2263 
2264  ret = GetCurrentDirectory(ret, path);
2265  if ( ret == 0 )
2266  {
2267  systemServiceExceptionCode(c->threadContext, "GetCurrentDirectory", GetLastError());
2268  return false;
2269  }
2270 
2271  return true;
2272 }
2273 
2274 static bool expandNonPath2fullPath(RexxCallContext *c, char *fSpec, char **pPath, size_t *pPathLen, int *lastSlashPos)
2275 {
2276  char *buf = NULL; // used to save current dir
2277  char drv[3] = {0}; // used to change current drive
2278  uint32_t ret = 0;
2279  bool result = false;
2280 
2281  // fSpec could be a drive designator.
2282  if ( fSpec[1] == ':' )
2283  {
2284  // Save the current drive and path, first get needed buffer size.
2285  ret = GetCurrentDirectory(0, 0);
2286  if ( ret == 0 )
2287  {
2288  systemServiceExceptionCode(c->threadContext, "GetCurrentDirectory", ret);
2289  goto done_out;
2290  }
2291 
2292  buf = (char *)LocalAlloc(LPTR, ret);
2293  if ( buf == NULL )
2294  {
2296  goto done_out;
2297  }
2298 
2299  ret = GetCurrentDirectory(ret, buf);
2300  if ( ret == 0 )
2301  {
2302  systemServiceExceptionCode(c->threadContext, "GetCurrentDirectory", ret);
2303  goto done_out;
2304  }
2305 
2306  // Just copy the drive letter and the colon, omit the rest. This is
2307  // necessary e.g. for something like "I:*"
2308  memcpy(drv, fSpec, 2);
2309 
2310  // Change to the specified drive, get the current directory, then go
2311  // back to where we came from.
2312  SetCurrentDirectory(drv);
2313  bool success = safeGetCurrentDirectory(c, pPath, pPathLen);
2314 
2315  SetCurrentDirectory(buf);
2316  LocalFree(buf);
2317  buf = NULL;
2318 
2319  if ( ! success )
2320  {
2321  systemServiceExceptionCode(c->threadContext, "GetCurrentDirectory", ret);
2322  goto done_out;
2323  }
2324 
2325  // make drive the path
2326  *lastSlashPos = 1;
2327  }
2328  else
2329  {
2330  // No drive designator, just get the current directory.
2331  if ( ! safeGetCurrentDirectory(c, pPath, pPathLen) )
2332  {
2333  goto done_out;
2334  }
2335  }
2336 
2337  // If we need a trailing slash, add one.
2338  char *path = *pPath;
2339  if ( path[strlen(path) - 1] != '\\' )
2340  {
2341  strcat(path, "\\");
2342  }
2343 
2344  result = true;
2345 
2346 done_out:
2347  safeLocalFree(buf);
2348  return result;
2349 }
2350 
2351 
2352 /**
2353  * Splits the path portion off from fSpec and returns it in the path buffer.
2354  *
2355  * When this function is called, there is always at least one slash in fSpec.
2356  *
2357  * @param c
2358  * @param fSpec
2359  * @param lastSlashPos
2360  * @param pPath
2361  * @param pPathLen
2362  *
2363  * @return bool
2364  *
2365  * @remarks The size of the path buffer is guarenteed to be at least the string
2366  * length of fSpec + FNAMESPEC_BUF_EXTRA (8) in size. Or MAX (264)
2367  * bytes in size. Whichever is larger. So path is big enough to
2368  * contain all of fSpec + 7 characters.
2369  *
2370  * We may have to enlarge the passed in path buffer. If we do, we
2371  * need to be sure and update the path buffer pointer and the path
2372  * length. As long as we keep pPath and pPathLen correct, the caller
2373  * will take care of freeing any memory.
2374  *
2375  * But if we do change pPath, we need to free the buffer it was
2376  * pointing to.
2377  */
2378 static bool expandPath2fullPath(RexxCallContext *c, char *fSpec, size_t lastSlashPos, char **pPath, size_t *pPathLen)
2379 {
2380  size_t l = 0; // Used to calculate lengths of strings.
2381 
2382  char *path = *pPath;
2383  size_t pathLen = *pPathLen;
2384 
2385  // If fSpec starts with a drive designator, then we have a full path. Copy
2386  // over the path portion, including the last slash, and null terminate it.
2387  if (fSpec[1] == ':')
2388  {
2389  l = lastSlashPos + 1;
2390  memcpy(path, fSpec, l);
2391  path[l] = '\0';
2392  }
2393  else
2394  {
2395  char fpath[MAX_PATH];
2396  char drive[_MAX_DRIVE];
2397  char dir[_MAX_DIR];
2398  char fname[_MAX_FNAME];
2399  char ext[_MAX_EXT];
2400  char lastChar;
2401 
2402  // fpath is the only buffer we need to worry about being too small.
2403  // Although, when compiled for ANSI, which we are, I really think it is
2404  // impossible for MAX_PATH to be too small.
2405  char *dFPath = fpath;
2406  size_t nFPath = MAX_PATH;
2407 
2408  if ( lastSlashPos == 0 )
2409  {
2410  // Only 1 slash at the beginning, get the full path.
2411  _fullpath(dFPath, "\\", nFPath);
2412 
2413  l = strlen(dFPath) + strlen(&fSpec[1]) + 1;
2414  if ( l > nFPath )
2415  {
2416  if ( ! getBiggerBuffer(c, &dFPath, &nFPath, l, nFPath) )
2417  {
2418  return false;
2419  }
2420  }
2421 
2422  strcat(dFPath, &fSpec[1]);
2423  }
2424  else
2425  {
2426  // Chop off the path part by putting a null at the last slash, get
2427  // the full path, and then put the slash back.
2428  fSpec[lastSlashPos] = '\0';
2429  if ( _fullpath(dFPath, fSpec, nFPath) == NULL )
2430  {
2431  // This will double the buffer until either _fullpath()
2432  // succeeds, or we run out of memory. If we go through the loop
2433  // more than once, and fail, we need to free memory allocated
2434  // for dFPath. We fix fSpec on failure, but that is not really
2435  // needed, the caller(s) will just quit on failure of this
2436  // function.
2437  do
2438  {
2439  if ( ! getBiggerBuffer(c, &dFPath, &nFPath, l, MAX_PATH) )
2440  {
2441  if ( nFPath != MAX_PATH )
2442  {
2443  LocalFree(dFPath);
2444  }
2445 
2446  fSpec[lastSlashPos] = '\\';
2447  return false;
2448  }
2449  } while ( _fullpath(dFPath, fSpec, nFPath) == NULL );
2450  }
2451 
2452  fSpec[lastSlashPos] = '\\';
2453 
2454  lastChar = dFPath[strlen(dFPath) - 1];
2455  if (lastChar != '\\' && lastChar != '/')
2456  {
2457  l = strlen(dFPath) + strlen(&fSpec[lastSlashPos]) + 1;
2458  if ( l > nFPath )
2459  {
2460  if ( ! getBiggerBuffer(c, &dFPath, &nFPath, l, MAX_PATH) )
2461  {
2462  // If dFPath was allocated, free it.
2463  if ( nFPath != MAX_PATH )
2464  {
2465  LocalFree(dFPath);
2466  }
2467  return false;
2468  }
2469  }
2470 
2471  strcat(dFPath, &fSpec[lastSlashPos]);
2472  }
2473  }
2474 
2475  _splitpath(dFPath, drive, dir, fname, ext);
2476 
2477  l = strlen(drive) + strlen(dir) + 1;
2478  if ( l > pathLen )
2479  {
2480  if ( ! getBiggerBuffer(c, &path, &pathLen, l, pathLen) )
2481  {
2482  return false;
2483  }
2484 
2485  LocalFree(*pPath);
2486  *pPath = path;
2487  *pPathLen = pathLen;
2488  }
2489 
2490  strcpy(path, drive);
2491  strcat(path, dir);
2492 
2493  // If path is invalid, (the empty string,) for some reason, copy the
2494  // path from fSpec. That is from the start of the string up through the
2495  // last slash. Then zero terminate it. The path buffer is guaranteed
2496  // big enough for this, see the remarks.
2497  if ( strlen(path) == 0 )
2498  {
2499  memcpy(path, fSpec, lastSlashPos + 1);
2500  path[lastSlashPos + 1] = '\0';
2501  }
2502 
2503  // If we need a trailing slash, add it. Again, the path buffer is
2504  // guaranteed to be big enough.
2505  if (path[strlen(path) - 1] != '\\')
2506  {
2507  strcat(path, "\\");
2508  }
2509  }
2510 
2511  return true;
2512 }
2513 
2514 /**
2515  * This is a SysFileTree() specific function..
2516  *
2517  * This function expands the file spec passed in to the funcition into its full
2518  * path name. The full path name is then split into the path portion and the
2519  * file name portion. The path portion is then returned in path and the file
2520  * name portion is returned in fileName.
2521  *
2522  * The path portion will end with the '\' char if fSpec contains a path.
2523  *
2524  * @param fSpec
2525  * @param path Pointer to path buffer. Path buffer is allocated memory,
2526  * not a static buffer.
2527  * @param filename
2528  * @param pathLen Pointer to size of the path buffer.
2529  *
2530  * @remarks On entry, the buffer pointed to by fSpec is guaranteed to be at
2531  * least strlen(fSpec) + FNAMESPEC_BUF_EXTRA (8). So, we can strcat
2532  * to it at least 7 characters and still have it null terminated.
2533  *
2534  * In addition, the path buffer is guarenteed to be at least that size
2535  * also.
2536  */
2537 static bool getPath(RexxCallContext *c, char *fSpec, char **path, char *filename, size_t *pathLen)
2538 {
2539  size_t len; // length of filespec
2540  int lastSlashPos; // position of last slash
2541 
2542  fSpec = adjustFSpec(fSpec);
2543 
2544  // Find the position of the last slash in fSpec
2545  len = strlen(fSpec);
2546 
2547  // Get the maximum position of the last '\'
2548  lastSlashPos = (int)len;
2549 
2550  // Step back through fSpec until at its beginning or at a '\' or '/' character
2551  while ( fSpec[lastSlashPos] != '\\' && fSpec[lastSlashPos] != '/' && lastSlashPos >= 0 )
2552  {
2553  --lastSlashPos;
2554  }
2555 
2556  // If lastSlashPos is less than 0, then there is no backslash present in
2557  // fSpec.
2558  if ( lastSlashPos < 0 )
2559  {
2560  if ( ! expandNonPath2fullPath(c, fSpec, path, pathLen, &lastSlashPos) )
2561  {
2562  return false;
2563  }
2564  }
2565  else
2566  {
2567  if ( ! expandPath2fullPath(c, fSpec, lastSlashPos, path, pathLen) )
2568  {
2569  return false;
2570  }
2571  }
2572 
2573  // Get the file name from fSpec, the portion just after the last '\'
2574  if ( fSpec[lastSlashPos + 1] != '\0' )
2575  {
2576  // The position after the last slash is not the null terminator so there
2577  // is something to copy over to the file name segment.
2578  strcpy(filename, &fSpec[lastSlashPos + 1]);
2579  }
2580  else
2581  {
2582  // The last slash is the last character in fSpec, just use wildcards for
2583  // the file name segment.
2584  strcpy(filename, "*.*");
2585  }
2586 
2587  return true;
2588 }
2589 
2590 /**
2591  * This is a SysFileTree specific function.
2592  *
2593  * Determines the options by converting the character based argument to the
2594  * correct set of flags.
2595  *
2596  * @param c
2597  * @param opts
2598  * @param pOpts
2599  *
2600  * @return bool
2601  */
2602 static bool goodOpts(RexxCallContext *c, char *opts, uint32_t *pOpts)
2603 {
2604  uint32_t options = *pOpts;
2605 
2606  while ( *opts )
2607  {
2608  switch( toupper(*opts) )
2609  {
2610  case 'S': // recurse into subdirectories
2611  options |= RECURSE;
2612  break;
2613 
2614  case 'O': // only return names
2615  options |= NAME_ONLY;
2616  break;
2617 
2618  case 'T': // use short time format, ignored if L is used
2619  options |= EDITABLE_TIME;
2620  break;
2621 
2622  case 'L': // use long time format
2623  options |= LONG_TIME;
2624  break;
2625 
2626  case 'F': // include only files
2627  options &= ~DO_DIRS;
2628  options |= DO_FILES;
2629  break;
2630 
2631  case 'D': // include only directories
2632  options |= DO_DIRS;
2633  options &= ~DO_FILES;
2634  break;
2635 
2636  case 'B': // include both files and directories
2637  options |= DO_DIRS;
2638  options |= DO_FILES;
2639  break;
2640 
2641  case 'I': // case insensitive? no op on Windows
2642  break;
2643 
2644  default: // error, unknown option
2645  return false;
2646  }
2647  opts++;
2648  }
2649 
2650  *pOpts = options;
2651  return true;
2652 }
2653 
2654 /**
2655  * This is a SysFileTree() specific helper function.
2656  *
2657  * Set a mask of unsigned ints to what is specified by a mask of chars.
2658  *
2659  * @param c
2660  * @param msk
2661  * @param mask
2662  *
2663  * @return True on success, false on error.
2664  *
2665  * @remarks If a character in position N is a '+' then the unsigned int at
2666  * position N is set to 1. This is turning it on.
2667  *
2668  * If a character in position N is a '-' then the unsigned int at
2669  * position N is set to -1. This is turning it off.
2670  *
2671  * If a character in position N is a '*' then the unsigned int at
2672  * position N is set to 0. This is saying ignore it, it doesn't
2673  * matter what the attribute is.
2674  */
2675 static bool goodMask(RexxCallContext *c, char *msk, int32_t *mask)
2676 {
2677  uint32_t y = 0;
2678 
2679  while (*msk)
2680  {
2681  if ( *msk == '+' )
2682  {
2683  mask[y] = 1;
2684  }
2685  else if ( *msk == '-' )
2686  {
2687  mask[y] = -1;
2688  }
2689  else if (*msk == '*')
2690  {
2691  mask[y] = 0;
2692  }
2693  else
2694  {
2695  return false;
2696  }
2697 
2698  y++;
2699  msk++;
2700  }
2701 
2702  return true;
2703 }
2704 
2705 /**
2706  * This is a SysFileTree specific helper function.
2707  *
2708  * Checks the validity of an attribute mask argument and converts the character
2709  * based mask into an integer based mask.
2710  *
2711  * @param context
2712  * @param msk
2713  * @param mask
2714  * @param argPos
2715  *
2716  * @return bool
2717  */
2718 static bool getMaskFromArg(RexxCallContext *context, char *msk, int32_t *mask, size_t argPos)
2719 {
2720  if ( argumentExists(argPos) && strlen(msk) > 0 )
2721  {
2722  if ( strlen(msk) > 5 )
2723  {
2724  badMaskException(context->threadContext, argPos, msk);
2725  return false;
2726  }
2727 
2728  if ( ! goodMask(context, msk, mask) )
2729  {
2730  badMaskException(context->threadContext, argPos, msk);
2731  return false;
2732  }
2733  }
2734  else
2735  {
2736  mask[0] = RXIGNORE;
2737  }
2738 
2739  return true;
2740 }
2741 
2742 /**
2743  * This is a SysFileTree specific helper function.
2744  *
2745  * Checks the validity of the options argument to SysFileTree and converts the
2746  * character based argument to the proper set of flags.
2747  *
2748  * @param context
2749  * @param opts
2750  * @param options
2751  * @param argPos
2752  *
2753  * @return bool
2754  */
2755 static bool getOptionsFromArg(RexxCallContext *context, char *opts, uint32_t *options, size_t argPos)
2756 {
2757  *options = DO_FILES | DO_DIRS;
2758 
2759  if ( argumentExists(argPos) )
2760  {
2761  if ( strlen(opts) == 0 )
2762  {
2763  nullStringException(context->threadContext, "SysFileTree", argPos);
2764  return false;
2765  }
2766 
2767  if ( ! goodOpts(context, opts, options) )
2768  {
2769  badSFTOptsException(context->threadContext, argPos, opts);
2770  return false;
2771  }
2772  }
2773 
2774  return true;
2775 }
2776 
2777 /**
2778  * This is a SysFileTree specific helper function.
2779  *
2780  * Allocates and returns a buffer containing the file specification to search
2781  * for.
2782  *
2783  * The file specification consists of the search string as sent by the Rexx
2784  * user, with possibly some glob characters added. The returned buffer is
2785  * bigger than the original string to accommodate these, possible, added
2786  * characters. The number of bytes added to the buffer is 8, which is what the
2787  * original IBM code used. 8 is probably 1 byte more than needed, but there is
2788  * no reason that this needs to be exact, better too long than too short.
2789  *
2790  * If the file speicfication ends in a slash ('\') or a period ('.') or two
2791  * periods ('..'), then a wild card specification ('*.*') is appended.
2792  *
2793  * However, note that there is also the case where a single '.' at the end of
2794  * the file specification is not used as a directory specifier, but rather is
2795  * tacked on to the end of a file name.
2796  *
2797  * Windows has a sometimes used convention that a '.' at the end of a file name
2798  * can be used to indicate the file has no extension. For example, given a file
2799  * named: MyFile a command of "dir MyFile." will produce a listing of "MyFile".
2800  *
2801  * In this case we want to leave the file specification alone. that is, do not
2802  * append a "*.*". A command of "dir *." will produce a directory listing of all
2803  * files that do not have an extension.
2804  *
2805  * @param context
2806  * @param fSpec
2807  * @param fSpecLen [returned] The length of the original fSpec argument,
2808  * not the length of the allocated buffer.
2809  * @param fSpecBufLen [returned] The length of the length of the allocated
2810  * fSpec buffer.
2811  * @param argPos
2812  *
2813  * @return A string specifying the file pattern to search for. The buffer
2814  * holding the string is larger than the original input specify.
2815  *
2816  * @remarks Caller is responsible for freeing memory. Memory is allocated
2817  * using LocalAlloc(), not malloc().
2818  *
2819  * If the returned buffer is null, a condition has been raised.
2820  *
2821  * FNAMESPEC_BUF_EXTRA (8) is sized to contain the terminating NULL.
2822  * So the allocated buffer has room to concatenate 7 characters.
2823  */
2824 static char *getFileSpecFromArg(RexxCallContext *context, CSTRING fSpec, size_t *fSpecLen,
2825  size_t *fSpecBufLen, size_t argPos)
2826 {
2827  size_t len = strlen(fSpec);
2828  if ( len == 0 )
2829  {
2830  nullStringException(context->threadContext, "SysFileTree", argPos);
2831  return NULL;
2832  }
2833 
2834  char *fileSpec = (char *)LocalAlloc(LPTR, len + FNAMESPEC_BUF_EXTRA);
2835  if ( fileSpec == NULL )
2836  {
2838  return NULL;
2839  }
2840 
2841  // Allocated buffer is zero filled (LPTR flag) already, no need to zero
2842  // terminate.
2843  memcpy(fileSpec, fSpec, len);
2844 
2845  if ( fileSpec[len - 1] == '\\' )
2846  {
2847  strcat(fileSpec, "*.*");
2848  }
2849  else if ( fileSpec[len - 1] == '.')
2850  {
2851  if ( len == 1 ||
2852  (len > 1 && (fileSpec[len - 2] == '\\' || fileSpec[len - 2] == '.')) )
2853  {
2854  strcat(fileSpec, "\\*.*");
2855  }
2856  }
2857 
2858  *fSpecLen = len;
2859  *fSpecBufLen = len + FNAMESPEC_BUF_EXTRA;
2860  return fileSpec;
2861 }
2862 
2863 /**
2864  * This is a SysFileTree specific helper function.
2865  *
2866  * Allocates and returns a buffer large enough to contain the path to search
2867  * along.
2868  *
2869  * We need a minimum size for the path buffer of at least MAX (264). But the
2870  * old code seemed to think fileSpecLen + FNAMESPEC_BUF_EXTRA could be longer
2871  * than that. I guess it could if the user put in a non-existent long file
2872  * path.
2873  *
2874  * The old code of checking fSpecLen is still used, but I'm unsure of its exact
2875  * purpose.
2876  *
2877  * @param context
2878  * @param fSpecLen
2879  * @param pathLen
2880  *
2881  * @return A buffer the larger of MAX or fSpecLen + FNAMESPEC_BUF_EXTRA bytes in
2882  * size. Returns NULL on failure.
2883  *
2884  * @remarks The caller is resposible for freeing the allocated memory.
2885  *
2886  * LocalAlloc(), not malloc() is used for memory allocation.
2887  *
2888  * Note that the path buffer is guarenteed to be FNAMESPEC_BUF_EXTRA
2889  * (8) bytes larger than the fNameSpec buffer in the caller. This is
2890  * important in later checks for buffer overflow.
2891  */
2892 static char *getPathBuffer(RexxCallContext *context, size_t fSpecLen, size_t *pathLen)
2893 {
2894  size_t bufLen = MAX;
2895 
2896  if ( fSpecLen + FNAMESPEC_BUF_EXTRA > MAX )
2897  {
2898  bufLen = fSpecLen + FNAMESPEC_BUF_EXTRA;
2899  }
2900 
2901  *pathLen = bufLen;
2902 
2903  char *path = (char *)LocalAlloc(LPTR, bufLen);
2904  if ( path == NULL )
2905  {
2907  }
2908 
2909  return path;
2910 }
2911 
2912 /**
2913  * Tests for illegal file name characters in fSpec.
2914  *
2915  * @param fSpec
2916  *
2917  * @return bool
2918  *
2919  * @note Double quotes in the file spec is not valid, spaces in file names do
2920  * not need to be within quotes in the string passed to the Windows API.
2921  *
2922  * A ':' is only valid if it is the second character. Technically a '*'
2923  * and a '?' are not valid characters for a file name, but they are okay
2924  * for fSpec. Same thing for '\' and '/', they are not valid in a file
2925  * name, but they are valid in fSpec. A '/' is iffy. The Windows API
2926  * accepts '/' as a directory separator, but most Rexx programmers
2927  * probably don't know that. Not sure if we should flag that as illegal
2928  * or not.
2929  */
2930 static bool illegalFileNameChars(char * fSpec)
2931 {
2932  static char illegal[] = "<>|\"";
2933 
2934  for ( size_t i = 0; i < 4; i++ )
2935  {
2936  if ( strchr(fSpec, illegal[i]) != NULL )
2937  {
2938  return true;
2939  }
2940  }
2941 
2942  char *pos = strchr(fSpec, ':');
2943  if ( pos != NULL )
2944  {
2945  if ( ((int32_t)(pos - fSpec + 1)) != 2 )
2946  {
2947  return true;
2948  }
2949  if ( strchr(pos + 1, ':') != NULL )
2950  {
2951  return true;
2952  }
2953  }
2954 
2955  return false;
2956 }
2957 /**
2958  * SysFileTree() implementation. Searches for files in a directory tree
2959  * matching the specified search pattern.
2960  *
2961  * @param fSpec [required] The search pattern, may contain glob characters
2962  * and full or partial path informattion. E.g., *.bat, or
2963  * ../../*.txt, or C:\temp. May not contain illegal file name
2964  * characters which are: ", <, >, |, and : The semicolon is
2965  * only legal if it is exactly the second character. Do not use
2966  * a double quote in fSpec, it is not needed and is taken as a
2967  * character in a file name, which is an illegal character.
2968  *
2969  * @param files [required] A stem to contain the returned results. On return,
2970  * files.0 contains the count N of found files and files.1
2971  * through files.N will contain the found files.
2972  *
2973  * @param opts [optional] Any combination of the following letters that
2974  * specify how the search takes place, or how the returned found
2975  * file line is formatted. Case is not significant:
2976  *
2977  * 'B' - Search for files and directories.
2978  * 'D' - Search for directories only.
2979  * 'F' - Search for files only.
2980  * 'O' - Only output file names.
2981  * 'S' - Recursively scan subdirectories.
2982  * 'T' - Combine time & date fields into one.
2983  * 'L' - Long time format
2984  * 'I' - Case Insensitive search.
2985  *
2986  * The defualt is 'B' using normal time (neither 'T' nor 'L'.)
2987  * The 'I'option is meaningless on Windows.
2988  *
2989  * @param targetAttr [optional] Target attribute mask. Only files with these
2990  * attributes will be searched for. The default is to ignore
2991  * the attributes of the files found, so all files found are
2992  * returned.
2993  *
2994  * @param newAttr [optional] New attribute mask. Each found file will have
2995  * its attributes set (changed) to match this mask. The
2996  * default is to not change any attributes.
2997  *
2998  * @return 0 on success, non-zero on error. For all errors, a condition is
2999  * raised.
3000  *
3001  * @remarks The original IBM code passed in fileSpec to recursiveFindFile(),
3002  * but then never used it in recursiveFineFile. So, that has been
3003  * eliminated.
3004  *
3005  */
3006 RexxRoutine5(uint32_t, SysFileTree, CSTRING, fSpec, RexxStemObject, files, OPTIONAL_CSTRING, opts,
3007  OPTIONAL_CSTRING, targetAttr, OPTIONAL_CSTRING, newAttr)
3008 {
3009  uint32_t result = 1; // Return value, 1 is an error.
3010  char *fileSpec = NULL; // File spec to search for.
3011  size_t fSpecLen = 0; // Length of the original fSpec string.
3012  size_t fSpecBufLen = 0; // Length of the allocated fSpec buffer.
3013  char *path = NULL; // Path to search along.
3014  size_t pathLen = 0; // Size of buffer holding path.
3015  RXTREEDATA treeData = {0}; // Struct for data.
3016 
3017  context->SetStemArrayElement(files, 0, context->WholeNumber(0));
3018 
3019  treeData.files = files;
3020  treeData.dFNameSpec = treeData.fNameSpec;
3021  treeData.nFNameSpec = FNAMESPEC_BUF_LEN;
3022 
3023  fileSpec = getFileSpecFromArg(context, fSpec, &fSpecLen, &fSpecBufLen, 1);
3024  if ( fileSpec == NULL )
3025  {
3026  goto done_out;
3027  }
3028 
3029  if ( illegalFileNameChars((char *)fSpec) )
3030  {
3031  result = ERROR_INVALID_NAME;
3032  goto done_out;
3033  }
3034 
3035  // Some, or all, of fileSpec will eventually be copied into
3036  // treeData.dFNameSpec. So, if we ensure that treeData.dFNameSpec is big
3037  // enough to hold fileSpec we do not need to worry about it any more.
3038  if ( fSpecBufLen >= FNAMESPEC_BUF_LEN )
3039  {
3040  if ( ! getBiggerBuffer(context, &treeData.dFNameSpec, &treeData.nFNameSpec, fSpecBufLen + 1, FNAMESPEC_BUF_LEN) )
3041  {
3042  goto done_out;
3043  }
3044  }
3045 
3046  path = getPathBuffer(context, fSpecLen, &pathLen);
3047  if ( path == NULL )
3048  {
3049  goto done_out;
3050  }
3051 
3052  uint32_t options = 0;
3053  if ( ! getOptionsFromArg(context, (char *)opts, &options, 3) )
3054  {
3055  goto done_out;
3056  }
3057 
3058  int32_t targetMask[5] = {0}; // Attribute mask of files to search for.
3059  int32_t newMask[5] = {0}; // Attribute mask to set found files to.
3060 
3061  if ( ! getMaskFromArg(context, (char *)targetAttr, targetMask, 4) )
3062  {
3063  goto done_out;
3064  }
3065 
3066  if ( ! getMaskFromArg(context, (char *)newAttr, newMask, 5) )
3067  {
3068  goto done_out;
3069  }
3070 
3071  // Get the full path segment and the file name segment by expanding the
3072  // file specification string. It seems highly unlikely, but possible, that
3073  // this could fail.
3074  if ( ! getPath(context, fileSpec, &path, treeData.dFNameSpec, &pathLen) )
3075  {
3076  goto done_out;
3077  }
3078 
3079  if ( recursiveFindFile(context, path, &treeData, targetMask, newMask, options) )
3080  {
3081  context->SetStemArrayElement(treeData.files, 0, context->WholeNumber(treeData.count));
3082  result = 0;
3083  }
3084 
3085 done_out:
3086  safeLocalFree(fileSpec);
3087  safeLocalFree(path);
3088  if ( treeData.nFNameSpec != FNAMESPEC_BUF_LEN )
3089  {
3090  LocalFree(treeData.dFNameSpec);
3091  }
3092  return result;
3093 }
3094 
3095 
3096 /*************************************************************************
3097 * Function: SysGetKey *
3098 * *
3099 * Syntax: call SysGetKey [echo] *
3100 * *
3101 * Params: echo - Either of the following: *
3102 * 'ECHO' - Echo the inputted key (default). *
3103 * 'NOECHO' - Do not echo the inputted key. *
3104 * *
3105 * Return: The key striked. *
3106 *************************************************************************/
3107 
3108 size_t RexxEntry SysGetKey(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3109 {
3110  INT tmp; /* Temp var used to hold */
3111  /* keystroke value */
3112  bool echo = true; /* Set to false if we */
3113  /* shouldn't echo */
3114 
3115  if (numargs > 1) /* too many arguments */
3116  return INVALID_ROUTINE; /* raise an error */
3117 
3118  if (numargs == 1) { /* validate arguments */
3119  if (!_stricmp(args[0].strptr, "NOECHO"))
3120  echo = false;
3121  else if (_stricmp(args[0].strptr, "ECHO"))
3122  return INVALID_ROUTINE; /* Invalid option */
3123  }
3124  if (ExtendedFlag) { /* if have an extended */
3125  tmp = ExtendedChar; /* get the second char */
3126  ExtendedFlag = false; /* do a real read next time */
3127  }
3128  else {
3129  tmp = _getch(); /* read a character */
3130 
3131  /* If a function key or arrow */
3132  if ((tmp == 0x00) || (tmp == 0xe0)) {
3133  ExtendedChar = _getch(); /* Read another character */
3134  ExtendedFlag = true;
3135  }
3136  else
3137  ExtendedFlag = false;
3138  }
3139  if (echo) /* echoing? */
3140  _putch(tmp); /* write the character back */
3141 
3142  wsprintf(retstr->strptr, "%c", tmp);
3143  retstr->strlength = 1;
3144 
3145  return VALID_ROUTINE; /* no error on call */
3146 }
3147 
3148 /*************************************************************************
3149 * Function: SysIni *
3150 * *
3151 * Syntax: call SysIni [inifile], app [,key/stem] [,val/stem] *
3152 * *
3153 * Params: inifile - INI file from which to query or write info. The *
3154 * default is the current user INI file. *
3155 * app - Application title to work with. May be either *
3156 * of the following: *
3157 * 'ALL:' - All app titles will be returned in the *
3158 * stem variable specified by the next *
3159 * parameter. *
3160 * other - Specific app to work with. *
3161 * key - Key to work with. May be any of the following: *
3162 * 'ALL:' - All key titles will be returned in *
3163 * the stem variable specified by the *
3164 * next parameter. *
3165 * 'DELETE:' - All keys associated with the app *
3166 * will be deleted. *
3167 * other - Specific key to work with. *
3168 * val - Key to work with. May be either of the following: *
3169 * 'DELETE:' - Delete app/key pair. *
3170 * other - Set app/key pair info to data spec- *
3171 * ified. *
3172 * stem - Name of stem variable in which to store results. *
3173 * Stem.0 = Number found (n). *
3174 * Stem.1 - Stem.n = Entries found. *
3175 * *
3176 * Return: other - Info queried from specific app/key pair. *
3177 * '' - Info queried and placed in stem or data *
3178 * deleted successfully. *
3179 * ERROR_NOMEM - Out of memory. *
3180 * ERROR_RETSTR - Error opening INI or querying/writing info.*
3181 *************************************************************************/
3182 
3183 size_t RexxEntry SysIni(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3184 {
3185  size_t x; /* Temp counter */
3186  size_t len; /* Len var used when creating */
3187  /* stem */
3188  size_t lSize; /* Size of queried info buffer*/
3189  /* area */
3190  LONG Error = FALSE; /* Set to true if error */
3191  /* encountered */
3192  bool WildCard = false; /* Set to true if a wildcard */
3193  /* operation */
3194  bool QueryApps; /* Set to true if a query */
3195  /* operation */
3196  bool terminate = true; /* perform WinTerminate call */
3197  RXSTEMDATA ldp; /* local data */
3198  size_t buffersize; /* return buffer size */
3199 
3200 
3201  buffersize = retstr->strlength; /* save default buffer size */
3202  retstr->strlength = 0; /* set return value */
3203  const char *Key = "";
3204  /* validate arguments */
3205  if (numargs < 2 || numargs > 4 || !RXVALIDSTRING(args[1]))
3206  {
3207  return INVALID_ROUTINE;
3208  }
3209  /* get pointers to args */
3210  const char *IniFile = args[0].strptr;
3211  if (!RXVALIDSTRING(args[0])) /* not specified? */
3212  IniFile = "WIN.INI"; /* default to WIN.INI */
3213  const char *App = args[1].strptr;
3214 
3215  if (numargs >= 3 && args[2].strptr)
3216  Key = args[2].strptr;
3217 
3218  const char *Val = NULL;
3219  if (numargs == 4)
3220  Val = args[3].strptr;
3221  /* Check KEY and APP values */
3222  /* for "WildCard" */
3223  if (!_stricmp(App, "ALL:"))
3224  {
3225  App = "";
3226  QueryApps = true;
3227  WildCard = true;
3228 
3229  if (numargs != 3)
3230  return INVALID_ROUTINE; /* Error - Not enough args */
3231  else
3232  x = 2; /* Arg number of STEM variable*/
3233  }
3234 
3235  else if (!_stricmp(Key, "ALL:"))
3236  {
3237  Key = "";
3238  Val = "";
3239  QueryApps = false;
3240  WildCard = true;
3241 
3242  if (numargs != 4)
3243  return INVALID_ROUTINE; /* Error - Not enough args */
3244 
3245  else
3246  x = 3; /* Arg number of STEM variable*/
3247  }
3248  /* If this is a "WildCard */
3249  /* search, then allocate mem */
3250  /* for stem struct and get the*/
3251  /* stem name */
3252  if (WildCard == true)
3253  {
3254 
3255  ldp.count = 0; /* get the stem variable name */
3256  strcpy(ldp.varname, args[x].strptr);
3257  ldp.stemlen = args[x].strlength;
3258  /* uppercase the name */
3259  memupper(ldp.varname, strlen(ldp.varname));
3260 
3261  if (ldp.varname[ldp.stemlen-1] != '.')
3262  ldp.varname[ldp.stemlen++] = '.';
3263  }
3264 
3265  char *returnVal = NULL;
3266  /* get value if is a query */
3267  if ((numargs == 3 && _stricmp(Key, "DELETE:")) ||
3268  WildCard == true)
3269  {
3270  lSize = 0x0000ffffL;
3271  /* Allocate a large buffer */
3272  returnVal = (char *)GlobalAlloc(GPTR, lSize);
3273  if (returnVal == NULL)
3274  {
3275  BUILDRXSTRING(retstr, ERROR_NOMEM);
3276  return VALID_ROUTINE;
3277  }
3278 
3279  if (WildCard && QueryApps)
3280  /* Retrieve the names of all */
3281  /* applications. */
3282  lSize = GetPrivateProfileString(NULL, NULL, "", returnVal, (DWORD)lSize, IniFile);
3283  else if (WildCard && !QueryApps)
3284  /* Retrieve all keys for an */
3285  /* application */
3286  lSize = GetPrivateProfileString(App, NULL, "", returnVal, (DWORD)lSize, IniFile);
3287  else
3288  /* Retrieve a single key value*/
3289  lSize = GetPrivateProfileString(App, Key, "", returnVal, (DWORD)lSize, IniFile);
3290 
3291  if (lSize <= 0)
3292  {
3293  Error = true;
3294  BUILDRXSTRING(retstr, ERROR_RETSTR);
3295  }
3296  else if (WildCard == false)
3297  {
3298  if (lSize > buffersize)
3299  retstr->strptr = (PCH)GlobalAlloc(GMEM_FIXED, lSize);
3300  if (retstr->strptr == NULL)
3301  {
3302  GlobalFree(returnVal); /* release buffer */
3303  BUILDRXSTRING(retstr, ERROR_NOMEM);
3304  return VALID_ROUTINE;
3305  }
3306  memcpy(retstr->strptr, returnVal, lSize);
3307  retstr->strlength = lSize;
3308  }
3309  }
3310  else
3311  { /* Set or delete Key */
3312 
3313  if (!_stricmp(Key, "DELETE:") || (numargs == 2) || !RXVALIDSTRING(args[2]))
3314  /* Delete application and all */
3315  /* associated keys */
3316  Error = !WritePrivateProfileString(App, NULL, NULL, IniFile);
3317  else if (!_stricmp(Val, "DELETE:") ||
3318  !RXVALIDSTRING(args[3]))
3319  /* Delete a single key */
3320  Error = !WritePrivateProfileString(App, Key, NULL, IniFile);
3321  else
3322  {
3323  lSize = args[3].strlength;
3324  /* Set a single key value */
3325  Error = !WritePrivateProfileString(App, Key, Val, IniFile);
3326  }
3327 
3328  if (Error)
3329  {
3330  BUILDRXSTRING(retstr, ERROR_RETSTR);
3331  }
3332  else
3333  retstr->strlength = 0; /* just return a null string */
3334  }
3335 
3336  /******************************************
3337  * If this was a wildcard search, change *
3338  * the Val variable from one long string *
3339  * of values to a REXX stem variable. *
3340  ******************************************/
3341 
3342  if (WildCard == true)
3343  { /* fill stem variable */
3344 
3345  if (Error == false)
3346  {
3347  x = 0;
3348  ldp.count = 0;
3349 
3350  do
3351  {
3352  /* Copy string terminated by \0 to Temp. Last string will end */
3353  /* in \0\0 and thus have a length of 0. */
3354  len = 0;
3355 
3356  const char *next = &returnVal[x]; /* point to string */
3357  len = strlen(next); /* get string length */
3358  /* if non-zero length, then */
3359  /* set the stem element */
3360  if (len != 0)
3361  {
3362  x += (len+1); /* Increment pointer past the */
3363  /* new string */
3364  strcpy(ldp.ibuf, next);
3365  ldp.vlen = len;
3366  ldp.count++;
3367  ltoa((long)ldp.count, ldp.varname+ldp.stemlen, 10);
3368 
3369  if (ldp.ibuf[ldp.vlen-1] == '\n')
3370  ldp.vlen--;
3371  ldp.shvb.shvnext = NULL;
3372  ldp.shvb.shvname.strptr = ldp.varname;
3373  ldp.shvb.shvname.strlength = strlen(ldp.varname);
3374  ldp.shvb.shvvalue.strptr = ldp.ibuf;
3375  ldp.shvb.shvvalue.strlength = ldp.vlen;
3376  ldp.shvb.shvnamelen = ldp.shvb.shvname.strlength;
3377  ldp.shvb.shvvaluelen = ldp.vlen;
3378  ldp.shvb.shvcode = RXSHV_SET;
3379  ldp.shvb.shvret = 0;
3380  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN)
3381  {
3382  if (returnVal != NULL)
3383  {
3384  GlobalFree(returnVal); /* release buffer */
3385  }
3386  return INVALID_ROUTINE; /* error on non-zero */
3387  }
3388  }
3389  }
3390 
3391  while (returnVal[x] != '\0');
3392  }
3393 
3394  else
3395  ldp.count = 0;
3396 
3397  if (returnVal != NULL)
3398  {
3399  GlobalFree(returnVal);
3400  returnVal = NULL;
3401  }
3402 
3403  /* set number returned */
3404  ltoa((long)ldp.count, ldp.ibuf, 10);
3405  ldp.varname[ldp.stemlen] = '0';
3406  ldp.varname[ldp.stemlen+1] = 0;
3407  ldp.shvb.shvnext = NULL;
3408  ldp.shvb.shvname.strptr = ldp.varname;
3409  ldp.shvb.shvname.strlength = ldp.stemlen+1;
3410  ldp.shvb.shvnamelen = ldp.stemlen+1;
3411  ldp.shvb.shvvalue.strptr = ldp.ibuf;
3412  ldp.shvb.shvvalue.strlength = strlen(ldp.ibuf);
3414  ldp.shvb.shvcode = RXSHV_SET;
3415  ldp.shvb.shvret = 0;
3416  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN)
3417  return INVALID_ROUTINE; /* error on non-zero */
3418 
3419  } /* * End - IF (Wildcard ... * */
3420  if (returnVal != NULL)
3421  {
3422  GlobalFree(returnVal); /* release buffer */
3423  }
3424 
3425  return VALID_ROUTINE; /* no error on call */
3426 }
3427 
3428 
3429 /*************************************************************************
3430 * Function: SysLoadFuncs *
3431 * *
3432 * Syntax: call SysLoadFuncs [option] *
3433 * *
3434 * Params: none *
3435 * *
3436 * Return: null string *
3437 *************************************************************************/
3438 
3439 size_t RexxEntry SysLoadFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3440 {
3441  // this is a NOP now
3442  retstr->strlength = 0; /* set return value */
3443  return VALID_ROUTINE;
3444 }
3445 
3446 
3447 /*************************************************************************
3448 * Function: SysMkDir *
3449 * *
3450 * Syntax: call SysMkDir dir *
3451 * *
3452 * Params: dir - Directory to be created. *
3453 * *
3454 * Return: NO_UTIL_ERROR *
3455 * Return code from CreateDirectory() *
3456 *************************************************************************/
3457 
3459 {
3460  return CreateDirectory(dir, NULL) != 0 ? 0 : GetLastError();
3461 }
3462 
3463 
3464 /*************************************************************************
3465 * Function: SysGetErrortext *
3466 * *
3467 * Syntax: call SysGetErrortext errnumber *
3468 * *
3469 * Params: errnumber - error number to be described *
3470 * *
3471 * Return: Description or empty string *
3472 *************************************************************************/
3473 
3474 size_t RexxEntry SysGetErrortext(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3475 {
3476  DWORD errnum;
3477  char *errmsg;
3478  size_t length;
3479 
3480  if (numargs != 1)
3481  {
3482  /* If no args, then its an */
3483  /* incorrect call */
3484  return INVALID_ROUTINE;
3485  }
3486 
3487  errnum = atoi(args[0].strptr);
3488  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,errnum,0,(LPSTR)&errmsg,64,NULL) == 0)
3489  {
3490  retstr->strptr[0] = 0x00;
3491  }
3492  else
3493  { /* succeeded */
3494  length = strlen(errmsg);
3495 
3496  // FormatMessage returns strings with trailing CrLf, which we want removed
3497  if (length >= 1 && errmsg[length - 1] == 0x0a)
3498  {
3499  errmsg[length - 1] = 0x00;
3500  length--;
3501  }
3502  if (length >= 1 && errmsg[length - 1] == 0x0d)
3503  {
3504  errmsg[length - 1] = 0x00;
3505  length--;
3506  }
3507 
3508  if (length >= retstr->strlength)
3509  {
3510  retstr->strptr = (PCH)GlobalAlloc(GMEM_ZEROINIT | GMEM_FIXED, strlen(errmsg+1));
3511  }
3512  strcpy(retstr->strptr,errmsg);
3513  LocalFree(errmsg);
3514  }
3515  retstr->strlength = strlen(retstr->strptr);
3516  return VALID_ROUTINE;
3517 }
3518 
3519 
3520 /*************************************************************************
3521 * Function: SysWinEncryptFile (W2K only) *
3522 * *
3523 * Syntax: call SysWinEncryptFile filename *
3524 * *
3525 * Params: filename - file to be encrypted *
3526 * *
3527 * Return: NO_UTIL_ERROR *
3528 * Return code from EncryptFile() *
3529 *************************************************************************/
3530 
3531 size_t RexxEntry SysWinEncryptFile(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3532 {
3533  ULONG rc; /* Ret code of func */
3534 
3535  if (numargs != 1)
3536  {
3537  /* If no args, then its an */
3538  /* incorrect call */
3539  return INVALID_ROUTINE;
3540  }
3541 
3542  rc = EncryptFile(args[0].strptr);
3543  if (rc)
3544  {
3545  sprintf(retstr->strptr, "%d", 0);
3546  }
3547  else
3548  {
3549  sprintf(retstr->strptr, "%d", GetLastError());
3550  }
3551  retstr->strlength = strlen(retstr->strptr);
3552  return VALID_ROUTINE;
3553 }
3554 
3555 /*************************************************************************
3556 * Function: SysWinDecryptFile (W2K only) *
3557 * *
3558 * Syntax: call SysWinDecryptFile filename *
3559 * *
3560 * Params: filename - file to be decrypted *
3561 * *
3562 * Return: NO_UTIL_ERROR *
3563 * Return code from DecryptFile() *
3564 *************************************************************************/
3565 
3566 size_t RexxEntry SysWinDecryptFile(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3567 {
3568 
3569  ULONG rc; /* Ret code of func */
3570 
3571  if (numargs != 1)
3572  /* If no args, then its an */
3573  /* incorrect call */
3574  return INVALID_ROUTINE;
3575 
3576  rc = DecryptFile(args[0].strptr,0);
3577 
3578  if (rc)
3579  sprintf(retstr->strptr, "%d", 0);
3580  else
3581  sprintf(retstr->strptr, "%d", GetLastError());
3582  retstr->strlength = strlen(retstr->strptr);
3583  return VALID_ROUTINE;
3584 }
3585 
3586 
3587 /*************************************************************************
3588 * Function: SysWinVer *
3589 * *
3590 * Syntax: call SysWinVer *
3591 * *
3592 * Return: Windows Version *
3593 *************************************************************************/
3594 
3595 size_t RexxEntry SysWinVer(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3596 {
3597  if (numargs != 0) /* validate arg count */
3598  return INVALID_ROUTINE;
3599 
3600  // MS has deprecated GetVersionEx(). Also, since 10.0.18363, querying
3601  // the version information from one of the system DLLs won't give the
3602  // correct version number any longer. The only remaining option seems
3603  // to be this:
3604  // https://stackoverflow.com/questions/36543301/detecting-windows-10-version
3605 
3606  #define STATUS_SUCCESS (0x00000000)
3607  typedef LONG NTSTATUS;
3608  typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
3609 
3610  HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
3611  if (hMod != NULL)
3612  {
3613  RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
3614  if (fxPtr != NULL)
3615  {
3616  RTL_OSVERSIONINFOW rovi;
3617  rovi.dwOSVersionInfoSize = sizeof(rovi);
3618  if (fxPtr(&rovi) == STATUS_SUCCESS)
3619  {
3620  // char retstr[256];
3621 
3622  // snprintf(retstr, sizeof(retstr), "Windows %d.%d.%d",
3623  wsprintf(retstr->strptr, "Windows %d.%d.%d",
3624  rovi.dwMajorVersion, rovi.dwMinorVersion, rovi.dwBuildNumber);
3625  //return context->String(retstr);
3626  }
3627  }
3628  }
3629  else
3630  {
3631  // just return the nullstring if we fail
3632  //return context->NullString();
3633  wsprintf(retstr->strptr, "");
3634  }
3635 
3636  retstr->strlength = strlen(retstr->strptr);
3637  return VALID_ROUTINE;
3638 }
3639 
3640 /*************************************************************************
3641 * Function: SysVersion *
3642 * *
3643 * Syntax: Say SysVersion *
3644 * *
3645 * Return: Operating System and Version *
3646 *************************************************************************/
3647 
3648 size_t RexxEntry SysVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3649 {
3650  /* this is only an alias for SysWinVer */
3651  return SysWinVer(name, numargs, args, queuename, retstr);
3652 }
3653 
3654 
3655 /*************************************************************************
3656 * Function: SysRmDir *
3657 * *
3658 * Syntax: call SysRmDir dir *
3659 * *
3660 * Params: dir - Directory to be removed. *
3661 * *
3662 * Return: NO_UTIL_ERROR *
3663 * Return code from RemoveDirectory() *
3664 *************************************************************************/
3665 
3667 {
3668  return RemoveDirectory(dir) != 0 ? 0 : GetLastError();
3669 }
3670 
3671 
3672 /*************************************************************************
3673 * Function: SysSearchPath *
3674 * *
3675 * Syntax: call SysSearchPath path, file [, options] *
3676 * *
3677 * Params: path - Environment variable name which specifies a path *
3678 * to be searched (ie 'PATH', 'DPATH', etc). *
3679 * file - The file to search for. *
3680 * options - 'C' - Current directory search first (default). *
3681 * 'N' - No Current directory search. Only searches *
3682 * the path as specified. *
3683 * *
3684 * Return: other - Full path and filespec of found file. *
3685 * '' - Specified file not found along path. *
3686 *************************************************************************/
3687 
3688 size_t RexxEntry SysSearchPath(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3689 {
3690  char szFullPath[_MAX_PATH]; /* returned file name */
3691  char szCurDir[_MAX_PATH]; /* current directory */
3692  char *szEnvStr = NULL;
3693 
3694  LPTSTR pszOnlyFileName; /* parm for searchpath */
3695  LPTSTR lpPath = NULL; /* ptr to search path+ */
3696  UINT errorMode;
3697 
3698  /* validate arguments */
3699  if (numargs < 2 || numargs > 3 ||
3700  !RXVALIDSTRING(args[0]) ||
3701  !RXVALIDSTRING(args[1]))
3702  {
3703  return INVALID_ROUTINE;
3704  }
3705 
3706  char opt = 'C'; // this is the default
3707  if (numargs == 3)
3708  { /* process options */
3709  opt = toupper(args[2].strptr[0]);
3710  if (opt != 'C' && opt != 'N')
3711  {
3712  return INVALID_ROUTINE; /* Invalid option */
3713  }
3714  }
3715 
3716  szEnvStr = (LPTSTR) malloc(sizeof(char) * MAX_ENVVAR);
3717  if (szEnvStr != NULL)
3718  {
3719  DWORD charCount = GetEnvironmentVariable(args[0].strptr, szEnvStr, MAX_ENVVAR);
3720  if (charCount == 0)
3721  {
3722  *szEnvStr = '\0';
3723  }
3724  else if (charCount > MAX_ENVVAR)
3725  {
3726  szEnvStr = (LPTSTR) realloc(szEnvStr, sizeof(char) * charCount);
3727  if (szEnvStr != NULL)
3728  {
3729  DWORD charCount2 = GetEnvironmentVariable(args[0].strptr, szEnvStr, charCount);
3730  if (charCount2 == 0 || charCount2 > charCount)
3731  {
3732  *szEnvStr = '\0';
3733  }
3734  }
3735  }
3736  }
3737 
3738  if (opt == 'N')
3739  {
3740  lpPath = (szEnvStr == NULL) ? NULL : strdup(szEnvStr);
3741  }
3742  else if (opt == 'C')
3743  {
3744  /* search current directory */
3745  DWORD charCount = GetCurrentDirectory(_MAX_PATH, szCurDir);
3746  if (charCount == 0 || charCount > _MAX_PATH)
3747  {
3748  szCurDir[0] = '\0';
3749  }
3750 
3751  if (szEnvStr != NULL)
3752  {
3753  lpPath = (LPTSTR) malloc(sizeof(char) * (strlen(szCurDir) + 1 + strlen(szEnvStr) + 1));
3754  if (lpPath != NULL)
3755  {
3756  strcpy(lpPath, szCurDir);
3757  strcat(lpPath, ";");
3758  strcat(lpPath, szEnvStr);
3759  }
3760  }
3761  else
3762  {
3763  lpPath = strdup(szCurDir);
3764  }
3765  }
3766 
3767  /* use DosSearchPath */
3768 
3769  DWORD charCount = 0;
3770  if (lpPath != NULL)
3771  {
3772  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
3773  charCount = SearchPath(
3774  (LPCTSTR)lpPath, /* path srch, NULL will+ */
3775  (LPCTSTR)args[1].strptr, /* address if filename */
3776  NULL, /* filename contains .ext */
3777  _MAX_PATH, /* size of fullname buffer */
3778  szFullPath, /* where to put results */
3779  &pszOnlyFileName);
3780  SetErrorMode(errorMode);
3781  }
3782  if (charCount == 0 || charCount > _MAX_PATH)
3783  {
3784  szFullPath[0]='\0'; /* set to NULL if failure */
3785  }
3786 
3787  BUILDRXSTRING(retstr, szFullPath); /* pass back result */
3788  free(szEnvStr);
3789  free(lpPath);
3790  return VALID_ROUTINE;
3791 }
3792 
3793 
3794 /*************************************************************************
3795 * Function: SysSleep *
3796 * *
3797 * Syntax: call SysSleep secs *
3798 * *
3799 * Params: secs - Number of seconds to sleep. *
3800 * must be in the range 0 .. 2147483 *
3801 * *
3802 * Return: 0 *
3803 *************************************************************************/
3804 RexxRoutine1(int, SysSleep, RexxStringObject, delay)
3805 {
3806  double seconds;
3807  // try to convert the provided delay to a valid floating point number
3808  if (context->ObjectToDouble(delay, &seconds) == 0 ||
3809  isnan(seconds) || seconds == HUGE_VAL || seconds == -HUGE_VAL)
3810  {
3811  // 88.902 The &1 argument must be a number; found "&2"
3812  context->RaiseException3(Rexx_Error_Invalid_argument_number, context->String("positional"), context->String("delay"), delay);
3813  return 1;
3814  }
3815 
3816  // according to MSDN the maximum is USER_TIMER_MAXIMUM (0x7FFFFFFF) milliseconds,
3817  // which translates to 2147483.647 seconds
3818  if (seconds < 0.0 || seconds > 2147483.0)
3819  {
3820  // 88.907 The &1 argument must be in the range &2 to &3; found "&4"
3821  context->RaiseException(Rexx_Error_Invalid_argument_range,
3822  context->ArrayOfFive(context->String("positional"), context->String("delay"),
3823  context->String("0"), context->String("2147483"), delay));
3824  return 1;
3825  }
3826 
3827  // convert to milliseconds, no overflow possible
3828  LONG milliseconds = (LONG) (seconds * 1000);
3829 
3830  /** Using Sleep with a long timeout risks sleeping on a thread with a message
3831  * queue, which can make the system sluggish, or possibly deadlocked. If the
3832  * sleep is longer than 333 milliseconds use a window timer to avoid this
3833  * risk.
3834  */
3835  if ( milliseconds > 333 )
3836  {
3837  if ( !(SetTimer(NULL, 0, milliseconds, (TIMERPROC) SleepTimerProc)) )
3838  {
3839  // no timer available, need to abort
3840  context->RaiseException1(Rexx_Error_System_resources_user_defined,
3841  context->String("System resources exhausted: cannot start timer"));
3842  return 1;
3843  }
3844 
3845  MSG msg;
3846  while ( GetMessage (&msg, NULL, 0, 0) )
3847  {
3848  if ( msg.message == OM_WAKEUP ) /* If our message, exit loop */
3849  break;
3850  TranslateMessage( &msg );
3851  DispatchMessage ( &msg );
3852  }
3853  }
3854  else
3855  {
3856  Sleep(milliseconds);
3857  }
3858 
3859  return 0;
3860 }
3861 
3862 /*********************************************************************
3863  * *
3864  * Routine : SleepTimerProc *
3865  * *
3866  * Purpose : callback routine for SetTimer set in SysSleep *
3867  * Notes : *
3868  * Arguments : hwnd - window handle *
3869  * uMsg - WM_TIMER message *
3870  * idEvent - timer identifier *
3871  * dwtime - current system time *
3872  * Returns : *
3873  * *
3874  *********************************************************************/
3875  VOID CALLBACK SleepTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) {
3876  DWORD ThreadId;
3877  KillTimer(NULL, idEvent); /* kill the timer that just ended */
3878  ThreadId = GetCurrentThreadId();
3879  PostThreadMessage(ThreadId, OM_WAKEUP, 0 , 0L); /* send ourself the wakeup message*/
3880  }
3881 
3882 /*************************************************************************
3883 * Function: SysTempFileName *
3884 * *
3885 * Syntax: call SysTempFileName template [,filler] *
3886 * *
3887 * Params: template - Description of filespec desired. For example: *
3888 * C:\TEMP\FILE.??? *
3889 * filler - A character which when found in template will be *
3890 * replaced with random digits until a unique file *
3891 * or directory is found. The default character *
3892 * is '?'. *
3893 * *
3894 * Return: other - Unique file/directory name. *
3895 * '' - No more files exist given specified template. *
3896 *************************************************************************/
3897 
3898 size_t RexxEntry SysTempFileName(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3899 {
3900  CHAR filler; /* filler character */
3901 
3902  if (numargs < 1 || /* validate arguments */
3903  numargs > 2 ||
3904  !RXVALIDSTRING(args[0]) ||
3905  args[0].strlength > 512)
3906  return INVALID_ROUTINE;
3907 
3908  if (numargs == 2 && /* get filler character */
3909  !RXNULLSTRING(args[1]))
3910  {
3911  if (args[1].strlength != 1) /* must be one character */
3912  return INVALID_ROUTINE;
3913  filler = args[1].strptr[0];
3914  }
3915  else
3916  {
3917  filler = '?';
3918  }
3919  /* get the file id */
3920  GetUniqueFileName(const_cast<char *>(args[0].strptr), filler, retstr->strptr);
3921  retstr->strlength = strlen(retstr->strptr);
3922 
3923  return VALID_ROUTINE;
3924 }
3925 
3926 
3927 /*************************************************************************
3928 * Function: SysTextScreenRead *
3929 * *
3930 * Syntax: call SysTextScreenRead row, col [,len] *
3931 * *
3932 * Params: row - Horizontal row on the screen to start reading from. *
3933 * The row at the top of the screen is 0. *
3934 * col - Vertical column on the screen to start reading from. *
3935 * The column at the left of the screen is 0. *
3936 * len - The number of characters to read. The default is the *
3937 * rest of the screen. *
3938 * *
3939 * Return: Characters read from text screen. *
3940 *************************************************************************/
3941 RexxRoutine3(RexxStringObject, SysTextScreenRead, int, row, int, col, OPTIONAL_int, len)
3942 {
3943  int lPos,lPosOffSet; /* positioning */
3944  /* (132x50) */
3945  int lBufferLen = 16000; /* default: 200x80 characters */
3946 
3947  COORD coordLine; /* coordinates of where to */
3948  /* read characters from */
3949  DWORD dwCharsRead,dwSumCharsRead; /* Handle to Standard Out */
3950  HANDLE hStdout;
3951  CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */
3952 
3953  hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
3954 
3955  if (!GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
3956  {
3957  context->InvalidRoutine();
3958  return NULLOBJECT;
3959  }
3960 
3961  if (argumentOmitted(3)) /* check the length */
3962  {
3963  len = csbiInfo.dwSize.Y * csbiInfo.dwSize.X;
3964  }
3965 
3966  coordLine.X = (short)col;
3967  coordLine.Y = (short)row;
3968 
3969  char buffer[256];
3970  char *ptr = buffer;
3971 
3972  if (len > sizeof(buffer))
3973  {
3974  // allocate a new buffer
3975  ptr = (char *)malloc(len);
3976  if (ptr == NULL)
3977  {
3978  context->InvalidRoutine();
3979  return NULL;
3980  }
3981  }
3982 
3983  if (len < lBufferLen)
3984  {
3985  lBufferLen = len;
3986  }
3987 
3988  lPos = 0; /* current position */
3989  lPosOffSet = row * csbiInfo.dwSize.X + col; /* add offset if not started at beginning */
3990  dwSumCharsRead = 0;
3991 
3992  while (lPos < len )
3993  {
3994 
3995  if (!ReadConsoleOutputCharacter(hStdout, &ptr[lPos], lBufferLen, coordLine, &dwCharsRead))
3996  {
3997  if (ptr != buffer) {
3998  free(ptr);
3999  }
4000  context->InvalidRoutine();
4001  return NULL;
4002  }
4003 
4004 
4005  lPos = lPos + lBufferLen;
4006  coordLine.Y = (short)((lPos + lPosOffSet) / csbiInfo.dwSize.X);
4007  coordLine.X = (short)((lPos + lPosOffSet) % csbiInfo.dwSize.X);
4008  dwSumCharsRead = dwSumCharsRead + dwCharsRead;
4009  }
4010 
4011  RexxStringObject result = context->NewString(ptr, dwSumCharsRead);
4012  if (ptr != buffer) {
4013  free(ptr);
4014  }
4015  return result;
4016 }
4017 
4018 /*************************************************************************
4019 * Function: SysTextScreenSize *
4020 * *
4021 * Syntax: call SysTextScreenSize [option], [rows, colummns] *
4022 * call SysTextScreenSize [option], [top, left, bottom, right] *
4023 * *
4024 * Params: option - "BUFFERSIZE", "WINDOWRECT", "MAXWINDOWSIZE" *
4025 * "BUFFERSIZE" (default) return or set console buffer size *
4026 * "WINDOWRECT" return or set windows position *
4027 * "MAXWINDOWSIZE" return maximum window size *
4028 * lines, columns - set buffer size to lines by columns *
4029 * top, left, bottom, right - set window size and position *
4030 * *
4031 * Return: "BUFFERSIZE" or "MAXWINDOWSIZE": rows columns *
4032 * "WINDOWRECT": top left bottom right *
4033 *************************************************************************/
4034 
4035 RexxRoutine5(RexxStringObject, SysTextScreenSize,
4036  OPTIONAL_CSTRING, optionString,
4037  OPTIONAL_stringsize_t, rows, OPTIONAL_stringsize_t, columns,
4038  OPTIONAL_stringsize_t, rows2, OPTIONAL_stringsize_t, columns2)
4039 {
4040  // check for valid option
4041  typedef enum { BUFFERSIZE, WINDOWRECT, MAXWINDOWSIZE } console_option;
4042  console_option option;
4043  if (optionString == NULL || stricmp(optionString, "BUFFERSIZE") == 0)
4044  {
4045  option = BUFFERSIZE;
4046  }
4047  else if (stricmp(optionString, "WINDOWRECT") == 0)
4048  {
4049  option = WINDOWRECT;
4050  }
4051  else if (stricmp(optionString, "MAXWINDOWSIZE") == 0)
4052  {
4053  option = MAXWINDOWSIZE;
4054  }
4055  else
4056  {
4057  context->InvalidRoutine();
4058  return 0;
4059  }
4060 
4061  // check for valid SET arguments: either none, or two more, or four more
4062  size_t setArgs;
4063  bool omitted45 = argumentOmitted(4) && argumentOmitted(5);
4064  bool exists23 = argumentExists(2) && argumentExists(3);
4065  if (argumentOmitted(2) && argumentOmitted(3) && omitted45)
4066  {
4067  setArgs = 0;
4068  }
4069  else if (exists23 && omitted45)
4070  {
4071  setArgs = 2;
4072  }
4073  else if (exists23 && argumentExists(4) && argumentExists(5))
4074  {
4075  setArgs = 4;
4076  }
4077  else
4078  {
4079  context->InvalidRoutine();
4080  return 0;
4081  }
4082 
4083  // check that all SET arguments fit a SHORT
4084  if (!(setArgs == 0 ||
4085  (setArgs == 2 && rows <= SHRT_MAX && columns <= SHRT_MAX) ||
4086  (setArgs == 4 && rows <= SHRT_MAX && columns <= SHRT_MAX && rows2 <= SHRT_MAX && columns2 <= SHRT_MAX)))
4087  {
4088  context->InvalidRoutine();
4089  return 0;
4090  }
4091 
4092  // real work starts here
4093  CONSOLE_SCREEN_BUFFER_INFO csbi; // console screen buffer information
4094  char buffer[100];
4095 
4096  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
4097 
4098  if (setArgs == 0)
4099  {
4100  // this is a GET requset, retrieve console information
4101  if (GetConsoleScreenBufferInfo(hStdout, &csbi) == NULL)
4102  {
4103  // console not in character mode, return two or four zeroes
4104  return context->NewStringFromAsciiz(option == WINDOWRECT ? "0 0 0 0" : "0 0");
4105  }
4106  }
4107 
4108  if (option == BUFFERSIZE && setArgs == 0)
4109  {
4110  // this is a BUFFERSIZE GET, returns two values
4111  sprintf(buffer, "%d %d", csbi.dwSize.Y, csbi.dwSize.X);
4112  }
4113  else if (option == WINDOWRECT && setArgs == 0)
4114  {
4115  // this is a WINDOWRECT GET, returns four values
4116  sprintf(buffer, "%d %d %d %d", csbi.srWindow.Top, csbi.srWindow.Left, csbi.srWindow.Bottom, csbi.srWindow.Right);
4117  }
4118  else if (option == MAXWINDOWSIZE && setArgs == 0)
4119  {
4120  // this is a MAXWINDOWSIZE GET, returns two values
4121  sprintf(buffer, "%d %d", csbi.dwMaximumWindowSize.Y, csbi.dwMaximumWindowSize.X);
4122  }
4123  else if (option == BUFFERSIZE && setArgs == 2)
4124  {
4125  // this is a BUFFERSIZE SET, requires two more arguments
4126  COORD consoleBuffer;
4127  consoleBuffer.Y = (SHORT)rows;
4128  consoleBuffer.X = (SHORT)columns;
4129  BOOL code = SetConsoleScreenBufferSize(hStdout, consoleBuffer);
4130  sprintf(buffer, "%d", code == 0 ? GetLastError() : 0);
4131  }
4132  else if (option == WINDOWRECT && setArgs == 4)
4133  {
4134  // this is a WINDOWRECT SET, requires four more arguments
4135  SMALL_RECT consoleWindow;
4136  consoleWindow.Top = (SHORT)rows;
4137  consoleWindow.Left = (SHORT)columns;
4138  consoleWindow.Bottom = (SHORT)rows2;
4139  consoleWindow.Right = (SHORT)columns2;
4140  BOOL code = SetConsoleWindowInfo(hStdout, 1, &consoleWindow);
4141  sprintf(buffer, "%d", code == 0 ? GetLastError() : 0);
4142  }
4143  else
4144  {
4145  context->InvalidRoutine();
4146  return 0;
4147  }
4148 
4149  // return the buffer as result
4150  return context->NewStringFromAsciiz(buffer);
4151 }
4152 
4153 /*************************************************************************
4154 * Function: RxWinExec *
4155 * *
4156 * Syntax: call RxWinExec command,CmdShow *
4157 * *
4158 * *
4159 * Parms: command - program to execute *
4160 * CmdShow - Any of the SW_ type values in winuser.h *
4161 * SW_SHOW 5 *
4162 * SW_HIDE 0 *
4163 * SW_MINIMIZE etc... 6 *
4164 * numeric values... *
4165 * *
4166 * Return: Process ID or Error code *
4167 *************************************************************************/
4168 
4169 size_t RexxEntry RxWinExec(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4170 {
4171 
4172  int CmdShow; /* show window style flags */
4173  int index; /* table index */
4174  ULONG pid; /* PID or error return code */
4175  size_t length; /* length of option */
4176  STARTUPINFO si;
4177  PROCESS_INFORMATION procInfo;
4178 
4179  // Show window types.
4180  PSZ show_styles[] =
4181  {
4182  "SHOWNORMAL",
4183  "SHOWNOACTIVATE",
4184  "SHOWMINNOACTIVE",
4185  "SHOWMINIMIZED",
4186  "SHOWMAXIMIZED",
4187  "HIDE",
4188  "MINIMIZE"
4189  };
4190 
4191  // Show window styles.
4192  ULONG show_flags[] =
4193  {
4194  SW_SHOWNORMAL,
4195  SW_SHOWNOACTIVATE,
4196  SW_SHOWMINNOACTIVE,
4197  SW_SHOWMINIMIZED,
4198  SW_SHOWMAXIMIZED,
4199  SW_HIDE,
4200  SW_MINIMIZE
4201  };
4202 
4203  BUILDRXSTRING(retstr, NO_UTIL_ERROR); /* pass back result */
4204 
4205  // Should be 1 or 2 args.
4206  if ( numargs < 1 || numargs > 2 || !RXVALIDSTRING(args[0]) ||
4207  (numargs == 2 && !RXVALIDSTRING(args[1])) || args[0].strlength > MAX_CREATEPROCESS_CMDLINE )
4208  {
4209  return INVALID_ROUTINE;
4210  }
4211 
4212  // Show type can be one and only one of the SW_XXX constants.
4213  if ( numargs < 2 || args[1].strptr == NULL )
4214  {
4215  CmdShow = SW_SHOWNORMAL;
4216  }
4217  else
4218  {
4219  // Get length of option and search the style table.
4220  length = args[1].strlength;
4221  for ( index = 0; index < sizeof(show_styles)/sizeof(PSZ); index++ )
4222  {
4223  if ( length == strlen(show_styles[index]) && memicmp(args[1].strptr, show_styles[index], length) == 0 )
4224  {
4225  CmdShow = show_flags[index];
4226  break;
4227  }
4228  }
4229 
4230  if ( index == sizeof(show_styles)/sizeof(PSZ) )
4231  {
4232  // User sent an argument, but not the right one.
4233  return INVALID_ROUTINE;
4234  }
4235  }
4236 
4237  ZeroMemory(&procInfo, sizeof(procInfo));
4238  ZeroMemory(&si, sizeof(si));
4239  si.cb = sizeof(si);
4240  si.dwFlags = STARTF_USESHOWWINDOW;
4241  si.wShowWindow = (WORD)CmdShow;
4242 
4243  if ( CreateProcess(NULL, (LPSTR)args[0].strptr, NULL, NULL, FALSE, 0, NULL,
4244  NULL, &si, &procInfo ) )
4245  {
4246  pid = procInfo.dwProcessId;
4247  }
4248  else
4249  {
4250  pid = GetLastError();
4251  if ( pid > 31 )
4252  {
4253  // Maintain compatibility to versions < ooRexx 3.1.2
4254  pid = (ULONG)-((int)pid);
4255  }
4256  }
4257 
4258  // Return value as string.
4259  sprintf(retstr->strptr, "%d", pid);
4260  retstr->strlength = strlen(retstr->strptr);
4261 
4262  // Close process / thread handles as they are not used / needed.
4263  CloseHandle(procInfo.hProcess);
4264  CloseHandle(procInfo.hThread);
4265 
4266  return VALID_ROUTINE;
4267 }
4268 
4269 
4270 /*************************************************************************
4271 * Function: SysAddRexxMacro *
4272 * *
4273 * Syntax: result = SysAddRexxMacro(name, file, <order>) *
4274 * *
4275 * Params: name - loaded name of the macro file *
4276 * file - file containing the loaded macro *
4277 * order - Either 'B'efore or 'A'fter. The default is 'B' *
4278 * *
4279 * Return: return code from RexxAddMacro *
4280 *************************************************************************/
4281 
4282 RexxRoutine3(int, SysAddRexxMacro, CSTRING, name, CSTRING, file, OPTIONAL_CSTRING, option)
4283 {
4284  size_t position; /* added position */
4285 
4286  position = RXMACRO_SEARCH_BEFORE; /* set default search position*/
4287  if (option != NULL) /* have an option? */
4288  {
4289  switch (*option)
4290  {
4291  case 'B': // 'B'efore
4292  case 'b':
4293  position = RXMACRO_SEARCH_BEFORE;
4294  break;
4295 
4296  case 'A': // 'A'fter
4297  case 'a':
4298  position = RXMACRO_SEARCH_AFTER;
4299  break;
4300 
4301  default:
4302  context->InvalidRoutine();
4303  return 0;
4304  }
4305  }
4306  /* try to add the macro */
4307  return(int)RexxAddMacro(name, file, position);
4308 }
4309 
4310 /*************************************************************************
4311 * Function: SysReorderRexxMacro *
4312 * *
4313 * Syntax: result = SysReorderRexxMacro(name, order) *
4314 * *
4315 * Params: name - loaded name of the macro file *
4316 * order - Either 'B'efore or 'A'fter. *
4317 * *
4318 * Return: return code from RexxReorderMacro *
4319 *************************************************************************/
4320 
4321 RexxRoutine2(int, SysReorderRexxMacro, CSTRING, name, CSTRING, option)
4322 {
4323  size_t position; /* added position */
4324 
4325  switch (*option)
4326  {
4327  case 'B': // 'B'efore
4328  case 'b':
4329  position = RXMACRO_SEARCH_BEFORE;
4330  break;
4331 
4332  case 'A': // 'A'fter
4333  case 'a':
4334  position = RXMACRO_SEARCH_AFTER;
4335  break;
4336 
4337  default:
4338  context->InvalidRoutine();
4339  return 0;
4340  }
4341  return(int)RexxReorderMacro(name, position);
4342 }
4343 
4344 /*************************************************************************
4345 * Function: SysDropRexxMacro *
4346 * *
4347 * Syntax: result = SysDropRexxMacro(name) *
4348 * *
4349 * Params: name - name of the macro space function *
4350 * *
4351 * Return: return code from RexxDropMacro *
4352 *************************************************************************/
4353 
4354 RexxRoutine1(int, SysDropRexxMacro, CSTRING, name)
4355 {
4356  return (int)RexxDropMacro(name);
4357 }
4358 
4359 /*************************************************************************
4360 * Function: SysQueryRexxMacro *
4361 * *
4362 * Syntax: result = SysQueryRexxMacro(name) *
4363 * *
4364 * Params: name - name of the macro space function *
4365 * *
4366 * Return: position of the macro ('B' or 'A'), returns null for errors.*
4367 *************************************************************************/
4368 
4369 RexxRoutine1(CSTRING, SysQueryRexxMacro, CSTRING, name)
4370 {
4371  unsigned short position; /* returned position */
4372 
4373  if (RexxQueryMacro(name, &position) != 0)
4374  {
4375  return "";
4376  }
4377  // before?
4378  if (position == RXMACRO_SEARCH_BEFORE)
4379  {
4380  return "B";
4381  }
4382  else
4383  {
4384  return "A"; /* must be 'A'fter */
4385  }
4386 }
4387 
4388 /*************************************************************************
4389 * Function: SysClearRexxMacroSpace *
4390 * *
4391 * Syntax: result = SysClearRexxMacroSpace() *
4392 * *
4393 * Params: none *
4394 * *
4395 * Return: return code from RexxClearMacroSpace() *
4396 *************************************************************************/
4397 
4398 RexxRoutine0(int, SysClearRexxMacroSpace)
4399 {
4400  return (int)RexxClearMacroSpace(); /* clear the macro space */
4401 }
4402 
4403 /*************************************************************************
4404 * Function: SysSaveRexxMacroSpace *
4405 * *
4406 * Syntax: result = SysSaveRexxMacroSpace(file) *
4407 * *
4408 * Params: file - name of the saved macro space file *
4409 * *
4410 * Return: return code from RexxSaveMacroSpace() *
4411 *************************************************************************/
4412 
4413 RexxRoutine1(int, SysSaveRexxMacroSpace, CSTRING, file)
4414 {
4415  return (int)RexxSaveMacroSpace(0, NULL, file);
4416 }
4417 
4418 /*************************************************************************
4419 * Function: SysLoadRexxMacroSpace *
4420 * *
4421 * Syntax: result = SysLoadRexxMacroSpace(file) *
4422 * *
4423 * Params: file - name of the saved macro space file *
4424 * *
4425 * Return: return code from RexxLoadMacroSpace() *
4426 *************************************************************************/
4427 
4428 RexxRoutine1(int, SysLoadRexxMacroSpace, CSTRING, file)
4429 {
4430  return (int)RexxLoadMacroSpace(0, NULL, file);
4431 }
4432 
4433 
4434 /*************************************************************************
4435 * Function: SysBootDrive *
4436 * *
4437 * Syntax: drive = SysBootDrive() *
4438 * *
4439 * Params: none *
4440 * *
4441 * Return: 'A: B: C: D: ...' *
4442 *************************************************************************/
4443 
4444 size_t RexxEntry SysBootDrive(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4445 {
4446  if (numargs) /* validate arguments */
4447  return INVALID_ROUTINE;
4448 
4449  if (GetSystemDirectory(retstr->strptr, 255) > 0)
4450  {
4451  retstr->strptr[2] = '\0';
4452  retstr->strlength = 2;
4453  }
4454  else
4455  retstr->strlength = 0;
4456  return VALID_ROUTINE; /* no error on call */
4457 }
4458 
4459 
4460 /*************************************************************************
4461 * Function: SysSystemDirectory *
4462 * *
4463 * Syntax: drive = SysSystemDirectory() *
4464 * *
4465 * Params: none *
4466 * *
4467 * Return: 'C:\WINDOWS ...' *
4468 *************************************************************************/
4469 
4470 size_t RexxEntry SysSystemDirectory(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4471 {
4472  if (numargs) /* validate arguments */
4473  return INVALID_ROUTINE;
4474 
4475  if (GetSystemDirectory(retstr->strptr, 255) > 0)
4476  {
4477  retstr->strlength = strlen(retstr->strptr);
4478  }
4479  else
4480  retstr->strlength = 0;
4481  return VALID_ROUTINE; /* no error on call */
4482 }
4483 
4484 
4485 /*************************************************************************
4486 * Function: SysFileSystemType *
4487 * *
4488 * Syntax: result = SysFileSystemType("drive") *
4489 * *
4490 * Params: drive - drive letter (in form of 'D:') *
4491 * or none - current drive *
4492 * *
4493 * Return: result - File System Name attached to the specified drive *
4494 * (FAT, HPFS ....) *
4495 * '' - Empty string in case of any error *
4496 *************************************************************************/
4497 
4498 size_t RexxEntry SysFileSystemType(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4499 {
4500  char * drive;
4501  CHAR chDriveLetter[4];
4502  UINT errorMode;
4503 
4504  /* validate arguments */
4505  if (numargs > 1 || ((numargs == 1) &&
4506  (args[0].strlength > 2 ||
4507  args[0].strlength == 0)))
4508  return INVALID_ROUTINE;
4509  /* validate the arg */
4510  /* drive letter? */
4511  if ((numargs == 1) && (strlen(args[0].strptr) == 2 && args[0].strptr[1] != ':'))
4512  return INVALID_ROUTINE;
4513 
4514  if (numargs == 1)
4515  {
4516  if (args[0].strlength == 1) /* need to add a : if only the*/
4517  {
4518  chDriveLetter[0]=args[0].strptr[0]; /* letter was passed in */
4519  chDriveLetter[1]=':';
4520  chDriveLetter[2]='\\'; /* need to add \ because of */
4521  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4522  }
4523  else /* have <letter>: , just copy over */
4524  {
4525  strcpy(chDriveLetter, args[0].strptr);
4526  chDriveLetter[2]='\\'; /* need to add \ because of */
4527  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4528  }
4529  drive = chDriveLetter;
4530  } else drive = NULL;
4531 
4532  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
4533 
4534  if (GetVolumeInformation(
4535  drive, // address of root directory of the file system
4536  NULL, // address of name of the volume
4537  0, // length of lpVolumeNameBuffer
4538  NULL, // address of volume serial number
4539  NULL, // address of system's maximum filename length
4540  NULL, // address of file system flags
4541  retstr->strptr, // address of name of file system
4542  255 // length of lpFileSystemNameBuffer
4543  ))
4544  retstr->strlength = strlen(retstr->strptr);
4545  else
4546  retstr->strlength = 0; /* return a null string */
4547 
4548  SetErrorMode(errorMode);
4549  return VALID_ROUTINE; /* good completion */
4550 }
4551 
4552 
4553 /*************************************************************************
4554 * Function: SysVolumeLabel *
4555 * *
4556 * Syntax: result = SysVolumeLabel("drive") *
4557 * *
4558 * Params: drive - drive letter (in form of 'D:') *
4559 * or none - current drive *
4560 * *
4561 * Return '' - Empty string in case of any error *
4562 *************************************************************************/
4563 
4564 size_t RexxEntry SysVolumeLabel(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4565 {
4566  char * drive;
4567  CHAR chDriveLetter[4];
4568 
4569  /* validate arguments */
4570  if (numargs > 1 || ((numargs == 1) &&
4571  (args[0].strlength > 2 ||
4572  args[0].strlength == 0)))
4573  return INVALID_ROUTINE;
4574  /* validate the arg */
4575  /* drive letter? */
4576  if ((numargs == 1) && (strlen(args[0].strptr) == 2 && args[0].strptr[1] != ':'))
4577  return INVALID_ROUTINE;
4578 
4579  if (numargs == 1)
4580  {
4581  if (args[0].strlength == 1) /* need to add a : if only the*/
4582  {
4583  chDriveLetter[0]=args[0].strptr[0]; /* letter was passed in */
4584  chDriveLetter[1]=':';
4585  chDriveLetter[2]='\\'; /* need to add \ because of */
4586  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4587  }
4588  else /* have <letter>: , just copy over */
4589  {
4590  strcpy(chDriveLetter, args[0].strptr);
4591  chDriveLetter[2]='\\'; /* need to add \ because of */
4592  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4593  }
4594  drive = chDriveLetter;
4595  } else drive = NULL;
4596 
4597  if (GetVolumeInformation(
4598  drive, /* address of root directory of the file system */
4599  retstr->strptr, /*address of name of the volume */
4600  255, /* length of lpVolumeNameBuffer */
4601  NULL, /* address of volume serial number */
4602  NULL, /* address of system's maximum filename length */
4603  NULL, /* address of file system flags */
4604  NULL, /* address of name of file system */
4605  0 /* length of lpFileSystemNameBuffer */
4606  ))
4607  retstr->strlength = strlen(retstr->strptr);
4608  else
4609  retstr->strlength = 0; /* return a null string */
4610  return VALID_ROUTINE; /* good completion */
4611 }
4612 
4613 
4614 /*************************************************************************
4615 * Function: SysCreateMutexSem *
4616 * *
4617 * Syntax: handle = SysCreateMutexSem(<name>) *
4618 * *
4619 * Params: name - optional name for a mutex semaphore *
4620 * *
4621 * Return: handle - token used as a mutex handle for *
4622 * SysRequestMutexSem, SysReleaseMutexSem, *
4623 * SysCloseMutexSem, and SysOpenEventSem *
4624 * '' - Empty string in case of any error *
4625 *************************************************************************/
4626 
4627 RexxRoutine1(RexxObjectPtr, SysCreateMutexSem, OPTIONAL_CSTRING, name)
4628 {
4629  HANDLE handle; /* mutex handle */
4630  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4631 
4632  handle = 0; /* zero the handle */
4633  if (name != NULL) /* request for named sem */
4634  {
4635  /* create it by name */
4636  handle = CreateMutex(&sa, false, name);
4637  if (!handle) /* may already be created */
4638  {
4639  /* try to open it */
4640  handle = OpenMutex(MUTEX_ALL_ACCESS, true, name);
4641  }
4642  }
4643  else /* unnamed semaphore */
4644  {
4645  handle = CreateMutex(&sa, false, NULL);
4646  }
4647 
4648  if (handle == NULL) {
4649  return context->NullString();
4650  }
4651 
4652  return context->Uintptr((uintptr_t)handle);
4653 }
4654 
4655 
4656 /*************************************************************************
4657 * Function: SysOpenMutexSem *
4658 * *
4659 * Syntax: result = SysOpenMutexSem(name) *
4660 * *
4661 * Params: name - name of the mutex semaphore *
4662 * *
4663 * Return: result - handle to the mutex *
4664 *************************************************************************/
4665 
4666 RexxRoutine1(uintptr_t, SysOpenMutexSem, CSTRING, name)
4667 {
4668  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4669 
4670  /* get a binary handle */
4671  return (uintptr_t) OpenMutex(MUTEX_ALL_ACCESS, true, name); /* try to open it */
4672 }
4673 
4674 /*************************************************************************
4675 * Function: SysReleaseMutexSem *
4676 * *
4677 * Syntax: result = SysReleaseMutexSem(handle) *
4678 * *
4679 * Params: handle - token returned from SysCreateMutexSem *
4680 * *
4681 * Return: result - return code from ReleaseMutex *
4682 *************************************************************************/
4683 
4684 RexxRoutine1(int, SysReleaseMutexSem, uintptr_t, h)
4685 {
4686  return !ReleaseMutex((HANDLE)h) ? GetLastError() : 0;
4687 }
4688 
4689 /*************************************************************************
4690 * Function: SysCloseMutexSem *
4691 * *
4692 * Syntax: result = SysCloseMutexSem(handle) *
4693 * *
4694 * Params: handle - token returned from SysCreateMutexSem *
4695 * *
4696 * Return: result - return code from CloseHandle *
4697 *************************************************************************/
4698 
4699 RexxRoutine1(int, SysCloseMutexSem, uintptr_t, h)
4700 {
4701  return !ReleaseMutex((HANDLE)h) ? GetLastError() : 0;
4702 }
4703 
4704 /*************************************************************************
4705 * Function: SysRequestMutexSem *
4706 * *
4707 * Syntax: result = SysRequestMutexSem(handle, <timeout>) *
4708 * *
4709 * Params: handle - token returned from SysCreateMutexSem *
4710 * *
4711 * Return: result - return code from WaitForSingleObject *
4712 *************************************************************************/
4713 
4714 RexxRoutine2(int, SysRequestMutexSem, uintptr_t, h, OPTIONAL_int, timeout)
4715 {
4716  if (argumentOmitted(2))
4717  {
4718  timeout = INFINITE; /* default is no timeout */
4719  }
4720  int rc = WaitForSingleObject((HANDLE)h, timeout);
4721  if (rc == WAIT_FAILED)
4722  {
4723  return GetLastError();
4724  }
4725  else
4726  {
4727  return rc; /* format the return code */
4728  }
4729 }
4730 
4731 /*************************************************************************
4732 * Function: SysCreateEventSem *
4733 * *
4734 * Syntax: handle = SysCreateEventSem(<name>,<manual>) *
4735 * *
4736 * Params: name - optional name for a event semaphore *
4737 * any second argument means manual reset event *
4738 * Return: handle - token used as a event sem handle for *
4739 * SysPostEventSem, SysClearEventSem, *
4740 * SysCloseEventSem, and SysOpenEventSem *
4741 * '' - Empty string in case of any error *
4742 *************************************************************************/
4743 
4744 RexxRoutine2(RexxObjectPtr, SysCreateEventSem, OPTIONAL_CSTRING, name, OPTIONAL_CSTRING, reset)
4745 {
4746  HANDLE handle; /* mutex handle */
4747  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4748  bool manual;
4749 
4750  handle = 0; /* zero the handle */
4751  if (reset != NULL)
4752  {
4753  manual = true;
4754  }
4755  else
4756  {
4757  manual = false;
4758  }
4759 
4760  if (name != NULL)
4761  { /* request for named sem */
4762  /* create it by name */
4763  handle = CreateEvent(&sa, manual, false, name);
4764  if (!handle) /* may already be created */
4765  {
4766  /* try to open it */
4767  handle = OpenEvent(EVENT_ALL_ACCESS, true, name);
4768  }
4769  }
4770  else /* unnamed semaphore */
4771  {
4772  handle = CreateEvent(&sa, manual, false, NULL);
4773  }
4774 
4775  if (handle == NULL) {
4776  return context->NullString();
4777  }
4778 
4779  return context->Uintptr((uintptr_t)handle);
4780 }
4781 
4782 /*************************************************************************
4783 * Function: SysOpenEventSem *
4784 * *
4785 * Syntax: result = SysOpenEventSem(name) *
4786 * *
4787 * Params: name - name of the event semaphore *
4788 * *
4789 * Return: result - return code from OpenEvent *
4790 *************************************************************************/
4791 
4792 RexxRoutine1(uintptr_t, SysOpenEventSem, CSTRING, name)
4793 {
4794  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4795 
4796  /* get a binary handle */
4797  return (uintptr_t)OpenEvent(EVENT_ALL_ACCESS, true, name); /* try to open it */
4798 }
4799 
4800 /*************************************************************************
4801 * Function: SysPostEventSem *
4802 * *
4803 * Syntax: result = SysPostEventSem(handle) *
4804 * *
4805 * Params: handle - token returned from SysCreateEventSem *
4806 * *
4807 * Return: result - return code from SetEvent *
4808 *************************************************************************/
4809 
4810 RexxRoutine1(int, SysPostEventSem, uintptr_t, h)
4811 {
4812  return !SetEvent((HANDLE)h) ? GetLastError() : 0;
4813 }
4814 
4815 /*************************************************************************
4816 * Function: SysResetEventSem *
4817 * *
4818 * Syntax: result = SysResetEventSem(handle) *
4819 * *
4820 * Params: handle - token returned from SysCreateEventSem *
4821 * *
4822 * Return: result - return code from ResetEvent *
4823 *************************************************************************/
4824 
4825 RexxRoutine1(int, SysResetEventSem, uintptr_t, h)
4826 {
4827  return !ResetEvent((HANDLE)h) ? GetLastError() : 0;
4828 }
4829 
4830 
4831 /*************************************************************************
4832 * Function: SysPulseEventSem *
4833 * *
4834 * Syntax: result = SysPulseEventSem(handle) *
4835 * *
4836 * Params: handle - token returned from SysCreateEventSem *
4837 * *
4838 * Return: result - return code from PulseEvent *
4839 *************************************************************************/
4840 
4841 RexxRoutine1(int, SysPulseEventSem, uintptr_t, h)
4842 {
4843  return !PulseEvent((HANDLE)h) ? GetLastError() : 0;
4844 }
4845 
4846 
4847 /*************************************************************************
4848 * Function: SysCloseEventSem *
4849 * *
4850 * Syntax: result = SysCloseEventSem(handle) *
4851 * *
4852 * Params: handle - token returned from SysCreateEventSem *
4853 * *
4854 * Return: result - return code from CloseHandle *
4855 *************************************************************************/
4856 
4857 RexxRoutine1(int, SysCloseEventSem, uintptr_t, h)
4858 {
4859  return !CloseHandle((HANDLE)h) ? GetLastError() : 0;
4860 }
4861 
4862 /*************************************************************************
4863 * Function: SysWaitEventSem *
4864 * *
4865 * Syntax: result = SysWaitEventSem(handle, <timeout>) *
4866 * *
4867 * Params: handle - token returned from SysWaitEventSem *
4868 * *
4869 * Return: result - return code from WaitForSingleObject *
4870 *************************************************************************/
4871 
4872 RexxRoutine2(int, SysWaitEventSem, uintptr_t, h, OPTIONAL_int, timeout)
4873 {
4874  if (!argumentExists(2))
4875  {
4876  timeout = INFINITE; /* default is no timeout */
4877  }
4878  /* request the semaphore */
4879  int rc = WaitForSingleObject((HANDLE)h, timeout);
4880  if (rc == WAIT_FAILED)
4881  {
4882  return GetLastError();
4883  }
4884  else
4885  {
4886  return rc; /* format the return code */
4887  }
4888 }
4889 
4890 
4891 /*************************************************************************
4892 * Function: SysSetPriority *
4893 * *
4894 * Syntax: result = SysSetPriority(Class, Level) *
4895 * *
4896 * Params: Class - The priority class (0-3 or HIGH,REALTIME,NORMAL,IDLE) *
4897 * Level - Amount to change (-15 to +15 or IDLE, LOWEST,...) *
4898 * *
4899 *************************************************************************/
4900 
4902 {
4903  HANDLE process = GetCurrentProcess();
4904  HANDLE thread = GetCurrentThread();
4905 
4906  DWORD iclass=-1;
4907  wholenumber_t classLevel; /* priority class */
4908  if (context->WholeNumber(classArg, &classLevel))
4909  {
4910  switch (classLevel)
4911  {
4912  case 0: iclass = IDLE_PRIORITY_CLASS;
4913  break;
4914  case 1: iclass = NORMAL_PRIORITY_CLASS;
4915  break;
4916  case 2: iclass = HIGH_PRIORITY_CLASS;
4917  break;
4918  case 3: iclass = REALTIME_PRIORITY_CLASS;
4919  default:
4920  context->InvalidRoutine();
4921  return 0;
4922  }
4923  }
4924  else
4925  {
4926  const char *classStr = context->ObjectToStringValue(classArg);
4927 
4928  if (stricmp(classStr, "REALTIME") == 0)
4929  {
4930  iclass = REALTIME_PRIORITY_CLASS;
4931  }
4932  else if (stricmp(classStr, "HIGH") == 0)
4933  {
4934  iclass = HIGH_PRIORITY_CLASS;
4935  }
4936  else if (!stricmp(classStr, "NORMAL") == 0)
4937  {
4938  iclass = NORMAL_PRIORITY_CLASS;
4939  }
4940  else if (stricmp(classStr, "IDLE") == 0)
4941  {
4942  iclass = IDLE_PRIORITY_CLASS;
4943  }
4944  else
4945  {
4946  context->InvalidRoutine();
4947  return 0;
4948  }
4949  }
4950 
4951 
4952  wholenumber_t level; /* priority level */
4953  if (context->WholeNumber(levelArg, &level))
4954  {
4955  if (level < -15 || level > 15)
4956  {
4957  context->InvalidRoutine();
4958  return 0;
4959  }
4960  }
4961  else
4962  {
4963  const char *levelStr = context->ObjectToStringValue(levelArg);
4964 
4965  if (stricmp(levelStr, "ABOVE_NORMAL") == 0)
4966  {
4967  level = THREAD_PRIORITY_ABOVE_NORMAL;
4968  }
4969  else if (stricmp(levelStr, "BELOW_NORMAL") == 0)
4970  {
4971  level = THREAD_PRIORITY_BELOW_NORMAL;
4972  }
4973  else if (stricmp(levelStr, "HIGHEST") == 0)
4974  {
4975  level = THREAD_PRIORITY_HIGHEST;
4976  }
4977  else if (stricmp(levelStr, "LOWEST") == 0)
4978  {
4979  level = THREAD_PRIORITY_LOWEST;
4980  }
4981  else if (stricmp(levelStr, "NORMAL") == 0)
4982  {
4983  level = THREAD_PRIORITY_NORMAL;
4984  }
4985  else if (stricmp(levelStr, "IDLE") == 0)
4986  {
4987  level = THREAD_PRIORITY_IDLE;
4988  }
4989  else if (stricmp(levelStr, "TIME_CRITICAL") == 0)
4990  {
4991  level = THREAD_PRIORITY_TIME_CRITICAL;
4992  }
4993  else
4994  {
4995  context->InvalidRoutine();
4996  return 0;
4997  }
4998  }
4999 
5000  int rc = SetPriorityClass(process, iclass);
5001  if (rc)
5002  {
5003  rc = SetThreadPriority(thread, (int)level);
5004  }
5005 
5006  return rc != 0 ? 0 : GetLastError();
5007 }
5008 
5009 
5010 /*************************************************************************
5011 * Function: SysQueryProcess *
5012 * *
5013 * Params: "PID" - (default) returns current process ID *
5014 * "TID" - (default) returns current thread ID *
5015 * "PPRIO" - (default) returns current process priority *
5016 * "TPRIO" - (default) returns current thread priority *
5017 * "PTIME" - (default) returns current process times *
5018 * "TTIME" - (default) returns current thread times *
5019 *************************************************************************/
5020 
5021 RexxRoutine1(RexxObjectPtr, SysQueryProcess, OPTIONAL_CSTRING, option)
5022 {
5023  if (option == NULL || stricmp(option, "PID") == 0)
5024  {
5025  return context->WholeNumber(GetCurrentProcessId());
5026  }
5027  if (stricmp(option, "TID") == 0)
5028  {
5029  return context->WholeNumber(GetCurrentThreadId());
5030  }
5031  if (stricmp(option, "PPRIO") == 0)
5032  {
5033  LONG p;
5034  p = GetPriorityClass(GetCurrentProcess());
5035 
5036  switch (p)
5037  {
5038  case HIGH_PRIORITY_CLASS:
5039  return context->String("HIGH");
5040  case IDLE_PRIORITY_CLASS:
5041  return context->String("IDLE");
5042  case NORMAL_PRIORITY_CLASS:
5043  return context->String("NORMAL");
5044  case REALTIME_PRIORITY_CLASS:
5045  return context->String("REALTIME");
5046  default:
5047  return context->String("UNKNOWN");
5048  }
5049  }
5050  if (stricmp(option, "TPRIO") == 0)
5051  {
5052  LONG p;
5053  p = GetThreadPriority(GetCurrentThread());
5054 
5055  switch (p)
5056  {
5057  case THREAD_PRIORITY_ABOVE_NORMAL:
5058  return context->String("ABOVE_NORMAL");
5059  case THREAD_PRIORITY_BELOW_NORMAL:
5060  return context->String("BELOW_NORMAL");
5061  case THREAD_PRIORITY_HIGHEST:
5062  return context->String("HIGHEST");
5063  case THREAD_PRIORITY_IDLE:
5064  return context->String("IDLE");
5065  case THREAD_PRIORITY_LOWEST:
5066  return context->String("LOWEST");
5067  break;
5068  case THREAD_PRIORITY_NORMAL:
5069  return context->String("NORMAL");
5070  break;
5071  case THREAD_PRIORITY_TIME_CRITICAL:
5072  return context->String("TIME_CRITICAL");
5073  default:
5074  return context->String("UNKNOWN");
5075  }
5076  }
5077  if (stricmp(option, "PTIME") == 0 || stricmp(option, "TTIME") == 0)
5078  {
5079  FILETIME createT, kernelT, userT, dummy;
5080  SYSTEMTIME createST, kernelST, userST;
5081  BOOL ok;
5082 
5083  if (*option == 'T' || *option == 't')
5084  {
5085  ok = GetThreadTimes(GetCurrentThread(), &createT,&dummy,&kernelT, &userT);
5086  }
5087  else
5088  {
5089  ok = GetProcessTimes(GetCurrentProcess(), &createT,&dummy,&kernelT, &userT);
5090  }
5091 
5092  if (ok)
5093  {
5094  FileTimeToLocalFileTime(&createT, &createT);
5095  FileTimeToSystemTime(&createT, &createST);
5096  FileTimeToSystemTime(&kernelT, &kernelST);
5097  FileTimeToSystemTime(&userT, &userST);
5098 
5099  char buffer[256];
5100 
5101  wsprintf(buffer, "Create: %4.4d/%2.2d/%2.2d %d:%2.2d:%2.2d:%3.3d "\
5102  "Kernel: %d:%2.2d:%2.2d:%3.3d User: %d:%2.2d:%2.2d:%3.3d",
5103  createST.wYear,createST.wMonth,createST.wDay,createST.wHour,createST.wMinute,
5104  createST.wSecond,createST.wMilliseconds,
5105  kernelST.wHour,kernelST.wMinute,kernelST.wSecond,kernelST.wMilliseconds,
5106  userST.wHour,userST.wMinute,userST.wSecond,userST.wMilliseconds);
5107 
5108  return context->String(buffer);
5109  }
5110  }
5111  context->InvalidRoutine();
5112  return NULLOBJECT;
5113 }
5114 
5115 
5116 /** SysShutDownSystem()
5117  *
5118  * Interface to the InitiateSystemShutdown() API on Windows.
5119  *
5120  * @param computer The name of the computer to shut down. If omitted
5121  * or the empty string, the local machine is shut
5122  * down. Otherwise this is the name of a remote
5123  * machine to shut down.
5124  * @param message If timout is not 0, a shut down dialog is displayed
5125  * on the machine being shut down, naming the user who
5126  * initiated the shut down, a timer counting down the
5127  * seconds until the machine is shut down, and
5128  * prompting the local user to log off. This parametr
5129  * can be an additional message to add to the dialog
5130  * box. It can be ommitted if no additional message
5131  * is desired.
5132  * @param timeout Number of seconds to display the shut down dialog.
5133  * If this is 0 no dialog is displayed. The default
5134  * is 30 seconds, see the remarks below. The user can
5135  * force a 0 timeout by explicitly specifying 0.
5136  * @param forceAppsClosed If true applications with unsaved data are forcibly
5137  * closed. If false, the user is presented with a
5138  * dialog telling the user to close the applcation(s).
5139  * @param reboot If true, the system is rebooted, if false the
5140  * system is shut down.
5141  *
5142  * @remarks Note that prior to 4.0.0, the defaults for all arguments were
5143  * exactly what the value of each parameter is if omitted.
5144  *
5145  * machine == NULL
5146  * message == NULL
5147  * timeout == 0
5148  * forceAppsClosed == false
5149  * reboot == false
5150  *
5151  * Because of this, there would be no need to check if any argument is
5152  * ommitted or not. However, the consequences of having a 0 timeout
5153  * value are severe if the system has an application open with unsaved
5154  * data. Therefore for 4.0.0 and on the default time out value is
5155  * changed to 30 (seconds.)
5156  */
5157 RexxRoutine5(uint32_t, SysShutDownSystem, OPTIONAL_CSTRING, computer, OPTIONAL_CSTRING, message, OPTIONAL_uint32_t, timeout,
5158  OPTIONAL_logical_t, forceAppsClosed, OPTIONAL_logical_t, reboot)
5159 {
5160  uint32_t result = 0;
5161 
5162  HANDLE hToken = NULL;
5163  TOKEN_PRIVILEGES tkp;
5164 
5165  // First we get the token for the current process so we can add the proper
5166  // shutdown privilege.
5167  if ( OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) == 0 )
5168  {
5169  result = GetLastError();
5170  goto done_out;
5171  }
5172 
5173  // Get the locally unique identifier for the shutdown privilege we need,
5174  // local or remote, depending on what the user specified.
5175  LPCTSTR privilegeName = (computer == NULL || *computer == '\0') ? SE_SHUTDOWN_NAME : SE_REMOTE_SHUTDOWN_NAME;
5176  if ( LookupPrivilegeValue(NULL, privilegeName, &tkp.Privileges[0].Luid) == 0 )
5177  {
5178  result = GetLastError();
5179  goto done_out;
5180  }
5181 
5182  // We are only going to adjust 1 privilege and we are going to enable it.
5183  tkp.PrivilegeCount = 1;
5184  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
5185 
5186  // The return from this function can not accurately be used to determine if
5187  // it failed or not. Instead we need to use GetLastError to determine
5188  // success.
5189  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
5190  result = GetLastError();
5191  if ( result != ERROR_SUCCESS )
5192  {
5193  goto done_out;
5194  }
5195 
5196  // Do not shut down in 0 seconds by default.
5197  if ( argumentOmitted(3) )
5198  {
5199  timeout = 30;
5200  }
5201 
5202  // Now just call the API with the parameters specified by the user.
5203  if ( InitiateSystemShutdown((LPSTR)computer, (LPSTR)message, timeout, (BOOL)forceAppsClosed, (BOOL)reboot) == 0 )
5204  {
5205  result = GetLastError();
5206  }
5207 
5208  // Finally, restore the shutdown privilege for this process to disabled.
5209  tkp.Privileges[0].Attributes = 0;
5210  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
5211 
5212 done_out:
5213  if ( hToken != NULL )
5214  {
5215  CloseHandle(hToken);
5216  }
5217  return result;
5218 }
5219 
5220 /*************************************************************************
5221 * Function: SysSwitchSession *
5222 * *
5223 * Syntax: result = SysSwitchSession(name) *
5224 * *
5225 * Params: name - name of target session *
5226 * *
5227 * Return: OS/2 error return code *
5228 *************************************************************************/
5229 
5230 RexxRoutine1(int, SysSwitchSession, CSTRING, name)
5231 {
5232  HWND hwnd = FindWindow(NULL, name);
5233 
5234  if (hwnd)
5235  {
5236  if (!SetForegroundWindow(hwnd))
5237  {
5238  return GetLastError();
5239  }
5240  else
5241  {
5242  return 0;
5243  }
5244  }
5245  else
5246  {
5247  return 1;
5248  }
5249 }
5250 
5251 /*************************************************************************
5252 * Function: SysWaitNamedPipe *
5253 * *
5254 * Syntax: result = SysWaitNamedPipe(name, timeout) *
5255 * *
5256 * Params: name - name of the pipe *
5257 * timeout - amount of time to wait. *
5258 * *
5259 * Return: Return code from WaitNamedPipe *
5260 *************************************************************************/
5261 
5262 RexxRoutine2(int, SysWaitNamedPipe, CSTRING, name, OPTIONAL_int, timeout)
5263 {
5264  if (argumentOmitted(2))
5265  {
5266  timeout = NMPWAIT_USE_DEFAULT_WAIT;
5267  }
5268  else
5269  {
5270  if (timeout < -1)
5271  {
5272  context->InvalidRoutine();
5273  return 0;
5274  }
5275  if (timeout == 0)
5276  {
5277  timeout = NMPWAIT_USE_DEFAULT_WAIT;
5278  }
5279  else if (timeout == -1)
5280  {
5281  timeout = NMPWAIT_WAIT_FOREVER;
5282  }
5283  }
5284 
5285  if (WaitNamedPipe(name, timeout))
5286  {
5287  return 0;
5288  }
5289  else
5290  {
5291  return GetLastError();
5292  }
5293 }
5294 
5295 
5296 /*************************************************************************
5297 * Function: SysDumpVariables *
5298 * *
5299 * Syntax: result = SysDumpVariables([filename]) *
5300 * *
5301 * Params: filename - name of the file where variables are appended to *
5302 * (dump is written to stdout if omitted) *
5303 * *
5304 * Return: 0 - dump completed OK *
5305 * -1 - failure during dump *
5306 *************************************************************************/
5307 
5308 size_t RexxEntry SysDumpVariables(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5309 {
5310  LONG rc; /* Ret code */
5311  SHVBLOCK shvb;
5312  HANDLE outFile = NULL;
5313  bool fCloseFile = false;
5314  DWORD dwBytesWritten = 0;
5315  char *buffer = NULL; /* ENG: write result file to */
5316  char *current, *end; /* memory first, much faster! */
5317  size_t buffer_size = 10240; /* buffer, realloc'd if needed*/
5318  size_t new_size; /* real new size */
5319 
5320  if ( (numargs > 1) || /* wrong number of arguments? */
5321  ((numargs > 0) && !RXVALIDSTRING(args[0])) )
5322  return INVALID_ROUTINE; /* raise error condition */
5323 
5324  if (numargs > 0)
5325  {
5326  /* open output file for append */
5327  outFile = CreateFile(args[0].strptr, GENERIC_WRITE | GENERIC_READ,
5328  0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
5329  FILE_FLAG_WRITE_THROUGH, NULL);
5330  if (outFile)
5331  {
5332  fCloseFile = true;
5333 
5334  /* seek to end of file */
5335  SetFilePointer(outFile, 0, 0, FILE_END);
5336  }
5337  }
5338  else
5339  outFile = GetStdHandle(STD_OUTPUT_HANDLE);
5340 
5341  /* write results to memory */
5342  /* first and then in one step */
5343  /* to disk */
5344  buffer = (char*) calloc(buffer_size,1);
5345  if (buffer == NULL)
5346  return INVALID_ROUTINE; /* raise error condition */
5347  current = buffer;
5348  end = current + buffer_size;
5349 
5350  do
5351  {
5352  /* prepare request block */
5353  shvb.shvnext = NULL;
5354  shvb.shvname.strptr = NULL; /* let REXX allocate the memory */
5355  shvb.shvname.strlength = 0;
5356  shvb.shvnamelen = 0;
5357  shvb.shvvalue.strptr = NULL; /* let REXX allocate the memory */
5358  shvb.shvvalue.strlength = 0;
5359  shvb.shvvaluelen = 0;
5360  shvb.shvcode = RXSHV_NEXTV;
5361  shvb.shvret = 0;
5362 
5363  rc = RexxVariablePool(&shvb);
5364 
5365  if (rc == RXSHV_OK)
5366  {
5367  new_size = 5 + 9 + 3 + shvb.shvname.strlength + shvb.shvvalue.strlength;
5368  /* if buffer is not big enough, */
5369  /* reallocate */
5370  if (current + new_size >= end) {
5371  size_t offset = current - buffer;
5372  buffer_size *= 2;
5373  /* if new buffer too small, use the minimal fitting size */
5374  if (buffer_size - offset < new_size) {
5375  buffer_size = new_size + offset;
5376  }
5377  buffer = (char *)realloc(buffer,buffer_size);
5378  current = buffer + offset;
5379  end = buffer + buffer_size;
5380  }
5381  sprintf(current, "Name=");
5382  current += 5;
5383  memcpy(current, shvb.shvname.strptr, shvb.shvname.strlength);
5384  current += shvb.shvname.strlength;
5385  sprintf(current, ", Value='");
5386  current += 9;
5387  memcpy(current, shvb.shvvalue.strptr, shvb.shvvalue.strlength);
5388  current += shvb.shvvalue.strlength;
5389  sprintf(current, "'\r\n");
5390  current += 3;
5391 
5392  /* free memory allocated by REXX */
5393  RexxFreeMemory((void *)shvb.shvname.strptr);
5394  RexxFreeMemory((void *)shvb.shvvalue.strptr);
5395 
5396  /* leave loop if this was the last var */
5397  if (shvb.shvret & RXSHV_LVAR)
5398  break;
5399  }
5400  } while (rc == RXSHV_OK);
5401 
5402  WriteFile(outFile, buffer, (DWORD)(current - buffer), &dwBytesWritten, NULL);
5403  free(buffer);
5404 
5405  if (fCloseFile)
5406  CloseHandle(outFile);
5407 
5408  if (rc != RXSHV_LVAR)
5409  RETVAL(-1)
5410  else
5411  RETVAL(0)
5412 }
5413 
5414 
5415 /*************************************************************************
5416 * Function: SysSetFileDateTime *
5417 * *
5418 * Syntax: result = SysSetFileDateTime(filename [,newdate] [,newtime]) *
5419 * *
5420 * Params: filename - name of the file to update *
5421 * newdate - new date to set in format YYYY-MM-DD (YYYY>1800) *
5422 * newtime - new time to set in format HH:MM:SS *
5423 * *
5424 * Return: 0 - file date/time was updated correctly *
5425 * -1 - failure attribute update *
5426 *************************************************************************/
5427 
5428 RexxRoutine3(int, SysSetFileDateTime, CSTRING, name, OPTIONAL_CSTRING, newdate, OPTIONAL_CSTRING, newtime)
5429 {
5430  BOOL fOk = FALSE;
5431  FILETIME sFileTime;
5432  FILETIME sLocalFileTime;
5433  SYSTEMTIME sLocalSysTime;
5434 
5435  /* open output file for read/write for update */
5436  HANDLE setFile = CreateFile(name, GENERIC_WRITE | GENERIC_READ,
5437  FILE_SHARE_READ | FILE_SHARE_WRITE,
5438  NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH |
5439  FILE_FLAG_BACKUP_SEMANTICS, NULL);
5440  if (setFile && (setFile != INVALID_HANDLE_VALUE))
5441  {
5442  fOk = GetFileTime(setFile, NULL, NULL, &sFileTime);
5443  fOk &= FileTimeToLocalFileTime(&sFileTime, &sLocalFileTime);
5444  fOk &= FileTimeToSystemTime(&sLocalFileTime, &sLocalSysTime);
5445  if (fOk)
5446  {
5447  /* file date/time could be read, now parse the new date/time */
5448  if (newdate != NULL)
5449  {
5450  /* parse new date */
5451  if (sscanf(newdate, "%4hu-%2hu-%2hu", &sLocalSysTime.wYear,
5452  &sLocalSysTime.wMonth, &sLocalSysTime.wDay) != 3)
5453  {
5454  fOk = false;
5455  }
5456 
5457  if (sLocalSysTime.wYear < 1800)
5458  {
5459  fOk = false;
5460  }
5461  }
5462 
5463  if (newtime != NULL)
5464  {
5465  /* parse new time */
5466  if (sscanf(newtime, "%2hu:%2hu:%2hu", &sLocalSysTime.wHour,
5467  &sLocalSysTime.wMinute, &sLocalSysTime.wSecond) != 3)
5468  {
5469  fOk = false;
5470  }
5471  }
5472 
5473  if (newdate == NULL && newtime == NULL)
5474  {
5475  /* we set the timestamp to the current time and date */
5476  GetLocalTime(&sLocalSysTime);
5477  }
5478 
5479  if (fOk)
5480  {
5481  fOk &= SystemTimeToFileTime(&sLocalSysTime, &sLocalFileTime);
5482  fOk &= LocalFileTimeToFileTime(&sLocalFileTime, &sFileTime);
5483  fOk &= SetFileTime(setFile, NULL, NULL, &sFileTime);
5484  }
5485  }
5486 
5487  CloseHandle(setFile);
5488  }
5489 
5490  return fOk ? 0 : 1;
5491 }
5492 
5493 /*************************************************************************
5494 * Function: SysGetFileDateTime *
5495 * *
5496 * Syntax: result = SysGetFileDateTime(filename [,timesel]) *
5497 * Params: filename - name of the file to query *
5498 * timesel - What filetime to query: Created/Access/Write *
5499 * *
5500 * Return: -1 - file date/time query failed *
5501 * other - date and time as YYYY-MM-DD HH:MM:SS *
5502 *************************************************************************/
5503 
5504 RexxRoutine2(RexxObjectPtr, SysGetFileDateTime, CSTRING, name, OPTIONAL_CSTRING, selector)
5505 {
5506  FILETIME sFileTime;
5507  FILETIME sLocalFileTime;
5508  FILETIME *psFileCreated = NULL;
5509  FILETIME *psFileAccessed = NULL;
5510  FILETIME *psFileWritten = NULL;
5511  SYSTEMTIME sLocalSysTime;
5512 
5513  if (selector != NULL)
5514  {
5515  switch (selector[0])
5516  {
5517  case 'c':
5518  case 'C':
5519  psFileCreated = &sFileTime;
5520  break;
5521  case 'a':
5522  case 'A':
5523  psFileAccessed = &sFileTime;
5524  break;
5525  case 'w':
5526  case 'W':
5527  psFileWritten = &sFileTime;
5528  break;
5529  default:
5530  context->InvalidRoutine();
5531  return NULLOBJECT;
5532  }
5533  }
5534  else
5535  {
5536  psFileWritten = &sFileTime;
5537  }
5538 
5539  /* open file for read to query time */
5540  HANDLE setFile = CreateFile(name, FILE_READ_ATTRIBUTES,
5541  FILE_SHARE_READ | FILE_SHARE_WRITE,
5542  NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH |
5543  FILE_FLAG_BACKUP_SEMANTICS, NULL);
5544  if (setFile != INVALID_HANDLE_VALUE)
5545  {
5546  BOOL fOk = GetFileTime(setFile, psFileCreated, psFileAccessed, psFileWritten);
5547  CloseHandle(setFile);
5548  fOk &= FileTimeToLocalFileTime(&sFileTime, &sLocalFileTime);
5549  fOk &= FileTimeToSystemTime(&sLocalFileTime, &sLocalSysTime);
5550 
5551  if (fOk)
5552  {
5553  char buffer[256];
5554  sprintf(buffer, "%4d-%02d-%02d %02d:%02d:%02d",
5555  sLocalSysTime.wYear,
5556  sLocalSysTime.wMonth,
5557  sLocalSysTime.wDay,
5558  sLocalSysTime.wHour,
5559  sLocalSysTime.wMinute,
5560  sLocalSysTime.wSecond);
5561  return context->String(buffer);
5562  }
5563  }
5564  return context->WholeNumber(-1);
5565 
5566 }
5567 
5568 
5569 RexxReturnCode REXXENTRY RexxStemSort(const char *stemname, int order, int type,
5570  size_t start, size_t end, size_t firstcol, size_t lastcol);
5571 
5572 /*************************************************************************
5573 * Function: SysStemSort *
5574 * *
5575 * Syntax: result = SysStemSort(stem, order, type, start, end, *
5576 * firstcol, lastcol) *
5577 * *
5578 * Params: stem - name of stem to sort *
5579 * order - 'A' or 'D' for sort order *
5580 * type - 'C', 'I', 'N' for comparision type *
5581 * start - first index to sort *
5582 * end - last index to sort *
5583 * firstcol - first column to use as sort key *
5584 * lastcol - last column to use as sort key *
5585 * *
5586 * Return: 0 - sort was successful *
5587 * -1 - sort failed *
5588 *************************************************************************/
5589 
5590 size_t RexxEntry SysStemSort(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5591 {
5592  CHAR stemName[255];
5593  size_t first = 1;
5594  size_t last = SIZE_MAX;
5595  size_t firstCol = 0;
5596  size_t lastCol = SIZE_MAX;
5597  INT sortType = SORT_CASESENSITIVE;
5598  INT sortOrder = SORT_ASCENDING;
5599 
5600  // validate arguments
5601  if ( (numargs < 1) || (numargs > 7) || !RXVALIDSTRING(args[0]) )
5602  {
5603  return INVALID_ROUTINE;
5604  }
5605 
5606  // remember stem name
5607  memset(stemName, 0, sizeof(stemName));
5608  strcpy(stemName, args[0].strptr);
5609  if ( stemName[args[0].strlength-1] != '.' )
5610  {
5611  stemName[args[0].strlength] = '.';
5612  }
5613 
5614  // check other parameters. sort order
5615  if ( (numargs >= 2) && RXVALIDSTRING(args[1]) )
5616  {
5617  switch ( args[1].strptr[0] )
5618  {
5619  case 'A':
5620  case 'a':
5621  sortOrder = SORT_ASCENDING;
5622  break;
5623  case 'D':
5624  case 'd':
5625  sortOrder = SORT_DECENDING;
5626  break;
5627  default:
5628  return INVALID_ROUTINE;
5629  }
5630  }
5631  // sort type
5632  if ( (numargs >= 3) && RXVALIDSTRING(args[2]) )
5633  {
5634  switch ( args[2].strptr[0] )
5635  {
5636  case 'C':
5637  case 'c':
5638  sortType = SORT_CASESENSITIVE;
5639  break;
5640  case 'I':
5641  case 'i':
5642  sortType = SORT_CASEIGNORE;
5643  break;
5644  default:
5645  return INVALID_ROUTINE;
5646  }
5647  }
5648  // first element to sort
5649  if ( (numargs >= 4) && RXVALIDSTRING(args[3]) )
5650  {
5651  if (!string2size_t(args[3].strptr, &first))
5652  {
5653  return INVALID_ROUTINE;
5654  }
5655  if ( first == 0 )
5656  {
5657  return INVALID_ROUTINE;
5658  }
5659  }
5660  // last element to sort
5661  if ( (numargs >= 5) && RXVALIDSTRING(args[4]) )
5662  {
5663  if (!string2size_t(args[4].strptr, &last))
5664  return INVALID_ROUTINE;
5665  if ( last < first )
5666  return INVALID_ROUTINE;
5667  }
5668  // first column to sort
5669  if ( (numargs >= 6) && RXVALIDSTRING(args[5]) )
5670  {
5671  if (!string2size_t(args[5].strptr, &firstCol))
5672  {
5673  return INVALID_ROUTINE;
5674  }
5675  firstCol--;
5676  }
5677  // last column to sort
5678  if ( (numargs == 7) && RXVALIDSTRING(args[6]) )
5679  {
5680  if (!string2size_t(args[6].strptr, &lastCol))
5681  {
5682  return INVALID_ROUTINE;
5683  }
5684  lastCol--;
5685  if ( lastCol < firstCol )
5686  {
5687  return INVALID_ROUTINE;
5688  }
5689 
5690  }
5691 
5692  // the sorting is done in the interpreter
5693  if ( !RexxStemSort(stemName, sortOrder, sortType, first, last, firstCol, lastCol) )
5694  {
5695  sprintf(retstr->strptr, "-1");
5696  retstr->strlength = 2;
5697  return INVALID_ROUTINE;
5698  }
5699 
5700  sprintf(retstr->strptr, "0");
5701  retstr->strlength = 1;
5702  return VALID_ROUTINE;
5703 }
5704 
5705 
5706 /*************************************************************************
5707 * Function: SysStemDelete *
5708 * *
5709 * Syntax: result = SysStemDelete(stem, startitem [,itemcount]) *
5710 * *
5711 * Params: stem - name of stem where item will be deleted *
5712 * startitem - index of item to delete *
5713 * itemcount - number of items to delete if more than 1 *
5714 * *
5715 * Return: 0 - delete was successful *
5716 * -1 - delete failed *
5717 *************************************************************************/
5718 
5719 RexxRoutine3(int, SysStemDelete, RexxStemObject, toStem, stringsize_t, start, OPTIONAL_stringsize_t, count)
5720 
5721 {
5722  if (argumentOmitted(3))
5723  {
5724  count = 1;
5725  }
5726 
5727  stringsize_t items;
5728 
5729  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
5730  if (temp == NULLOBJECT || !context->StringSize(temp, &items))
5731  {
5732  context->InvalidRoutine();
5733  return 0;
5734  }
5735 
5736  // make sure the deletion site is within the bounds
5737  if (start + count - 1 > items)
5738  {
5739  context->InvalidRoutine();
5740  return 0;
5741  }
5742 
5743  stringsize_t index;
5744  /* now copy the remaining indices up front */
5745  for ( index = start; index + count <= items; index++)
5746  {
5747  // copy from the old index to the new index
5748  RexxObjectPtr value = context->GetStemArrayElement(toStem, index + count);
5749  // is this a sparse array?
5750  if (value == NULLOBJECT)
5751  {
5752  // return this as a failure
5753  return -1;
5754  }
5755  context->SetStemArrayElement(toStem, index, value);
5756  }
5757 
5758  /* now delete the items at the end */
5759  for (index = items - count + 1; index <= items; index++)
5760  {
5761  context->DropStemArrayElement(toStem, index);
5762  }
5763 
5764  context->SetStemArrayElement(toStem, 0, context->StringSize(items - count));
5765  return 0;
5766 }
5767 
5768 
5769 /*************************************************************************
5770 * Function: SysStemInsert *
5771 * *
5772 * Syntax: result = SysStemInsert(stem, position, value) *
5773 * *
5774 * Params: stem - name of stem where item will be inserted *
5775 * position - index where new item will be inserted *
5776 * value - new item value *
5777 * *
5778 * Return: 0 - insert was successful *
5779 * -1 - insert failed *
5780 *************************************************************************/
5781 
5782 RexxRoutine3(int, SysStemInsert, RexxStemObject, toStem, stringsize_t, position, RexxObjectPtr, newValue)
5783 {
5784  stringsize_t count;
5785 
5786  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
5787  if (temp == NULLOBJECT || !context->StringSize(temp, &count))
5788  {
5789  context->InvalidRoutine();
5790  return 0;
5791  }
5792 
5793  /* check wether new position is within limits */
5794  if (position == 0 || (position > count + 1))
5795  {
5796  context->InvalidRoutine();
5797  return 0;
5798  }
5799 
5800  for (size_t index = count; index >= position; index--)
5801  {
5802  // copy from the old index to the new index
5803  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5804  // is this a sparse array?
5805  if (value == NULLOBJECT)
5806  {
5807  // return this as a failure
5808  return -1;
5809  }
5810  context->SetStemArrayElement(toStem, index + 1, value);
5811  }
5812 
5813  // now set the new value and increase the count at stem.0
5814  context->SetStemArrayElement(toStem, position, newValue);
5815  context->SetStemArrayElement(toStem, 0, context->WholeNumber(count + 1));
5816  return 0;
5817 }
5818 
5819 
5820 /*************************************************************************
5821 * Function: SysStemCopy *
5822 * *
5823 * Syntax: result = SysStemCopy(fromstem, tostem, from, to, count *
5824 * [,insert]) *
5825 * *
5826 * Params: fromstem - name of source stem *
5827 * tostem - - name of target stem *
5828 * from - first index in source stem to copy *
5829 * to - position where items are copied/inserted in target stem*
5830 * count - number of items to copy/insert *
5831 * insert - 'I' to indicate insert instead of 'O' overwrite *
5832 * *
5833 * Return: 0 - stem copy was successful *
5834 * -1 - stem copy failed *
5835 *************************************************************************/
5836 
5837 RexxRoutine6(int, SysStemCopy, RexxStemObject, fromStem, RexxStemObject, toStem,
5838  OPTIONAL_stringsize_t, from, OPTIONAL_stringsize_t, to, OPTIONAL_stringsize_t, count,
5839  OPTIONAL_CSTRING, option)
5840 {
5841  bool inserting = false;
5842 
5843  /* get copy type */
5844  if (option != NULL)
5845  {
5846  switch (*option)
5847  {
5848  case 'I':
5849  case 'i':
5850  inserting = true;
5851  break;
5852  case 'O':
5853  case 'o':
5854  inserting = false;
5855  break;
5856  default:
5857  {
5858  context->InvalidRoutine();
5859  return 0;
5860  }
5861  }
5862  }
5863 
5864  stringsize_t fromCount;
5865 
5866  RexxObjectPtr temp = context->GetStemArrayElement(fromStem, 0);
5867  if (temp == NULLOBJECT || !context->StringSize(temp, &fromCount))
5868  {
5869  context->InvalidRoutine();
5870  return 0;
5871  }
5872 
5873  // default from location is the first element
5874  if (argumentOmitted(3))
5875  {
5876  from = 1;
5877  }
5878 
5879  if (argumentOmitted(4))
5880  {
5881  to = 1;
5882  }
5883 
5884  // was a count explicitly specified?
5885  if (argumentExists(5))
5886  {
5887  // this must be in range
5888  if ((count > (fromCount - from + 1)) || (fromCount == 0))
5889  {
5890  context->InvalidRoutine();
5891  return 0;
5892  }
5893  }
5894  else
5895  {
5896  // default is to copy everything from the starting position.
5897  count = fromCount - from + 1;
5898  }
5899 
5900  stringsize_t toCount = 0;
5901  // but if it is set, then use that value
5902  temp = context->GetStemArrayElement(toStem, 0);
5903  if (temp != NULLOBJECT && !context->StringSize(temp, &toCount))
5904  {
5905  context->InvalidRoutine();
5906  return 0;
5907  }
5908 
5909  // copying out of range? Error
5910  if (to > toCount + 1)
5911  {
5912  context->InvalidRoutine();
5913  return 0;
5914  }
5915 
5916  if (inserting)
5917  {
5918  /* if we are about to insert the items we have to make room */
5919  for (size_t index = toCount; index >= to; index--)
5920  {
5921  // copy from the old index to the new index
5922  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5923  // is this a sparse array?
5924  if (value == NULLOBJECT)
5925  {
5926  // return this as a failure
5927  return -1;
5928  }
5929  context->SetStemArrayElement(toStem, index + count, value);
5930  }
5931 
5932 
5933  // set the new count value in the target
5934  toCount += count;
5935  context->SetStemArrayElement(toStem, 0, context->StringSize(toCount));
5936  }
5937  /* now do the actual copying from the source to target */
5938  for (size_t index = 0; index < count; index++)
5939  {
5940  // just retrieve and copy
5941  RexxObjectPtr value = context->GetStemArrayElement(fromStem, from + index);
5942  // is this a sparse array?
5943  if (value == NULLOBJECT)
5944  {
5945  // return this as a failure
5946  return -1;
5947  }
5948  context->SetStemArrayElement(toStem, to + index, value);
5949  }
5950 
5951  // do we need to update the size?
5952  if (to + count - 1 > toCount)
5953  {
5954  context->SetStemArrayElement(toStem, 0, context->StringSize(to + count - 1));
5955  }
5956  return 0;
5957 }
5958 
5959 
5960 /*************************************************************************
5961 * Function: SysUtilVersion *
5962 * *
5963 * Syntax: Say SysUtilVersion *
5964 * *
5965 * Return: REXXUTIL.DLL Version *
5966 *************************************************************************/
5967 
5969 {
5970  char buffer[256];
5971  /* format into the buffer */
5972  sprintf(buffer, "%d.%d.%d", ORX_VER, ORX_REL, ORX_MOD);
5973  return context->String(buffer);
5974 }
5975 
5976 /**
5977  * Check if the dwFlags arguement to WideCharToMultiByte() can be used by the
5978  * specified code page. See MSDN documentation for WideCharToMultiByte() for
5979  * clarification. This is used by SysFromUnicode()
5980  *
5981  * @param cp Code page to check.
5982  *
5983  * @return Return true if dwFlags can be non-zero, return false if dwFlags must
5984  * be zero.
5985  */
5986 static bool canUseWideCharFlags(UINT cp)
5987 {
5988  if ( cp == CP_SYMBOL || cp == CP_UTF7 || cp == CP_UTF8 )
5989  {
5990  return false;
5991  }
5992  if ( 50220 <= cp && cp <= 50222 )
5993  {
5994  return false;
5995  }
5996  if ( cp == 50225 || cp == 50227 || cp == 50229 || cp == 52936 || cp == 54936 )
5997  {
5998  return false;
5999  }
6000  if ( 57002 <= cp && cp <= 57011 )
6001  {
6002  return false;
6003  }
6004  return true;
6005 }
6006 
6007 /*************************************************************************
6008 * Function: SysFromUnicode *
6009 * Converts a UNICODE string to an ASCII string *
6010 * *
6011 * Syntax: result = SysFromUniCode(string,CodePage,MappingFlags, *
6012 * DefaultChar, outstem.) *
6013 * *
6014 * Params: string - unicode string to be converted *
6015 * Codepage - target codepage *
6016 * MappingFlags - Mapping flags *
6017 * DefaultChar - default for unmappable chars *
6018 * outstem. - stem containg the result *
6019 * .!USEDDEFAULTCHAR - 1: character used as default *
6020 * .!TEXT - converted text *
6021 * *
6022 * *
6023 * Return: 0 - successfull completetion *
6024 * error code from WideCharToMultiByte *
6025 
6026  The following are the OEM code-page identifiers.
6027 
6028  437 MS-DOS United States
6029  708 Arabic (ASMO 708)
6030  709 Arabic (ASMO 449+, BCON V4)
6031  710 Arabic (Transparent Arabic)
6032  720 Arabic (Transparent ASMO)
6033  737 Greek (formerly 437G)
6034  775 Baltic
6035  850 MS-DOS Multilingual (Latin I)
6036  852 MS-DOS Slavic (Latin II)
6037  855 IBM Cyrillic (primarily Russian)
6038  857 IBM Turkish
6039  860 MS-DOS Portuguese
6040  861 MS-DOS Icelandic
6041  862 Hebrew
6042  863 MS-DOS Canadian-French
6043  864 Arabic
6044  865 MS-DOS Nordic
6045  866 MS-DOS Russian (former USSR)
6046  869 IBM Modern Greek
6047  874 Thai
6048  932 Japan
6049  936 Chinese (PRC, Singapore)
6050  949 Korean
6051  950 Chinese (Taiwan; Hong Kong SAR, PRC)
6052  1361 Korean (Johab)
6053 
6054  The following are the ANSI code-page identifiers.
6055 
6056  874 Thai
6057  932 Japan
6058  936 Chinese (PRC, Singapore)
6059  949 Korean
6060  950 Chinese (Taiwan; Hong Kong SAR, PRC)
6061  1200 Unicode (BMP of ISO 10646)
6062  1250 Windows 3.1 Eastern European
6063  1251 Windows 3.1 Cyrillic
6064  1252 Windows 3.1 Latin 1 (US, Western Europe)
6065  1253 Windows 3.1 Greek
6066  1254 Windows 3.1 Turkish
6067  1255 Hebrew
6068  1256 Arabic
6069  1257 Baltic
6070 
6071  COMPOSITECHECK :
6072  Convert composite characters to precomposed characters.
6073 
6074  DISCARDNS :
6075  Discard nonspacing characters during conversion.
6076 
6077  SEPCHARS :
6078  Generate separate characters during conversion. This is the default conversion behavior.
6079 
6080  DEFAULTCHAR :
6081  Replace exceptions with the default character during conversion.
6082 
6083 *************************************************************************/
6084 
6085 RexxRoutine5(int, SysFromUniCode, RexxStringObject, sourceString, OPTIONAL_CSTRING, codePageOpt,
6086  OPTIONAL_CSTRING, mappingFlags, OPTIONAL_CSTRING, defaultChar, RexxStemObject, stem)
6087 {
6088  const char *source = context->StringData(sourceString);
6089  size_t sourceLength = context->StringLength(sourceString);
6090 
6091  UINT codePage;
6092  /* evaluate codepage */
6093  if (codePageOpt == NULL)
6094  {
6095  codePage = GetOEMCP();
6096  }
6097  else
6098  {
6099  if (_stricmp(codePageOpt, "THREAD_ACP") == 0)
6100  {
6101  codePage = CP_THREAD_ACP;
6102  }
6103  else if (_stricmp(codePageOpt,"ACP") == 0)
6104  {
6105  codePage = CP_ACP;
6106  }
6107  else if (_stricmp(codePageOpt,"MACCP") == 0)
6108  {
6109  codePage = CP_MACCP;
6110  }
6111  else if (_stricmp(codePageOpt,"OEMCP") == 0)
6112  {
6113  codePage = CP_OEMCP;
6114  }
6115  else if (_stricmp(codePageOpt,"SYMBOL") == 0)
6116  {
6117  codePage = CP_SYMBOL;
6118  }
6119  else if (_stricmp(codePageOpt,"UTF7") == 0)
6120  {
6121  codePage = CP_UTF7;
6122  }
6123  else if (_stricmp(codePageOpt,"UTF8") == 0)
6124  {
6125  codePage = CP_UTF8;
6126  }
6127  else
6128  {
6129  codePage = atoi(codePageOpt);
6130  }
6131  }
6132 
6133  DWORD dwFlags = 0;
6134  /* evaluate the mapping flags */
6135  if (mappingFlags != NULL && *mappingFlags != '\0' )
6136  {
6137  /* The WC_SEPCHARS, WC_DISCARDNS, and WC_DEFAULTCHAR flags must also
6138  * specify the WC_COMPOSITECHECK flag. So, we add that for the user if
6139  * they skipped it. Those 4 flags are only available for code pages <
6140  * 50000, excluding 42 (CP_SYMBOL). See the remarks section in the MSDN
6141  * docs for clarification.
6142  */
6143  if ( codePage < 50000 && codePage != CP_SYMBOL )
6144  {
6145  if ( StrStrI(mappingFlags, "COMPOSITECHECK") != NULL )
6146  {
6147  dwFlags |= WC_COMPOSITECHECK;
6148  }
6149  if ( StrStrI(mappingFlags, "SEPCHARS") != NULL )
6150  {
6151  dwFlags |= WC_SEPCHARS | WC_COMPOSITECHECK;
6152  }
6153  if ( StrStrI(mappingFlags, "DISCARDNS") != NULL )
6154  {
6155  dwFlags |= WC_DISCARDNS| WC_COMPOSITECHECK;
6156  }
6157  if ( StrStrI(mappingFlags, "DEFAULTCHAR") != NULL )
6158  {
6159  dwFlags |= WC_DEFAULTCHAR | WC_COMPOSITECHECK;
6160  }
6161  }
6162 
6163  if ( StrStrI(mappingFlags, "NO_BEST_FIT") != NULL )
6164  {
6165  dwFlags |= WC_NO_BEST_FIT_CHARS;
6166  }
6167 
6168  if ( StrStrI(mappingFlags, "ERR_INVALID") != NULL )
6169  {
6170  if ( codePage == CP_UTF8 && isAtLeastVista() )
6171  {
6172  dwFlags |= WC_ERR_INVALID_CHARS;
6173  }
6174  }
6175  else if ( dwFlags == 0 && ! (codePage < 50000 && codePage != CP_SYMBOL) )
6176  {
6177  context->InvalidRoutine();
6178  return 0;
6179  }
6180  }
6181 
6182  /* evaluate default charcter */
6183  const char *strDefaultChar = NULL;
6184  BOOL bUsedDefaultChar = FALSE;
6185  BOOL *pUsedDefaultChar = &bUsedDefaultChar;
6186 
6187  if (defaultChar != NULL && (dwFlags & WC_DEFAULTCHAR) == WC_DEFAULTCHAR)
6188  {
6189  strDefaultChar = defaultChar;
6190  }
6191  else
6192  {
6193  /* use our own default character rather than relying on the windows default */
6194  strDefaultChar = "?";
6195  }
6196 
6197  /* There are a number of invalid combinations of arguments to
6198  * WideCharToMultiByte(), see the MSDN docs. Eliminate them here.
6199  */
6200  if ( codePage == CP_UTF8 && dwFlags == WC_ERR_INVALID_CHARS)
6201  {
6202  strDefaultChar = NULL;
6203  pUsedDefaultChar = NULL;
6204  }
6205  else if ( ! canUseWideCharFlags(codePage) )
6206  {
6207  dwFlags = 0;
6208  strDefaultChar = NULL;
6209  pUsedDefaultChar = NULL;
6210  }
6211 
6212  /* Allocate space for the string, to allow double zero byte termination */
6213  char *strptr = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sourceLength + 4);
6214  if (strptr == NULL)
6215  {
6216  context->InvalidRoutine();
6217  return 0;
6218  }
6219  memcpy(strptr, source, sourceLength);
6220 
6221  /* Query the number of bytes required to store the Dest string */
6222  int iBytesNeeded = WideCharToMultiByte( codePage,
6223  dwFlags,
6224  (LPWSTR) strptr,
6225  (int)(sourceLength/2),
6226  NULL,
6227  0,
6228  NULL,
6229  NULL);
6230 
6231  if (iBytesNeeded == 0)
6232  {
6233  GlobalFree(strptr);
6234  return GetLastError();
6235  }
6236 
6237  // hard error, stop
6238  char *str = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, iBytesNeeded + 4);
6239  if (str == NULL)
6240  {
6241  context->InvalidRoutine();
6242  return 0;
6243  }
6244 
6245  /* Do the conversion */
6246  int iBytesDestination = WideCharToMultiByte(codePage, // codepage
6247  dwFlags, // conversion flags
6248  (LPWSTR) strptr, // source string
6249  (int)(sourceLength/2), // source string length
6250  str, // target string
6251  (int)iBytesNeeded, // size of target buffer
6252  strDefaultChar,
6253  pUsedDefaultChar);
6254 
6255  if (iBytesDestination == 0) // call to function fails
6256  {
6257  GlobalFree(str); // free allocated string
6258  GlobalFree(strptr); // free allocated string
6259  return GetLastError(); // return error from function call
6260  }
6261 
6262  // set whether the default character was used in the output stem
6263  if (bUsedDefaultChar)
6264  {
6265  context->SetStemElement(stem, "!USEDDEFAULTCHAR", context->True());
6266  }
6267  else
6268  {
6269  context->SetStemElement(stem, "!USEDDEFAULTCHAR", context->False());
6270  }
6271 
6272  context->SetStemElement(stem, "!TEXT", context->String(str, iBytesNeeded));
6273  GlobalFree(strptr); // free allocated string
6274  GlobalFree(str); // free allocated string
6275  return 0;
6276 }
6277 
6278 /*
6279 * Syntax: result = SysToUniCode(string,CodePage,MappingFlags,outstem.)
6280 */
6281 /*************************************************************************
6282 * Function: SysToUnicode *
6283 * Converts an ASCII to UNICODE *
6284 * *
6285 * Syntax: result = SysToUniCode(string,CodePage,MappingFlags,outstem.)*
6286 * *
6287 * Params: string - ascii string to be converted *
6288 * Codepage - target codepage *
6289 * MappingFlags - Mapping flags *
6290 * outstem. - stem containg the result *
6291 * .!TEXT - converted text *
6292 * *
6293 * Return: 0 - successfull completetion *
6294 * error code from WideCharToMultiByteToWideChars *
6295 
6296  For available codepages see function SysFromUniCode.
6297 
6298  Additional parameters for codepages:
6299 
6300  ACP ANSI code page
6301  MACCP Macintosh code page
6302  OEMCP OEM code page
6303  SYMBOL Windows 2000: Symbol code page (42)
6304  THREAD_ACP Windows 2000: The current thread's ANSI code page
6305  UTF7 Windows NT 4.0 and Windows 2000: Translate using UTF-7
6306  UTF8 Windows NT 4.0 and Windows 2000: Translate using UTF-8.
6307  When this is set, dwFlags must be zero.
6308 
6309  PRECOMPOSED Always use precomposed characters-that is, characters
6310  in which a base character and a nonspacing character
6311  have a single character value.
6312  This is the default translation option.
6313  Cannot be used with MB_COMPOSITE.
6314  COMPOSITE Always use composite characters that is,
6315  characters in which a base character and a nonspacing
6316  character have different character values.
6317  Cannot be used with MB_PRECOMPOSED.
6318  ERR_INVALID_CHARS If the function encounters an invalid input character,
6319  it fails and GetLastError returns
6320  ERROR_NO_UNICODE_TRANSLATION.
6321  USEGLYPHCHARS Use glyph characters instead of control characters.
6322 
6323 
6324 
6325 *************************************************************************/
6326 RexxRoutine4(int, SysToUniCode, RexxStringObject, source, OPTIONAL_CSTRING, codePageOpt,
6327  OPTIONAL_CSTRING, mappingFlags, RexxStemObject, stem)
6328 {
6329  // evaluate codepage
6330  UINT codePage;
6331  if (codePageOpt == NULL)
6332  {
6333  codePage = GetOEMCP();
6334  }
6335  else
6336  {
6337  if (_stricmp(codePageOpt,"THREAD_ACP") == 0)
6338  {
6339  codePage = CP_THREAD_ACP;
6340  }
6341  else if (_stricmp(codePageOpt,"ACP") == 0)
6342  {
6343  codePage = CP_ACP;
6344  }
6345  else if (_stricmp(codePageOpt,"MACCP") == 0)
6346  {
6347  codePage = CP_MACCP;
6348  }
6349  else if (_stricmp(codePageOpt,"OEMCP") == 0)
6350  {
6351  codePage = CP_OEMCP;
6352  }
6353  else if (_stricmp(codePageOpt,"SYMBOL") == 0)
6354  {
6355  codePage = CP_SYMBOL;
6356  }
6357  else if (_stricmp(codePageOpt,"UTF7") == 0)
6358  {
6359  codePage = CP_UTF7;
6360  }
6361  else if (_stricmp(codePageOpt,"UTF8") == 0)
6362  {
6363  codePage = CP_UTF8;
6364  }
6365  else
6366  {
6367  codePage = atoi(codePageOpt);
6368  }
6369  }
6370 
6371  DWORD dwFlags = 0;
6372  // evaluate the mapping flags
6373  if (mappingFlags != NULL)
6374  {
6375  if (mystrstr(mappingFlags, "PRECOMPOSED"))
6376  {
6377  dwFlags |= MB_PRECOMPOSED;
6378  }
6379  if (mystrstr(mappingFlags, "COMPOSITE"))
6380  {
6381  dwFlags |= MB_COMPOSITE;
6382  }
6383  if (mystrstr(mappingFlags, "ERR_INVALID"))
6384  {
6385  dwFlags |= MB_ERR_INVALID_CHARS;
6386  }
6387  if (mystrstr(mappingFlags, "USEGLYPHCHARS"))
6388  {
6389  dwFlags |= MB_USEGLYPHCHARS;
6390  }
6391  if (dwFlags == 0)
6392  {
6393  context->InvalidRoutine();
6394  return 0;
6395  }
6396  }
6397 
6398  /* Query the number of bytes required to store the Dest string */
6399  ULONG ulWCharsNeeded = MultiByteToWideChar( codePage, dwFlags,
6400  context->StringData(source), (int)context->StringLength(source), NULL, NULL);
6401 
6402  if (ulWCharsNeeded == 0)
6403  {
6404  return GetLastError();
6405  }
6406 
6407  ULONG ulDataLen = (ulWCharsNeeded)*2;
6408 
6409  LPWSTR lpwstr = (LPWSTR)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, ulDataLen+4);
6410 
6411  // hard error, stop
6412  if (lpwstr == NULL)
6413  {
6414  context->InvalidRoutine();
6415  return 0;
6416  }
6417 
6418 
6419  /* Do the conversion */
6420  ulWCharsNeeded = MultiByteToWideChar(codePage, dwFlags,
6421  context->StringData(source), (int)context->StringLength(source),
6422  lpwstr, ulWCharsNeeded);
6423 
6424  if (ulWCharsNeeded == 0) // call to function fails
6425  {
6426  GlobalFree(lpwstr); // free allocated string
6427  return GetLastError();
6428  }
6429 
6430  context->SetStemElement(stem, "!TEXT", context->String((const char *)lpwstr, ulDataLen));
6431  GlobalFree(lpwstr); // free allocated string
6432  return 0;
6433 }
6434 
6435 /*************************************************************************
6436 * Function: SysWinGetPrinters *
6437 * *
6438 * Syntax: call SysWinGetPrinters stem. *
6439 * *
6440 * Params: stem. - stem to store infos in *
6441 * *
6442 * Return: error number *
6443 *************************************************************************/
6444 
6445 RexxRoutine1(uint32_t, SysWinGetPrinters, RexxStemObject, stem)
6446 {
6447  DWORD realSize = 0;
6448  DWORD entries = 0;
6449  DWORD currentSize = 10*sizeof(PRINTER_INFO_2)*sizeof(char);
6450  char *pArray = (char*) malloc(sizeof(char)*currentSize);
6451 
6452  while (true)
6453  {
6454  if (EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE)pArray,
6455  currentSize, &realSize, &entries) == 0)
6456  {
6457  // this is not a failure if we get ERROR_INSUFFICIENT_BUFFER
6458  DWORD rc = GetLastError();
6459  if ( rc != ERROR_INSUFFICIENT_BUFFER )
6460  {
6461  free(pArray);
6462  return rc;
6463  }
6464  }
6465  if ( currentSize >= realSize )
6466  {
6467  break;
6468  }
6469  currentSize = realSize;
6470  realSize = 0;
6471  pArray = (char*) realloc(pArray, sizeof(char)*currentSize);
6472  }
6473 
6474  PRINTER_INFO_2 *pResult = (PRINTER_INFO_2*) pArray;
6475 
6476  // set stem.0 to the number of entries then add all the found printers
6477  context->SetStemArrayElement(stem, 0, context->WholeNumber(entries));
6478  while ( entries-- )
6479  {
6480  char szBuffer[256];
6481  sprintf(szBuffer,"%s,%s,%s", pResult[entries].pPrinterName, pResult[entries].pDriverName,
6482  pResult[entries].pPortName);
6483  context->SetStemArrayElement(stem, entries + 1, context->String(szBuffer));
6484  }
6485  free(pArray);
6486  return 0; // a little reversed...success is false, failure is true
6487 }
6488 
6489 /*************************************************************************
6490 * Function: SysWinGetDefaultPrinter *
6491 * *
6492 * Syntax: call SysWinGetDefaultPrinter *
6493 * *
6494 * Params: none *
6495 * *
6496 * Return: string describing default printer *
6497 *************************************************************************/
6498 
6499 RexxRoutine0(RexxStringObject, SysWinGetDefaultPrinter)
6500 {
6501  char buffer[256];
6502  buffer[0] = '\0';
6503 
6504  GetProfileString("Windows", "DEVICE", ",,,", buffer, sizeof(buffer));
6505  return context->String(buffer);
6506 }
6507 
6508 
6509 /*************************************************************************
6510 * Function: SysWinSetDefaultPrinter *
6511 * *
6512 * Syntax: call SysWinSetDefaultPrinter printer *
6513 * *
6514 * Params: string describing default printer *
6515 * *
6516 * Return: 0 on success, otherwise the OS system error number. *
6517 *************************************************************************/
6518 RexxRoutine1(int, SysWinSetDefaultPrinter, CSTRING, printer)
6519 {
6520  int count = 0;
6521 
6522  // Two forms of input are allowed. The old form of
6523  // "Printername,Drivername,Portname" and for W2K or later just the printer
6524  // name. Count the commas to determine which form this might be.
6525  for ( size_t i = 0; printer[i] != '\0'; i++ )
6526  {
6527  if (printer[i] == ',' )
6528  {
6529  count++;
6530  }
6531  }
6532 
6533  if (count != 0 && count != 2)
6534  {
6535  context->InvalidRoutine();
6536  return 0;
6537  }
6538  SetLastError(0);
6539 
6540  if (count == 0 )
6541  {
6542  // This is W2K or later and the user specified just the printer name.
6543  // This code will work on W2K through Vista.
6544  if (SetDefaultPrinter(printer) == 0)
6545  {
6546  return 0;
6547  }
6548  else
6549  {
6550  return GetLastError();
6551  }
6552  }
6553  else
6554  {
6555  // The user still specified the old format. Microssoft
6556  // only provides WriteProfileString() for backward compatibility to
6557  // 16-bit Windows, and at some point this may no longer be supported.
6558  // But it still appears to work on XP.
6559  if (WriteProfileString("Windows", "DEVICE", printer) == 0)
6560  {
6561  return 0;
6562  }
6563  else
6564  {
6565  if ( SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0L, 0L, SMTO_NORMAL, 1000, NULL) == 0 )
6566  {
6567  // If a window just timed out, then GetLastError() will return 0
6568  // and the user will get the succes code. If GetLastError()
6569  // does not return 0, then chances are something really is
6570  // wrong.
6571  return 0;
6572  }
6573  else
6574  {
6575  return GetLastError();
6576  }
6577  }
6578  }
6579 }
6580 
6581 
6582 /*************************************************************************
6583 * Function: SysFileCopy *
6584 * *
6585 * Syntax: call SysFileCopy FROMfile TOfile *
6586 * *
6587 * Params: FROMfile - file to be copied. *
6588 * TOfile - target file of copy operation. *
6589 * *
6590 * Return: Return code from CopyFile() function. *
6591 *************************************************************************/
6592 
6593 RexxRoutine2(int, SysFileCopy, CSTRING, fromFile, CSTRING, toFile)
6594 {
6595  return CopyFile(fromFile, toFile, 0) ? 0 : GetLastError();
6596 }
6597 
6598 /*************************************************************************
6599 * Function: SysFileMove *
6600 * *
6601 * Syntax: call SysFileMove FROMfile TOfile *
6602 * *
6603 * Params: FROMfile - file to be moved. *
6604 * TOfile - target file of move operation. *
6605 * *
6606 * Return: Return code from MoveFile() function. *
6607 *************************************************************************/
6608 
6609 RexxRoutine2(int, SysFileMove, CSTRING, fromFile, CSTRING, toFile)
6610 {
6611  return MoveFile(fromFile, toFile) ? 0 : GetLastError();
6612 }
6613 
6614 /*************************************************************************
6615 * Function: SysFileExist *
6616 * *
6617 * Syntax: call SysFileExist file *
6618 * *
6619 * Params: file - file to check existance of. *
6620 * *
6621 * Return: Logical. *
6622 *************************************************************************/
6623 
6624 RexxRoutine1(logical_t, SysIsFile, CSTRING, file)
6625 {
6626  DWORD dwAttrs = GetFileAttributes(file);
6627  // not a file if either of these is one
6628  return (dwAttrs != 0xffffffff) && ((dwAttrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == 0);
6629 }
6630 
6631 /*************************************************************************
6632 * Function: SysDirExist *
6633 * *
6634 * Syntax: call SysDirExist dir *
6635 * *
6636 * Params: dir - dir to check existance of. *
6637 * *
6638 * Return: Logical. *
6639 *************************************************************************/
6640 
6641 RexxRoutine1(logical_t, SysIsFileDirectory, CSTRING, file)
6642 {
6643  DWORD dwAttrs = GetFileAttributes(file);
6644  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY);
6645 }
6646 
6647 /*************************************************************************
6648 * Function: SysIsFileLink *
6649 * *
6650 * Syntax: call SysIsFileLink file *
6651 * *
6652 * Params: file - file to check if it is a Link (Alias). *
6653 * *
6654 * Return: Logical. *
6655 *************************************************************************/
6656 
6657 RexxRoutine1(logical_t, SysIsFileLink, CSTRING, file)
6658 {
6659  DWORD dwAttrs = GetFileAttributes(file);
6660  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT);
6661 }
6662 
6663 /*************************************************************************
6664 * Function: SysIsFileCompressed *
6665 * *
6666 * Syntax: call SysIsFileCompressed file *
6667 * *
6668 * Params: file - file to check if it is compressed. *
6669 * *
6670 * Return: Logical. *
6671 *************************************************************************/
6672 
6673 RexxRoutine1(logical_t, SysIsFileCompressed, CSTRING, file)
6674 {
6675  DWORD dwAttrs = GetFileAttributes(file);
6676  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_COMPRESSED);
6677 }
6678 
6679 /*************************************************************************
6680 * Function: SysIsFileEncrypted *
6681 * *
6682 * Syntax: call SysIsFileEncrypted file *
6683 * *
6684 * Params: file - file to check if it is Encrypted. *
6685 * *
6686 * Return: Logical. *
6687 *************************************************************************/
6688 
6689 RexxRoutine1(logical_t, SysIsFileEncrypted, CSTRING, file)
6690 {
6691  DWORD dwAttrs = GetFileAttributes(file);
6692  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_ENCRYPTED);
6693 }
6694 
6695 /*************************************************************************
6696 * Function: SysIsFileNotContentIndexed *
6697 * *
6698 * Syntax: call SysIsFileNotContentIndexed file *
6699 * *
6700 * Params: file - file to check if it is to be indexed by the indexing *
6701 * service. *
6702 * *
6703 * Return: Logical. *
6704 *************************************************************************/
6705 
6706 RexxRoutine1(logical_t, SysIsFileNotContentIndexed, CSTRING, file)
6707 {
6708  DWORD dwAttrs = GetFileAttributes(file);
6709  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
6710 }
6711 
6712 /*************************************************************************
6713 * Function: SysIsFileOffline *
6714 * *
6715 * Syntax: call SysIsFileOffline file *
6716 * *
6717 * Params: file - file to check if it is offline *
6718 * *
6719 * Return: Logical. *
6720 *************************************************************************/
6721 
6722 RexxRoutine1(logical_t, SysIsFileOffline, CSTRING, file)
6723 {
6724  DWORD dwAttrs = GetFileAttributes(file);
6725  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_OFFLINE);
6726 }
6727 
6728 /*************************************************************************
6729 * Function: SysIsFileSparse *
6730 * *
6731 * Syntax: call SysIsFileSparse file *
6732 * *
6733 * Params: file - file to check if it is sparse *
6734 * *
6735 * Return: Logical. *
6736 *************************************************************************/
6737 
6738 RexxRoutine1(logical_t, SysIsFileSparse, CSTRING, file)
6739 {
6740  DWORD dwAttrs = GetFileAttributes(file);
6741  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_SPARSE_FILE);
6742 }
6743 
6744 
6745 /*************************************************************************
6746 * Function: SysIsFileTemporary *
6747 * *
6748 * Syntax: call SysIsFileTemporary file *
6749 * *
6750 * Params: file - file to check if it is temporary *
6751 * *
6752 * Return: Logical. *
6753 *************************************************************************/
6754 
6755 RexxRoutine1(logical_t, SysIsFileTemporary, CSTRING, file)
6756 {
6757  DWORD dwAttrs = GetFileAttributes(file);
6758  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_TEMPORARY);
6759 }
6760 
6761 
6762 /*************************************************************************
6763 * Function: SysFileExists *
6764 * *
6765 * Syntax: call SysFileExists file *
6766 * *
6767 * Params: file - file to check existence *
6768 * *
6769 * Return: Logical. *
6770 *************************************************************************/
6771 
6772 RexxRoutine1(logical_t, SysFileExists, CSTRING, file)
6773 {
6774  DWORD dwAttrs = GetFileAttributes(file);
6775  return (dwAttrs != 0xffffffff);
6776 }
6777 
6778 
6779 /*************************************************************************
6780 * Function: SysGetLongPathName *
6781 * Converts the specified path to its long form *
6782 * *
6783 * Syntax: longPath = SysGetLongPathName(path) *
6784 * *
6785 * Params: path - a path to an existing file *
6786 * *
6787 * Return: longPath - path converted to its long form *
6788 * NULL string if path doesn't exist or call fails *
6789 *************************************************************************/
6790 
6791 RexxRoutine1(RexxStringObject, SysGetLongPathName, CSTRING, path)
6792 {
6793  CHAR longPath[MAX]; // long version of path
6794  DWORD code = GetLongPathName(path, longPath, MAX);
6795  if ((code == 0) || (code >= MAX)) // call failed of buffer too small
6796  {
6797  return context->NullString();
6798  }
6799  else
6800  {
6801  return context->NewStringFromAsciiz(longPath);
6802  }
6803 }
6804 
6805 
6806 /*************************************************************************
6807 * Function: SysGetShortPathName *
6808 * Converts the specified path to its short form *
6809 * *
6810 * Syntax: shortPath = SysGetShortPathName(path) *
6811 * *
6812 * Params: path - a path to an existing file *
6813 * *
6814 * Return: shortPath - path converted to its short form *
6815 * NULL string if path doesn't exist or call fails *
6816 *************************************************************************/
6817 
6818 RexxRoutine1(RexxStringObject, SysGetShortPathName, CSTRING, path)
6819 {
6820  CHAR shortPath[MAX]; // short version of path
6821  DWORD code = GetShortPathName(path, shortPath, MAX);
6822  if ((code == 0) || (code >= MAX)) // call failed of buffer too small
6823  {
6824  return context->NullString();
6825  }
6826  else
6827  {
6828  return context->NewStringFromAsciiz(shortPath);
6829  }
6830 }
6831 
6832 
6833 // now build the actual entry list
6835 {
6837  REXX_TYPED_ROUTINE(SysCurPos, SysCurPos),
6838  REXX_TYPED_ROUTINE(SysCurState, SysCurState),
6844  REXX_TYPED_ROUTINE(SysFileTree, SysFileTree),
6853  REXX_TYPED_ROUTINE(SysSleep, SysSleep),
6855  REXX_TYPED_ROUTINE(SysTextScreenRead, SysTextScreenRead),
6856  REXX_TYPED_ROUTINE(SysTextScreenSize, SysTextScreenSize),
6857  REXX_TYPED_ROUTINE(SysAddRexxMacro, SysAddRexxMacro),
6858  REXX_TYPED_ROUTINE(SysDropRexxMacro, SysDropRexxMacro),
6859  REXX_TYPED_ROUTINE(SysReorderRexxMacro, SysReorderRexxMacro),
6860  REXX_TYPED_ROUTINE(SysQueryRexxMacro, SysQueryRexxMacro),
6861  REXX_TYPED_ROUTINE(SysClearRexxMacroSpace, SysClearRexxMacroSpace),
6862  REXX_TYPED_ROUTINE(SysLoadRexxMacroSpace, SysLoadRexxMacroSpace),
6863  REXX_TYPED_ROUTINE(SysSaveRexxMacroSpace, SysSaveRexxMacroSpace),
6868  REXX_TYPED_ROUTINE(SysCreateMutexSem, SysCreateMutexSem),
6869  REXX_TYPED_ROUTINE(SysOpenMutexSem, SysOpenMutexSem),
6870  REXX_TYPED_ROUTINE(SysCloseMutexSem, SysCloseMutexSem),
6871  REXX_TYPED_ROUTINE(SysRequestMutexSem, SysRequestMutexSem),
6872  REXX_TYPED_ROUTINE(SysReleaseMutexSem, SysReleaseMutexSem),
6873  REXX_TYPED_ROUTINE(SysCreateEventSem, SysCreateEventSem),
6874  REXX_TYPED_ROUTINE(SysOpenEventSem, SysOpenEventSem),
6875  REXX_TYPED_ROUTINE(SysCloseEventSem, SysCloseEventSem),
6876  REXX_TYPED_ROUTINE(SysResetEventSem, SysResetEventSem),
6877  REXX_TYPED_ROUTINE(SysPostEventSem, SysPostEventSem),
6878  REXX_TYPED_ROUTINE(SysPulseEventSem, SysPulseEventSem),
6879  REXX_TYPED_ROUTINE(SysWaitEventSem, SysWaitEventSem),
6881  REXX_TYPED_ROUTINE(SysSwitchSession, SysSwitchSession),
6882  REXX_TYPED_ROUTINE(SysWaitNamedPipe, SysWaitNamedPipe),
6888  REXX_TYPED_ROUTINE(SysStemDelete, SysStemDelete),
6889  REXX_TYPED_ROUTINE(SysStemInsert, SysStemInsert),
6890  REXX_TYPED_ROUTINE(SysStemCopy, SysStemCopy),
6896  REXX_TYPED_ROUTINE(SysFromUniCode, SysFromUniCode),
6897  REXX_TYPED_ROUTINE(SysToUniCode, SysToUniCode),
6898  REXX_TYPED_ROUTINE(SysWinGetPrinters, SysWinGetPrinters),
6899  REXX_TYPED_ROUTINE(SysWinGetDefaultPrinter, SysWinGetDefaultPrinter),
6900  REXX_TYPED_ROUTINE(SysWinSetDefaultPrinter, SysWinSetDefaultPrinter),
6901 
6902  REXX_TYPED_ROUTINE(SysShutDownSystem, SysShutDownSystem),
6903  REXX_TYPED_ROUTINE(SysFileCopy, SysFileCopy),
6904  REXX_TYPED_ROUTINE(SysFileMove, SysFileMove),
6905  REXX_TYPED_ROUTINE(SysIsFile, SysIsFile),
6906  REXX_TYPED_ROUTINE(SysIsFileDirectory, SysIsFileDirectory),
6907  REXX_TYPED_ROUTINE(SysIsFileLink, SysIsFileLink),
6908  REXX_TYPED_ROUTINE(SysIsFileCompressed, SysIsFileCompressed),
6909  REXX_TYPED_ROUTINE(SysIsFileEncrypted, SysIsFileEncrypted),
6910  REXX_TYPED_ROUTINE(SysIsFileNotContentIndexed, SysIsFileNotContentIndexed),
6911  REXX_TYPED_ROUTINE(SysIsFileOffline, SysIsFileOffline),
6912  REXX_TYPED_ROUTINE(SysIsFileSparse, SysIsFileSparse),
6913  REXX_TYPED_ROUTINE(SysIsFileTemporary, SysIsFileTemporary),
6914  REXX_TYPED_ROUTINE(SysFileExists, SysFileExists),
6915  REXX_TYPED_ROUTINE(SysGetLongPathName, SysGetLongPathName),
6916  REXX_TYPED_ROUTINE(SysGetShortPathName, SysGetShortPathName),
6918 };
6919 
6921 {
6923  REXX_INTERPRETER_4_0_0, // anything after 4.0.0 will work
6924  "REXXUTIL", // name of the package
6925  "4.0", // package information
6926  NULL, // no load/unload functions
6927  NULL,
6928  rexxutil_routines, // the exported functions
6929  NULL // no methods in this package
6930 };
6931 
6932 // package loading stub.
#define min(a, b)
Definition: ArrayClass.cpp:82
RexxReturnCode RexxEntry RexxVariablePool(PSHVBLOCK pshvblock)
int type
Definition: cmdparse.cpp:1888
#define argumentExists(i)
Definition: oorexxapi.h:3777
#define REXX_INTERPRETER_4_0_0
Definition: oorexxapi.h:216
#define REXX_CLASSIC_ROUTINE(n, e)
Definition: oorexxapi.h:192
#define argumentOmitted(i)
Definition: oorexxapi.h:3778
#define REXX_LAST_ROUTINE()
Definition: oorexxapi.h:193
#define REXX_TYPED_ROUTINE(n, e)
Definition: oorexxapi.h:191
#define STANDARD_PACKAGE_HEADER
Definition: oorexxapi.h:230
#define Rexx_Error_Invalid_argument_range
Definition: oorexxerrors.h:469
#define Rexx_Error_System_resources_user_defined
Definition: oorexxerrors.h:68
#define Rexx_Error_Incorrect_call_user_defined
Definition: oorexxerrors.h:397
#define Rexx_Error_Invalid_argument_number
Definition: oorexxerrors.h:464
#define Rexx_Error_System_service_user_defined
Definition: oorexxerrors.h:453
#define Rexx_Error_Incorrect_call_null
Definition: oorexxerrors.h:382
RexxReturnCode REXXENTRY RexxLoadMacroSpace(size_t, CONSTANT_STRING *, CONSTANT_STRING)
RexxReturnCode REXXENTRY RexxAddMacro(CONSTANT_STRING, CONSTANT_STRING, size_t)
const char * CSTRING
Definition: rexx.h:78
RexxReturnCode REXXENTRY RexxFreeMemory(void *)
size_t logical_t
Definition: rexx.h:231
struct _RexxStringObject * RexxStringObject
Definition: rexx.h:128
RexxReturnCode REXXENTRY RexxSaveMacroSpace(size_t, CONSTANT_STRING *, CONSTANT_STRING)
#define RXVALIDSTRING(r)
Definition: rexx.h:179
RexxReturnCode REXXENTRY RexxQueryMacro(CONSTANT_STRING, unsigned short *)
struct _RexxObjectPtr * RexxObjectPtr
Definition: rexx.h:127
#define NULLOBJECT
Definition: rexx.h:147
RexxReturnCode REXXENTRY RexxReorderMacro(CONSTANT_STRING, size_t)
ssize_t wholenumber_t
Definition: rexx.h:230
#define RXNULLSTRING(r)
Definition: rexx.h:177
int RexxReturnCode
Definition: rexx.h:73
RexxReturnCode REXXENTRY RexxClearMacroSpace(void)
size_t stringsize_t
Definition: rexx.h:228
#define RexxEntry
Definition: rexx.h:235
RexxReturnCode REXXENTRY RexxDropMacro(CONSTANT_STRING)
struct _RexxStemObject * RexxStemObject
Definition: rexx.h:139
#define RXMACRO_SEARCH_BEFORE
Definition: rexxapidefs.h:220
#define RXSHV_NEXTV
Definition: rexxapidefs.h:103
#define RXSHV_BADN
Definition: rexxapidefs.h:116
#define RXMACRO_SEARCH_AFTER
Definition: rexxapidefs.h:221
#define RXSHV_SET
Definition: rexxapidefs.h:97
#define RXSHV_OK
Definition: rexxapidefs.h:112
#define RXSHV_LVAR
Definition: rexxapidefs.h:114
const char * strptr
Definition: rexx.h:163
size_t strlength
Definition: rexx.h:162
const char * scan
size_t strlength
Definition: rexx.h:157
char * strptr
Definition: rexx.h:158
Definition: oorexxapi.h:242
Definition: oorexxapi.h:177
size_t shvvaluelen
Definition: rexx.h:209
CONSTANT_RXSTRING shvname
Definition: rexx.h:206
unsigned char shvret
Definition: rexx.h:211
unsigned char shvcode
Definition: rexx.h:210
RXSTRING shvvalue
Definition: rexx.h:207
size_t shvnamelen
Definition: rexx.h:208
struct _SHVBLOCK * shvnext
Definition: rexx.h:205
RexxThreadContext * threadContext
Definition: oorexxapi.h:2200
char ibuf[IBUF_LEN]
char varname[MAX]
SHVBLOCK shvb
char stemname[MAX]
char fileTime[FILETIME_BUF_LEN]
char fNameSpec[FNAMESPEC_BUF_LEN]
char foundFile[FOUNDFILE_BUF_LEN]
RexxStemObject files
char * dFNameSpec
char foundFileLine[FOUNDFILELINE_BUF_LEN]
char fileAttr[FILEATTR_BUF_LEN]
size_t nFNameSpec
#define REXXENTRY
size_t RexxEntry SysSetPriority(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
int MoveFile(CSTRING fromFile, CSTRING toFile)
size_t RexxEntry SysGetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysMkDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysUtilVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysQueryProcess(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
int SearchPath(int SearchFlag, const char *path, const char *filename, char *buf, size_t buf_size)
size_t RexxEntry SysSetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysRmDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysFileDelete(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
char line[LINEBUFSIZE]
#define isnan(x)
UINT_PTR uintptr_t
int int32_t
#define SIZE_MAX
unsigned int uint32_t
#define MAX_DIGITS
#define MAX_READ
RexxRoutine2(RexxStringObject, SysCurPos, OPTIONAL_stringsize_t, inrow, OPTIONAL_stringsize_t, incol)
size_t RexxEntry SysSearchPath(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
static int ExtendedFlag
size_t RexxEntry SysSystemDirectory(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysDriveInfo(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define MAX_LINE_LEN
static bool getOptionsFromArg(RexxCallContext *context, char *opts, uint32_t *options, size_t argPos)
#define CH_NL
size_t RexxEntry SysDriveMap(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define ERROR_RETSTR
size_t RexxEntry SysVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
bool SetFileMode(const char *file, size_t attr)
bool string2size_t(const char *string, size_t *number)
#define BUILDRXSTRING(t, s)
RexxPackageEntry rexxutil_package_entry
OOREXX_GET_PACKAGE(rexxutil)
RexxRoutine6(int, SysStemCopy, RexxStemObject, fromStem, RexxStemObject, toStem, OPTIONAL_stringsize_t, from, OPTIONAL_stringsize_t, to, OPTIONAL_stringsize_t, count, OPTIONAL_CSTRING, option)
#define IBUF_LEN
bool isAtLeastVista(void)
#define LONG_TIME
static bool formatFile(RexxCallContext *c, char *path, RXTREEDATA *treeData, int32_t *newMask, uint32_t options, WIN32_FIND_DATA *wfd)
#define LOCAL
static bool safeGetCurrentDirectory(RexxCallContext *c, char **pPath, size_t *pPathLen)
#define MAX
struct _GetFileData GetFileData
bool GetLine(char *line, size_t size, GetFileData *filedata)
static bool expandPath2fullPath(RexxCallContext *c, char *fSpec, size_t lastSlashPos, char **pPath, size_t *pPathLen)
static void badMaskException(RexxThreadContext *c, size_t pos, CSTRING actual)
RexxRoutine1(int, SysCurState, CSTRING, option)
size_t RexxEntry SysWinDecryptFile(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define OM_WAKEUP
static bool getMaskFromArg(RexxCallContext *context, char *msk, int32_t *mask, size_t argPos)
#define DO_DIRS
RexxReturnCode REXXENTRY RexxStemSort(const char *stemname, int order, int type, size_t start, size_t end, size_t firstcol, size_t lastcol)
static bool setAttr(const char *file, uint32_t attr)
VOID GetUniqueFileName(CHAR *Template, CHAR Filler, CHAR *file)
#define FNAMESPEC_BUF_LEN
static char * getFileSpecFromArg(RexxCallContext *context, CSTRING fSpec, size_t *fSpecLen, size_t *fSpecBufLen, size_t argPos)
RexxRoutineEntry rexxutil_routines[]
size_t RexxEntry SysDumpVariables(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
VOID CALLBACK SleepTimerProc(HWND, UINT, UINT, DWORD)
static char * adjustFSpec(char *fSpec)
#define VALID_ROUTINE
size_t RexxEntry SysBootDrive(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
RexxRoutine4(int, SysToUniCode, RexxStringObject, source, OPTIONAL_CSTRING, codePageOpt, OPTIONAL_CSTRING, mappingFlags, RexxStemObject, stem)
#define CDROM
struct RxTreeData RXTREEDATA
#define REMOTE
static void badSFTOptsException(RexxThreadContext *c, size_t pos, CSTRING actual)
void safeLocalFree(void *p)
#define FNAMESPEC_BUF_EXTRA
#define ERROR_NOMEM
static bool recursiveFindFile(RexxCallContext *c, char *path, RXTREEDATA *treeData, int32_t *targetMask, int32_t *newMask, uint32_t options)
#define RAMDISK
static bool isWindowsVersion(DWORD major, DWORD minor, unsigned int sp, unsigned int type, unsigned int condition)
static bool getPath(RexxCallContext *c, char *fSpec, char **path, char *filename, size_t *pathLen)
size_t RexxEntry SysStemSort(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysLoadFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t neededSize(size_t need, size_t have)
size_t RexxEntry SysCls(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define FOUNDFILE_BUF_LEN
#define SORT_ASCENDING
void CloseFile(GetFileData *filedata)
#define MAX_CREATEPROCESS_CMDLINE
#define NO_UTIL_ERROR
static bool expandNonPath2fullPath(RexxCallContext *c, char *fSpec, char **pPath, size_t *pPathLen, int *lastSlashPos)
size_t RexxEntry SysVolumeLabel(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
bool MyOpenFile(const char *file, GetFileData *filedata)
size_t RexxEntry RxWinExec(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define CH_CR
static bool getBiggerBuffer(RexxCallContext *c, char **dBuf, size_t *nBuf, size_t needed, size_t nStaticBuffer)
#define SORT_CASEIGNORE
size_t RexxEntry SysGetErrortext(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
RexxRoutine3(RexxStringObject, SysTextScreenRead, int, row, int, col, OPTIONAL_int, len)
#define WC_ERR_INVALID_CHARS
size_t RexxEntry SysIni(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define DO_FILES
#define FREE
size_t RexxEntry SysWinEncryptFile(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
struct RxStemData RXSTEMDATA
static char ExtendedChar
#define RNDFACTOR
RexxRoutine0(int, SysClearRexxMacroSpace)
size_t RexxEntry SysDropFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define FOUNDFILELINE_BUF_LEN
#define SORT_CASESENSITIVE
#define EDITABLE_TIME
static uint32_t newAttr(int32_t *mask, uint32_t attr)
void nullStringException(RexxThreadContext *c, CSTRING fName, size_t pos)
static bool illegalFileNameChars(char *fSpec)
#define REMOVABLE
RexxRoutine5(uint32_t, SysFileTree, CSTRING, fSpec, RexxStemObject, files, OPTIONAL_CSTRING, opts, OPTIONAL_CSTRING, targetAttr, OPTIONAL_CSTRING, newAttr)
size_t RexxEntry SysGetKey(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysFileSearch(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
char * mystrstr(const char *haystack, const char *needle, size_t hlen, size_t nlen, bool sensitive)
size_t RexxEntry SysFileSystemType(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
BOOL(WINAPI * P_GDFSE)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)
#define USED
#define FILETIME_BUF_LEN
#define RECURSE
#define MAX_ENVVAR
size_t RexxEntry SysWinVer(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
static char * getPathBuffer(RexxCallContext *context, size_t fSpecLen, size_t *pathLen)
#define SORT_DECENDING
#define RETVAL(retc)
static bool goodOpts(RexxCallContext *c, char *opts, uint32_t *pOpts)
#define INVALID_ROUTINE
static bool sameAttr(int32_t *mask, uint32_t attr, uint32_t options)
static P_GDFSE pGetDiskFreeSpaceEx
#define STATUS_SUCCESS
#define NAME_ONLY
#define RXIGNORE
size_t RexxEntry SysTempFileName(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
void memupper(char *location, size_t length)
void outOfMemoryException(RexxThreadContext *c)
static bool canUseWideCharFlags(UINT cp)
static void systemServiceExceptionCode(RexxThreadContext *c, CSTRING api, uint32_t rc)
#define ERROR_FILEOPEN
#define FILEATTR_BUF_LEN
bool ReadNextBuffer(GetFileData *filedata)
static bool goodMask(RexxCallContext *c, char *msk, int32_t *mask)