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  OSVERSIONINFO vi;
3535 
3536  if (numargs != 1)
3537  {
3538  /* If no args, then its an */
3539  /* incorrect call */
3540  return INVALID_ROUTINE;
3541  }
3542 
3543  vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
3544 
3545  if (rc = GetVersionEx(&vi))
3546  {
3547  /* allow this only on W2K or newer */
3548  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT && vi.dwMajorVersion > 4)
3549  {
3550  rc = EncryptFile(args[0].strptr);
3551  }
3552  else
3553  {
3554  rc = 0;
3555  SetLastError(ERROR_CANNOT_MAKE);
3556  }
3557  }
3558  if (rc)
3559  {
3560  sprintf(retstr->strptr, "%d", 0);
3561  }
3562  else
3563  {
3564  sprintf(retstr->strptr, "%d", GetLastError());
3565  }
3566  retstr->strlength = strlen(retstr->strptr);
3567  return VALID_ROUTINE;
3568 }
3569 
3570 /*************************************************************************
3571 * Function: SysWinDecryptFile (W2K only) *
3572 * *
3573 * Syntax: call SysWinDecryptFile filename *
3574 * *
3575 * Params: filename - file to be decrypted *
3576 * *
3577 * Return: NO_UTIL_ERROR *
3578 * Return code from DecryptFile() *
3579 *************************************************************************/
3580 
3581 size_t RexxEntry SysWinDecryptFile(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3582 {
3583 
3584  ULONG rc; /* Ret code of func */
3585  OSVERSIONINFO vi;
3586 
3587  if (numargs != 1)
3588  /* If no args, then its an */
3589  /* incorrect call */
3590  return INVALID_ROUTINE;
3591 
3592  vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
3593 
3594  if (rc = GetVersionEx(&vi)) {
3595  /* allow this only on W2K or newer */
3596  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT && vi.dwMajorVersion > 4)
3597  rc = DecryptFile(args[0].strptr,0);
3598  else {
3599  rc = 0;
3600  SetLastError(ERROR_CANNOT_MAKE);
3601  }
3602  }
3603 
3604  if (rc)
3605  sprintf(retstr->strptr, "%d", 0);
3606  else
3607  sprintf(retstr->strptr, "%d", GetLastError());
3608  retstr->strlength = strlen(retstr->strptr);
3609  return VALID_ROUTINE;
3610 }
3611 
3612 
3613 /*************************************************************************
3614 * Function: SysWinVer *
3615 * *
3616 * Syntax: call SysWinVer *
3617 * *
3618 * Return: Windows Version *
3619 *************************************************************************/
3620 
3621 size_t RexxEntry SysWinVer(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3622 {
3623 
3624  OSVERSIONINFO vi; /* Return RXSTRING */
3625  char chVerBuf[12];
3626 
3627  vi.dwOSVersionInfoSize = sizeof(vi); /* if not set --> violation error */
3628 
3629  if (numargs != 0) /* validate arg count */
3630  return INVALID_ROUTINE;
3631 
3632  GetVersionEx(&vi); /* get version with extended api */
3633  if (vi.dwPlatformId == VER_PLATFORM_WIN32s)
3634  strcpy(chVerBuf, "Windows"); /* Windows 3.1 */
3635  else
3636  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
3637  strcpy(chVerBuf, "WindowsNT"); /* Windows NT */
3638  else strcpy(chVerBuf, "Windows95"); /* Windows 95 */
3639 
3640  /* format into the buffer */
3641  wsprintf(retstr->strptr,"%s %lu.%02lu",
3642  chVerBuf,
3643  vi.dwMajorVersion,
3644  vi.dwMinorVersion);
3645 
3646  retstr->strlength = strlen(retstr->strptr);
3647  return VALID_ROUTINE;
3648 }
3649 
3650 /*************************************************************************
3651 * Function: SysVersion *
3652 * *
3653 * Syntax: Say SysVersion *
3654 * *
3655 * Return: Operating System and Version *
3656 *************************************************************************/
3657 
3658 size_t RexxEntry SysVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3659 {
3660  /* this is only an alias for SysWinVer */
3661  return SysWinVer(name, numargs, args, queuename, retstr);
3662 }
3663 
3664 
3665 /*************************************************************************
3666 * Function: SysRmDir *
3667 * *
3668 * Syntax: call SysRmDir dir *
3669 * *
3670 * Params: dir - Directory to be removed. *
3671 * *
3672 * Return: NO_UTIL_ERROR *
3673 * Return code from RemoveDirectory() *
3674 *************************************************************************/
3675 
3677 {
3678  return RemoveDirectory(dir) != 0 ? 0 : GetLastError();
3679 }
3680 
3681 
3682 /*************************************************************************
3683 * Function: SysSearchPath *
3684 * *
3685 * Syntax: call SysSearchPath path, file [, options] *
3686 * *
3687 * Params: path - Environment variable name which specifies a path *
3688 * to be searched (ie 'PATH', 'DPATH', etc). *
3689 * file - The file to search for. *
3690 * options - 'C' - Current directory search first (default). *
3691 * 'N' - No Current directory search. Only searches *
3692 * the path as specified. *
3693 * *
3694 * Return: other - Full path and filespec of found file. *
3695 * '' - Specified file not found along path. *
3696 *************************************************************************/
3697 
3698 size_t RexxEntry SysSearchPath(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3699 {
3700  char szFullPath[_MAX_PATH]; /* returned file name */
3701  char szCurDir[_MAX_PATH]; /* current directory */
3702  char *szEnvStr = NULL;
3703 
3704  LPTSTR pszOnlyFileName; /* parm for searchpath */
3705  LPTSTR lpPath = NULL; /* ptr to search path+ */
3706  UINT errorMode;
3707 
3708  /* validate arguments */
3709  if (numargs < 2 || numargs > 3 ||
3710  !RXVALIDSTRING(args[0]) ||
3711  !RXVALIDSTRING(args[1]))
3712  {
3713  return INVALID_ROUTINE;
3714  }
3715 
3716  char opt = 'C'; // this is the default
3717  if (numargs == 3)
3718  { /* process options */
3719  opt = toupper(args[2].strptr[0]);
3720  if (opt != 'C' && opt != 'N')
3721  {
3722  return INVALID_ROUTINE; /* Invalid option */
3723  }
3724  }
3725 
3726  szEnvStr = (LPTSTR) malloc(sizeof(char) * MAX_ENVVAR);
3727  if (szEnvStr != NULL)
3728  {
3729  DWORD charCount = GetEnvironmentVariable(args[0].strptr, szEnvStr, MAX_ENVVAR);
3730  if (charCount == 0)
3731  {
3732  *szEnvStr = '\0';
3733  }
3734  else if (charCount > MAX_ENVVAR)
3735  {
3736  szEnvStr = (LPTSTR) realloc(szEnvStr, sizeof(char) * charCount);
3737  if (szEnvStr != NULL)
3738  {
3739  DWORD charCount2 = GetEnvironmentVariable(args[0].strptr, szEnvStr, charCount);
3740  if (charCount2 == 0 || charCount2 > charCount)
3741  {
3742  *szEnvStr = '\0';
3743  }
3744  }
3745  }
3746  }
3747 
3748  if (opt == 'N')
3749  {
3750  lpPath = (szEnvStr == NULL) ? NULL : strdup(szEnvStr);
3751  }
3752  else if (opt == 'C')
3753  {
3754  /* search current directory */
3755  DWORD charCount = GetCurrentDirectory(_MAX_PATH, szCurDir);
3756  if (charCount == 0 || charCount > _MAX_PATH)
3757  {
3758  szCurDir[0] = '\0';
3759  }
3760 
3761  if (szEnvStr != NULL)
3762  {
3763  lpPath = (LPTSTR) malloc(sizeof(char) * (strlen(szCurDir) + 1 + strlen(szEnvStr) + 1));
3764  if (lpPath != NULL)
3765  {
3766  strcpy(lpPath, szCurDir);
3767  strcat(lpPath, ";");
3768  strcat(lpPath, szEnvStr);
3769  }
3770  }
3771  else
3772  {
3773  lpPath = strdup(szCurDir);
3774  }
3775  }
3776 
3777  /* use DosSearchPath */
3778 
3779  DWORD charCount = 0;
3780  if (lpPath != NULL)
3781  {
3782  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
3783  charCount = SearchPath(
3784  (LPCTSTR)lpPath, /* path srch, NULL will+ */
3785  (LPCTSTR)args[1].strptr, /* address if filename */
3786  NULL, /* filename contains .ext */
3787  _MAX_PATH, /* size of fullname buffer */
3788  szFullPath, /* where to put results */
3789  &pszOnlyFileName);
3790  SetErrorMode(errorMode);
3791  }
3792  if (charCount == 0 || charCount > _MAX_PATH)
3793  {
3794  szFullPath[0]='\0'; /* set to NULL if failure */
3795  }
3796 
3797  BUILDRXSTRING(retstr, szFullPath); /* pass back result */
3798  free(szEnvStr);
3799  free(lpPath);
3800  return VALID_ROUTINE;
3801 }
3802 
3803 
3804 /*************************************************************************
3805 * Function: SysSleep *
3806 * *
3807 * Syntax: call SysSleep secs *
3808 * *
3809 * Params: secs - Number of seconds to sleep. *
3810 * must be in the range 0 .. 2147483 *
3811 * *
3812 * Return: 0 *
3813 *************************************************************************/
3814 RexxRoutine1(int, SysSleep, RexxStringObject, delay)
3815 {
3816  double seconds;
3817  // try to convert the provided delay to a valid floating point number
3818  if (context->ObjectToDouble(delay, &seconds) == 0 ||
3819  isnan(seconds) || seconds == HUGE_VAL || seconds == -HUGE_VAL)
3820  {
3821  // 88.902 The &1 argument must be a number; found "&2"
3822  context->RaiseException3(Rexx_Error_Invalid_argument_number, context->String("positional"), context->String("delay"), delay);
3823  return 1;
3824  }
3825 
3826  // according to MSDN the maximum is USER_TIMER_MAXIMUM (0x7FFFFFFF) milliseconds,
3827  // which translates to 2147483.647 seconds
3828  if (seconds < 0.0 || seconds > 2147483.0)
3829  {
3830  // 88.907 The &1 argument must be in the range &2 to &3; found "&4"
3831  context->RaiseException(Rexx_Error_Invalid_argument_range,
3832  context->ArrayOfFive(context->String("positional"), context->String("delay"),
3833  context->String("0"), context->String("2147483"), delay));
3834  return 1;
3835  }
3836 
3837  // convert to milliseconds, no overflow possible
3838  LONG milliseconds = (LONG) (seconds * 1000);
3839 
3840  /** Using Sleep with a long timeout risks sleeping on a thread with a message
3841  * queue, which can make the system sluggish, or possibly deadlocked. If the
3842  * sleep is longer than 333 milliseconds use a window timer to avoid this
3843  * risk.
3844  */
3845  if ( milliseconds > 333 )
3846  {
3847  if ( !(SetTimer(NULL, 0, milliseconds, (TIMERPROC) SleepTimerProc)) )
3848  {
3849  // no timer available, need to abort
3850  context->RaiseException1(Rexx_Error_System_resources_user_defined,
3851  context->String("System resources exhausted: cannot start timer"));
3852  return 1;
3853  }
3854 
3855  MSG msg;
3856  while ( GetMessage (&msg, NULL, 0, 0) )
3857  {
3858  if ( msg.message == OM_WAKEUP ) /* If our message, exit loop */
3859  break;
3860  TranslateMessage( &msg );
3861  DispatchMessage ( &msg );
3862  }
3863  }
3864  else
3865  {
3866  Sleep(milliseconds);
3867  }
3868 
3869  return 0;
3870 }
3871 
3872 /*********************************************************************
3873  * *
3874  * Routine : SleepTimerProc *
3875  * *
3876  * Purpose : callback routine for SetTimer set in SysSleep *
3877  * Notes : *
3878  * Arguments : hwnd - window handle *
3879  * uMsg - WM_TIMER message *
3880  * idEvent - timer identifier *
3881  * dwtime - current system time *
3882  * Returns : *
3883  * *
3884  *********************************************************************/
3885  VOID CALLBACK SleepTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) {
3886  DWORD ThreadId;
3887  KillTimer(NULL, idEvent); /* kill the timer that just ended */
3888  ThreadId = GetCurrentThreadId();
3889  PostThreadMessage(ThreadId, OM_WAKEUP, 0 , 0L); /* send ourself the wakeup message*/
3890  }
3891 
3892 /*************************************************************************
3893 * Function: SysTempFileName *
3894 * *
3895 * Syntax: call SysTempFileName template [,filler] *
3896 * *
3897 * Params: template - Description of filespec desired. For example: *
3898 * C:\TEMP\FILE.??? *
3899 * filler - A character which when found in template will be *
3900 * replaced with random digits until a unique file *
3901 * or directory is found. The default character *
3902 * is '?'. *
3903 * *
3904 * Return: other - Unique file/directory name. *
3905 * '' - No more files exist given specified template. *
3906 *************************************************************************/
3907 
3908 size_t RexxEntry SysTempFileName(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3909 {
3910  CHAR filler; /* filler character */
3911 
3912  if (numargs < 1 || /* validate arguments */
3913  numargs > 2 ||
3914  !RXVALIDSTRING(args[0]) ||
3915  args[0].strlength > 512)
3916  return INVALID_ROUTINE;
3917 
3918  if (numargs == 2 && /* get filler character */
3919  !RXNULLSTRING(args[1]))
3920  {
3921  if (args[1].strlength != 1) /* must be one character */
3922  return INVALID_ROUTINE;
3923  filler = args[1].strptr[0];
3924  }
3925  else
3926  {
3927  filler = '?';
3928  }
3929  /* get the file id */
3930  GetUniqueFileName(const_cast<char *>(args[0].strptr), filler, retstr->strptr);
3931  retstr->strlength = strlen(retstr->strptr);
3932 
3933  return VALID_ROUTINE;
3934 }
3935 
3936 
3937 /*************************************************************************
3938 * Function: SysTextScreenRead *
3939 * *
3940 * Syntax: call SysTextScreenRead row, col [,len] *
3941 * *
3942 * Params: row - Horizontal row on the screen to start reading from. *
3943 * The row at the top of the screen is 0. *
3944 * col - Vertical column on the screen to start reading from. *
3945 * The column at the left of the screen is 0. *
3946 * len - The number of characters to read. The default is the *
3947 * rest of the screen. *
3948 * *
3949 * Return: Characters read from text screen. *
3950 *************************************************************************/
3951 RexxRoutine3(RexxStringObject, SysTextScreenRead, int, row, int, col, OPTIONAL_int, len)
3952 {
3953  int lPos,lPosOffSet; /* positioning */
3954  /* (132x50) */
3955  int lBufferLen = 16000; /* default: 200x80 characters */
3956 
3957  COORD coordLine; /* coordinates of where to */
3958  /* read characters from */
3959  DWORD dwCharsRead,dwSumCharsRead; /* Handle to Standard Out */
3960  HANDLE hStdout;
3961  CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */
3962 
3963  hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
3964 
3965  if (!GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
3966  {
3967  context->InvalidRoutine();
3968  return NULLOBJECT;
3969  }
3970 
3971  if (argumentOmitted(3)) /* check the length */
3972  {
3973  len = csbiInfo.dwSize.Y * csbiInfo.dwSize.X;
3974  }
3975 
3976  coordLine.X = (short)col;
3977  coordLine.Y = (short)row;
3978 
3979  char buffer[256];
3980  char *ptr = buffer;
3981 
3982  if (len > sizeof(buffer))
3983  {
3984  // allocate a new buffer
3985  ptr = (char *)malloc(len);
3986  if (ptr == NULL)
3987  {
3988  context->InvalidRoutine();
3989  return NULL;
3990  }
3991  }
3992 
3993  if (len < lBufferLen)
3994  {
3995  lBufferLen = len;
3996  }
3997 
3998  lPos = 0; /* current position */
3999  lPosOffSet = row * csbiInfo.dwSize.X + col; /* add offset if not started at beginning */
4000  dwSumCharsRead = 0;
4001 
4002  while (lPos < len )
4003  {
4004 
4005  if (!ReadConsoleOutputCharacter(hStdout, &ptr[lPos], lBufferLen, coordLine, &dwCharsRead))
4006  {
4007  if (ptr != buffer) {
4008  free(ptr);
4009  }
4010  context->InvalidRoutine();
4011  return NULL;
4012  }
4013 
4014 
4015  lPos = lPos + lBufferLen;
4016  coordLine.Y = (short)((lPos + lPosOffSet) / csbiInfo.dwSize.X);
4017  coordLine.X = (short)((lPos + lPosOffSet) % csbiInfo.dwSize.X);
4018  dwSumCharsRead = dwSumCharsRead + dwCharsRead;
4019  }
4020 
4021  RexxStringObject result = context->NewString(ptr, dwSumCharsRead);
4022  if (ptr != buffer) {
4023  free(ptr);
4024  }
4025  return result;
4026 }
4027 
4028 /*************************************************************************
4029 * Function: SysTextScreenSize *
4030 * *
4031 * Syntax: call SysTextScreenSize [option], [rows, colummns] *
4032 * call SysTextScreenSize [option], [top, left, bottom, right] *
4033 * *
4034 * Params: option - "BUFFERSIZE", "WINDOWRECT", "MAXWINDOWSIZE" *
4035 * "BUFFERSIZE" (default) return or set console buffer size *
4036 * "WINDOWRECT" return or set windows position *
4037 * "MAXWINDOWSIZE" return maximum window size *
4038 * lines, columns - set buffer size to lines by columns *
4039 * top, left, bottom, right - set window size and position *
4040 * *
4041 * Return: "BUFFERSIZE" or "MAXWINDOWSIZE": rows columns *
4042 * "WINDOWRECT": top left bottom right *
4043 *************************************************************************/
4044 
4045 RexxRoutine5(RexxStringObject, SysTextScreenSize,
4046  OPTIONAL_CSTRING, optionString,
4047  OPTIONAL_stringsize_t, rows, OPTIONAL_stringsize_t, columns,
4048  OPTIONAL_stringsize_t, rows2, OPTIONAL_stringsize_t, columns2)
4049 {
4050  // check for valid option
4051  typedef enum { BUFFERSIZE, WINDOWRECT, MAXWINDOWSIZE } console_option;
4052  console_option option;
4053  if (optionString == NULL || stricmp(optionString, "BUFFERSIZE") == 0)
4054  {
4055  option = BUFFERSIZE;
4056  }
4057  else if (stricmp(optionString, "WINDOWRECT") == 0)
4058  {
4059  option = WINDOWRECT;
4060  }
4061  else if (stricmp(optionString, "MAXWINDOWSIZE") == 0)
4062  {
4063  option = MAXWINDOWSIZE;
4064  }
4065  else
4066  {
4067  context->InvalidRoutine();
4068  return 0;
4069  }
4070 
4071  // check for valid SET arguments: either none, or two more, or four more
4072  size_t setArgs;
4073  bool omitted45 = argumentOmitted(4) && argumentOmitted(5);
4074  bool exists23 = argumentExists(2) && argumentExists(3);
4075  if (argumentOmitted(2) && argumentOmitted(3) && omitted45)
4076  {
4077  setArgs = 0;
4078  }
4079  else if (exists23 && omitted45)
4080  {
4081  setArgs = 2;
4082  }
4083  else if (exists23 && argumentExists(4) && argumentExists(5))
4084  {
4085  setArgs = 4;
4086  }
4087  else
4088  {
4089  context->InvalidRoutine();
4090  return 0;
4091  }
4092 
4093  // check that all SET arguments fit a SHORT
4094  if (!(setArgs == 0 ||
4095  (setArgs == 2 && rows <= SHRT_MAX && columns <= SHRT_MAX) ||
4096  (setArgs == 4 && rows <= SHRT_MAX && columns <= SHRT_MAX && rows2 <= SHRT_MAX && columns2 <= SHRT_MAX)))
4097  {
4098  context->InvalidRoutine();
4099  return 0;
4100  }
4101 
4102  // real work starts here
4103  CONSOLE_SCREEN_BUFFER_INFO csbi; // console screen buffer information
4104  char buffer[100];
4105 
4106  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
4107 
4108  if (setArgs == 0)
4109  {
4110  // this is a GET requset, retrieve console information
4111  if (GetConsoleScreenBufferInfo(hStdout, &csbi) == NULL)
4112  {
4113  // console not in character mode, return two or four zeroes
4114  return context->NewStringFromAsciiz(option == WINDOWRECT ? "0 0 0 0" : "0 0");
4115  }
4116  }
4117 
4118  if (option == BUFFERSIZE && setArgs == 0)
4119  {
4120  // this is a BUFFERSIZE GET, returns two values
4121  sprintf(buffer, "%d %d", csbi.dwSize.Y, csbi.dwSize.X);
4122  }
4123  else if (option == WINDOWRECT && setArgs == 0)
4124  {
4125  // this is a WINDOWRECT GET, returns four values
4126  sprintf(buffer, "%d %d %d %d", csbi.srWindow.Top, csbi.srWindow.Left, csbi.srWindow.Bottom, csbi.srWindow.Right);
4127  }
4128  else if (option == MAXWINDOWSIZE && setArgs == 0)
4129  {
4130  // this is a MAXWINDOWSIZE GET, returns two values
4131  sprintf(buffer, "%d %d", csbi.dwMaximumWindowSize.Y, csbi.dwMaximumWindowSize.X);
4132  }
4133  else if (option == BUFFERSIZE && setArgs == 2)
4134  {
4135  // this is a BUFFERSIZE SET, requires two more arguments
4136  COORD consoleBuffer;
4137  consoleBuffer.Y = (SHORT)rows;
4138  consoleBuffer.X = (SHORT)columns;
4139  BOOL code = SetConsoleScreenBufferSize(hStdout, consoleBuffer);
4140  sprintf(buffer, "%d", code == 0 ? GetLastError() : 0);
4141  }
4142  else if (option == WINDOWRECT && setArgs == 4)
4143  {
4144  // this is a WINDOWRECT SET, requires four more arguments
4145  SMALL_RECT consoleWindow;
4146  consoleWindow.Top = (SHORT)rows;
4147  consoleWindow.Left = (SHORT)columns;
4148  consoleWindow.Bottom = (SHORT)rows2;
4149  consoleWindow.Right = (SHORT)columns2;
4150  BOOL code = SetConsoleWindowInfo(hStdout, 1, &consoleWindow);
4151  sprintf(buffer, "%d", code == 0 ? GetLastError() : 0);
4152  }
4153  else
4154  {
4155  context->InvalidRoutine();
4156  return 0;
4157  }
4158 
4159  // return the buffer as result
4160  return context->NewStringFromAsciiz(buffer);
4161 }
4162 
4163 /*************************************************************************
4164 * Function: RxWinExec *
4165 * *
4166 * Syntax: call RxWinExec command,CmdShow *
4167 * *
4168 * *
4169 * Parms: command - program to execute *
4170 * CmdShow - Any of the SW_ type values in winuser.h *
4171 * SW_SHOW 5 *
4172 * SW_HIDE 0 *
4173 * SW_MINIMIZE etc... 6 *
4174 * numeric values... *
4175 * *
4176 * Return: Process ID or Error code *
4177 *************************************************************************/
4178 
4179 size_t RexxEntry RxWinExec(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4180 {
4181 
4182  int CmdShow; /* show window style flags */
4183  int index; /* table index */
4184  ULONG pid; /* PID or error return code */
4185  size_t length; /* length of option */
4186  STARTUPINFO si;
4187  PROCESS_INFORMATION procInfo;
4188 
4189  // Show window types.
4190  PSZ show_styles[] =
4191  {
4192  "SHOWNORMAL",
4193  "SHOWNOACTIVATE",
4194  "SHOWMINNOACTIVE",
4195  "SHOWMINIMIZED",
4196  "SHOWMAXIMIZED",
4197  "HIDE",
4198  "MINIMIZE"
4199  };
4200 
4201  // Show window styles.
4202  ULONG show_flags[] =
4203  {
4204  SW_SHOWNORMAL,
4205  SW_SHOWNOACTIVATE,
4206  SW_SHOWMINNOACTIVE,
4207  SW_SHOWMINIMIZED,
4208  SW_SHOWMAXIMIZED,
4209  SW_HIDE,
4210  SW_MINIMIZE
4211  };
4212 
4213  BUILDRXSTRING(retstr, NO_UTIL_ERROR); /* pass back result */
4214 
4215  // Should be 1 or 2 args.
4216  if ( numargs < 1 || numargs > 2 || !RXVALIDSTRING(args[0]) ||
4217  (numargs == 2 && !RXVALIDSTRING(args[1])) || args[0].strlength > MAX_CREATEPROCESS_CMDLINE )
4218  {
4219  return INVALID_ROUTINE;
4220  }
4221 
4222  // Show type can be one and only one of the SW_XXX constants.
4223  if ( numargs < 2 || args[1].strptr == NULL )
4224  {
4225  CmdShow = SW_SHOWNORMAL;
4226  }
4227  else
4228  {
4229  // Get length of option and search the style table.
4230  length = args[1].strlength;
4231  for ( index = 0; index < sizeof(show_styles)/sizeof(PSZ); index++ )
4232  {
4233  if ( length == strlen(show_styles[index]) && memicmp(args[1].strptr, show_styles[index], length) == 0 )
4234  {
4235  CmdShow = show_flags[index];
4236  break;
4237  }
4238  }
4239 
4240  if ( index == sizeof(show_styles)/sizeof(PSZ) )
4241  {
4242  // User sent an argument, but not the right one.
4243  return INVALID_ROUTINE;
4244  }
4245  }
4246 
4247  ZeroMemory(&procInfo, sizeof(procInfo));
4248  ZeroMemory(&si, sizeof(si));
4249  si.cb = sizeof(si);
4250  si.dwFlags = STARTF_USESHOWWINDOW;
4251  si.wShowWindow = (WORD)CmdShow;
4252 
4253  if ( CreateProcess(NULL, (LPSTR)args[0].strptr, NULL, NULL, FALSE, 0, NULL,
4254  NULL, &si, &procInfo ) )
4255  {
4256  pid = procInfo.dwProcessId;
4257  }
4258  else
4259  {
4260  pid = GetLastError();
4261  if ( pid > 31 )
4262  {
4263  // Maintain compatibility to versions < ooRexx 3.1.2
4264  pid = (ULONG)-((int)pid);
4265  }
4266  }
4267 
4268  // Return value as string.
4269  sprintf(retstr->strptr, "%d", pid);
4270  retstr->strlength = strlen(retstr->strptr);
4271 
4272  // Close process / thread handles as they are not used / needed.
4273  CloseHandle(procInfo.hProcess);
4274  CloseHandle(procInfo.hThread);
4275 
4276  return VALID_ROUTINE;
4277 }
4278 
4279 
4280 /*************************************************************************
4281 * Function: SysAddRexxMacro *
4282 * *
4283 * Syntax: result = SysAddRexxMacro(name, file, <order>) *
4284 * *
4285 * Params: name - loaded name of the macro file *
4286 * file - file containing the loaded macro *
4287 * order - Either 'B'efore or 'A'fter. The default is 'B' *
4288 * *
4289 * Return: return code from RexxAddMacro *
4290 *************************************************************************/
4291 
4292 RexxRoutine3(int, SysAddRexxMacro, CSTRING, name, CSTRING, file, OPTIONAL_CSTRING, option)
4293 {
4294  size_t position; /* added position */
4295 
4296  position = RXMACRO_SEARCH_BEFORE; /* set default search position*/
4297  if (option != NULL) /* have an option? */
4298  {
4299  switch (*option)
4300  {
4301  case 'B': // 'B'efore
4302  case 'b':
4303  position = RXMACRO_SEARCH_BEFORE;
4304  break;
4305 
4306  case 'A': // 'A'fter
4307  case 'a':
4308  position = RXMACRO_SEARCH_AFTER;
4309  break;
4310 
4311  default:
4312  context->InvalidRoutine();
4313  return 0;
4314  }
4315  }
4316  /* try to add the macro */
4317  return(int)RexxAddMacro(name, file, position);
4318 }
4319 
4320 /*************************************************************************
4321 * Function: SysReorderRexxMacro *
4322 * *
4323 * Syntax: result = SysReorderRexxMacro(name, order) *
4324 * *
4325 * Params: name - loaded name of the macro file *
4326 * order - Either 'B'efore or 'A'fter. *
4327 * *
4328 * Return: return code from RexxReorderMacro *
4329 *************************************************************************/
4330 
4331 RexxRoutine2(int, SysReorderRexxMacro, CSTRING, name, CSTRING, option)
4332 {
4333  size_t position; /* added position */
4334 
4335  switch (*option)
4336  {
4337  case 'B': // 'B'efore
4338  case 'b':
4339  position = RXMACRO_SEARCH_BEFORE;
4340  break;
4341 
4342  case 'A': // 'A'fter
4343  case 'a':
4344  position = RXMACRO_SEARCH_AFTER;
4345  break;
4346 
4347  default:
4348  context->InvalidRoutine();
4349  return 0;
4350  }
4351  return(int)RexxReorderMacro(name, position);
4352 }
4353 
4354 /*************************************************************************
4355 * Function: SysDropRexxMacro *
4356 * *
4357 * Syntax: result = SysDropRexxMacro(name) *
4358 * *
4359 * Params: name - name of the macro space function *
4360 * *
4361 * Return: return code from RexxDropMacro *
4362 *************************************************************************/
4363 
4364 RexxRoutine1(int, SysDropRexxMacro, CSTRING, name)
4365 {
4366  return (int)RexxDropMacro(name);
4367 }
4368 
4369 /*************************************************************************
4370 * Function: SysQueryRexxMacro *
4371 * *
4372 * Syntax: result = SysQueryRexxMacro(name) *
4373 * *
4374 * Params: name - name of the macro space function *
4375 * *
4376 * Return: position of the macro ('B' or 'A'), returns null for errors.*
4377 *************************************************************************/
4378 
4379 RexxRoutine1(CSTRING, SysQueryRexxMacro, CSTRING, name)
4380 {
4381  unsigned short position; /* returned position */
4382 
4383  if (RexxQueryMacro(name, &position) != 0)
4384  {
4385  return "";
4386  }
4387  // before?
4388  if (position == RXMACRO_SEARCH_BEFORE)
4389  {
4390  return "B";
4391  }
4392  else
4393  {
4394  return "A"; /* must be 'A'fter */
4395  }
4396 }
4397 
4398 /*************************************************************************
4399 * Function: SysClearRexxMacroSpace *
4400 * *
4401 * Syntax: result = SysClearRexxMacroSpace() *
4402 * *
4403 * Params: none *
4404 * *
4405 * Return: return code from RexxClearMacroSpace() *
4406 *************************************************************************/
4407 
4408 RexxRoutine0(int, SysClearRexxMacroSpace)
4409 {
4410  return (int)RexxClearMacroSpace(); /* clear the macro space */
4411 }
4412 
4413 /*************************************************************************
4414 * Function: SysSaveRexxMacroSpace *
4415 * *
4416 * Syntax: result = SysSaveRexxMacroSpace(file) *
4417 * *
4418 * Params: file - name of the saved macro space file *
4419 * *
4420 * Return: return code from RexxSaveMacroSpace() *
4421 *************************************************************************/
4422 
4423 RexxRoutine1(int, SysSaveRexxMacroSpace, CSTRING, file)
4424 {
4425  return (int)RexxSaveMacroSpace(0, NULL, file);
4426 }
4427 
4428 /*************************************************************************
4429 * Function: SysLoadRexxMacroSpace *
4430 * *
4431 * Syntax: result = SysLoadRexxMacroSpace(file) *
4432 * *
4433 * Params: file - name of the saved macro space file *
4434 * *
4435 * Return: return code from RexxLoadMacroSpace() *
4436 *************************************************************************/
4437 
4438 RexxRoutine1(int, SysLoadRexxMacroSpace, CSTRING, file)
4439 {
4440  return (int)RexxLoadMacroSpace(0, NULL, file);
4441 }
4442 
4443 
4444 /*************************************************************************
4445 * Function: SysBootDrive *
4446 * *
4447 * Syntax: drive = SysBootDrive() *
4448 * *
4449 * Params: none *
4450 * *
4451 * Return: 'A: B: C: D: ...' *
4452 *************************************************************************/
4453 
4454 size_t RexxEntry SysBootDrive(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4455 {
4456  if (numargs) /* validate arguments */
4457  return INVALID_ROUTINE;
4458 
4459  if (GetSystemDirectory(retstr->strptr, 255) > 0)
4460  {
4461  retstr->strptr[2] = '\0';
4462  retstr->strlength = 2;
4463  }
4464  else
4465  retstr->strlength = 0;
4466  return VALID_ROUTINE; /* no error on call */
4467 }
4468 
4469 
4470 /*************************************************************************
4471 * Function: SysSystemDirectory *
4472 * *
4473 * Syntax: drive = SysSystemDirectory() *
4474 * *
4475 * Params: none *
4476 * *
4477 * Return: 'C:\WINDOWS ...' *
4478 *************************************************************************/
4479 
4480 size_t RexxEntry SysSystemDirectory(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4481 {
4482  if (numargs) /* validate arguments */
4483  return INVALID_ROUTINE;
4484 
4485  if (GetSystemDirectory(retstr->strptr, 255) > 0)
4486  {
4487  retstr->strlength = strlen(retstr->strptr);
4488  }
4489  else
4490  retstr->strlength = 0;
4491  return VALID_ROUTINE; /* no error on call */
4492 }
4493 
4494 
4495 /*************************************************************************
4496 * Function: SysFileSystemType *
4497 * *
4498 * Syntax: result = SysFileSystemType("drive") *
4499 * *
4500 * Params: drive - drive letter (in form of 'D:') *
4501 * or none - current drive *
4502 * *
4503 * Return: result - File System Name attached to the specified drive *
4504 * (FAT, HPFS ....) *
4505 * '' - Empty string in case of any error *
4506 *************************************************************************/
4507 
4508 size_t RexxEntry SysFileSystemType(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4509 {
4510  char * drive;
4511  CHAR chDriveLetter[4];
4512  UINT errorMode;
4513 
4514  /* validate arguments */
4515  if (numargs > 1 || ((numargs == 1) &&
4516  (args[0].strlength > 2 ||
4517  args[0].strlength == 0)))
4518  return INVALID_ROUTINE;
4519  /* validate the arg */
4520  /* drive letter? */
4521  if ((numargs == 1) && (strlen(args[0].strptr) == 2 && args[0].strptr[1] != ':'))
4522  return INVALID_ROUTINE;
4523 
4524  if (numargs == 1)
4525  {
4526  if (args[0].strlength == 1) /* need to add a : if only the*/
4527  {
4528  chDriveLetter[0]=args[0].strptr[0]; /* letter was passed in */
4529  chDriveLetter[1]=':';
4530  chDriveLetter[2]='\\'; /* need to add \ because of */
4531  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4532  }
4533  else /* have <letter>: , just copy over */
4534  {
4535  strcpy(chDriveLetter, args[0].strptr);
4536  chDriveLetter[2]='\\'; /* need to add \ because of */
4537  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4538  }
4539  drive = chDriveLetter;
4540  } else drive = NULL;
4541 
4542  errorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
4543 
4544  if (GetVolumeInformation(
4545  drive, // address of root directory of the file system
4546  NULL, // address of name of the volume
4547  0, // length of lpVolumeNameBuffer
4548  NULL, // address of volume serial number
4549  NULL, // address of system's maximum filename length
4550  NULL, // address of file system flags
4551  retstr->strptr, // address of name of file system
4552  255 // length of lpFileSystemNameBuffer
4553  ))
4554  retstr->strlength = strlen(retstr->strptr);
4555  else
4556  retstr->strlength = 0; /* return a null string */
4557 
4558  SetErrorMode(errorMode);
4559  return VALID_ROUTINE; /* good completion */
4560 }
4561 
4562 
4563 /*************************************************************************
4564 * Function: SysVolumeLabel *
4565 * *
4566 * Syntax: result = SysVolumeLabel("drive") *
4567 * *
4568 * Params: drive - drive letter (in form of 'D:') *
4569 * or none - current drive *
4570 * *
4571 * Return '' - Empty string in case of any error *
4572 *************************************************************************/
4573 
4574 size_t RexxEntry SysVolumeLabel(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4575 {
4576  char * drive;
4577  CHAR chDriveLetter[4];
4578 
4579  /* validate arguments */
4580  if (numargs > 1 || ((numargs == 1) &&
4581  (args[0].strlength > 2 ||
4582  args[0].strlength == 0)))
4583  return INVALID_ROUTINE;
4584  /* validate the arg */
4585  /* drive letter? */
4586  if ((numargs == 1) && (strlen(args[0].strptr) == 2 && args[0].strptr[1] != ':'))
4587  return INVALID_ROUTINE;
4588 
4589  if (numargs == 1)
4590  {
4591  if (args[0].strlength == 1) /* need to add a : if only the*/
4592  {
4593  chDriveLetter[0]=args[0].strptr[0]; /* letter was passed in */
4594  chDriveLetter[1]=':';
4595  chDriveLetter[2]='\\'; /* need to add \ because of */
4596  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4597  }
4598  else /* have <letter>: , just copy over */
4599  {
4600  strcpy(chDriveLetter, args[0].strptr);
4601  chDriveLetter[2]='\\'; /* need to add \ because of */
4602  chDriveLetter[3]='\0'; /* bug in getvolumeinfo */
4603  }
4604  drive = chDriveLetter;
4605  } else drive = NULL;
4606 
4607  if (GetVolumeInformation(
4608  drive, /* address of root directory of the file system */
4609  retstr->strptr, /*address of name of the volume */
4610  255, /* length of lpVolumeNameBuffer */
4611  NULL, /* address of volume serial number */
4612  NULL, /* address of system's maximum filename length */
4613  NULL, /* address of file system flags */
4614  NULL, /* address of name of file system */
4615  0 /* length of lpFileSystemNameBuffer */
4616  ))
4617  retstr->strlength = strlen(retstr->strptr);
4618  else
4619  retstr->strlength = 0; /* return a null string */
4620  return VALID_ROUTINE; /* good completion */
4621 }
4622 
4623 
4624 /*************************************************************************
4625 * Function: SysCreateMutexSem *
4626 * *
4627 * Syntax: handle = SysCreateMutexSem(<name>) *
4628 * *
4629 * Params: name - optional name for a mutex semaphore *
4630 * *
4631 * Return: handle - token used as a mutex handle for *
4632 * SysRequestMutexSem, SysReleaseMutexSem, *
4633 * SysCloseMutexSem, and SysOpenEventSem *
4634 * '' - Empty string in case of any error *
4635 *************************************************************************/
4636 
4637 RexxRoutine1(RexxObjectPtr, SysCreateMutexSem, OPTIONAL_CSTRING, name)
4638 {
4639  HANDLE handle; /* mutex handle */
4640  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4641 
4642  handle = 0; /* zero the handle */
4643  if (name != NULL) /* request for named sem */
4644  {
4645  /* create it by name */
4646  handle = CreateMutex(&sa, false, name);
4647  if (!handle) /* may already be created */
4648  {
4649  /* try to open it */
4650  handle = OpenMutex(MUTEX_ALL_ACCESS, true, name);
4651  }
4652  }
4653  else /* unnamed semaphore */
4654  {
4655  handle = CreateMutex(&sa, false, NULL);
4656  }
4657 
4658  if (handle == NULL) {
4659  return context->NullString();
4660  }
4661 
4662  return context->Uintptr((uintptr_t)handle);
4663 }
4664 
4665 
4666 /*************************************************************************
4667 * Function: SysOpenMutexSem *
4668 * *
4669 * Syntax: result = SysOpenMutexSem(name) *
4670 * *
4671 * Params: name - name of the mutex semaphore *
4672 * *
4673 * Return: result - handle to the mutex *
4674 *************************************************************************/
4675 
4676 RexxRoutine1(uintptr_t, SysOpenMutexSem, CSTRING, name)
4677 {
4678  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4679 
4680  /* get a binary handle */
4681  return (uintptr_t) OpenMutex(MUTEX_ALL_ACCESS, true, name); /* try to open it */
4682 }
4683 
4684 /*************************************************************************
4685 * Function: SysReleaseMutexSem *
4686 * *
4687 * Syntax: result = SysReleaseMutexSem(handle) *
4688 * *
4689 * Params: handle - token returned from SysCreateMutexSem *
4690 * *
4691 * Return: result - return code from ReleaseMutex *
4692 *************************************************************************/
4693 
4694 RexxRoutine1(int, SysReleaseMutexSem, uintptr_t, h)
4695 {
4696  return !ReleaseMutex((HANDLE)h) ? GetLastError() : 0;
4697 }
4698 
4699 /*************************************************************************
4700 * Function: SysCloseMutexSem *
4701 * *
4702 * Syntax: result = SysCloseMutexSem(handle) *
4703 * *
4704 * Params: handle - token returned from SysCreateMutexSem *
4705 * *
4706 * Return: result - return code from CloseHandle *
4707 *************************************************************************/
4708 
4709 RexxRoutine1(int, SysCloseMutexSem, uintptr_t, h)
4710 {
4711  return !ReleaseMutex((HANDLE)h) ? GetLastError() : 0;
4712 }
4713 
4714 /*************************************************************************
4715 * Function: SysRequestMutexSem *
4716 * *
4717 * Syntax: result = SysRequestMutexSem(handle, <timeout>) *
4718 * *
4719 * Params: handle - token returned from SysCreateMutexSem *
4720 * *
4721 * Return: result - return code from WaitForSingleObject *
4722 *************************************************************************/
4723 
4724 RexxRoutine2(int, SysRequestMutexSem, uintptr_t, h, OPTIONAL_int, timeout)
4725 {
4726  if (argumentOmitted(2))
4727  {
4728  timeout = INFINITE; /* default is no timeout */
4729  }
4730  int rc = WaitForSingleObject((HANDLE)h, timeout);
4731  if (rc == WAIT_FAILED)
4732  {
4733  return GetLastError();
4734  }
4735  else
4736  {
4737  return rc; /* format the return code */
4738  }
4739 }
4740 
4741 /*************************************************************************
4742 * Function: SysCreateEventSem *
4743 * *
4744 * Syntax: handle = SysCreateEventSem(<name>,<manual>) *
4745 * *
4746 * Params: name - optional name for a event semaphore *
4747 * any second argument means manual reset event *
4748 * Return: handle - token used as a event sem handle for *
4749 * SysPostEventSem, SysClearEventSem, *
4750 * SysCloseEventSem, and SysOpenEventSem *
4751 * '' - Empty string in case of any error *
4752 *************************************************************************/
4753 
4754 RexxRoutine2(RexxObjectPtr, SysCreateEventSem, OPTIONAL_CSTRING, name, OPTIONAL_CSTRING, reset)
4755 {
4756  HANDLE handle; /* mutex handle */
4757  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4758  bool manual;
4759 
4760  handle = 0; /* zero the handle */
4761  if (reset != NULL)
4762  {
4763  manual = true;
4764  }
4765  else
4766  {
4767  manual = false;
4768  }
4769 
4770  if (name != NULL)
4771  { /* request for named sem */
4772  /* create it by name */
4773  handle = CreateEvent(&sa, manual, false, name);
4774  if (!handle) /* may already be created */
4775  {
4776  /* try to open it */
4777  handle = OpenEvent(EVENT_ALL_ACCESS, true, name);
4778  }
4779  }
4780  else /* unnamed semaphore */
4781  {
4782  handle = CreateEvent(&sa, manual, false, NULL);
4783  }
4784 
4785  if (handle == NULL) {
4786  return context->NullString();
4787  }
4788 
4789  return context->Uintptr((uintptr_t)handle);
4790 }
4791 
4792 /*************************************************************************
4793 * Function: SysOpenEventSem *
4794 * *
4795 * Syntax: result = SysOpenEventSem(name) *
4796 * *
4797 * Params: name - name of the event semaphore *
4798 * *
4799 * Return: result - return code from OpenEvent *
4800 *************************************************************************/
4801 
4802 RexxRoutine1(uintptr_t, SysOpenEventSem, CSTRING, name)
4803 {
4804  SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
4805 
4806  /* get a binary handle */
4807  return (uintptr_t)OpenEvent(EVENT_ALL_ACCESS, true, name); /* try to open it */
4808 }
4809 
4810 /*************************************************************************
4811 * Function: SysPostEventSem *
4812 * *
4813 * Syntax: result = SysPostEventSem(handle) *
4814 * *
4815 * Params: handle - token returned from SysCreateEventSem *
4816 * *
4817 * Return: result - return code from SetEvent *
4818 *************************************************************************/
4819 
4820 RexxRoutine1(int, SysPostEventSem, uintptr_t, h)
4821 {
4822  return !SetEvent((HANDLE)h) ? GetLastError() : 0;
4823 }
4824 
4825 /*************************************************************************
4826 * Function: SysResetEventSem *
4827 * *
4828 * Syntax: result = SysResetEventSem(handle) *
4829 * *
4830 * Params: handle - token returned from SysCreateEventSem *
4831 * *
4832 * Return: result - return code from ResetEvent *
4833 *************************************************************************/
4834 
4835 RexxRoutine1(int, SysResetEventSem, uintptr_t, h)
4836 {
4837  return !ResetEvent((HANDLE)h) ? GetLastError() : 0;
4838 }
4839 
4840 
4841 /*************************************************************************
4842 * Function: SysPulseEventSem *
4843 * *
4844 * Syntax: result = SysPulseEventSem(handle) *
4845 * *
4846 * Params: handle - token returned from SysCreateEventSem *
4847 * *
4848 * Return: result - return code from PulseEvent *
4849 *************************************************************************/
4850 
4851 RexxRoutine1(int, SysPulseEventSem, uintptr_t, h)
4852 {
4853  return !PulseEvent((HANDLE)h) ? GetLastError() : 0;
4854 }
4855 
4856 
4857 /*************************************************************************
4858 * Function: SysCloseEventSem *
4859 * *
4860 * Syntax: result = SysCloseEventSem(handle) *
4861 * *
4862 * Params: handle - token returned from SysCreateEventSem *
4863 * *
4864 * Return: result - return code from CloseHandle *
4865 *************************************************************************/
4866 
4867 RexxRoutine1(int, SysCloseEventSem, uintptr_t, h)
4868 {
4869  return !CloseHandle((HANDLE)h) ? GetLastError() : 0;
4870 }
4871 
4872 /*************************************************************************
4873 * Function: SysWaitEventSem *
4874 * *
4875 * Syntax: result = SysWaitEventSem(handle, <timeout>) *
4876 * *
4877 * Params: handle - token returned from SysWaitEventSem *
4878 * *
4879 * Return: result - return code from WaitForSingleObject *
4880 *************************************************************************/
4881 
4882 RexxRoutine2(int, SysWaitEventSem, uintptr_t, h, OPTIONAL_int, timeout)
4883 {
4884  if (!argumentExists(2))
4885  {
4886  timeout = INFINITE; /* default is no timeout */
4887  }
4888  /* request the semaphore */
4889  int rc = WaitForSingleObject((HANDLE)h, timeout);
4890  if (rc == WAIT_FAILED)
4891  {
4892  return GetLastError();
4893  }
4894  else
4895  {
4896  return rc; /* format the return code */
4897  }
4898 }
4899 
4900 
4901 /*************************************************************************
4902 * Function: SysSetPriority *
4903 * *
4904 * Syntax: result = SysSetPriority(Class, Level) *
4905 * *
4906 * Params: Class - The priority class (0-3 or HIGH,REALTIME,NORMAL,IDLE) *
4907 * Level - Amount to change (-15 to +15 or IDLE, LOWEST,...) *
4908 * *
4909 *************************************************************************/
4910 
4912 {
4913  HANDLE process = GetCurrentProcess();
4914  HANDLE thread = GetCurrentThread();
4915 
4916  DWORD iclass=-1;
4917  wholenumber_t classLevel; /* priority class */
4918  if (context->WholeNumber(classArg, &classLevel))
4919  {
4920  switch (classLevel)
4921  {
4922  case 0: iclass = IDLE_PRIORITY_CLASS;
4923  break;
4924  case 1: iclass = NORMAL_PRIORITY_CLASS;
4925  break;
4926  case 2: iclass = HIGH_PRIORITY_CLASS;
4927  break;
4928  case 3: iclass = REALTIME_PRIORITY_CLASS;
4929  default:
4930  context->InvalidRoutine();
4931  return 0;
4932  }
4933  }
4934  else
4935  {
4936  const char *classStr = context->ObjectToStringValue(classArg);
4937 
4938  if (stricmp(classStr, "REALTIME") == 0)
4939  {
4940  iclass = REALTIME_PRIORITY_CLASS;
4941  }
4942  else if (stricmp(classStr, "HIGH") == 0)
4943  {
4944  iclass = HIGH_PRIORITY_CLASS;
4945  }
4946  else if (!stricmp(classStr, "NORMAL") == 0)
4947  {
4948  iclass = NORMAL_PRIORITY_CLASS;
4949  }
4950  else if (stricmp(classStr, "IDLE") == 0)
4951  {
4952  iclass = IDLE_PRIORITY_CLASS;
4953  }
4954  else
4955  {
4956  context->InvalidRoutine();
4957  return 0;
4958  }
4959  }
4960 
4961 
4962  wholenumber_t level; /* priority level */
4963  if (context->WholeNumber(levelArg, &level))
4964  {
4965  if (level < -15 || level > 15)
4966  {
4967  context->InvalidRoutine();
4968  return 0;
4969  }
4970  }
4971  else
4972  {
4973  const char *levelStr = context->ObjectToStringValue(levelArg);
4974 
4975  if (stricmp(levelStr, "ABOVE_NORMAL") == 0)
4976  {
4977  level = THREAD_PRIORITY_ABOVE_NORMAL;
4978  }
4979  else if (stricmp(levelStr, "BELOW_NORMAL") == 0)
4980  {
4981  level = THREAD_PRIORITY_BELOW_NORMAL;
4982  }
4983  else if (stricmp(levelStr, "HIGHEST") == 0)
4984  {
4985  level = THREAD_PRIORITY_HIGHEST;
4986  }
4987  else if (stricmp(levelStr, "LOWEST") == 0)
4988  {
4989  level = THREAD_PRIORITY_LOWEST;
4990  }
4991  else if (stricmp(levelStr, "NORMAL") == 0)
4992  {
4993  level = THREAD_PRIORITY_NORMAL;
4994  }
4995  else if (stricmp(levelStr, "IDLE") == 0)
4996  {
4997  level = THREAD_PRIORITY_IDLE;
4998  }
4999  else if (stricmp(levelStr, "TIME_CRITICAL") == 0)
5000  {
5001  level = THREAD_PRIORITY_TIME_CRITICAL;
5002  }
5003  else
5004  {
5005  context->InvalidRoutine();
5006  return 0;
5007  }
5008  }
5009 
5010  int rc = SetPriorityClass(process, iclass);
5011  if (rc)
5012  {
5013  rc = SetThreadPriority(thread, (int)level);
5014  }
5015 
5016  return rc != 0 ? 0 : GetLastError();
5017 }
5018 
5019 
5020 /*************************************************************************
5021 * Function: SysQueryProcess *
5022 * *
5023 * Params: "PID" - (default) returns current process ID *
5024 * "TID" - (default) returns current thread ID *
5025 * "PPRIO" - (default) returns current process priority *
5026 * "TPRIO" - (default) returns current thread priority *
5027 * "PTIME" - (default) returns current process times *
5028 * "TTIME" - (default) returns current thread times *
5029 *************************************************************************/
5030 
5031 RexxRoutine1(RexxObjectPtr, SysQueryProcess, OPTIONAL_CSTRING, option)
5032 {
5033  if (option == NULL || stricmp(option, "PID") == 0)
5034  {
5035  return context->WholeNumber(GetCurrentProcessId());
5036  }
5037  if (stricmp(option, "TID") == 0)
5038  {
5039  return context->WholeNumber(GetCurrentThreadId());
5040  }
5041  if (stricmp(option, "PPRIO") == 0)
5042  {
5043  LONG p;
5044  p = GetPriorityClass(GetCurrentProcess());
5045 
5046  switch (p)
5047  {
5048  case HIGH_PRIORITY_CLASS:
5049  return context->String("HIGH");
5050  case IDLE_PRIORITY_CLASS:
5051  return context->String("IDLE");
5052  case NORMAL_PRIORITY_CLASS:
5053  return context->String("NORMAL");
5054  case REALTIME_PRIORITY_CLASS:
5055  return context->String("REALTIME");
5056  default:
5057  return context->String("UNKNOWN");
5058  }
5059  }
5060  if (stricmp(option, "TPRIO") == 0)
5061  {
5062  LONG p;
5063  p = GetThreadPriority(GetCurrentThread());
5064 
5065  switch (p)
5066  {
5067  case THREAD_PRIORITY_ABOVE_NORMAL:
5068  return context->String("ABOVE_NORMAL");
5069  case THREAD_PRIORITY_BELOW_NORMAL:
5070  return context->String("BELOW_NORMAL");
5071  case THREAD_PRIORITY_HIGHEST:
5072  return context->String("HIGHEST");
5073  case THREAD_PRIORITY_IDLE:
5074  return context->String("IDLE");
5075  case THREAD_PRIORITY_LOWEST:
5076  return context->String("LOWEST");
5077  break;
5078  case THREAD_PRIORITY_NORMAL:
5079  return context->String("NORMAL");
5080  break;
5081  case THREAD_PRIORITY_TIME_CRITICAL:
5082  return context->String("TIME_CRITICAL");
5083  default:
5084  return context->String("UNKNOWN");
5085  }
5086  }
5087  if (stricmp(option, "PTIME") == 0 || stricmp(option, "TTIME") == 0)
5088  {
5089  FILETIME createT, kernelT, userT, dummy;
5090  SYSTEMTIME createST, kernelST, userST;
5091  BOOL ok;
5092 
5093  if (*option == 'T' || *option == 't')
5094  {
5095  ok = GetThreadTimes(GetCurrentThread(), &createT,&dummy,&kernelT, &userT);
5096  }
5097  else
5098  {
5099  ok = GetProcessTimes(GetCurrentProcess(), &createT,&dummy,&kernelT, &userT);
5100  }
5101 
5102  if (ok)
5103  {
5104  FileTimeToLocalFileTime(&createT, &createT);
5105  FileTimeToSystemTime(&createT, &createST);
5106  FileTimeToSystemTime(&kernelT, &kernelST);
5107  FileTimeToSystemTime(&userT, &userST);
5108 
5109  char buffer[256];
5110 
5111  wsprintf(buffer, "Create: %4.4d/%2.2d/%2.2d %d:%2.2d:%2.2d:%3.3d "\
5112  "Kernel: %d:%2.2d:%2.2d:%3.3d User: %d:%2.2d:%2.2d:%3.3d",
5113  createST.wYear,createST.wMonth,createST.wDay,createST.wHour,createST.wMinute,
5114  createST.wSecond,createST.wMilliseconds,
5115  kernelST.wHour,kernelST.wMinute,kernelST.wSecond,kernelST.wMilliseconds,
5116  userST.wHour,userST.wMinute,userST.wSecond,userST.wMilliseconds);
5117 
5118  return context->String(buffer);
5119  }
5120  }
5121  context->InvalidRoutine();
5122  return NULLOBJECT;
5123 }
5124 
5125 
5126 /** SysShutDownSystem()
5127  *
5128  * Interface to the InitiateSystemShutdown() API on Windows.
5129  *
5130  * @param computer The name of the computer to shut down. If omitted
5131  * or the empty string, the local machine is shut
5132  * down. Otherwise this is the name of a remote
5133  * machine to shut down.
5134  * @param message If timout is not 0, a shut down dialog is displayed
5135  * on the machine being shut down, naming the user who
5136  * initiated the shut down, a timer counting down the
5137  * seconds until the machine is shut down, and
5138  * prompting the local user to log off. This parametr
5139  * can be an additional message to add to the dialog
5140  * box. It can be ommitted if no additional message
5141  * is desired.
5142  * @param timeout Number of seconds to display the shut down dialog.
5143  * If this is 0 no dialog is displayed. The default
5144  * is 30 seconds, see the remarks below. The user can
5145  * force a 0 timeout by explicitly specifying 0.
5146  * @param forceAppsClosed If true applications with unsaved data are forcibly
5147  * closed. If false, the user is presented with a
5148  * dialog telling the user to close the applcation(s).
5149  * @param reboot If true, the system is rebooted, if false the
5150  * system is shut down.
5151  *
5152  * @remarks Note that prior to 4.0.0, the defaults for all arguments were
5153  * exactly what the value of each parameter is if omitted.
5154  *
5155  * machine == NULL
5156  * message == NULL
5157  * timeout == 0
5158  * forceAppsClosed == false
5159  * reboot == false
5160  *
5161  * Because of this, there would be no need to check if any argument is
5162  * ommitted or not. However, the consequences of having a 0 timeout
5163  * value are severe if the system has an application open with unsaved
5164  * data. Therefore for 4.0.0 and on the default time out value is
5165  * changed to 30 (seconds.)
5166  */
5167 RexxRoutine5(uint32_t, SysShutDownSystem, OPTIONAL_CSTRING, computer, OPTIONAL_CSTRING, message, OPTIONAL_uint32_t, timeout,
5168  OPTIONAL_logical_t, forceAppsClosed, OPTIONAL_logical_t, reboot)
5169 {
5170  uint32_t result = 0;
5171 
5172  HANDLE hToken = NULL;
5173  TOKEN_PRIVILEGES tkp;
5174 
5175  // First we get the token for the current process so we can add the proper
5176  // shutdown privilege.
5177  if ( OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) == 0 )
5178  {
5179  result = GetLastError();
5180  goto done_out;
5181  }
5182 
5183  // Get the locally unique identifier for the shutdown privilege we need,
5184  // local or remote, depending on what the user specified.
5185  LPCTSTR privilegeName = (computer == NULL || *computer == '\0') ? SE_SHUTDOWN_NAME : SE_REMOTE_SHUTDOWN_NAME;
5186  if ( LookupPrivilegeValue(NULL, privilegeName, &tkp.Privileges[0].Luid) == 0 )
5187  {
5188  result = GetLastError();
5189  goto done_out;
5190  }
5191 
5192  // We are only going to adjust 1 privilege and we are going to enable it.
5193  tkp.PrivilegeCount = 1;
5194  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
5195 
5196  // The return from this function can not accurately be used to determine if
5197  // it failed or not. Instead we need to use GetLastError to determine
5198  // success.
5199  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
5200  result = GetLastError();
5201  if ( result != ERROR_SUCCESS )
5202  {
5203  goto done_out;
5204  }
5205 
5206  // Do not shut down in 0 seconds by default.
5207  if ( argumentOmitted(3) )
5208  {
5209  timeout = 30;
5210  }
5211 
5212  // Now just call the API with the parameters specified by the user.
5213  if ( InitiateSystemShutdown((LPSTR)computer, (LPSTR)message, timeout, (BOOL)forceAppsClosed, (BOOL)reboot) == 0 )
5214  {
5215  result = GetLastError();
5216  }
5217 
5218  // Finally, restore the shutdown privilege for this process to disabled.
5219  tkp.Privileges[0].Attributes = 0;
5220  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
5221 
5222 done_out:
5223  if ( hToken != NULL )
5224  {
5225  CloseHandle(hToken);
5226  }
5227  return result;
5228 }
5229 
5230 /*************************************************************************
5231 * Function: SysSwitchSession *
5232 * *
5233 * Syntax: result = SysSwitchSession(name) *
5234 * *
5235 * Params: name - name of target session *
5236 * *
5237 * Return: OS/2 error return code *
5238 *************************************************************************/
5239 
5240 RexxRoutine1(int, SysSwitchSession, CSTRING, name)
5241 {
5242  HWND hwnd = FindWindow(NULL, name);
5243 
5244  if (hwnd)
5245  {
5246  if (!SetForegroundWindow(hwnd))
5247  {
5248  return GetLastError();
5249  }
5250  else
5251  {
5252  return 0;
5253  }
5254  }
5255  else
5256  {
5257  return 1;
5258  }
5259 }
5260 
5261 /*************************************************************************
5262 * Function: SysWaitNamedPipe *
5263 * *
5264 * Syntax: result = SysWaitNamedPipe(name, timeout) *
5265 * *
5266 * Params: name - name of the pipe *
5267 * timeout - amount of time to wait. *
5268 * *
5269 * Return: Return code from WaitNamedPipe *
5270 *************************************************************************/
5271 
5272 RexxRoutine2(int, SysWaitNamedPipe, CSTRING, name, OPTIONAL_int, timeout)
5273 {
5274  if (argumentOmitted(2))
5275  {
5276  timeout = NMPWAIT_USE_DEFAULT_WAIT;
5277  }
5278  else
5279  {
5280  if (timeout < -1)
5281  {
5282  context->InvalidRoutine();
5283  return 0;
5284  }
5285  if (timeout == 0)
5286  {
5287  timeout = NMPWAIT_USE_DEFAULT_WAIT;
5288  }
5289  else if (timeout == -1)
5290  {
5291  timeout = NMPWAIT_WAIT_FOREVER;
5292  }
5293  }
5294 
5295  if (WaitNamedPipe(name, timeout))
5296  {
5297  return 0;
5298  }
5299  else
5300  {
5301  return GetLastError();
5302  }
5303 }
5304 
5305 
5306 /*************************************************************************
5307 * Function: SysDumpVariables *
5308 * *
5309 * Syntax: result = SysDumpVariables([filename]) *
5310 * *
5311 * Params: filename - name of the file where variables are appended to *
5312 * (dump is written to stdout if omitted) *
5313 * *
5314 * Return: 0 - dump completed OK *
5315 * -1 - failure during dump *
5316 *************************************************************************/
5317 
5318 size_t RexxEntry SysDumpVariables(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5319 {
5320  LONG rc; /* Ret code */
5321  SHVBLOCK shvb;
5322  HANDLE outFile = NULL;
5323  bool fCloseFile = false;
5324  DWORD dwBytesWritten = 0;
5325  char *buffer = NULL; /* ENG: write result file to */
5326  char *current, *end; /* memory first, much faster! */
5327  size_t buffer_size = 10240; /* buffer, realloc'd if needed*/
5328  size_t new_size; /* real new size */
5329 
5330  if ( (numargs > 1) || /* wrong number of arguments? */
5331  ((numargs > 0) && !RXVALIDSTRING(args[0])) )
5332  return INVALID_ROUTINE; /* raise error condition */
5333 
5334  if (numargs > 0)
5335  {
5336  /* open output file for append */
5337  outFile = CreateFile(args[0].strptr, GENERIC_WRITE | GENERIC_READ,
5338  0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
5339  FILE_FLAG_WRITE_THROUGH, NULL);
5340  if (outFile)
5341  {
5342  fCloseFile = true;
5343 
5344  /* seek to end of file */
5345  SetFilePointer(outFile, 0, 0, FILE_END);
5346  }
5347  }
5348  else
5349  outFile = GetStdHandle(STD_OUTPUT_HANDLE);
5350 
5351  /* write results to memory */
5352  /* first and then in one step */
5353  /* to disk */
5354  buffer = (char*) calloc(buffer_size,1);
5355  if (buffer == NULL)
5356  return INVALID_ROUTINE; /* raise error condition */
5357  current = buffer;
5358  end = current + buffer_size;
5359 
5360  do
5361  {
5362  /* prepare request block */
5363  shvb.shvnext = NULL;
5364  shvb.shvname.strptr = NULL; /* let REXX allocate the memory */
5365  shvb.shvname.strlength = 0;
5366  shvb.shvnamelen = 0;
5367  shvb.shvvalue.strptr = NULL; /* let REXX allocate the memory */
5368  shvb.shvvalue.strlength = 0;
5369  shvb.shvvaluelen = 0;
5370  shvb.shvcode = RXSHV_NEXTV;
5371  shvb.shvret = 0;
5372 
5373  rc = RexxVariablePool(&shvb);
5374 
5375  if (rc == RXSHV_OK)
5376  {
5377  new_size = 5 + 9 + 3 + shvb.shvname.strlength + shvb.shvvalue.strlength;
5378  /* if buffer is not big enough, */
5379  /* reallocate */
5380  if (current + new_size >= end) {
5381  size_t offset = current - buffer;
5382  buffer_size *= 2;
5383  /* if new buffer too small, use the minimal fitting size */
5384  if (buffer_size - offset < new_size) {
5385  buffer_size = new_size + offset;
5386  }
5387  buffer = (char *)realloc(buffer,buffer_size);
5388  current = buffer + offset;
5389  end = buffer + buffer_size;
5390  }
5391  sprintf(current, "Name=");
5392  current += 5;
5393  memcpy(current, shvb.shvname.strptr, shvb.shvname.strlength);
5394  current += shvb.shvname.strlength;
5395  sprintf(current, ", Value='");
5396  current += 9;
5397  memcpy(current, shvb.shvvalue.strptr, shvb.shvvalue.strlength);
5398  current += shvb.shvvalue.strlength;
5399  sprintf(current, "'\r\n");
5400  current += 3;
5401 
5402  /* free memory allocated by REXX */
5403  RexxFreeMemory((void *)shvb.shvname.strptr);
5404  RexxFreeMemory((void *)shvb.shvvalue.strptr);
5405 
5406  /* leave loop if this was the last var */
5407  if (shvb.shvret & RXSHV_LVAR)
5408  break;
5409  }
5410  } while (rc == RXSHV_OK);
5411 
5412  WriteFile(outFile, buffer, (DWORD)(current - buffer), &dwBytesWritten, NULL);
5413  free(buffer);
5414 
5415  if (fCloseFile)
5416  CloseHandle(outFile);
5417 
5418  if (rc != RXSHV_LVAR)
5419  RETVAL(-1)
5420  else
5421  RETVAL(0)
5422 }
5423 
5424 
5425 /*************************************************************************
5426 * Function: SysSetFileDateTime *
5427 * *
5428 * Syntax: result = SysSetFileDateTime(filename [,newdate] [,newtime]) *
5429 * *
5430 * Params: filename - name of the file to update *
5431 * newdate - new date to set in format YYYY-MM-DD (YYYY>1800) *
5432 * newtime - new time to set in format HH:MM:SS *
5433 * *
5434 * Return: 0 - file date/time was updated correctly *
5435 * -1 - failure attribute update *
5436 *************************************************************************/
5437 
5438 RexxRoutine3(int, SysSetFileDateTime, CSTRING, name, OPTIONAL_CSTRING, newdate, OPTIONAL_CSTRING, newtime)
5439 {
5440  BOOL fOk = FALSE;
5441  FILETIME sFileTime;
5442  FILETIME sLocalFileTime;
5443  SYSTEMTIME sLocalSysTime;
5444 
5445  /* open output file for read/write for update */
5446  HANDLE setFile = CreateFile(name, GENERIC_WRITE | GENERIC_READ,
5447  FILE_SHARE_READ | FILE_SHARE_WRITE,
5448  NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH |
5449  FILE_FLAG_BACKUP_SEMANTICS, NULL);
5450  if (setFile && (setFile != INVALID_HANDLE_VALUE))
5451  {
5452  fOk = GetFileTime(setFile, NULL, NULL, &sFileTime);
5453  fOk &= FileTimeToLocalFileTime(&sFileTime, &sLocalFileTime);
5454  fOk &= FileTimeToSystemTime(&sLocalFileTime, &sLocalSysTime);
5455  if (fOk)
5456  {
5457  /* file date/time could be read, now parse the new date/time */
5458  if (newdate != NULL)
5459  {
5460  /* parse new date */
5461  if (sscanf(newdate, "%4hu-%2hu-%2hu", &sLocalSysTime.wYear,
5462  &sLocalSysTime.wMonth, &sLocalSysTime.wDay) != 3)
5463  {
5464  fOk = false;
5465  }
5466 
5467  if (sLocalSysTime.wYear < 1800)
5468  {
5469  fOk = false;
5470  }
5471  }
5472 
5473  if (newtime != NULL)
5474  {
5475  /* parse new time */
5476  if (sscanf(newtime, "%2hu:%2hu:%2hu", &sLocalSysTime.wHour,
5477  &sLocalSysTime.wMinute, &sLocalSysTime.wSecond) != 3)
5478  {
5479  fOk = false;
5480  }
5481  }
5482 
5483  if (newdate == NULL && newtime == NULL)
5484  {
5485  /* we set the timestamp to the current time and date */
5486  GetLocalTime(&sLocalSysTime);
5487  }
5488 
5489  if (fOk)
5490  {
5491  fOk &= SystemTimeToFileTime(&sLocalSysTime, &sLocalFileTime);
5492  fOk &= LocalFileTimeToFileTime(&sLocalFileTime, &sFileTime);
5493  fOk &= SetFileTime(setFile, NULL, NULL, &sFileTime);
5494  }
5495  }
5496 
5497  CloseHandle(setFile);
5498  }
5499 
5500  return fOk ? 0 : 1;
5501 }
5502 
5503 /*************************************************************************
5504 * Function: SysGetFileDateTime *
5505 * *
5506 * Syntax: result = SysGetFileDateTime(filename [,timesel]) *
5507 * Params: filename - name of the file to query *
5508 * timesel - What filetime to query: Created/Access/Write *
5509 * *
5510 * Return: -1 - file date/time query failed *
5511 * other - date and time as YYYY-MM-DD HH:MM:SS *
5512 *************************************************************************/
5513 
5514 RexxRoutine2(RexxObjectPtr, SysGetFileDateTime, CSTRING, name, OPTIONAL_CSTRING, selector)
5515 {
5516  FILETIME sFileTime;
5517  FILETIME sLocalFileTime;
5518  FILETIME *psFileCreated = NULL;
5519  FILETIME *psFileAccessed = NULL;
5520  FILETIME *psFileWritten = NULL;
5521  SYSTEMTIME sLocalSysTime;
5522 
5523  if (selector != NULL)
5524  {
5525  switch (selector[0])
5526  {
5527  case 'c':
5528  case 'C':
5529  psFileCreated = &sFileTime;
5530  break;
5531  case 'a':
5532  case 'A':
5533  psFileAccessed = &sFileTime;
5534  break;
5535  case 'w':
5536  case 'W':
5537  psFileWritten = &sFileTime;
5538  break;
5539  default:
5540  context->InvalidRoutine();
5541  return NULLOBJECT;
5542  }
5543  }
5544  else
5545  {
5546  psFileWritten = &sFileTime;
5547  }
5548 
5549  /* open file for read to query time */
5550  HANDLE setFile = CreateFile(name, FILE_READ_ATTRIBUTES,
5551  FILE_SHARE_READ | FILE_SHARE_WRITE,
5552  NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH |
5553  FILE_FLAG_BACKUP_SEMANTICS, NULL);
5554  if (setFile != INVALID_HANDLE_VALUE)
5555  {
5556  BOOL fOk = GetFileTime(setFile, psFileCreated, psFileAccessed, psFileWritten);
5557  CloseHandle(setFile);
5558  fOk &= FileTimeToLocalFileTime(&sFileTime, &sLocalFileTime);
5559  fOk &= FileTimeToSystemTime(&sLocalFileTime, &sLocalSysTime);
5560 
5561  if (fOk)
5562  {
5563  char buffer[256];
5564  sprintf(buffer, "%4d-%02d-%02d %02d:%02d:%02d",
5565  sLocalSysTime.wYear,
5566  sLocalSysTime.wMonth,
5567  sLocalSysTime.wDay,
5568  sLocalSysTime.wHour,
5569  sLocalSysTime.wMinute,
5570  sLocalSysTime.wSecond);
5571  return context->String(buffer);
5572  }
5573  }
5574  return context->WholeNumber(-1);
5575 
5576 }
5577 
5578 
5579 RexxReturnCode REXXENTRY RexxStemSort(const char *stemname, int order, int type,
5580  size_t start, size_t end, size_t firstcol, size_t lastcol);
5581 
5582 /*************************************************************************
5583 * Function: SysStemSort *
5584 * *
5585 * Syntax: result = SysStemSort(stem, order, type, start, end, *
5586 * firstcol, lastcol) *
5587 * *
5588 * Params: stem - name of stem to sort *
5589 * order - 'A' or 'D' for sort order *
5590 * type - 'C', 'I', 'N' for comparision type *
5591 * start - first index to sort *
5592 * end - last index to sort *
5593 * firstcol - first column to use as sort key *
5594 * lastcol - last column to use as sort key *
5595 * *
5596 * Return: 0 - sort was successful *
5597 * -1 - sort failed *
5598 *************************************************************************/
5599 
5600 size_t RexxEntry SysStemSort(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5601 {
5602  CHAR stemName[255];
5603  size_t first = 1;
5604  size_t last = SIZE_MAX;
5605  size_t firstCol = 0;
5606  size_t lastCol = SIZE_MAX;
5607  INT sortType = SORT_CASESENSITIVE;
5608  INT sortOrder = SORT_ASCENDING;
5609 
5610  // validate arguments
5611  if ( (numargs < 1) || (numargs > 7) || !RXVALIDSTRING(args[0]) )
5612  {
5613  return INVALID_ROUTINE;
5614  }
5615 
5616  // remember stem name
5617  memset(stemName, 0, sizeof(stemName));
5618  strcpy(stemName, args[0].strptr);
5619  if ( stemName[args[0].strlength-1] != '.' )
5620  {
5621  stemName[args[0].strlength] = '.';
5622  }
5623 
5624  // check other parameters. sort order
5625  if ( (numargs >= 2) && RXVALIDSTRING(args[1]) )
5626  {
5627  switch ( args[1].strptr[0] )
5628  {
5629  case 'A':
5630  case 'a':
5631  sortOrder = SORT_ASCENDING;
5632  break;
5633  case 'D':
5634  case 'd':
5635  sortOrder = SORT_DECENDING;
5636  break;
5637  default:
5638  return INVALID_ROUTINE;
5639  }
5640  }
5641  // sort type
5642  if ( (numargs >= 3) && RXVALIDSTRING(args[2]) )
5643  {
5644  switch ( args[2].strptr[0] )
5645  {
5646  case 'C':
5647  case 'c':
5648  sortType = SORT_CASESENSITIVE;
5649  break;
5650  case 'I':
5651  case 'i':
5652  sortType = SORT_CASEIGNORE;
5653  break;
5654  default:
5655  return INVALID_ROUTINE;
5656  }
5657  }
5658  // first element to sort
5659  if ( (numargs >= 4) && RXVALIDSTRING(args[3]) )
5660  {
5661  if (!string2size_t(args[3].strptr, &first))
5662  {
5663  return INVALID_ROUTINE;
5664  }
5665  if ( first == 0 )
5666  {
5667  return INVALID_ROUTINE;
5668  }
5669  }
5670  // last element to sort
5671  if ( (numargs >= 5) && RXVALIDSTRING(args[4]) )
5672  {
5673  if (!string2size_t(args[4].strptr, &last))
5674  return INVALID_ROUTINE;
5675  if ( last < first )
5676  return INVALID_ROUTINE;
5677  }
5678  // first column to sort
5679  if ( (numargs >= 6) && RXVALIDSTRING(args[5]) )
5680  {
5681  if (!string2size_t(args[5].strptr, &firstCol))
5682  {
5683  return INVALID_ROUTINE;
5684  }
5685  firstCol--;
5686  }
5687  // last column to sort
5688  if ( (numargs == 7) && RXVALIDSTRING(args[6]) )
5689  {
5690  if (!string2size_t(args[6].strptr, &lastCol))
5691  {
5692  return INVALID_ROUTINE;
5693  }
5694  lastCol--;
5695  if ( lastCol < firstCol )
5696  {
5697  return INVALID_ROUTINE;
5698  }
5699 
5700  }
5701 
5702  // the sorting is done in the interpreter
5703  if ( !RexxStemSort(stemName, sortOrder, sortType, first, last, firstCol, lastCol) )
5704  {
5705  sprintf(retstr->strptr, "-1");
5706  retstr->strlength = 2;
5707  return INVALID_ROUTINE;
5708  }
5709 
5710  sprintf(retstr->strptr, "0");
5711  retstr->strlength = 1;
5712  return VALID_ROUTINE;
5713 }
5714 
5715 
5716 /*************************************************************************
5717 * Function: SysStemDelete *
5718 * *
5719 * Syntax: result = SysStemDelete(stem, startitem [,itemcount]) *
5720 * *
5721 * Params: stem - name of stem where item will be deleted *
5722 * startitem - index of item to delete *
5723 * itemcount - number of items to delete if more than 1 *
5724 * *
5725 * Return: 0 - delete was successful *
5726 * -1 - delete failed *
5727 *************************************************************************/
5728 
5729 RexxRoutine3(int, SysStemDelete, RexxStemObject, toStem, stringsize_t, start, OPTIONAL_stringsize_t, count)
5730 
5731 {
5732  if (argumentOmitted(3))
5733  {
5734  count = 1;
5735  }
5736 
5737  stringsize_t items;
5738 
5739  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
5740  if (temp == NULLOBJECT || !context->StringSize(temp, &items))
5741  {
5742  context->InvalidRoutine();
5743  return 0;
5744  }
5745 
5746  // make sure the deletion site is within the bounds
5747  if (start + count - 1 > items)
5748  {
5749  context->InvalidRoutine();
5750  return 0;
5751  }
5752 
5753  stringsize_t index;
5754  /* now copy the remaining indices up front */
5755  for ( index = start; index + count <= items; index++)
5756  {
5757  // copy from the old index to the new index
5758  RexxObjectPtr value = context->GetStemArrayElement(toStem, index + count);
5759  // is this a sparse array?
5760  if (value == NULLOBJECT)
5761  {
5762  // return this as a failure
5763  return -1;
5764  }
5765  context->SetStemArrayElement(toStem, index, value);
5766  }
5767 
5768  /* now delete the items at the end */
5769  for (index = items - count + 1; index <= items; index++)
5770  {
5771  context->DropStemArrayElement(toStem, index);
5772  }
5773 
5774  context->SetStemArrayElement(toStem, 0, context->StringSize(items - count));
5775  return 0;
5776 }
5777 
5778 
5779 /*************************************************************************
5780 * Function: SysStemInsert *
5781 * *
5782 * Syntax: result = SysStemInsert(stem, position, value) *
5783 * *
5784 * Params: stem - name of stem where item will be inserted *
5785 * position - index where new item will be inserted *
5786 * value - new item value *
5787 * *
5788 * Return: 0 - insert was successful *
5789 * -1 - insert failed *
5790 *************************************************************************/
5791 
5792 RexxRoutine3(int, SysStemInsert, RexxStemObject, toStem, stringsize_t, position, RexxObjectPtr, newValue)
5793 {
5794  stringsize_t count;
5795 
5796  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
5797  if (temp == NULLOBJECT || !context->StringSize(temp, &count))
5798  {
5799  context->InvalidRoutine();
5800  return 0;
5801  }
5802 
5803  /* check wether new position is within limits */
5804  if (position == 0 || (position > count + 1))
5805  {
5806  context->InvalidRoutine();
5807  return 0;
5808  }
5809 
5810  for (size_t index = count; index >= position; index--)
5811  {
5812  // copy from the old index to the new index
5813  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5814  // is this a sparse array?
5815  if (value == NULLOBJECT)
5816  {
5817  // return this as a failure
5818  return -1;
5819  }
5820  context->SetStemArrayElement(toStem, index + 1, value);
5821  }
5822 
5823  // now set the new value and increase the count at stem.0
5824  context->SetStemArrayElement(toStem, position, newValue);
5825  context->SetStemArrayElement(toStem, 0, context->WholeNumber(count + 1));
5826  return 0;
5827 }
5828 
5829 
5830 /*************************************************************************
5831 * Function: SysStemCopy *
5832 * *
5833 * Syntax: result = SysStemCopy(fromstem, tostem, from, to, count *
5834 * [,insert]) *
5835 * *
5836 * Params: fromstem - name of source stem *
5837 * tostem - - name of target stem *
5838 * from - first index in source stem to copy *
5839 * to - position where items are copied/inserted in target stem*
5840 * count - number of items to copy/insert *
5841 * insert - 'I' to indicate insert instead of 'O' overwrite *
5842 * *
5843 * Return: 0 - stem copy was successful *
5844 * -1 - stem copy failed *
5845 *************************************************************************/
5846 
5847 RexxRoutine6(int, SysStemCopy, RexxStemObject, fromStem, RexxStemObject, toStem,
5848  OPTIONAL_stringsize_t, from, OPTIONAL_stringsize_t, to, OPTIONAL_stringsize_t, count,
5849  OPTIONAL_CSTRING, option)
5850 {
5851  bool inserting = false;
5852 
5853  /* get copy type */
5854  if (option != NULL)
5855  {
5856  switch (*option)
5857  {
5858  case 'I':
5859  case 'i':
5860  inserting = true;
5861  break;
5862  case 'O':
5863  case 'o':
5864  inserting = false;
5865  break;
5866  default:
5867  {
5868  context->InvalidRoutine();
5869  return 0;
5870  }
5871  }
5872  }
5873 
5874  stringsize_t fromCount;
5875 
5876  RexxObjectPtr temp = context->GetStemArrayElement(fromStem, 0);
5877  if (temp == NULLOBJECT || !context->StringSize(temp, &fromCount))
5878  {
5879  context->InvalidRoutine();
5880  return 0;
5881  }
5882 
5883  // default from location is the first element
5884  if (argumentOmitted(3))
5885  {
5886  from = 1;
5887  }
5888 
5889  if (argumentOmitted(4))
5890  {
5891  to = 1;
5892  }
5893 
5894  // was a count explicitly specified?
5895  if (argumentExists(5))
5896  {
5897  // this must be in range
5898  if ((count > (fromCount - from + 1)) || (fromCount == 0))
5899  {
5900  context->InvalidRoutine();
5901  return 0;
5902  }
5903  }
5904  else
5905  {
5906  // default is to copy everything from the starting position.
5907  count = fromCount - from + 1;
5908  }
5909 
5910  stringsize_t toCount = 0;
5911  // but if it is set, then use that value
5912  temp = context->GetStemArrayElement(toStem, 0);
5913  if (temp != NULLOBJECT && !context->StringSize(temp, &toCount))
5914  {
5915  context->InvalidRoutine();
5916  return 0;
5917  }
5918 
5919  // copying out of range? Error
5920  if (to > toCount + 1)
5921  {
5922  context->InvalidRoutine();
5923  return 0;
5924  }
5925 
5926  if (inserting)
5927  {
5928  /* if we are about to insert the items we have to make room */
5929  for (size_t index = toCount; index >= to; index--)
5930  {
5931  // copy from the old index to the new index
5932  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5933  // is this a sparse array?
5934  if (value == NULLOBJECT)
5935  {
5936  // return this as a failure
5937  return -1;
5938  }
5939  context->SetStemArrayElement(toStem, index + count, value);
5940  }
5941 
5942 
5943  // set the new count value in the target
5944  toCount += count;
5945  context->SetStemArrayElement(toStem, 0, context->StringSize(toCount));
5946  }
5947  /* now do the actual copying from the source to target */
5948  for (size_t index = 0; index < count; index++)
5949  {
5950  // just retrieve and copy
5951  RexxObjectPtr value = context->GetStemArrayElement(fromStem, from + index);
5952  // is this a sparse array?
5953  if (value == NULLOBJECT)
5954  {
5955  // return this as a failure
5956  return -1;
5957  }
5958  context->SetStemArrayElement(toStem, to + index, value);
5959  }
5960 
5961  // do we need to update the size?
5962  if (to + count - 1 > toCount)
5963  {
5964  context->SetStemArrayElement(toStem, 0, context->StringSize(to + count - 1));
5965  }
5966  return 0;
5967 }
5968 
5969 
5970 /*************************************************************************
5971 * Function: SysUtilVersion *
5972 * *
5973 * Syntax: Say SysUtilVersion *
5974 * *
5975 * Return: REXXUTIL.DLL Version *
5976 *************************************************************************/
5977 
5979 {
5980  char buffer[256];
5981  /* format into the buffer */
5982  sprintf(buffer, "%d.%d.%d", ORX_VER, ORX_REL, ORX_MOD);
5983  return context->String(buffer);
5984 }
5985 
5986 /**
5987  * Check if the dwFlags arguement to WideCharToMultiByte() can be used by the
5988  * specified code page. See MSDN documentation for WideCharToMultiByte() for
5989  * clarification. This is used by SysFromUnicode()
5990  *
5991  * @param cp Code page to check.
5992  *
5993  * @return Return true if dwFlags can be non-zero, return false if dwFlags must
5994  * be zero.
5995  */
5996 static bool canUseWideCharFlags(UINT cp)
5997 {
5998  if ( cp == CP_SYMBOL || cp == CP_UTF7 || cp == CP_UTF8 )
5999  {
6000  return false;
6001  }
6002  if ( 50220 <= cp && cp <= 50222 )
6003  {
6004  return false;
6005  }
6006  if ( cp == 50225 || cp == 50227 || cp == 50229 || cp == 52936 || cp == 54936 )
6007  {
6008  return false;
6009  }
6010  if ( 57002 <= cp && cp <= 57011 )
6011  {
6012  return false;
6013  }
6014  return true;
6015 }
6016 
6017 /*************************************************************************
6018 * Function: SysFromUnicode *
6019 * Converts a UNICODE string to an ASCII string *
6020 * *
6021 * Syntax: result = SysFromUniCode(string,CodePage,MappingFlags, *
6022 * DefaultChar, outstem.) *
6023 * *
6024 * Params: string - unicode string to be converted *
6025 * Codepage - target codepage *
6026 * MappingFlags - Mapping flags *
6027 * DefaultChar - default for unmappable chars *
6028 * outstem. - stem containg the result *
6029 * .!USEDDEFAULTCHAR - 1: character used as default *
6030 * .!TEXT - converted text *
6031 * *
6032 * *
6033 * Return: 0 - successfull completetion *
6034 * error code from WideCharToMultiByte *
6035 
6036  The following are the OEM code-page identifiers.
6037 
6038  437 MS-DOS United States
6039  708 Arabic (ASMO 708)
6040  709 Arabic (ASMO 449+, BCON V4)
6041  710 Arabic (Transparent Arabic)
6042  720 Arabic (Transparent ASMO)
6043  737 Greek (formerly 437G)
6044  775 Baltic
6045  850 MS-DOS Multilingual (Latin I)
6046  852 MS-DOS Slavic (Latin II)
6047  855 IBM Cyrillic (primarily Russian)
6048  857 IBM Turkish
6049  860 MS-DOS Portuguese
6050  861 MS-DOS Icelandic
6051  862 Hebrew
6052  863 MS-DOS Canadian-French
6053  864 Arabic
6054  865 MS-DOS Nordic
6055  866 MS-DOS Russian (former USSR)
6056  869 IBM Modern Greek
6057  874 Thai
6058  932 Japan
6059  936 Chinese (PRC, Singapore)
6060  949 Korean
6061  950 Chinese (Taiwan; Hong Kong SAR, PRC)
6062  1361 Korean (Johab)
6063 
6064  The following are the ANSI code-page identifiers.
6065 
6066  874 Thai
6067  932 Japan
6068  936 Chinese (PRC, Singapore)
6069  949 Korean
6070  950 Chinese (Taiwan; Hong Kong SAR, PRC)
6071  1200 Unicode (BMP of ISO 10646)
6072  1250 Windows 3.1 Eastern European
6073  1251 Windows 3.1 Cyrillic
6074  1252 Windows 3.1 Latin 1 (US, Western Europe)
6075  1253 Windows 3.1 Greek
6076  1254 Windows 3.1 Turkish
6077  1255 Hebrew
6078  1256 Arabic
6079  1257 Baltic
6080 
6081  COMPOSITECHECK :
6082  Convert composite characters to precomposed characters.
6083 
6084  DISCARDNS :
6085  Discard nonspacing characters during conversion.
6086 
6087  SEPCHARS :
6088  Generate separate characters during conversion. This is the default conversion behavior.
6089 
6090  DEFAULTCHAR :
6091  Replace exceptions with the default character during conversion.
6092 
6093 *************************************************************************/
6094 
6095 RexxRoutine5(int, SysFromUniCode, RexxStringObject, sourceString, OPTIONAL_CSTRING, codePageOpt,
6096  OPTIONAL_CSTRING, mappingFlags, OPTIONAL_CSTRING, defaultChar, RexxStemObject, stem)
6097 {
6098  const char *source = context->StringData(sourceString);
6099  size_t sourceLength = context->StringLength(sourceString);
6100 
6101  UINT codePage;
6102  /* evaluate codepage */
6103  if (codePageOpt == NULL)
6104  {
6105  codePage = GetOEMCP();
6106  }
6107  else
6108  {
6109  if (_stricmp(codePageOpt, "THREAD_ACP") == 0)
6110  {
6111  codePage = CP_THREAD_ACP;
6112  }
6113  else if (_stricmp(codePageOpt,"ACP") == 0)
6114  {
6115  codePage = CP_ACP;
6116  }
6117  else if (_stricmp(codePageOpt,"MACCP") == 0)
6118  {
6119  codePage = CP_MACCP;
6120  }
6121  else if (_stricmp(codePageOpt,"OEMCP") == 0)
6122  {
6123  codePage = CP_OEMCP;
6124  }
6125  else if (_stricmp(codePageOpt,"SYMBOL") == 0)
6126  {
6127  codePage = CP_SYMBOL;
6128  }
6129  else if (_stricmp(codePageOpt,"UTF7") == 0)
6130  {
6131  codePage = CP_UTF7;
6132  }
6133  else if (_stricmp(codePageOpt,"UTF8") == 0)
6134  {
6135  codePage = CP_UTF8;
6136  }
6137  else
6138  {
6139  codePage = atoi(codePageOpt);
6140  }
6141  }
6142 
6143  DWORD dwFlags = 0;
6144  /* evaluate the mapping flags */
6145  if (mappingFlags != NULL && *mappingFlags != '\0' )
6146  {
6147  /* The WC_SEPCHARS, WC_DISCARDNS, and WC_DEFAULTCHAR flags must also
6148  * specify the WC_COMPOSITECHECK flag. So, we add that for the user if
6149  * they skipped it. Those 4 flags are only available for code pages <
6150  * 50000, excluding 42 (CP_SYMBOL). See the remarks section in the MSDN
6151  * docs for clarification.
6152  */
6153  if ( codePage < 50000 && codePage != CP_SYMBOL )
6154  {
6155  if ( StrStrI(mappingFlags, "COMPOSITECHECK") != NULL )
6156  {
6157  dwFlags |= WC_COMPOSITECHECK;
6158  }
6159  if ( StrStrI(mappingFlags, "SEPCHARS") != NULL )
6160  {
6161  dwFlags |= WC_SEPCHARS | WC_COMPOSITECHECK;
6162  }
6163  if ( StrStrI(mappingFlags, "DISCARDNS") != NULL )
6164  {
6165  dwFlags |= WC_DISCARDNS| WC_COMPOSITECHECK;
6166  }
6167  if ( StrStrI(mappingFlags, "DEFAULTCHAR") != NULL )
6168  {
6169  dwFlags |= WC_DEFAULTCHAR | WC_COMPOSITECHECK;
6170  }
6171  }
6172 
6173  if ( StrStrI(mappingFlags, "NO_BEST_FIT") != NULL )
6174  {
6175  dwFlags |= WC_NO_BEST_FIT_CHARS;
6176  }
6177 
6178  if ( StrStrI(mappingFlags, "ERR_INVALID") != NULL )
6179  {
6180  if ( codePage == CP_UTF8 && isAtLeastVista() )
6181  {
6182  dwFlags |= WC_ERR_INVALID_CHARS;
6183  }
6184  }
6185  else if ( dwFlags == 0 && ! (codePage < 50000 && codePage != CP_SYMBOL) )
6186  {
6187  context->InvalidRoutine();
6188  return 0;
6189  }
6190  }
6191 
6192  /* evaluate default charcter */
6193  const char *strDefaultChar = NULL;
6194  BOOL bUsedDefaultChar = FALSE;
6195  BOOL *pUsedDefaultChar = &bUsedDefaultChar;
6196 
6197  if (defaultChar != NULL && (dwFlags & WC_DEFAULTCHAR) == WC_DEFAULTCHAR)
6198  {
6199  strDefaultChar = defaultChar;
6200  }
6201  else
6202  {
6203  /* use our own default character rather than relying on the windows default */
6204  strDefaultChar = "?";
6205  }
6206 
6207  /* There are a number of invalid combinations of arguments to
6208  * WideCharToMultiByte(), see the MSDN docs. Eliminate them here.
6209  */
6210  if ( codePage == CP_UTF8 && dwFlags == WC_ERR_INVALID_CHARS)
6211  {
6212  strDefaultChar = NULL;
6213  pUsedDefaultChar = NULL;
6214  }
6215  else if ( ! canUseWideCharFlags(codePage) )
6216  {
6217  dwFlags = 0;
6218  strDefaultChar = NULL;
6219  pUsedDefaultChar = NULL;
6220  }
6221 
6222  /* Allocate space for the string, to allow double zero byte termination */
6223  char *strptr = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sourceLength + 4);
6224  if (strptr == NULL)
6225  {
6226  context->InvalidRoutine();
6227  return 0;
6228  }
6229  memcpy(strptr, source, sourceLength);
6230 
6231  /* Query the number of bytes required to store the Dest string */
6232  int iBytesNeeded = WideCharToMultiByte( codePage,
6233  dwFlags,
6234  (LPWSTR) strptr,
6235  (int)(sourceLength/2),
6236  NULL,
6237  0,
6238  NULL,
6239  NULL);
6240 
6241  if (iBytesNeeded == 0)
6242  {
6243  GlobalFree(strptr);
6244  return GetLastError();
6245  }
6246 
6247  // hard error, stop
6248  char *str = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, iBytesNeeded + 4);
6249  if (str == NULL)
6250  {
6251  context->InvalidRoutine();
6252  return 0;
6253  }
6254 
6255  /* Do the conversion */
6256  int iBytesDestination = WideCharToMultiByte(codePage, // codepage
6257  dwFlags, // conversion flags
6258  (LPWSTR) strptr, // source string
6259  (int)(sourceLength/2), // source string length
6260  str, // target string
6261  (int)iBytesNeeded, // size of target buffer
6262  strDefaultChar,
6263  pUsedDefaultChar);
6264 
6265  if (iBytesDestination == 0) // call to function fails
6266  {
6267  GlobalFree(str); // free allocated string
6268  GlobalFree(strptr); // free allocated string
6269  return GetLastError(); // return error from function call
6270  }
6271 
6272  // set whether the default character was used in the output stem
6273  if (bUsedDefaultChar)
6274  {
6275  context->SetStemElement(stem, "!USEDDEFAULTCHAR", context->True());
6276  }
6277  else
6278  {
6279  context->SetStemElement(stem, "!USEDDEFAULTCHAR", context->False());
6280  }
6281 
6282  context->SetStemElement(stem, "!TEXT", context->String(str, iBytesNeeded));
6283  GlobalFree(strptr); // free allocated string
6284  GlobalFree(str); // free allocated string
6285  return 0;
6286 }
6287 
6288 /*
6289 * Syntax: result = SysToUniCode(string,CodePage,MappingFlags,outstem.)
6290 */
6291 /*************************************************************************
6292 * Function: SysToUnicode *
6293 * Converts an ASCII to UNICODE *
6294 * *
6295 * Syntax: result = SysToUniCode(string,CodePage,MappingFlags,outstem.)*
6296 * *
6297 * Params: string - ascii string to be converted *
6298 * Codepage - target codepage *
6299 * MappingFlags - Mapping flags *
6300 * outstem. - stem containg the result *
6301 * .!TEXT - converted text *
6302 * *
6303 * Return: 0 - successfull completetion *
6304 * error code from WideCharToMultiByteToWideChars *
6305 
6306  For available codepages see function SysFromUniCode.
6307 
6308  Additional parameters for codepages:
6309 
6310  ACP ANSI code page
6311  MACCP Macintosh code page
6312  OEMCP OEM code page
6313  SYMBOL Windows 2000: Symbol code page (42)
6314  THREAD_ACP Windows 2000: The current thread's ANSI code page
6315  UTF7 Windows NT 4.0 and Windows 2000: Translate using UTF-7
6316  UTF8 Windows NT 4.0 and Windows 2000: Translate using UTF-8.
6317  When this is set, dwFlags must be zero.
6318 
6319  PRECOMPOSED Always use precomposed characters-that is, characters
6320  in which a base character and a nonspacing character
6321  have a single character value.
6322  This is the default translation option.
6323  Cannot be used with MB_COMPOSITE.
6324  COMPOSITE Always use composite characters that is,
6325  characters in which a base character and a nonspacing
6326  character have different character values.
6327  Cannot be used with MB_PRECOMPOSED.
6328  ERR_INVALID_CHARS If the function encounters an invalid input character,
6329  it fails and GetLastError returns
6330  ERROR_NO_UNICODE_TRANSLATION.
6331  USEGLYPHCHARS Use glyph characters instead of control characters.
6332 
6333 
6334 
6335 *************************************************************************/
6336 RexxRoutine4(int, SysToUniCode, RexxStringObject, source, OPTIONAL_CSTRING, codePageOpt,
6337  OPTIONAL_CSTRING, mappingFlags, RexxStemObject, stem)
6338 {
6339  // evaluate codepage
6340  UINT codePage;
6341  if (codePageOpt == NULL)
6342  {
6343  codePage = GetOEMCP();
6344  }
6345  else
6346  {
6347  if (_stricmp(codePageOpt,"THREAD_ACP") == 0)
6348  {
6349  codePage = CP_THREAD_ACP;
6350  }
6351  else if (_stricmp(codePageOpt,"ACP") == 0)
6352  {
6353  codePage = CP_ACP;
6354  }
6355  else if (_stricmp(codePageOpt,"MACCP") == 0)
6356  {
6357  codePage = CP_MACCP;
6358  }
6359  else if (_stricmp(codePageOpt,"OEMCP") == 0)
6360  {
6361  codePage = CP_OEMCP;
6362  }
6363  else if (_stricmp(codePageOpt,"SYMBOL") == 0)
6364  {
6365  codePage = CP_SYMBOL;
6366  }
6367  else if (_stricmp(codePageOpt,"UTF7") == 0)
6368  {
6369  codePage = CP_UTF7;
6370  }
6371  else if (_stricmp(codePageOpt,"UTF8") == 0)
6372  {
6373  codePage = CP_UTF8;
6374  }
6375  else
6376  {
6377  codePage = atoi(codePageOpt);
6378  }
6379  }
6380 
6381  DWORD dwFlags = 0;
6382  // evaluate the mapping flags
6383  if (mappingFlags != NULL)
6384  {
6385  if (mystrstr(mappingFlags, "PRECOMPOSED"))
6386  {
6387  dwFlags |= MB_PRECOMPOSED;
6388  }
6389  if (mystrstr(mappingFlags, "COMPOSITE"))
6390  {
6391  dwFlags |= MB_COMPOSITE;
6392  }
6393  if (mystrstr(mappingFlags, "ERR_INVALID"))
6394  {
6395  dwFlags |= MB_ERR_INVALID_CHARS;
6396  }
6397  if (mystrstr(mappingFlags, "USEGLYPHCHARS"))
6398  {
6399  dwFlags |= MB_USEGLYPHCHARS;
6400  }
6401  if (dwFlags == 0)
6402  {
6403  context->InvalidRoutine();
6404  return 0;
6405  }
6406  }
6407 
6408  /* Query the number of bytes required to store the Dest string */
6409  ULONG ulWCharsNeeded = MultiByteToWideChar( codePage, dwFlags,
6410  context->StringData(source), (int)context->StringLength(source), NULL, NULL);
6411 
6412  if (ulWCharsNeeded == 0)
6413  {
6414  return GetLastError();
6415  }
6416 
6417  ULONG ulDataLen = (ulWCharsNeeded)*2;
6418 
6419  LPWSTR lpwstr = (LPWSTR)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, ulDataLen+4);
6420 
6421  // hard error, stop
6422  if (lpwstr == NULL)
6423  {
6424  context->InvalidRoutine();
6425  return 0;
6426  }
6427 
6428 
6429  /* Do the conversion */
6430  ulWCharsNeeded = MultiByteToWideChar(codePage, dwFlags,
6431  context->StringData(source), (int)context->StringLength(source),
6432  lpwstr, ulWCharsNeeded);
6433 
6434  if (ulWCharsNeeded == 0) // call to function fails
6435  {
6436  GlobalFree(lpwstr); // free allocated string
6437  return GetLastError();
6438  }
6439 
6440  context->SetStemElement(stem, "!TEXT", context->String((const char *)lpwstr, ulDataLen));
6441  GlobalFree(lpwstr); // free allocated string
6442  return 0;
6443 }
6444 
6445 /*************************************************************************
6446 * Function: SysWinGetPrinters *
6447 * *
6448 * Syntax: call SysWinGetPrinters stem. *
6449 * *
6450 * Params: stem. - stem to store infos in *
6451 * *
6452 * Return: error number *
6453 *************************************************************************/
6454 
6455 RexxRoutine1(uint32_t, SysWinGetPrinters, RexxStemObject, stem)
6456 {
6457  DWORD realSize = 0;
6458  DWORD entries = 0;
6459  DWORD currentSize = 10*sizeof(PRINTER_INFO_2)*sizeof(char);
6460  char *pArray = (char*) malloc(sizeof(char)*currentSize);
6461 
6462  while (true)
6463  {
6464  if (EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE)pArray,
6465  currentSize, &realSize, &entries) == 0)
6466  {
6467  // this is not a failure if we get ERROR_INSUFFICIENT_BUFFER
6468  DWORD rc = GetLastError();
6469  if ( rc != ERROR_INSUFFICIENT_BUFFER )
6470  {
6471  free(pArray);
6472  return rc;
6473  }
6474  }
6475  if ( currentSize >= realSize )
6476  {
6477  break;
6478  }
6479  currentSize = realSize;
6480  realSize = 0;
6481  pArray = (char*) realloc(pArray, sizeof(char)*currentSize);
6482  }
6483 
6484  PRINTER_INFO_2 *pResult = (PRINTER_INFO_2*) pArray;
6485 
6486  // set stem.0 to the number of entries then add all the found printers
6487  context->SetStemArrayElement(stem, 0, context->WholeNumber(entries));
6488  while ( entries-- )
6489  {
6490  char szBuffer[256];
6491  sprintf(szBuffer,"%s,%s,%s", pResult[entries].pPrinterName, pResult[entries].pDriverName,
6492  pResult[entries].pPortName);
6493  context->SetStemArrayElement(stem, entries + 1, context->String(szBuffer));
6494  }
6495  free(pArray);
6496  return 0; // a little reversed...success is false, failure is true
6497 }
6498 
6499 /*************************************************************************
6500 * Function: SysWinGetDefaultPrinter *
6501 * *
6502 * Syntax: call SysWinGetDefaultPrinter *
6503 * *
6504 * Params: none *
6505 * *
6506 * Return: string describing default printer *
6507 *************************************************************************/
6508 
6509 RexxRoutine0(RexxStringObject, SysWinGetDefaultPrinter)
6510 {
6511  char buffer[256];
6512  buffer[0] = '\0';
6513 
6514  GetProfileString("Windows", "DEVICE", ",,,", buffer, sizeof(buffer));
6515  return context->String(buffer);
6516 }
6517 
6518 
6519 /*************************************************************************
6520 * Function: SysWinSetDefaultPrinter *
6521 * *
6522 * Syntax: call SysWinSetDefaultPrinter printer *
6523 * *
6524 * Params: string describing default printer *
6525 * *
6526 * Return: 0 on success, otherwise the OS system error number. *
6527 *************************************************************************/
6528 RexxRoutine1(int, SysWinSetDefaultPrinter, CSTRING, printer)
6529 {
6530  int count = 0;
6531 
6532  // Two forms of input are allowed. The old form of
6533  // "Printername,Drivername,Portname" and for W2K or later just the printer
6534  // name. Count the commas to determine which form this might be.
6535  for ( size_t i = 0; printer[i] != '\0'; i++ )
6536  {
6537  if (printer[i] == ',' )
6538  {
6539  count++;
6540  }
6541  }
6542 
6543  if (count != 0 && count != 2)
6544  {
6545  context->InvalidRoutine();
6546  return 0;
6547  }
6548  SetLastError(0);
6549 
6550  if (count == 0 )
6551  {
6552  // This is W2K or later and the user specified just the printer name.
6553  // This code will work on W2K through Vista.
6554  if (SetDefaultPrinter(printer) == 0)
6555  {
6556  return 0;
6557  }
6558  else
6559  {
6560  return GetLastError();
6561  }
6562  }
6563  else
6564  {
6565  // The user still specified the old format. Microssoft
6566  // only provides WriteProfileString() for backward compatibility to
6567  // 16-bit Windows, and at some point this may no longer be supported.
6568  // But it still appears to work on XP.
6569  if (WriteProfileString("Windows", "DEVICE", printer) == 0)
6570  {
6571  return 0;
6572  }
6573  else
6574  {
6575  if ( SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0L, 0L, SMTO_NORMAL, 1000, NULL) == 0 )
6576  {
6577  // If a window just timed out, then GetLastError() will return 0
6578  // and the user will get the succes code. If GetLastError()
6579  // does not return 0, then chances are something really is
6580  // wrong.
6581  return 0;
6582  }
6583  else
6584  {
6585  return GetLastError();
6586  }
6587  }
6588  }
6589 }
6590 
6591 
6592 /*************************************************************************
6593 * Function: SysFileCopy *
6594 * *
6595 * Syntax: call SysFileCopy FROMfile TOfile *
6596 * *
6597 * Params: FROMfile - file to be copied. *
6598 * TOfile - target file of copy operation. *
6599 * *
6600 * Return: Return code from CopyFile() function. *
6601 *************************************************************************/
6602 
6603 RexxRoutine2(int, SysFileCopy, CSTRING, fromFile, CSTRING, toFile)
6604 {
6605  return CopyFile(fromFile, toFile, 0) ? 0 : GetLastError();
6606 }
6607 
6608 /*************************************************************************
6609 * Function: SysFileMove *
6610 * *
6611 * Syntax: call SysFileMove FROMfile TOfile *
6612 * *
6613 * Params: FROMfile - file to be moved. *
6614 * TOfile - target file of move operation. *
6615 * *
6616 * Return: Return code from MoveFile() function. *
6617 *************************************************************************/
6618 
6619 RexxRoutine2(int, SysFileMove, CSTRING, fromFile, CSTRING, toFile)
6620 {
6621  return MoveFile(fromFile, toFile) ? 0 : GetLastError();
6622 }
6623 
6624 /*************************************************************************
6625 * Function: SysFileExist *
6626 * *
6627 * Syntax: call SysFileExist file *
6628 * *
6629 * Params: file - file to check existance of. *
6630 * *
6631 * Return: Logical. *
6632 *************************************************************************/
6633 
6634 RexxRoutine1(logical_t, SysIsFile, CSTRING, file)
6635 {
6636  DWORD dwAttrs = GetFileAttributes(file);
6637  // not a file if either of these is one
6638  return (dwAttrs != 0xffffffff) && ((dwAttrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == 0);
6639 }
6640 
6641 /*************************************************************************
6642 * Function: SysDirExist *
6643 * *
6644 * Syntax: call SysDirExist dir *
6645 * *
6646 * Params: dir - dir to check existance of. *
6647 * *
6648 * Return: Logical. *
6649 *************************************************************************/
6650 
6651 RexxRoutine1(logical_t, SysIsFileDirectory, CSTRING, file)
6652 {
6653  DWORD dwAttrs = GetFileAttributes(file);
6654  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY);
6655 }
6656 
6657 /*************************************************************************
6658 * Function: SysIsFileLink *
6659 * *
6660 * Syntax: call SysIsFileLink file *
6661 * *
6662 * Params: file - file to check if it is a Link (Alias). *
6663 * *
6664 * Return: Logical. *
6665 *************************************************************************/
6666 
6667 RexxRoutine1(logical_t, SysIsFileLink, CSTRING, file)
6668 {
6669  DWORD dwAttrs = GetFileAttributes(file);
6670  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT);
6671 }
6672 
6673 /*************************************************************************
6674 * Function: SysIsFileCompressed *
6675 * *
6676 * Syntax: call SysIsFileCompressed file *
6677 * *
6678 * Params: file - file to check if it is compressed. *
6679 * *
6680 * Return: Logical. *
6681 *************************************************************************/
6682 
6683 RexxRoutine1(logical_t, SysIsFileCompressed, CSTRING, file)
6684 {
6685  DWORD dwAttrs = GetFileAttributes(file);
6686  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_COMPRESSED);
6687 }
6688 
6689 /*************************************************************************
6690 * Function: SysIsFileEncrypted *
6691 * *
6692 * Syntax: call SysIsFileEncrypted file *
6693 * *
6694 * Params: file - file to check if it is Encrypted. *
6695 * *
6696 * Return: Logical. *
6697 *************************************************************************/
6698 
6699 RexxRoutine1(logical_t, SysIsFileEncrypted, CSTRING, file)
6700 {
6701  DWORD dwAttrs = GetFileAttributes(file);
6702  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_ENCRYPTED);
6703 }
6704 
6705 /*************************************************************************
6706 * Function: SysIsFileNotContentIndexed *
6707 * *
6708 * Syntax: call SysIsFileNotContentIndexed file *
6709 * *
6710 * Params: file - file to check if it is to be indexed by the indexing *
6711 * service. *
6712 * *
6713 * Return: Logical. *
6714 *************************************************************************/
6715 
6716 RexxRoutine1(logical_t, SysIsFileNotContentIndexed, CSTRING, file)
6717 {
6718  DWORD dwAttrs = GetFileAttributes(file);
6719  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
6720 }
6721 
6722 /*************************************************************************
6723 * Function: SysIsFileOffline *
6724 * *
6725 * Syntax: call SysIsFileOffline file *
6726 * *
6727 * Params: file - file to check if it is offline *
6728 * *
6729 * Return: Logical. *
6730 *************************************************************************/
6731 
6732 RexxRoutine1(logical_t, SysIsFileOffline, CSTRING, file)
6733 {
6734  DWORD dwAttrs = GetFileAttributes(file);
6735  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_OFFLINE);
6736 }
6737 
6738 /*************************************************************************
6739 * Function: SysIsFileSparse *
6740 * *
6741 * Syntax: call SysIsFileSparse file *
6742 * *
6743 * Params: file - file to check if it is sparse *
6744 * *
6745 * Return: Logical. *
6746 *************************************************************************/
6747 
6748 RexxRoutine1(logical_t, SysIsFileSparse, CSTRING, file)
6749 {
6750  DWORD dwAttrs = GetFileAttributes(file);
6751  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_SPARSE_FILE);
6752 }
6753 
6754 
6755 /*************************************************************************
6756 * Function: SysIsFileTemporary *
6757 * *
6758 * Syntax: call SysIsFileTemporary file *
6759 * *
6760 * Params: file - file to check if it is temporary *
6761 * *
6762 * Return: Logical. *
6763 *************************************************************************/
6764 
6765 RexxRoutine1(logical_t, SysIsFileTemporary, CSTRING, file)
6766 {
6767  DWORD dwAttrs = GetFileAttributes(file);
6768  return (dwAttrs != 0xffffffff) && (dwAttrs & FILE_ATTRIBUTE_TEMPORARY);
6769 }
6770 
6771 
6772 /*************************************************************************
6773 * Function: SysFileExists *
6774 * *
6775 * Syntax: call SysFileExists file *
6776 * *
6777 * Params: file - file to check existence *
6778 * *
6779 * Return: Logical. *
6780 *************************************************************************/
6781 
6782 RexxRoutine1(logical_t, SysFileExists, CSTRING, file)
6783 {
6784  DWORD dwAttrs = GetFileAttributes(file);
6785  return (dwAttrs != 0xffffffff);
6786 }
6787 
6788 
6789 /*************************************************************************
6790 * Function: SysGetLongPathName *
6791 * Converts the specified path to its long form *
6792 * *
6793 * Syntax: longPath = SysGetLongPathName(path) *
6794 * *
6795 * Params: path - a path to an existing file *
6796 * *
6797 * Return: longPath - path converted to its long form *
6798 * NULL string if path doesn't exist or call fails *
6799 *************************************************************************/
6800 
6801 RexxRoutine1(RexxStringObject, SysGetLongPathName, CSTRING, path)
6802 {
6803  CHAR longPath[MAX]; // long version of path
6804  DWORD code = GetLongPathName(path, longPath, MAX);
6805  if ((code == 0) || (code >= MAX)) // call failed of buffer too small
6806  {
6807  return context->NullString();
6808  }
6809  else
6810  {
6811  return context->NewStringFromAsciiz(longPath);
6812  }
6813 }
6814 
6815 
6816 /*************************************************************************
6817 * Function: SysGetShortPathName *
6818 * Converts the specified path to its short form *
6819 * *
6820 * Syntax: shortPath = SysGetShortPathName(path) *
6821 * *
6822 * Params: path - a path to an existing file *
6823 * *
6824 * Return: shortPath - path converted to its short form *
6825 * NULL string if path doesn't exist or call fails *
6826 *************************************************************************/
6827 
6828 RexxRoutine1(RexxStringObject, SysGetShortPathName, CSTRING, path)
6829 {
6830  CHAR shortPath[MAX]; // short version of path
6831  DWORD code = GetShortPathName(path, shortPath, MAX);
6832  if ((code == 0) || (code >= MAX)) // call failed of buffer too small
6833  {
6834  return context->NullString();
6835  }
6836  else
6837  {
6838  return context->NewStringFromAsciiz(shortPath);
6839  }
6840 }
6841 
6842 
6843 // now build the actual entry list
6845 {
6847  REXX_TYPED_ROUTINE(SysCurPos, SysCurPos),
6848  REXX_TYPED_ROUTINE(SysCurState, SysCurState),
6854  REXX_TYPED_ROUTINE(SysFileTree, SysFileTree),
6863  REXX_TYPED_ROUTINE(SysSleep, SysSleep),
6865  REXX_TYPED_ROUTINE(SysTextScreenRead, SysTextScreenRead),
6866  REXX_TYPED_ROUTINE(SysTextScreenSize, SysTextScreenSize),
6867  REXX_TYPED_ROUTINE(SysAddRexxMacro, SysAddRexxMacro),
6868  REXX_TYPED_ROUTINE(SysDropRexxMacro, SysDropRexxMacro),
6869  REXX_TYPED_ROUTINE(SysReorderRexxMacro, SysReorderRexxMacro),
6870  REXX_TYPED_ROUTINE(SysQueryRexxMacro, SysQueryRexxMacro),
6871  REXX_TYPED_ROUTINE(SysClearRexxMacroSpace, SysClearRexxMacroSpace),
6872  REXX_TYPED_ROUTINE(SysLoadRexxMacroSpace, SysLoadRexxMacroSpace),
6873  REXX_TYPED_ROUTINE(SysSaveRexxMacroSpace, SysSaveRexxMacroSpace),
6878  REXX_TYPED_ROUTINE(SysCreateMutexSem, SysCreateMutexSem),
6879  REXX_TYPED_ROUTINE(SysOpenMutexSem, SysOpenMutexSem),
6880  REXX_TYPED_ROUTINE(SysCloseMutexSem, SysCloseMutexSem),
6881  REXX_TYPED_ROUTINE(SysRequestMutexSem, SysRequestMutexSem),
6882  REXX_TYPED_ROUTINE(SysReleaseMutexSem, SysReleaseMutexSem),
6883  REXX_TYPED_ROUTINE(SysCreateEventSem, SysCreateEventSem),
6884  REXX_TYPED_ROUTINE(SysOpenEventSem, SysOpenEventSem),
6885  REXX_TYPED_ROUTINE(SysCloseEventSem, SysCloseEventSem),
6886  REXX_TYPED_ROUTINE(SysResetEventSem, SysResetEventSem),
6887  REXX_TYPED_ROUTINE(SysPostEventSem, SysPostEventSem),
6888  REXX_TYPED_ROUTINE(SysPulseEventSem, SysPulseEventSem),
6889  REXX_TYPED_ROUTINE(SysWaitEventSem, SysWaitEventSem),
6891  REXX_TYPED_ROUTINE(SysSwitchSession, SysSwitchSession),
6892  REXX_TYPED_ROUTINE(SysWaitNamedPipe, SysWaitNamedPipe),
6898  REXX_TYPED_ROUTINE(SysStemDelete, SysStemDelete),
6899  REXX_TYPED_ROUTINE(SysStemInsert, SysStemInsert),
6900  REXX_TYPED_ROUTINE(SysStemCopy, SysStemCopy),
6906  REXX_TYPED_ROUTINE(SysFromUniCode, SysFromUniCode),
6907  REXX_TYPED_ROUTINE(SysToUniCode, SysToUniCode),
6908  REXX_TYPED_ROUTINE(SysWinGetPrinters, SysWinGetPrinters),
6909  REXX_TYPED_ROUTINE(SysWinGetDefaultPrinter, SysWinGetDefaultPrinter),
6910  REXX_TYPED_ROUTINE(SysWinSetDefaultPrinter, SysWinSetDefaultPrinter),
6911 
6912  REXX_TYPED_ROUTINE(SysShutDownSystem, SysShutDownSystem),
6913  REXX_TYPED_ROUTINE(SysFileCopy, SysFileCopy),
6914  REXX_TYPED_ROUTINE(SysFileMove, SysFileMove),
6915  REXX_TYPED_ROUTINE(SysIsFile, SysIsFile),
6916  REXX_TYPED_ROUTINE(SysIsFileDirectory, SysIsFileDirectory),
6917  REXX_TYPED_ROUTINE(SysIsFileLink, SysIsFileLink),
6918  REXX_TYPED_ROUTINE(SysIsFileCompressed, SysIsFileCompressed),
6919  REXX_TYPED_ROUTINE(SysIsFileEncrypted, SysIsFileEncrypted),
6920  REXX_TYPED_ROUTINE(SysIsFileNotContentIndexed, SysIsFileNotContentIndexed),
6921  REXX_TYPED_ROUTINE(SysIsFileOffline, SysIsFileOffline),
6922  REXX_TYPED_ROUTINE(SysIsFileSparse, SysIsFileSparse),
6923  REXX_TYPED_ROUTINE(SysIsFileTemporary, SysIsFileTemporary),
6924  REXX_TYPED_ROUTINE(SysFileExists, SysFileExists),
6925  REXX_TYPED_ROUTINE(SysGetLongPathName, SysGetLongPathName),
6926  REXX_TYPED_ROUTINE(SysGetShortPathName, SysGetShortPathName),
6928 };
6929 
6931 {
6933  REXX_INTERPRETER_4_0_0, // anything after 4.0.0 will work
6934  "REXXUTIL", // name of the package
6935  "4.0", // package information
6936  NULL, // no load/unload functions
6937  NULL,
6938  rexxutil_routines, // the exported functions
6939  NULL // no methods in this package
6940 };
6941 
6942 // package loading stub.
#define min(a, b)
Definition: ArrayClass.cpp:82
RexxReturnCode RexxEntry RexxVariablePool(PSHVBLOCK pshvblock)
int type
Definition: cmdparse.cpp:383
#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:419
#define Rexx_Error_System_resources_user_defined
Definition: oorexxerrors.h:68
#define Rexx_Error_Incorrect_call_user_defined
Definition: oorexxerrors.h:356
#define Rexx_Error_Invalid_argument_number
Definition: oorexxerrors.h:414
#define Rexx_Error_System_service_user_defined
Definition: oorexxerrors.h:404
#define Rexx_Error_Incorrect_call_null
Definition: oorexxerrors.h:343
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:412
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 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)