unix/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-2018 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 unix support */
40 /* */
41 /* unix-based system utility functions */
42 /* */
43 /******************************************************************************/
44 /**********************************************************************
45 * *
46 * This program extends the REXX language by providing many *
47 * REXX external functions. *
48 * These are a partial list of functions included: *
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 * SysGetMessage -- Retrieves a message text from an OS/2 *
74 * message file, substituting values *
75 * provided. *
76 * SysIni -- Reads and/or updates entries in .INI *
77 * files. *
78 * SysLoadFuncs -- Makes all functions in this package *
79 * known to REXX so REXX programs may *
80 * call them. *
81 * SysMkDir -- Creates a directory *
82 * SysVersion -- Returns the system Version number *
83 * SysLinVer -- Returns the Linux Version number *
84 * SysRmDir -- Removes a directory *
85 * SysSearchPath -- Searches throught a specified path *
86 * for a file. *
87 * SysSleep -- Suspends execution for a number of *
88 * seconds. *
89 * SysTempFilename -- Creates a unique filename *
90 * SysTextScreenRead -- Reads characters from the screen, *
91 * in an OS/2 fullscreen or windowed *
92 * command prompt session. *
93 * SysTextScreenSize -- Returns the size of the window in *
94 * rows and columns, *
95 * in an OS/2 fullscreen or windowed *
96 * command prompt session. *
97 * for a file. *
98 * for a file. *
99 * SysCreateMutexSem -- Create a Mutex semaphore *
100 * SysOpenMutexSem -- Open a Mutex semaphore *
101 * SysCloseMutexSem -- Close a Mutex semaphore *
102 * SysRequestMutexSem -- Request a Mutex semaphore *
103 * SysReleaseMutexSem -- Release a Mutex semaphore *
104 * SysCreateEventSem -- Create an Event semaphore *
105 * SysOpenEventSem -- Open an Event semaphore *
106 * SysCloseEventSem -- Close an Event semaphore *
107 * SysPostEventSem -- Post an Event semaphore *
108 * SysResetEventSem -- Reset an Event semaphore *
109 * SysWaitEventSem -- Wait on an Event semaphore *
110 * SysAddRexxMacro -- Load program into macro space *
111 * SysDropRexxMacro -- Drop program from macro space *
112 * SysReorderRexxMacro -- Reorder program in macro space *
113 * SysQueryRexxMacro -- Query ordering of macro space program *
114 * SysClearRexxMacroSpace -- Remove all programs from macro space*
115 * SysLoadRexxMacroSpace -- Load a Rexx macro space *
116 * SysSaveRexxMacroSpace -- Save a Rexx macro space *
117 * to the destination folder *
118 * to the destination folder *
119 * (Merlin only). *
120 * (for SysSwitchSession) *
121 * SysDumpVariables -- Dump current variables to a file *
122 * SysSetFileDateTime -- Set the last modified date of a file *
123 * SysGetFileDateTime -- Get the last modified date of a file *
124 * SysStemSort -- sort a stem array *
125 * SysStemDelete -- delete items in a stem array *
126 * SysStemInsert -- insert items into a stem array *
127 * SysStemCopy -- copy items from one stem array to other*
128 * SysGetErrortext -- Retrieve textual desc. of error number *
129 * SysQueryProcess -- Get information on current proc/thread *
130 * SysUtilVersion -- query version of REXXUTIL.DLL *
131 * *
132 * SysAddFuncPkg -- CREXX for AIX function support *
133 * SysAddCmdPkg -- CREXX for AIX function support *
134 * SysDropFuncPkg -- CREXX for AIX function support *
135 * SysDropCmdPkg -- CREXX for AIX function support *
136 * SysGetpid -- CREXX for AIX function support *
137 * SysFork -- CREXX for AIX function support *
138 * SysWait -- CREXX for AIX function support *
139 * SysCreatePipe -- CREXX for AIX function support *
140 * *
141 * SysFileCopy -- Copy files on the file system *
142 * SysFileMove -- Move / Rename files or directories *
143 * SysIsFile -- does file exist? *
144 * SysIsFileDirectory -- is file a subdirectory? *
145 * SysIsFileLink -- is file a link? *
146 * *
147 **********************************************************************/
148 #ifdef HAVE_CONFIG_H
149 # include "config.h"
150 #endif
151 
152 #if defined( HAVE_LOCALE_H )
153 # include <locale.h>
154 #endif
155 
156 #include "oorexxapi.h"
157 #include "PlatformDefinitions.h" // first need: DEFRXSTRING
158 
159 #if defined( HAVE_SYS_WAIT_H )
160 # include <sys/wait.h>
161 #endif
162 
163 #include <sys/ipc.h>
164 #include <memory.h>
165 
166 #if defined( HAVE_MALLOC_H )
167 # include <malloc.h>
168 #endif
169 
170 #include <fcntl.h>
171 #include <ctype.h>
172 #include <string.h>
173 #include <stdlib.h>
174 #include <stdio.h>
175 #include <unistd.h>
176 #include <limits.h>
177 #include <math.h>
178 #include <limits.h>
179 #include <sys/stat.h> /* mkdir() function */
180 #include <errno.h> /* get the errno variable */
181 #include <stddef.h>
182 #include <sys/types.h>
183 #if !defined(AIX)
184 #include <sys/syscall.h>
185 #endif
186 #include <sys/utsname.h>
187 #include <sys/ipc.h>
188 #include <pthread.h>
189 #include <semaphore.h>
190 #include <signal.h>
191 #include <time.h>
192 #include <netdb.h>
193 
194 
195 #if defined( HAVE_SYS_SEM_H )
196 # include <sys/sem.h>
197 #endif
198 
199 #include <dirent.h> /* directory functions */
200 #include <sys/time.h> /* needed for the select func */
201 
202 #include <time.h> /* needed for the select func */
203 
204 #if defined( HAVE_SYS_SELECT_H )
205 # include <sys/select.h> /* needed for the select func */
206 #endif
207 
208 #if defined( HAVE_SYS_LDR_H )
209 # include <sys/ldr.h> /* needed for the load func */
210 #endif
211 
212 #if defined( HAVE_STRINGS_H )
213 # include <strings.h>
214 #endif
215 
216 #include <utime.h> /* moved, used by AIX & Linux */
217 
218 #if defined( HAVE_SYS_UTSNAME_H )
219 # include <sys/utsname.h> /* get the uname() function */
220 #endif
221 
222 #include <signal.h>
223 
224 #if defined( HAVE_SYS_RESOURCE_H )
225 # include <sys/resource.h> /* get the getpriority() func */
226 #endif
227 
228 #if defined( HAVE_FEATURES_H )
229 # include <features.h> /* catopen() */
230 #endif
231 
232 #if defined( HAVE_NL_TYPES_H )
233 # include <nl_types.h> /* catopen() */
234 #endif
235 
236 #include <termios.h> /* needed for SysGetKey */
237 #include <fnmatch.h> /* fnmatch() */
238 #include <libgen.h> /* dirname, basename */
239 
240 #if !defined( HAVE_UNION_SEMUN )
241 union semun {
242  int val;
243  struct semid_ds *buf;
244  unsigned short *array;
245 };
246 #endif
247 
248 #if defined __APPLE__
249 # define open64 open
250 // avoid warning: '(l)stat64' is deprecated: first deprecated in macOS 10.6
251 # define stat64 stat
252 # define lstat64 lstat
253 #endif
254 
255 
256 extern char *resolve_tilde(const char *);
257 
258 #define INVALID_ROUTINE 40
259 #define MAX_DIGITS 9
260 #define NO_UTIL_ERROR "0" /* No error whatsoever */
261 #define VALID_ROUTINE 0 /* Successful completion */
262 //#define MAX_LINE_LEN 2048 /* max line length */
263 #define MAX_LINE_LEN 4096 /* max line length */
264 #define MAX_READ 0x10000 /* full segment of buffer */
265 #define CH_EOF 0x1A /* end of file marker */
266 #define CH_CR '\r' /* carriage return character */
267 #define CH_NL '\n' /* new line character */
268 #define MAX 256 /* temporary buffer length */
269 #define IBUF_LEN 4096 /* Input buffer length */
270 #define CURRENT_DIR_FIRST 0 /* search flag 'C' */
271 #define ENVIRONMENT_ONLY 1 /* search flag 'N' */
272 #define OFFSET 1000 /* needed to prevent collision*/
273  /* with the return codes */
274 #define MAXUSECOUNT 65535 /* max semaphore usecount */
275 #define REXXMESSAGEFILE "rexx.cat"
276 
277 // SysFileTree initial buffer sizes;
278 #define FNAMESPEC_BUF_LEN IBUF_LEN
279 #define FOUNDFILE_BUF_LEN IBUF_LEN
280 #define FILETIME_BUF_LEN 64
281 #define FILEATTR_BUF_LEN 16
282 #define FOUNDFILELINE_BUF_LEN FOUNDFILE_BUF_LEN + FILETIME_BUF_LEN + FILEATTR_BUF_LEN
283 
284 
285 /*********************************************************************/
286 /* Various definitions used by the math functions */
287 /*********************************************************************/
288 #define SINE 0 /* trig function defines... */
289 #define COSINE 3 /* the ordering is important, */
290 #define TANGENT 1 /* as these get transformed */
291 #define COTANGENT 2 /* depending on the angle */
292 #define MAXTRIG 3 /* value */
293 #define ARCSINE 0 /* defines for arc trig */
294 #define ARCCOSINE 1 /* functions. Ordering is */
295 #define ARCTANGENT 2 /* not as important here */
296 
297 
298 #define pi 3.14159265358979323846l /* pi value */
299 
300 #define DEGREES 'D' /* degrees option */
301 #define RADIANS 'R' /* radians option */
302 #define GRADES 'G' /* grades option */
303 
304 #define DEFAULT_PRECISION 9 /* default precision to use */
305 #define MAX_PRECISION 16 /* maximum available precision*/
306 
307 
308 
309 /*********************************************************************/
310 /* Numeric Error Return Strings */
311 /*********************************************************************/
312 
313 #define NO_UTIL_ERROR "0" /* No error whatsoever */
314 #define ERROR_NOMEM "2" /* Insufficient memory */
315 #define ERROR_FILEOPEN "3" /* Error opening text file */
316 
317 /*********************************************************************/
318 /* Alpha Numeric Return Strings */
319 /*********************************************************************/
320 
321 #define ERROR_RETSTR "ERROR:"
322 
323 /*********************************************************************/
324 /* Numeric Return calls */
325 /*********************************************************************/
326 
327 #define INVALID_ROUTINE 40 /* Raise Rexx error */
328 #define VALID_ROUTINE 0 /* Successful completion */
329 
330 /*********************************************************************/
331 /* Defines used by SysStemSort */
332 /*********************************************************************/
333 #define SORT_CASESENSITIVE 0
334 #define SORT_CASEIGNORE 1
335 
336 
337 #define SORT_ASCENDING 0
338 #define SORT_DECENDING 1
339 
340 #define SORT_NUMERIC 3
341 
342 #define SORT_DEF_AVG_SIZE 20
343 
344 /*********************************************************************/
345 /* Some useful macros */
346 /*********************************************************************/
347 
348 #define BUILDRXSTRING(t, s) { \
349  strcpy((t)->strptr,(s));\
350  (t)->strlength = strlen((s)); \
351 }
352 
353 
354 // No way to know the size of the buffer.
355 // Assumption: DEFRXSTRING, see RexxNativeActivation::callRegisteredRoutine
356 #define RETVAL(retc) { \
357  snprintf(retstr->strptr, DEFRXSTRING, "%d", retc); \
358  retstr->strlength = strlen(retstr->strptr); \
359  return VALID_ROUTINE; \
360 }
361 
362 /*********************************************************************/
363 /* Defines uses by SysTree */
364 /*********************************************************************/
365 
366 #define RECURSE 0x0002
367 #define DO_DIRS 0x0004
368 #define DO_FILES 0x0008
369 #define NAME_ONLY 0x0010
370 #define EDITABLE_TIME 0x0020
371 #define LONG_TIME 0x0040
372 #define CASELESS 0x0080
373 #define IGNORE 2 /* Ignore attributes entirely */
374 
375 
376 /******************************************************************************/
377 /* Defines used by SysGetKey */
378 /******************************************************************************/
379 
380 #define stty(a,b) (void)tcsetattr(a,TCSANOW,b) /* simple set attr. */
381 #define gtty(a,b) (void)tcgetattr(a,b) /* simple get attr. */
382 #define discard_input(a) tcflush(a,TCIFLUSH) /* simple flush */
383 #define restore_tty(a) stty(ttyfd,a) /* new settings STDIN */
384 
385 
386 /* original terminal settings */
387 struct termios in_orig; /* original settings (important!!) */
388 
389 
390 /*
391  * Data structure used with SysFileTree()
392  *
393  * dFNameSpec, dFoundFile, and dFoundFileLine start out pointing to the static
394  * fNameSpec, foundFile, foundFileLine, buffers.
395  *
396  * The nFNameSpec, nFoundFile, and nFoundLine, fields are set to the the byte
397  * count of the static buffers.
398  *
399  * If any buffer turns out to be too small, memory is allocated for a larger
400  * buffer and the corresponding count variable is set to the new size.
401  *
402  * On return from SysFileTree() any allocated memory in the struct is freed.
403  * To test if memory was or wasn't allocated, test if the count variable is not
404  * or is the same size as the static buffers.
405  */
406 typedef struct RxTreeData {
407  size_t count; // Number of found file lines
408  RexxStemObject files; // Stem that holds results.
409  char fNameSpec[FNAMESPEC_BUF_LEN]; // File name portion of the search for file spec, may contain glob characters.
410  char foundFile[FOUNDFILE_BUF_LEN]; // Full path name of found file
411  char foundFileLine[FOUNDFILELINE_BUF_LEN]; // Buffer for found file line, includes foundFile, fileTime, and fileAttr
412  char fileTime[FILETIME_BUF_LEN]; // Time and size of found file
413  char fileAttr[FILEATTR_BUF_LEN]; // File attribute string of found file
414  char *dFNameSpec; // Starts out pointing at fNameSpec
415  char *dFoundFile; // Starts out pointing at foundFile
416  char *dFoundFileLine; // Starts out pointing at foundFileLine
417  size_t nFNameSpec; // CouNt of bytes in dFNameSpec buffer
418  size_t nFoundFile; // CouNt of bytes in dFoundFile
419  size_t nFoundFileLine; // CouNt of bytes in dFoundFileLine buffer
421 
422 
423 /*********************************************************************/
424 /* RxTree Structure used by GetLine, OpenFile and CloseFile */
425 /*********************************************************************/
426 
427 typedef struct _GetFileData {
428  char * buffer; /* file read buffer */
429  size_t size; /* file size */
430  size_t data; /* data left in buffer */
431  size_t residual; /* size left to read */
432  const char *scan; /* current scan position */
433  FILE *handle; /* file handle */
435 
436 /*********************************************************************/
437 /* RxStemData */
438 /* Structure which describes as generic */
439 /* stem variable. */
440 /*********************************************************************/
441 
442 typedef struct RxStemData {
443  SHVBLOCK shvb; /* Request block for RxVar */
444  char ibuf[IBUF_LEN]; /* Input buffer */
445  char varname[MAX]; /* Buffer for the variable */
446  /* name */
447  char stemname[MAX]; /* Buffer for the variable */
448  /* name */
449  size_t stemlen; /* Length of stem. */
450  size_t vlen; /* Length of variable value */
451  size_t j; /* Temp counter */
452  size_t tlong; /* Temp counter */
453  size_t count; /* Number of elements */
454  /* processed */
456 
457 /*********************************************************************/
458 /* SORTMEM used by SysStemSort */
459 /*********************************************************************/
460 typedef struct _SORT_MEM {
461  size_t ulSize;
462  size_t ulRemaining;
463  size_t ulItems;
464  char * pNextBlock;
465  struct _SORT_MEM *pNext;
466  char pData;
468 
469 
470 #ifdef XX__cplusplus
471 extern "C" {
472 #endif
473 
474 /********************************************************************
475 * Function: string2size_t(string, number) *
476 * *
477 * Purpose: Validates and converts an ASCII-Z string from string *
478 * form to an unsigned long. Returns false if the number *
479 * is not valid, true if the number was successfully *
480 * converted. *
481 * *
482 * RC: true - Good number converted *
483 * false - Invalid number supplied. *
484 *********************************************************************/
486  const char *string, /* string to convert */
487  size_t *number) /* converted number */
488 {
489  size_t accumulator; /* converted number */
490  size_t length; /* length of number */
491 
492  length = strlen(string); /* get length of string */
493  if (length == 0 || /* if null string */
494  length > MAX_DIGITS + 1) /* or too long */
495  {
496  return false; /* not valid */
497  }
498 
499  accumulator = 0; /* start with zero */
500 
501  while (length) /* while more digits */
502  {
503  if (!isdigit(*string)) /* not a digit? */
504  {
505  return false; /* tell caller */
506  }
507  /* add to accumulator */
508  accumulator = accumulator * 10 + (*string - '0');
509  length--; /* reduce length */
510  string++; /* step pointer */
511  }
512  *number = accumulator; /* return the value */
513  return true; /* good number */
514 }
515 
516 /*********************************************************************/
517 /**************** REXXUTIL Supporting Functions ********************/
518 /**************** REXXUTIL Supporting Functions ********************/
519 /**************** REXXUTIL Supporting Functions ********************/
520 /*********************************************************************/
521 
522 /********************************************************************
523 * Function: ReadNextBuffer(filedata) *
524 * *
525 * Purpose: Reads the next buffer of data. *
526 * *
527 * RC: 0 buffer was read *
528 * 1 - error occurred reading buffer *
529 *********************************************************************/
531  GetFileData *filedata ) /* global file information */
532 {
533  size_t size; /* size to read */
534  char *endptr; /* end of file pointer */
535  /* get size of this read */
536  if(filedata->residual >= MAX_READ)
537  size = MAX_READ; /* read as much as possible */
538  else
539  size = filedata->residual; /* take the rest */
540  /* read the file in */
541  filedata->data = fread(filedata->buffer, 1, size, filedata->handle);
542  if(!filedata->data) /* problems ? */
543  return (1); /* get out */
544  if (filedata->data != size) /* not get all of it? */
545  filedata->residual = 0; /* no residual */
546  else /* residual is remainder */
547  filedata->residual = filedata->residual - size;
548  /* look for a EOF mark */
549  endptr = (char *)memchr(filedata->buffer, CH_EOF, filedata->data);
550 
551  if (endptr) { /* found an EOF mark */
552  /* set new length */
553  filedata->data = (size_t)(endptr - filedata->buffer);
554  filedata->residual = 0; /* no residual */
555  }
556  filedata->scan = filedata->buffer; /* set position to beginning */
557  return 0;
558 }
559 
560 /***********************************************************************/
561 /* Function: strupr(string) */
562 /* Purpose: Uppercas the given string */
563 /***********************************************************************/
564 void strupr(char * string){
565 
566  for(;*string != '\0';string++){ /* while not at the end */
567  *string = toupper(*string);
568  }
569 }
570 
571 /********************************************************************
572 * Function: OpenFile(file, filedata) *
573 * *
574 * Purpose: Prepares a file for reading. *
575 * *
576 * RC: 0 - file was opened successfully *
577 * 1 - file open error occurred *
578 *********************************************************************/
579 
581  const char *file, /* file name */
582  GetFileData *filedata ) /* global file information */
583 {
584  struct stat64 finfo; /* file information */
585  char * endptr = NULL; /* end of buffer pointer */
586 
587  /* try to open the file */
588  if((filedata->handle = fopen(file,"r")) == NULL)
589  return (1); /* return failure */
590  /* retrieve the file size */
591  if((stat64(file,&finfo) == -1)||(!finfo.st_size)){
592  fclose(filedata->handle); /* close the file */
593  return (1); /* and quit */
594  }
595  if (finfo.st_size <= MAX_READ) { /* less than a single buffer */
596  /* allocate buffer for file */
597  if((filedata->buffer = (char *)malloc(finfo.st_size)) == NULL ){
598  fclose(filedata->handle); /* close the file */
599  return (1); /* and quit */
600  }
601  filedata->size = finfo.st_size; /* save file size */
602  filedata->residual = 0; /* no left over information */
603  /* read the file in */
604  filedata->data = fread(filedata->buffer, 1,
605  finfo.st_size, filedata->handle);
606  if(filedata->data != (size_t)finfo.st_size){/* problems ? */
607  free(filedata->buffer); /* free the buffer */
608  fclose(filedata->handle); /* close the file */
609  return (1); /* and quit */
610  } /* look for a EOF mark */
611  endptr = (char *)memchr(filedata->buffer, CH_EOF, filedata->data);
612  if (endptr) /* found an EOF mark */
613  /* set new length */
614  filedata->data = (size_t)(endptr - filedata->buffer);
615  filedata->scan = filedata->buffer; /* set position to beginning */
616  }
617  else { /* need to read partial */
618  /* allocate buffer for read */
619  if((filedata->buffer = (char *)malloc(MAX_READ)) == NULL ){
620  fclose(filedata->handle); /* close the file */
621  return (1); /* and quit */
622  }
623  filedata->size = finfo.st_size; /* save file size */
624  /* and set remainder */
625  filedata->residual = filedata->size;
626  /* read the file in */
627  if (ReadNextBuffer(filedata)) {
628  free(filedata->buffer); /* free the buffer */
629  fclose(filedata->handle); /* close the file */
630  return 1;
631  }
632  }
633  return 0; /* file is opened */
634 }
635 
636 /********************************************************************
637 * Function: CloseFile(filedata) *
638 * *
639 * Purpose: Close a file *
640 *********************************************************************/
642  GetFileData *filedata ) /* global file information */
643 {
644  fclose(filedata->handle); /* close the file */
645  free(filedata->buffer); /* release the file buffer */
646 }
647 
648 /*********************************************************************
649 * Function: GetLine(line, size, filedata) *
650 * *
651 * Purpose: Reads a line of data using buffered reads. At end of *
652 * file, zero is returned to indicate nothing left. *
653 * *
654 * RC: true - line was read successfully *
655 * false - end of file was reached *
656 *********************************************************************/
657 
659  char *line, /* returned line */
660  size_t size, /* size of line buffer */
661  GetFileData *filedata ) /* file handle */
662 {
663  const char *scan; /* current scan pointer */
664  size_t length; /* line length */
665  size_t copylength; /* copied length */
666 
667 
668  if (!(filedata->data)) { /* if out of current buffer */
669  if (filedata->residual) { /* may be another buffer */
670  ReadNextBuffer(filedata); /* try to read one */
671  if (!filedata->data) /* nothing more? */
672  return 1; /* all done */
673  }
674  else
675  return (1); /* return EOF condition */
676  }
677  /* look for a line feed */
678  scan = (const char *)memchr(filedata->scan, CH_NL, filedata->data);
679  if (scan) { /* found one */
680  /* calculate the length */
681  length = scan - filedata->scan;
682  copylength = length;
683  if (copylength > size)
684  {
685  copylength = size;
686  }
687  /* copy over the data */
688  memcpy(line, filedata->scan, copylength);
689  line[copylength] = '\0'; /* make into ASCIIZ string */
690 
691  /* we don't want the CR character in the result string*/
692  if ( line[copylength - 1] == CH_CR ) {
693  line[copylength - 1] = '\0';
694  }
695 
696  filedata->data -= length + 1; /* reduce the length */
697  filedata->scan = scan + 1; /* set new scan point */
698 
699  if (!filedata->data) { /* all used up */
700  if (filedata->residual) /* more to read */
701  ReadNextBuffer(filedata); /* read the next buffer */
702  }
703  return 0; /* this worked ok */
704  }
705  else /* ran off the end */
706  {
707 /* now we have scanned the whole buffer, but didn't find LF. */
708 /* we have two situation that can appear: */
709 /* 1.) size > filedata->data ==> there is still room in the working */
710 /* buffer, we can see whether we have scanned the whole file */
711 /* --> ReadNextBuffer, or this was it, and we return */
712 /* 2.) size < filedata->buffer ==> we have scanned to the end of the */
713 /* buffer, more than what would fit into it, but still we */
714 /* haven't had a hit. So copy all elements into the buffer */
715 /* read the next buffer, GetLine to get the next LF */
716 /* and return what was put into buffer. Be ALWAYS AWARE that */
717 /* that buffer limits to 2047 bytes, and that we only return up */
718 /* to 2047 bytes of a line. The rest of the line is not returned */
719 /* and not checked for search argument. Nevertheless, this */
720 /* garantees, that the line counter (argument 'N') corresponds */
721 /* with the input file */
722 
723  /* get length to copy */
724  if (size > filedata->data)
725  {
726  copylength = filedata->data; /* copy the rest into linebuffer */
727  /* copy over the data */
728  memcpy(line, filedata->scan, copylength);
729  line[copylength] = '\0'; /* make into ASCIIZ string */
730 
731  /* all data should be read, filedata->data must be zero */
732  filedata->data -= copylength;
733  /* scan should be at the end */
734  filedata->scan += copylength; /* set new scan point */
735 
736  /* if no more data to read in the file, return OK */
737  if (!filedata->residual)
738  return 0;
739  else
740  return GetLine(line + copylength, size - copylength, filedata);
741  }
742  else /* the line is full, scan until LF found but no copy */
743  {
744  copylength = filedata->data;
745  if (size < copylength)
746  {
747  copylength = size;
748  }
749  /* copy over the data */
750  memcpy(line, filedata->scan, copylength);
751  line[copylength] = '\0'; /* make into ASCIIZ string */
752 
753  /* we don't want the CR character in the result string*/
754  filedata->data = 0; /* no data in buffer */
755  filedata->scan += filedata->data; /* set scan point to end */
756 
757  if (filedata->residual) /* more to read */
758  {
759  ReadNextBuffer(filedata); /* read the next buffer */
760  return GetLine(line + copylength, 0, filedata);
761  }
762  else
763  return 0;
764  }
765  }
766 }
767 
768 /********************************************************************
769 * Function: mystrstr(haystack, needle, hlen, nlen, sensitive) *
770 * *
771 * Purpose: Determines if the string 'needle' is in the *
772 * string 'haystack' by returning it's position or *
773 * a NULL if not found. The length of haystack and *
774 * needle are given by 'hlen' and 'nlen' respectively. *
775 * *
776 * If 'sensitive' is true, then the search is case *
777 * sensitive, else it is case insensitive. *
778 * *
779 * RC: num - The pos where needle was found. *
780 * NULL - needle not found. *
781 * *
782 * Used By: SysFileSearch() *
783 *********************************************************************/
784 
785 const char *mystrstr(
786  const char *haystack,
787  const char *needle,
788  size_t hlen,
789  size_t nlen,
790  bool sensitive)
791 
792 {
793  char line[MAX_LINE_LEN];
794  char target[MAX_LINE_LEN];
795  size_t p;
796 
797  /* Copy line - Change nulls to spaces and uppercase if needed */
798 
799  for (p = 0; p < hlen; p++) {
800 
801  if (haystack[p] == '\0')
802  line[p] = ' ';
803  else if (sensitive)
804  line[p] = haystack[p];
805  else line[p] = toupper(haystack[p]);
806  }
807  line[p] = '\0';
808  /* Copy target - Change nulls to spaces and uppercase if needed */
809 
810  for (p = 0; p < nlen; p++) {
811 
812  if (needle[p] == '\0')
813  target[p] = ' ';
814  else if (sensitive)
815  target[p] = needle[p];
816  else target[p] = toupper(needle[p]);
817  }
818  target[p] = '\0';
819  const char *hit = strstr(line, target);
820  if (hit == NULL)
821  {
822  return NULL;
823  }
824  // adjust original pointer to offset
825  return haystack + (hit - line);
826 }
827 
828 /*************************************************************************
829 * Function: get_next_path *
830 * *
831 * Purpose: Read one path out of an environment value pointed to by *
832 * ppenv. *
833 * Used by the 'SearchPath' func. *
834 * Return: 0 - read successful *
835 * 1 - end of environment entry *
836 *************************************************************************/
838  char * *ppenv, /* environment pointer */
839  char * path_buf) /* path buffer */
840 {
841  int i; /* counter */
842 
843  if(*ppenv == NULL) /* environment entry available ? */
844  return (1); /* return end of envrionment */
845  if(**ppenv == ':') /* if we point to the seperator */
846  (*ppenv)++; /* jump over */
847  if(**ppenv == '\0') /* if end of environment entry */
848  return (1); /* return so */
849  /* copy the path out of the */
850  /* environment entry */
851  for(i=0;(**ppenv != ':')&&(**ppenv != '\0');(*ppenv)++){
852  if(i>MAX_LINE_LEN) /* if buffer to short */
853  return (1); /* get out */
854  path_buf[i++] = **ppenv; /* copy this character */
855  }
856  path_buf[i] = '\0'; /* terminate the string */
857  return (0); /* found another path */
858 }
859 
860 /*************************************************************************
861 * Function: SearchPath *
862 * *
863 * Purpose: Search a file along the given environment entry and return *
864 * the full filespec if found. *
865 * *
866 * Return: 0 - found the file(=>buf is modified) *
867 * 1 - not found *
868 *************************************************************************/
870  int SearchFlag, /* search curr dir first ? */
871  const char * path, /* environment variable name */
872  const char * filename, /* searched file */
873  char * buf, /* returned filespec if found */
874  size_t buf_size) /* size of the return buffer */
875 {
876 
877  int rc = 1; /* init rc to not found */
878  DIR *dp; /* directory pointer */
879  struct dirent *ep; /* directory entry pointer */
880  int length; /* path length */
881  char path_buf[IBUF_LEN]; /* current search path */
882  char * penv; /* ptr to the environment */
883 
884  if(!SearchFlag){ /* search curr dir first ? */
885  dp = opendir("./"); /* open current directory */
886  if(dp != NULL){ /* worked well ? */
887  while((ep = readdir(dp))){ /* while entries in the dir */
888  /* if we have a match */
889  if(!strcmp(ep->d_name,filename)){
890  if(!getcwd(buf,buf_size)) /* copy the cwd to return buf */
891  return rc; /* failed, get out */
892  length = strlen(buf); /* get the length of the path */
893  if((length+2+strlen(filename))>buf_size)/* check buf space */
894  return rc; /* not enough, get out */
895  buf[length] = '/'; /* add a slash */
896  buf[length+1] = '\0'; /* and update the terminater */
897  strcat(buf,filename); /* now complete the filespec */
898  rc = 0; /* Yep,found ! */
899  }
900  }
901  (void)closedir(dp); /* close the directory */
902  }
903  }
904  if(!rc) /* if we already found it */
905  return rc; /* return to caller */
906 
907  /* Now search along the environment entry */
908  penv = getenv(path); /* get the environment entry */
909  if(!penv) /* if couldn't get the env */
910  return rc; /* get out */
911  /* while we have another path */
912  /* to search for the file */
913  while(!get_next_path(&penv,path_buf)){
914  dp = opendir(path_buf); /* open the directory */
915  if(dp != NULL){ /* worked well ? */
916  while((ep = readdir(dp))){ /* while entries in the dir */
917  /* if we have a match */
918  if(!strcmp(ep->d_name,filename)){
919  if(strlen(path_buf)>buf_size)/* check the size */
920  return rc; /* get out */
921  strcpy(buf,path_buf); /* copy path to the return buf*/
922  length = strlen(buf); /* get the length of the path */
923  if((length+2+strlen(filename))>buf_size)/* check buf space */
924  return rc; /* not enough, get out */
925  buf[length] = '/'; /* add a slash */
926  buf[length+1] = '\0'; /* and update the terminater */
927  strcat(buf,filename); /* now complete the filespec */
928  (void)closedir(dp); /* close the directory */
929  return 0; /* Yep,found ! */
930  }
931  }
932  (void)closedir(dp); /* close the directory */
933  }
934  }
935  return rc; /* return not found */
936 }
937 
938 
939 /********************************************************************
940 * Function: string2long(string, number) *
941 * *
942 * Purpose: Validates and converts an ASCII-Z string from string *
943 * form to an unsigned long. Returns false if the number *
944 * is not valid, true if the number was successfully *
945 * converted. *
946 * *
947 * RC: true - Good number converted *
948 * false - Invalid number supplied. *
949 *********************************************************************/
950 
952  const char * string,
953  int *number)
954 {
955  int accumulator; /* converted number */
956  int length; /* length of number */
957  int sign; /* sign of number */
958 
959  sign = 1; /* set default sign */
960  if (*string == '-') { /* negative? */
961  sign = -1; /* change sign */
962  string++; /* step past sign */
963  }
964 
965  length = strlen(string); /* get length of string */
966  if (length == 0 || /* if null string */
967  length > MAX_DIGITS) /* or too long */
968  return false; /* not valid */
969 
970  accumulator = 0; /* start with zero */
971 
972  while (length) { /* while more digits */
973  if (!isdigit(*string)) /* not a digit? */
974  return false; /* tell caller */
975  /* add to accumulator */
976  accumulator = accumulator * 10 + (*string - '0');
977  length--; /* reduce length */
978  string++; /* step pointer */
979  }
980  *number = accumulator * sign; /* return the value */
981  return true; /* good number */
982 }
983 
984 
985 
986 void restore_terminal(int signal){
987 
988  stty(STDIN_FILENO,&in_orig); /* restore the terminal settings */
989  raise(signal); /* propagate signal */
990 }
991 
992 /******************************************************************************/
993 /* getkey */
994 /******************************************************************************/
995 /* echo == false => no echo */
996 /* echo == true => echo */
997 /******************************************************************************/
998 int getkey(char *ret, bool echo){
999  /* restore original TTY settings on exit */
1000 
1001 int ttyfd = STDIN_FILENO; /* standard tty filedescriptor */
1002 /* Set the cleanup handler for unconditional process termination */
1003 struct sigaction new_action;
1004 
1005 
1006 /* Set up the structure to specify the new action */
1007 new_action.sa_handler = restore_terminal;
1008 sigfillset(&new_action.sa_mask);
1009 new_action.sa_flags = SA_RESTART;
1010 
1011 /* Termination signals */
1012 sigaction(SIGINT, &new_action, NULL); /* exitClear on SIGINT signal */
1013 sigaction(SIGTERM, &new_action, NULL); /* exitClear on SIGTERM signal */
1014 sigaction(SIGQUIT, &new_action, NULL); /* exitClear on SIGQUIT signal */
1015 sigaction(SIGHUP, &new_action, NULL); /* exitClear on SIGHUP signal */
1016 sigaction(SIGTSTP, &new_action, NULL); /* exitClear on SIGTSTP signal */
1017 sigaction(SIGTTIN, &new_action, NULL); /* exitClear on SIGTTIN signal */
1018 sigaction(SIGTTOU, &new_action, NULL); /* exitClear on SIGTTOU signal */
1019 /* Error signals */
1020 sigaction(SIGSEGV, &new_action, NULL); /* exitClear on SIGSEGV signal */
1021 sigaction(SIGFPE, &new_action, NULL); /* exitClear on SIGFPE signal */
1022 sigaction(SIGILL, &new_action, NULL); /* exitClear on SIGILL signal */
1023 sigaction(SIGBUS, &new_action, NULL); /* exitClear on SIGBUS signal */
1024 sigaction(SIGPIPE, &new_action, NULL); /* exitClear on broken pipe */
1025 
1026 
1027  if( !isatty(ttyfd) ){ /* connected to a terminal ? */
1028  ret[0] = '\0';
1029  return 0;
1030  }
1031  ttyfd = STDIN_FILENO; /* STDIN_FILENO is out default fd */
1032 
1033  /* open tty */
1034  ttyfd = open("/dev/tty", O_RDONLY); /* get filedescriptor (fd) for tty */
1035 
1036  struct termios in_raw; /* global for save reset after SIGNAL */
1037 
1038  gtty(ttyfd, &in_orig); /* determine existing tty settings */
1039 
1040  /* restore original TTY settings on exit */
1041 
1042  /* change STDIN settings to raw */
1043  gtty(ttyfd, &in_raw); /* save old settings */
1044 
1045  in_raw.c_lflag &= ~ICANON; /* no canonical mode */
1046  if (!echo) /* no echo flag set */
1047  in_raw.c_lflag &= ~ECHO; /* no echo */
1048  in_raw.c_cc[VMIN] = 1; /* read 1 byte before returning */
1049  in_raw.c_cc[VTIME] = 0; /* return immediatly (no timeout) */
1050  stty(ttyfd, &in_raw); /* execute settings now! */
1051 
1052 
1053  ret[0] = getchar(); /* read the char */
1054 
1055  ret[1] = '\0'; /* terminate string */
1056 
1057  restore_tty(&in_orig); /* for standard I/O behavior */
1058  close(ttyfd); /* close the terminal */
1059  return 0; /* everything is fine */
1060 }
1061 
1062 /*************************************************************************
1063 * Function: SysSleep *
1064 * *
1065 * Syntax: call SysSleep secs *
1066 * *
1067 * Params: secs - Number of seconds to sleep. *
1068 * must be in the range 0 .. 999999999 *
1069 * *
1070 * Return: 0 *
1071 *************************************************************************/
1072 RexxRoutine1(int, SysSleep, RexxStringObject, delay)
1073 {
1074  double seconds;
1075  // try to convert the provided delay to a valid floating point number
1076  if (context->ObjectToDouble(delay, &seconds) == 0 ||
1077  isnan(seconds) || seconds == HUGE_VAL || seconds == -HUGE_VAL)
1078  {
1079  // 88.902 The &1 argument must be a number; found "&2"
1080  context->RaiseException3(Rexx_Error_Invalid_argument_number, context->String("positional"), context->String("delay"), delay);
1081  return 1;
1082  }
1083 
1084  // we (arbitrarily) limit the number of seconds to 999999999
1085  if (seconds < 0.0 || seconds > 999999999)
1086  {
1087  // 88.907 The &1 argument must be in the range &2 to &3; found "&4"
1088  context->RaiseException(Rexx_Error_Invalid_argument_range,
1089  context->ArrayOfFive(context->String("positional"), context->String("delay"),
1090  context->String("0"), context->String("999999999"), delay));
1091  return 1;
1092  }
1093 
1094  // split into two part: secs and nanoseconds
1095  long secs = (long) seconds;
1096  long nanoseconds = (long) ((seconds - secs) * 1000000000);
1097 
1098 #if defined( HAVE_NANOSLEEP )
1099  struct timespec Rqtp, Rmtp;
1100  Rqtp.tv_sec = secs;
1101  Rqtp.tv_nsec = nanoseconds;
1102  nanosleep(&Rqtp, &Rmtp);
1103 #elif defined( HAVE_NSLEEP )
1104  struct timestruc_t Rqtp, Rmtp;
1105  Rqtp.tv_sec = secs;
1106  Rqtp.tv_nsec = nanoseconds;
1107  nsleep(&Rqtp, &Rmtp);
1108 #else
1109  sleep( secs );
1110 #endif
1111 
1112  return 0;
1113 }
1114 
1115 /*************************************************************************
1116 * Function: SysLoadFuncs *
1117 * *
1118 * Syntax: call SysLoadFuncs [option] *
1119 * *
1120 * Params: none *
1121 * *
1122 * Return: null string *
1123 *************************************************************************/
1124 size_t RexxEntry SysLoadFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1125 {
1126  // this is a NOP now
1127  retstr->strlength = 0; /* set return value */
1128  return VALID_ROUTINE;
1129 }
1130 
1131 
1132 /*************************************************************************
1133 * Function: SysDropFuncs *
1134 * *
1135 * Syntax: call SysDropFuncs *
1136 * *
1137 * Return: NO_UTIL_ERROR - Successful. *
1138 *************************************************************************/
1139 
1140 size_t RexxEntry SysDropFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1141 {
1142  // this is a NOP now
1143  retstr->strlength = 0; /* set return value */
1144  return VALID_ROUTINE;
1145 }
1146 
1147 
1148 /**********************************************************************
1149 * Function: SysCls *
1150 * *
1151 * Syntax: call SysCls *
1152 * *
1153 * Return: NO_UTIL_ERROR - Successful. *
1154 **********************************************************************/
1155 
1156 size_t RexxEntry SysCls(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1157 {
1158  if (numargs) /* arguments specified? */
1159  return INVALID_ROUTINE; /* raise the error */
1160  int ignore = system("clear"); /* do the clear */ // think about the use of 'execve', Weigold
1161  BUILDRXSTRING(retstr, NO_UTIL_ERROR);/* pass back result */
1162  return VALID_ROUTINE; /* no error on call */
1163 }
1164 
1165 
1166 /*************************************************************************
1167 * Function: SysAddRexxMacro *
1168 * *
1169 * Syntax: result = SysAddRexxMacro(name, file, <order>) *
1170 * *
1171 * Params: name - loaded name of the macro file *
1172 * file - file containing the loaded macro *
1173 * order - Either 'B'efore or 'A'fter. The default is 'B' *
1174 * *
1175 * Return: return code from RexxAddMacro *
1176 *************************************************************************/
1177 
1178 RexxRoutine3(int, SysAddRexxMacro, CSTRING, name, CSTRING, file, OPTIONAL_CSTRING, option)
1179 {
1180  size_t position; /* added position */
1181 
1182  position = RXMACRO_SEARCH_BEFORE; /* set default search position*/
1183  if (option != NULL) /* have an option? */
1184  {
1185  switch (*option)
1186  {
1187  case 'B': // 'B'efore
1188  case 'b':
1189  position = RXMACRO_SEARCH_BEFORE;
1190  break;
1191 
1192  case 'A': // 'A'fter
1193  case 'a':
1194  position = RXMACRO_SEARCH_AFTER;
1195  break;
1196 
1197  default:
1198  context->InvalidRoutine();
1199  return 0;
1200  }
1201  }
1202  /* try to add the macro */
1203  return(int)RexxAddMacro(name, file, position);
1204 }
1205 
1206 /*************************************************************************
1207 * Function: SysReorderRexxMacro *
1208 * *
1209 * Syntax: result = SysReorderRexxMacro(name, order) *
1210 * *
1211 * Params: name - loaded name of the macro file *
1212 * order - Either 'B'efore or 'A'fter. *
1213 * *
1214 * Return: return code from RexxReorderMacro *
1215 *************************************************************************/
1216 
1217 RexxRoutine2(int, SysReorderRexxMacro, CSTRING, name, CSTRING, option)
1218 {
1219  size_t position; /* added position */
1220 
1221  switch (*option)
1222  {
1223  case 'B': // 'B'efore
1224  case 'b':
1225  position = RXMACRO_SEARCH_BEFORE;
1226  break;
1227 
1228  case 'A': // 'A'fter
1229  case 'a':
1230  position = RXMACRO_SEARCH_AFTER;
1231  break;
1232 
1233  default:
1234  context->InvalidRoutine();
1235  return 0;
1236  }
1237  return(int)RexxReorderMacro(name, position);
1238 }
1239 
1240 /*************************************************************************
1241 * Function: SysDropRexxMacro *
1242 * *
1243 * Syntax: result = SysDropRexxMacro(name) *
1244 * *
1245 * Params: name - name of the macro space function *
1246 * *
1247 * Return: return code from RexxDropMacro *
1248 *************************************************************************/
1249 
1250 RexxRoutine1(int, SysDropRexxMacro, CSTRING, name)
1251 {
1252  return (int)RexxDropMacro(name);
1253 }
1254 
1255 /*************************************************************************
1256 * Function: SysQueryRexxMacro *
1257 * *
1258 * Syntax: result = SysQueryRexxMacro(name) *
1259 * *
1260 * Params: name - name of the macro space function *
1261 * *
1262 * Return: position of the macro ('B' or 'A'), returns null for errors.*
1263 *************************************************************************/
1264 
1265 RexxRoutine1(CSTRING, SysQueryRexxMacro, CSTRING, name)
1266 {
1267  unsigned short position; /* returned position */
1268 
1269  if (RexxQueryMacro(name, &position) != 0)
1270  {
1271  return "";
1272  }
1273  // before?
1274  if (position == RXMACRO_SEARCH_BEFORE)
1275  {
1276  return "B";
1277  }
1278  else
1279  {
1280  return "A"; /* must be 'A'fter */
1281  }
1282 }
1283 
1284 /*************************************************************************
1285 * Function: SysClearRexxMacroSpace *
1286 * *
1287 * Syntax: result = SysClearRexxMacroSpace() *
1288 * *
1289 * Params: none *
1290 * *
1291 * Return: return code from RexxClearMacroSpace() *
1292 *************************************************************************/
1293 
1294 RexxRoutine0(int, SysClearRexxMacroSpace)
1295 {
1296  return (int)RexxClearMacroSpace(); /* clear the macro space */
1297 }
1298 
1299 /*************************************************************************
1300 * Function: SysSaveRexxMacroSpace *
1301 * *
1302 * Syntax: result = SysSaveRexxMacroSpace(file) *
1303 * *
1304 * Params: file - name of the saved macro space file *
1305 * *
1306 * Return: return code from RexxSaveMacroSpace() *
1307 *************************************************************************/
1308 
1309 RexxRoutine1(int, SysSaveRexxMacroSpace, CSTRING, file)
1310 {
1311  return (int)RexxSaveMacroSpace(0, NULL, file);
1312 }
1313 
1314 /*************************************************************************
1315 * Function: SysLoadRexxMacroSpace *
1316 * *
1317 * Syntax: result = SysLoadRexxMacroSpace(file) *
1318 * *
1319 * Params: file - name of the saved macro space file *
1320 * *
1321 * Return: return code from RexxLoadMacroSpace() *
1322 *************************************************************************/
1323 
1324 RexxRoutine1(int, SysLoadRexxMacroSpace, CSTRING, file)
1325 {
1326  return (int)RexxLoadMacroSpace(0, NULL, file);
1327 }
1328 
1329 
1330 /*************************************************************************
1331 * Function: SysMkDir *
1332 * *
1333 * Syntax: call SysMkDir dir *
1334 * *
1335 * Params: dir - Directory to be created. *
1336 * *
1337 * Return: NO_UTIL_ERROR *
1338 * *
1339 *************************************************************************/
1340 
1341 size_t RexxEntry SysMkDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1342 {
1343  size_t rc; /* Ret code of func */
1344  const char * path; /* given path */
1345  char * dir_buf = NULL; /* full directory path */
1346  int mode; // permission (optional)
1347 
1348  if (numargs < 1 || numargs > 2)
1349  /* If not 1 or 2 args, its an */
1350  /* incorrect call */
1351  return INVALID_ROUTINE;
1352  path = args[0].strptr; /* directory to make */
1353  /* check for using '~/' */
1354  if(*(path) == '~'){
1355  dir_buf = resolve_tilde(path); /* get absolut path */
1356  path = dir_buf; /* directory to make */
1357  }
1358  /* Make the dir; standard permissions are rwxr-xr-x */
1359  /* we do not restrict permission, this is done by root in the file */
1360  /* /etc/security/user. We allow anything. System restricts */
1361  /* according to the user settings --> smitty/user */
1362  if (numargs < 2 || !string2int(args[1].strptr, &mode))
1363  {
1364  mode = S_IRWXU | S_IRWXG | S_IRWXO;
1365  }
1366  rc = mkdir(path, mode);
1367  if(!rc){ /* if worked well */
1368  snprintf(retstr->strptr, DEFRXSTRING, "%d", (int)rc); /* result is return code */
1369  retstr->strlength = strlen(retstr->strptr);
1370  return VALID_ROUTINE;
1371  }
1372  else{ /* if there was an error */
1373  switch (errno) {
1374 
1375  case EACCES: {
1376  snprintf(retstr->strptr, DEFRXSTRING, "%d", 5); /* result: Access denied */
1377  retstr->strlength = strlen(retstr->strptr);
1378  break;
1379  }
1380  case EEXIST: {
1381  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/*result: already exists */
1382  retstr->strlength = strlen(retstr->strptr);
1383  break;
1384  }
1385  case EMLINK: {
1386  snprintf(retstr->strptr, DEFRXSTRING, "%d", 206);/* result: exceeds range */
1387  retstr->strlength = strlen(retstr->strptr);
1388  break;
1389  }
1390  case ENOSPC: {
1391  snprintf(retstr->strptr, DEFRXSTRING, "%d", 206);/* result: exceeds range */
1392  retstr->strlength = strlen(retstr->strptr);
1393  break;
1394  }
1395  case EROFS: {
1396  snprintf(retstr->strptr, DEFRXSTRING, "%d", 108);/* result:read only system*/
1397  retstr->strlength = strlen(retstr->strptr);
1398  break;
1399  }
1400  default:
1401  snprintf(retstr->strptr, DEFRXSTRING, "%d", 2); /* result: file not found */
1402  retstr->strlength = strlen(retstr->strptr);
1403  break;
1404  }
1405  }
1406  if(dir_buf) /* did we allocate ? */
1407  free(dir_buf); /* free the buffer memory */
1408  return VALID_ROUTINE;
1409 }
1410 
1411 
1412 /*************************************************************************
1413 * Function: SysRmDir *
1414 * *
1415 * Syntax: call SysRmDir dir *
1416 * *
1417 * Params: dir - Directory to be removed. *
1418 * *
1419 * Return: NO_UTIL_ERROR *
1420 * *
1421 *************************************************************************/
1422 
1423 size_t RexxEntry SysRmDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1424 {
1425  size_t rc; /* Ret code of func */
1426  const char * path; /* given path */
1427  char * dir_buf = NULL; /* full directory path */
1428 
1429  if (numargs != 1)
1430  /* If no args, then its an */
1431  /* incorrect call */
1432  return INVALID_ROUTINE;
1433  path = args[0].strptr; /* directory to remove */
1434  /* check for using '~/' */
1435  if(*(path) == '~'){
1436  dir_buf = resolve_tilde(path); /* get absolut path */
1437  path = dir_buf; /* directory to remove */
1438  }
1439 
1440  rc = rmdir(path); /* Remove the directory */
1441  if(!rc){ /* if worked well */
1442  snprintf(retstr->strptr, DEFRXSTRING, "%d", (int)rc); /* result is return code */
1443  retstr->strlength = strlen(retstr->strptr);
1444  return VALID_ROUTINE;
1445  }
1446  else{ /* if there was an error */
1447  switch (errno) {
1448 
1449  case EACCES: {
1450  snprintf(retstr->strptr, DEFRXSTRING, "%d", 5); /* result: Access denied */
1451  retstr->strlength = strlen(retstr->strptr);
1452  break;
1453  }
1454 #if defined( ENOTEMPTY ) && defined( EEXIST )
1455  case EEXIST: {
1456  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/*result: already exists */
1457  retstr->strlength = strlen(retstr->strptr);
1458  break;
1459  }
1460 #elif defined( ENOTEMPTY )
1461  case ENOTEMPTY: { /* sometimes used for EEXITST */
1462  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/*result: already exists */
1463  retstr->strlength = strlen(retstr->strptr);
1464  break;
1465  }
1466 #elif defined( EEXIST )
1467  case EEXIST: {
1468  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/*result: already exists */
1469  retstr->strlength = strlen(retstr->strptr);
1470  break;
1471  }
1472 #endif
1473  case EBUSY: {
1474  snprintf(retstr->strptr, DEFRXSTRING, "%d", 5);/* result: currently in use */
1475  retstr->strlength = strlen(retstr->strptr);
1476  break;
1477  }
1478  case ENOENT: {
1479  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/* result: doesn't exitst */
1480  retstr->strlength = strlen(retstr->strptr);
1481  break;
1482  }
1483  case EROFS: {
1484  snprintf(retstr->strptr, DEFRXSTRING, "%d", 108);/* result:read only system*/
1485  retstr->strlength = strlen(retstr->strptr);
1486  break;
1487  }
1488  default:
1489  snprintf(retstr->strptr, DEFRXSTRING, "%d", 2); /* result: dir not found */
1490  retstr->strlength = strlen(retstr->strptr);
1491  break;
1492  }
1493  }
1494  if(dir_buf) /* did we allocate ? */
1495  free(dir_buf); /* free the buffer memory */
1496  return VALID_ROUTINE;
1497 }
1498 
1499 /*************************************************************************
1500 * Function: SysFileDelete *
1501 * *
1502 * Syntax: call SysFileDelete file *
1503 * *
1504 * Params: file - file to be deleted. *
1505 * *
1506 * Return: Return code from DosDelete() function. *
1507 *************************************************************************/
1508 
1509 size_t RexxEntry SysFileDelete(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1510 {
1511  size_t rc; /* Ret code of func */
1512  const char * path; /* given path */
1513  char * dir_buf = NULL; /* full directory path */
1514 
1515  if (numargs != 1)
1516  /* If no args, then its an */
1517  /* incorrect call */
1518  return INVALID_ROUTINE;
1519  path = args[0].strptr; /* file to remove */
1520  /* check for using '~/' */
1521  if(*(path) == '~'){
1522  dir_buf = resolve_tilde(path); /* get absolut path */
1523  path = dir_buf; /* file to remove */
1524  }
1525 
1526  rc = remove(path); /* Remove the file */
1527  if(!rc){ /* if worked well */
1528  snprintf(retstr->strptr, DEFRXSTRING, "%d", (int)rc); /* result is return code */
1529  retstr->strlength = strlen(retstr->strptr);
1530  return VALID_ROUTINE;
1531  }
1532  else{ /* if there was an error */
1533  switch (errno) {
1534 
1535  case EACCES: {
1536  snprintf(retstr->strptr, DEFRXSTRING, "%d", 5); /* result: Access denied */
1537  retstr->strlength = strlen(retstr->strptr);
1538  break;
1539  }
1540  case EBUSY: {
1541  snprintf(retstr->strptr, DEFRXSTRING, "%d", 5);/* result: currently in use */
1542  retstr->strlength = strlen(retstr->strptr);
1543  break;
1544  }
1545  case ENOENT: {
1546  snprintf(retstr->strptr, DEFRXSTRING, "%d", 87);/* result: doesn't exist */
1547  retstr->strlength = strlen(retstr->strptr);
1548  break;
1549  }
1550  case EROFS: {
1551  snprintf(retstr->strptr, DEFRXSTRING, "%d", 108);/* result:read only system*/
1552  retstr->strlength = strlen(retstr->strptr);
1553  break;
1554  }
1555  default:
1556  snprintf(retstr->strptr, DEFRXSTRING, "%d", 2); /* result: file not found */
1557  retstr->strlength = strlen(retstr->strptr);
1558  break;
1559  }
1560  }
1561  if(dir_buf) /* did we allocate ? */
1562  free(dir_buf); /* free the buffer memory */
1563  return VALID_ROUTINE;
1564 }
1565 
1566 /*************************************************************************
1567 * Function: SysFileSearch *
1568 * *
1569 * Syntax: call SysFileSearch target, file, stem [, options] *
1570 * *
1571 * Params: target - String to search for. *
1572 * file - Filespec to search. *
1573 * stem - Stem variable name to place results in. *
1574 * options - Any combo of the following: *
1575 * 'C' - Case sensitive search (non-default). *
1576 * 'N' - Preceed each found string in result stem *
1577 * with it line number in file (non-default).*
1578 * *
1579 * Return: NO_UTIL_ERROR - Successful. *
1580 * ERROR_NOMEM - Out of memory. *
1581 *************************************************************************/
1582 
1583 size_t RexxEntry SysFileSearch(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1584 {
1585  const char *target; /* search string */
1586  const char *file; /* search file */
1587  const char *opts; /* option string */
1588  const char *ptr; /* Pointer to char str found */
1589  size_t num = 0; /* Line number */
1590  size_t len; /* Length of string */
1591  size_t len2; /* Length of string */
1592  bool linenums = false; /* Set true for linenums in */
1593  /* output */
1594  bool sensitive = false; /* Set true for case-sens */
1595  /* search */
1596  RXSTEMDATA ldp; /* stem data */
1597  GetFileData filedata; /* file read information */
1598  char * dir_buf = NULL; /* directory buffer */
1599  char * line = NULL; /* Line read from file */
1600 
1601  BUILDRXSTRING(retstr, NO_UTIL_ERROR);/* pass back result */
1602  /* validate arguments */
1603  if (numargs < 3 || numargs > 4 ||
1604  !RXVALIDSTRING(args[0]) ||
1605  !RXVALIDSTRING(args[1]) ||
1606  !RXVALIDSTRING(args[2]))
1607  return INVALID_ROUTINE; /* raise an error */
1608 
1609  target = args[0].strptr; /* get target pointer */
1610  file = args[1].strptr; /* get file name */
1611 
1612  if(*(file) == '~'){ /* check for using '~/' */
1613  dir_buf = resolve_tilde(file); /* get absolut path */
1614  file = dir_buf; /* full path to the file */
1615  }
1616  if (numargs == 4) { /* process options */
1617  opts = args[3].strptr; /* point to the options */
1618  if (strstr(opts, "N") || strstr(opts, "n"))
1619  linenums = true;
1620 
1621  if (strstr(opts, "C") || strstr(opts, "c"))
1622  sensitive = true;
1623  }
1624  /* Initialize data area */
1625  ldp.count = 0;
1626  strcpy(ldp.varname, args[2].strptr);
1627  ldp.stemlen = args[2].strlength;
1628  strupr(ldp.varname); /* uppercase the name */
1629  if (ldp.varname[ldp.stemlen-1] != '.')
1630  ldp.varname[ldp.stemlen++] = '.';
1631  if (OpenFile(file, &filedata)) { /* open the file */
1632  BUILDRXSTRING(retstr, ERROR_FILEOPEN);
1633  if(dir_buf) /* did we allocate ? */
1634  free(dir_buf); /* free it */
1635  return VALID_ROUTINE; /* finished */
1636  }
1637 
1638  line = (char *) malloc(4096 * sizeof(char));
1639  /* do the search...found lines*/
1640  /* are saved in stem vars */
1641  while (!GetLine(line, MAX_LINE_LEN - 1, &filedata)) {
1642  len = strlen(line);
1643  num++;
1644 
1645  ptr = mystrstr(line, target, len, args[0].strlength, sensitive);
1646  if (ptr) {
1647  if (linenums) {
1648  snprintf(ldp.ibuf, sizeof ldp.ibuf, "%d ", (int)num);
1649  len2 = strlen(ldp.ibuf);
1650  memcpy(ldp.ibuf+len2, line, len < IBUF_LEN-len2 ? len : IBUF_LEN-len2);
1651  ldp.vlen = IBUF_LEN < len+len2 ? IBUF_LEN : len + len2;
1652  }
1653  else {
1654  memcpy(ldp.ibuf, line, len);
1655  ldp.vlen = len;
1656  }
1657  ldp.count++;
1658  snprintf(ldp.varname+ldp.stemlen, sizeof ldp.varname, "%d", (int)ldp.count);
1659  if (ldp.ibuf[ldp.vlen-1] == '\n')
1660  ldp.vlen--;
1661  ldp.shvb.shvnext = NULL;
1662  ldp.shvb.shvname.strptr = ldp.varname;
1663  ldp.shvb.shvname.strlength = strlen(ldp.varname);
1664  ldp.shvb.shvnamelen = ldp.shvb.shvname.strlength;
1665  ldp.shvb.shvvalue.strptr = ldp.ibuf;
1666  ldp.shvb.shvvalue.strlength = ldp.vlen;
1667  ldp.shvb.shvvaluelen = ldp.vlen;
1668  ldp.shvb.shvcode = RXSHV_SET;
1669  ldp.shvb.shvret = 0;
1670  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN) {
1671  CloseFile(&filedata); /* close the file */
1672  if(dir_buf) /* did we allocate ? */
1673  free(dir_buf); /* free it */
1674  free(line);
1675  return INVALID_ROUTINE; /* error on non-zero */
1676  }
1677  }
1678  }
1679  free(line);
1680  CloseFile(&filedata); /* Close that file */
1681  /* set stem.0 to lines read */
1682  snprintf(ldp.ibuf, sizeof ldp.ibuf, "%d", (int)ldp.count);
1683  ldp.varname[ldp.stemlen] = '0';
1684  ldp.varname[ldp.stemlen+1] = 0;
1685  ldp.shvb.shvnext = NULL;
1686  ldp.shvb.shvname.strptr = ldp.varname;
1687  ldp.shvb.shvname.strlength = ldp.stemlen+1;
1688  ldp.shvb.shvnamelen = ldp.stemlen+1;
1689  ldp.shvb.shvvalue.strptr = ldp.ibuf;
1690  ldp.shvb.shvvalue.strlength = strlen(ldp.ibuf);
1692  ldp.shvb.shvcode = RXSHV_SET;
1693  ldp.shvb.shvret = 0;
1694  if (RexxVariablePool(&ldp.shvb) == RXSHV_BADN){
1695  if(dir_buf) /* did we allocate ? */
1696  free(dir_buf); /* free it */
1697  return INVALID_ROUTINE; /* error on non-zero */
1698  }
1699  if(dir_buf) /* did we allocate ? */
1700  free(dir_buf); /* free it */
1701  return VALID_ROUTINE; /* no error on call */
1702 }
1703 
1704 /*************************************************************************
1705 * Function: SysSearchPath *
1706 * *
1707 * Syntax: call SysSearchPath path, file [, options] *
1708 * *
1709 * Params: path - Environment variable name which specifies a path *
1710 * to be searched (ie 'PATH', 'DPATH', etc). *
1711 * file - The file to search for. *
1712 * options - 'C' - Current directory search first (default). *
1713 * 'N' - No Current directory search. Only searches *
1714 * the path as specified. *
1715 * *
1716 * Return: other - Full path and filespec of found file. *
1717 * '' - Specified file not found along path. *
1718 *************************************************************************/
1719 
1720 size_t RexxEntry SysSearchPath(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1721 {
1722  char buf[IBUF_LEN]={0}; /* returned file name */
1723  const char * opts; /* option string */
1724  int SearchFlag; /* Search control variable */
1725  /* default=search current 1st */
1726  int ulRc; /* return value of SearchPath */
1727 
1728 
1729  SearchFlag = CURRENT_DIR_FIRST; /* set default search flag */
1730  /* validate arguments */
1731  if (numargs < 2 || numargs > 3 ||
1732  !RXVALIDSTRING(args[0]) ||
1733  !RXVALIDSTRING(args[1]))
1734  return INVALID_ROUTINE;
1735 
1736  if (numargs == 3) { /* process options */
1737 
1738  opts = args[2].strptr; /* point to the options */
1739  if ((*opts == 'N') || (*opts == 'n'))
1740  SearchFlag = ENVIRONMENT_ONLY ;
1741  /* do not search current dir */
1742 
1743  else if ((*opts == 'C') || (*opts == 'c'))
1744  SearchFlag = CURRENT_DIR_FIRST;
1745  /* search current 1st(default)*/
1746  else
1747  return INVALID_ROUTINE; /* Invalid option */
1748  }
1749 
1750  ulRc = SearchPath(SearchFlag, args[0].strptr, args[1].strptr,
1751  (char *)buf, sizeof(buf));
1752 
1753  /* if environment variable could not be found, try again with */
1754  /* uppercase name. */
1755  if (ulRc)
1756  {
1757  char *temp = strdup(args[0].strptr);
1758  strupr(temp);
1759  ulRc = SearchPath(SearchFlag, temp, args[1].strptr,
1760  (char *)buf, sizeof(buf));
1761  free(temp);
1762  }
1763 
1764  BUILDRXSTRING(retstr, (const char *)buf); /* pass back result */
1765  return VALID_ROUTINE;
1766 }
1767 #ifdef LINUX
1768 /*************************************************************************
1769 * Function: SysLinVer *
1770 * *
1771 * Syntax: call SysLinVer *
1772 * *
1773 * Return: Linux Version *
1774 *************************************************************************/
1775 
1776 size_t RexxEntry SysLinVer(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1777 {
1778 
1779  struct utsname info; /* info structur */
1780 
1781  if (numargs != 0) /* validate arg count */
1782  return INVALID_ROUTINE;
1783 
1784  if(uname(&info) < 0) /* if no info stored */
1785  return INVALID_ROUTINE; /* get out */
1786 
1787  snprintf(retstr->strptr, DEFRXSTRING, "%s %s",info.sysname,info.release);
1788  retstr->strlength = strlen(retstr->strptr);
1789  return VALID_ROUTINE;
1790 }
1791 #endif
1792 
1793 /*************************************************************************
1794 * Function: SysVersion *
1795 * *
1796 * Syntax: call SysVersion *
1797 * *
1798 * Return: Operating System name (LINUX/AIX/WINDOWS) and Version *
1799 *************************************************************************/
1800 
1801 size_t RexxEntry SysVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
1802 {
1803 
1804  struct utsname info; /* info structur */
1805 
1806  if (numargs != 0) /* validate arg count */
1807  return INVALID_ROUTINE;
1808 
1809  if(uname(&info) < 0) /* if no info stored */
1810  return INVALID_ROUTINE; /* get out */
1811 
1812  snprintf(retstr->strptr, DEFRXSTRING, "%s %s.%s",info.sysname, info.version, info.release);
1813 
1814  retstr->strlength = strlen(retstr->strptr);
1815  return VALID_ROUTINE;
1816 }
1817 
1818 
1819 /*************************************************************************
1820 * Semaphore data struct *
1821 *************************************************************************/
1822 
1823 typedef struct RxSemData {
1824  bool named; /* Named semaphore? */
1825  sem_t * handle; /* Semaphore pointer */
1827 
1828 
1829 /*************************************************************************
1830 * Function: SysCreateEventSem *
1831 * *
1832 * Syntax: handle = SysCreateEventSem(<name>) *
1833 * *
1834 * Params: name - optional name for a event semaphore *
1835 * *
1836 * Return: handle - token used as a event sem handle for *
1837 * SysPostEventSem, SysClearEventSem, *
1838 * SysCloseEventSem, and SysOpenEventSem *
1839 * '' - Empty string in case of any error *
1840 *************************************************************************/
1841 
1842 RexxRoutine2(RexxObjectPtr, SysCreateEventSem, OPTIONAL_CSTRING, name, OPTIONAL_CSTRING, reset)
1843 {
1844  RXSEMDATA *semdata;
1845  int rc;
1846 
1847  // Note that the reset arg has no meaning on Unix/Linux and is unused.
1848  semdata = (RXSEMDATA *)malloc(sizeof(RXSEMDATA));
1849  if (semdata == NULL) {
1850  return context->String("");
1851  }
1852  if (name == NULL) {
1853  /* this is an unnamed semaphore */
1854  semdata->handle = (sem_t *)malloc(sizeof(sem_t));
1855  rc = sem_init(semdata->handle, 0, 0);
1856  if (rc == -1) {
1857  free(semdata);
1858  return context->String("");
1859  }
1860  semdata->named = false;
1861  }
1862  else {
1863  /* this is a named semaphore */
1864  semdata->handle = sem_open(name, (O_CREAT | O_EXCL), (S_IRWXU | S_IRWXG), 0);
1865  if (semdata->handle == SEM_FAILED ) {
1866  free(semdata);
1867  return context->String("");
1868  }
1869  semdata->named = true;
1870  }
1871  return context->Uintptr((uintptr_t)semdata);
1872 }
1873 
1874 
1875 /*************************************************************************
1876 * Function: SysOpenEventSem *
1877 * *
1878 * Syntax: result = SysOpenEventSem(handle) *
1879 * *
1880 * Params: handle - token returned from SysCreateEventSem *
1881 * *
1882 * Return: result - return code from DosOpenEventSem *
1883 *************************************************************************/
1884 
1885 RexxRoutine1(uintptr_t, SysOpenEventSem, CSTRING, name)
1886 {
1887  RXSEMDATA *semdata;
1888 
1889  semdata = (RXSEMDATA *)malloc(sizeof(RXSEMDATA));
1890  if (semdata == NULL) {
1891  return 0;
1892  }
1893  semdata->handle = sem_open(name, 0);
1894  if (semdata->handle == SEM_FAILED ) {
1895  return 0;
1896  }
1897  semdata->named = true;
1898  return (uintptr_t)semdata;
1899 }
1900 
1901 
1902 /*************************************************************************
1903 * Function: SysResetEventSem *
1904 * *
1905 * Syntax: result = SysResetEventSem(handle) *
1906 * *
1907 * Params: handle - token returned from SysCreateEventSem *
1908 * *
1909 * Return: result - return code from DosResetEventSem *
1910 *************************************************************************/
1911 
1912 RexxRoutine1(int, SysResetEventSem, uintptr_t, vhandle)
1913 {
1914  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
1915 
1916  sem_init(semdata->handle, 1, 0);
1917  return 0;
1918 }
1919 
1920 
1921 /*************************************************************************
1922 * Function: SysPostEventSem *
1923 * *
1924 * Syntax: result = SysPostEventSem(handle) *
1925 * *
1926 * Params: handle - token returned from SysCreateEventSem *
1927 * *
1928 * Return: result - return code from DosPostEventSem *
1929 *************************************************************************/
1930 
1931 RexxRoutine1(int, SysPostEventSem, uintptr_t, vhandle)
1932 {
1933  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
1934  int rc;
1935 
1936  rc = sem_post(semdata->handle);
1937  if (rc) {
1938  return 6;
1939  }
1940  return 0;
1941 }
1942 
1943 
1944 /*************************************************************************
1945 * Function: SysCloseEventSem *
1946 * *
1947 * Syntax: result = SysCloseEventSem(handle) *
1948 * *
1949 * Params: handle - token returned from SysCreateEventSem *
1950 * *
1951 * Return: result - return code from DosCloseEventSem *
1952 *************************************************************************/
1953 
1954 RexxRoutine1(int, SysCloseEventSem, uintptr_t, vhandle)
1955 {
1956  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
1957 
1958  if (semdata->named == false) {
1959  /* this is an unnamed semaphore so we must free the target */
1960  if (sem_destroy(semdata->handle)) {
1961  if (errno == EINVAL) {
1962  return 6;
1963  }
1964  else if (errno) {
1965  return 102;
1966  }
1967  }
1968  }
1969  else {
1970  /* this is a named semaphore */
1971  if (sem_close(semdata->handle)) {
1972  if (errno == EINVAL) {
1973  return 6;
1974  }
1975  else if (errno) {
1976  return 102;
1977  }
1978  }
1979  }
1980  free(semdata);
1981  return 0;
1982 }
1983 
1984 
1985 #define SEM_WAIT_PERIOD 100 /* POSIX says this should be 10ms */
1986 
1987 
1988 /*************************************************************************
1989 * Function: SysWaitEventSem *
1990 * *
1991 * Syntax: result = SysWaitEventSem(handle, <timeout>) *
1992 * *
1993 * Params: handle - token returned from SysWaitEventSem *
1994 * *
1995 * Return: result - return code from DosWaitEventSem *
1996 *************************************************************************/
1997 
1998 RexxRoutine2(int, SysWaitEventSem, uintptr_t, vhandle, OPTIONAL_int, timeout)
1999 {
2000  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
2001  int rc = 0;
2002 
2003  if (timeout != 0) {
2004  /* this looping construct will cause us to wait longer than the */
2005  /* specified timeout due to the latency involved in the loop, */
2006  /* but that cannot be helped */
2007  while (timeout > 0) {
2008  rc = sem_trywait(semdata->handle);
2009  if (rc == 0) {
2010  break;
2011  }
2012  if (usleep(SEM_WAIT_PERIOD * 1000) == 0) {
2013  timeout -= SEM_WAIT_PERIOD;
2014  }
2015  }
2016  }
2017  else {
2018  rc = sem_wait(semdata->handle);
2019  }
2020  if (rc) {
2021  if (errno == EAGAIN) {
2022  return 121;
2023  }
2024  else if (errno == EINVAL) {
2025  return 6;
2026  }
2027  }
2028  return 0;
2029 }
2030 
2031 
2032 /*************************************************************************
2033 * Function: SysCreateMutexSem *
2034 * *
2035 * Syntax: handle = SysCreateMutexSem(<name>) *
2036 * *
2037 * Params: name - optional name for a event semaphore *
2038 * *
2039 * Return: handle - token used as a event sem handle for *
2040 * SysPostEventSem, SysClearEventSem, *
2041 * SysCloseEventSem, and SysOpenEventSem *
2042 * '' - Empty string in case of any error *
2043 *************************************************************************/
2044 
2045 RexxRoutine1(RexxObjectPtr, SysCreateMutexSem, OPTIONAL_CSTRING, name)
2046 {
2047  RXSEMDATA *semdata;
2048  int rc;
2049 
2050  semdata = (RXSEMDATA *)malloc(sizeof(RXSEMDATA));
2051  if (semdata == NULL) {
2052  return context->String("");
2053  }
2054  if (strlen(name) == 0) {
2055  /* this is an unnamed semaphore */
2056  semdata->handle = (sem_t *)malloc(sizeof(sem_t));
2057  rc = sem_init(semdata->handle, 0, 0);
2058  if (rc == -1) {
2059  free(semdata);
2060  return context->String("");
2061  }
2062  semdata->named = false;
2063  }
2064  else {
2065  /* this is a named semaphore */
2066  semdata->handle = sem_open(name, (O_CREAT | O_EXCL), (S_IRWXU | S_IRWXG), 0);
2067  if (semdata->handle == SEM_FAILED ) {
2068  free(semdata);
2069  return context->String("");
2070  }
2071  semdata->named = true;
2072  }
2073  rc = sem_post(semdata->handle);
2074  return context->Uintptr((uintptr_t)semdata);
2075 }
2076 
2077 
2078 /*************************************************************************
2079 * Function: SysOpenMutexSem *
2080 * *
2081 * Syntax: result = SysOpenMutexSem(handle) *
2082 * *
2083 * Params: handle - token returned from SysCreateMutexSem *
2084 * *
2085 * Return: result - return code from DosOpenEventSem *
2086 *************************************************************************/
2087 
2088 RexxRoutine1(uintptr_t, SysOpenMutexSem, CSTRING, name)
2089 {
2090  RXSEMDATA *semdata;
2091 
2092  semdata = (RXSEMDATA *)malloc(sizeof(RXSEMDATA));
2093  if (semdata == NULL) {
2094  return 0;
2095  }
2096  semdata->handle = sem_open(name, 0);
2097  if (semdata->handle == SEM_FAILED ) {
2098  return 0;
2099  }
2100  semdata->named = true;
2101  return (uintptr_t)semdata;
2102 }
2103 
2104 
2105 /*************************************************************************
2106 * Function: SysRequestMutexSem *
2107 * *
2108 * Syntax: result = SysRequestMutexSem(handle, <timeout>) *
2109 * *
2110 * Params: handle - token returned from SysRequestMutexSem *
2111 * *
2112 * Return: result - return code from DosWaitEventSem *
2113 *************************************************************************/
2114 
2115 RexxRoutine2(int, SysRequestMutexSem, uintptr_t, vhandle, OPTIONAL_int, timeout)
2116 {
2117  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
2118  int rc = 0;
2119 
2120  if (timeout != 0) {
2121  /* this looping construct will cause us to wait longer than the */
2122  /* specified timeout due to the latency involved in the loop, */
2123  /* but that cannot be helped */
2124  while (timeout > 0) {
2125  rc = sem_trywait(semdata->handle);
2126  if (rc == 0) {
2127  break;
2128  }
2129  if (usleep(SEM_WAIT_PERIOD * 1000) == 0) {
2130  timeout -= SEM_WAIT_PERIOD;
2131  }
2132  }
2133  }
2134  else {
2135  rc = sem_wait(semdata->handle);
2136  }
2137  if (rc) {
2138  if (errno == EAGAIN) {
2139  return 121;
2140  }
2141  else if (errno == EINVAL) {
2142  return 6;
2143  }
2144  }
2145  return 0;
2146 }
2147 
2148 
2149 /*************************************************************************
2150 * Function: SysReleaseMutexSem *
2151 * *
2152 * Syntax: result = SysReleaseMutexSem(handle) *
2153 * *
2154 * Params: handle - token returned from SysCreateMutexSem *
2155 * *
2156 * Return: result - return code from DosCloseEventSem *
2157 *************************************************************************/
2158 
2159 RexxRoutine1(int, SysReleaseMutexSem, uintptr_t, vhandle)
2160 {
2161  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
2162  int rc;
2163  int val;
2164 
2165  rc = sem_getvalue(semdata->handle, &val);
2166  if (rc) {
2167  if (errno == EINVAL) {
2168  return 6;
2169  }
2170  else {
2171  return 288;
2172  }
2173  }
2174  if (val == 0) {
2175  rc = sem_post(semdata->handle);
2176  if (rc) {
2177  return 6;
2178  }
2179  }
2180  return 0;
2181 
2182 }
2183 
2184 
2185 /*************************************************************************
2186 * Function: SysCloseMutexSem *
2187 * *
2188 * Syntax: result = SysCloseMutexSem(handle) *
2189 * *
2190 * Params: handle - token returned from SysCreateMutexSem *
2191 * *
2192 * Return: result - return code from DosCloseEventSem *
2193 *************************************************************************/
2194 
2195 RexxRoutine1(int, SysCloseMutexSem, uintptr_t, vhandle)
2196 {
2197  RXSEMDATA *semdata = (RXSEMDATA *)vhandle;
2198 
2199  if (semdata->named == false) {
2200  /* this is an unnamed semaphore so we must free the target */
2201  if (sem_destroy(semdata->handle)) {
2202  if (errno == EINVAL) {
2203  return 6;
2204  }
2205  else if (errno) {
2206  return 102;
2207  }
2208  }
2209  }
2210  else {
2211  /* this is a named semaphore */
2212  if (sem_close(semdata->handle)) {
2213  if (errno == EINVAL) {
2214  return 6;
2215  }
2216  else if (errno) {
2217  return 102;
2218  }
2219  }
2220  }
2221  free(semdata);
2222  return 0;
2223 }
2224 
2225 
2226 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
2227  * *
2228  * SysFileTree() implmentation and helper functions. *
2229  * *
2230 \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
2231 
2232 /* Enum for the different buffers in the RXTREEDATA struct */
2233 typedef enum
2234 {
2239 
2240 /**
2241  * Returns a value that is greater than 'need' by doubling 'have' until that
2242  * value is reached.
2243  */
2244 inline size_t neededSize(size_t need, size_t have)
2245 {
2246  while ( have < need )
2247  {
2248  have *= 2;
2249  }
2250  return have;
2251 }
2252 
2254 {
2255  c->RaiseException1(Rexx_Error_System_service_user_defined, c->String("failed to allocate memory"));
2256 }
2257 
2258 /**
2259  * <routineName> argument <argPos> must not be a null string
2260  *
2261  * SysFileTree argument 2 must not be a null string
2262  *
2263  * @param c Threade context we are operating in.
2264  * @param fName Routine name.
2265  * @param pos Argument position.
2266  */
2267 void inline nullStringException(RexxThreadContext *c, CSTRING fName, size_t pos)
2268 {
2269  c->RaiseException3(Rexx_Error_Incorrect_call_null, c->String(fName), c->String("positional"), c->StringSize(pos));
2270 }
2271 
2272 /**
2273  * <routineName>() argument <argPos> must be less than <len> characters in
2274  * length; length is <realLen>
2275  *
2276  * SysFileTree() argument 2 must be less than 4095 characters in length; length
2277  * is 5000
2278  *
2279  * Raises 88.900
2280  *
2281  * @param c Thread context we are executing in.
2282  * @param pos Argumet position
2283  * @param len Fixed length
2284  * @param realLen Actual length
2285  */
2286 static void stringTooLongException(RexxThreadContext *c, CSTRING funcName, size_t pos, size_t len, size_t realLen)
2287 {
2288  char buf[256];
2289  snprintf(buf, sizeof(buf), "%s() argument %lu must be less than %lu characters in length; length is %lu",
2290  funcName, pos, len, realLen);
2291 
2292  c->RaiseException1(Rexx_Error_Incorrect_call_user_defined, c->String(buf));
2293 }
2294 
2295 /**
2296  * This is a SysFileTree specific function.
2297  *
2298  * @param c
2299  * @param pos
2300  * @param actual
2301  */
2302 static void badSFTOptsException(RexxThreadContext *c, size_t pos, CSTRING actual)
2303 {
2304  char buf[256] = {0};
2305  snprintf(buf, sizeof(buf),
2306  "SysFileTree argument %lu must be a combination of F, D, B, S, T, L, I, or O; found \"%s\"",
2307  pos, actual);
2308 
2309  c->RaiseException1(Rexx_Error_Incorrect_call_user_defined, c->String(buf));
2310 }
2311 
2312 /**
2313  * Dynamically allocates memory for a buffer in the RXTREEDATA structure so that
2314  * it is bigger than the size specified.
2315  *
2316  * @param need The allocated buffer size must be greater than this.
2317  * @param treeData Pointer to the RXTREEDATA struct.
2318  * @param whichBuffer Identifies the buffer to increase.
2319  *
2320  * @return True on success, false if memory allocation fails.
2321  *
2322  * @remarks For SysFileTree() we start out with very large static buffers to
2323  * use for full path names and the maniupulation of path names. But,
2324  * on unixes, directories can be nested to any arbitary depth.
2325  *
2326  * It is unlikely, but possible, that a static buffer will be too
2327  * small. If this happens, we double the size of the buffer until it
2328  * is large enough by allocating a new buffer.
2329  */
2330 static bool increaseBuffer(RexxCallContext *c, size_t need, RXTREEDATA *treeData, TreeDataBuffers whichBuffer)
2331 {
2332  if ( whichBuffer == FILESPEC_BUFFER )
2333  {
2334  if ( treeData->nFNameSpec != FNAMESPEC_BUF_LEN )
2335  {
2336  free(treeData->dFNameSpec);
2337  }
2338 
2339  treeData->nFNameSpec = neededSize(need, treeData->nFNameSpec);
2340  treeData->dFNameSpec = (char *)malloc(sizeof(char) * treeData->nFNameSpec);
2341 
2342  if ( treeData->dFNameSpec == NULL )
2343  {
2345  return false;
2346  }
2347  }
2348  else if ( whichBuffer == FOUNDFILE_BUFFER )
2349  {
2350  if ( treeData->nFoundFile != FOUNDFILE_BUF_LEN )
2351  {
2352  free(treeData->dFoundFile);
2353  }
2354 
2355  treeData->nFoundFile = neededSize(need, treeData->nFoundFile);
2356  treeData->dFoundFile = (char *)malloc(sizeof(char) * treeData->nFoundFile);
2357 
2358  if ( treeData->dFoundFile == NULL )
2359  {
2361  return false;
2362  }
2363  }
2364  else
2365  {
2366  if ( treeData->nFoundFileLine != FOUNDFILELINE_BUFFER )
2367  {
2368  free(treeData->dFoundFileLine);
2369  }
2370 
2371  treeData->nFoundFile = neededSize(need, treeData->nFoundFile);
2372  treeData->dFoundFile = (char *)malloc(sizeof(char) * treeData->nFoundFile);
2373 
2374  if ( treeData->dFoundFile == NULL )
2375  {
2377  return false;
2378  }
2379  }
2380 
2381  return true;
2382 }
2383 
2384 /**
2385  * Allocates a buffer twice as big as the buffer passed in.
2386  *
2387  * @param c Call context we are operating in.
2388  * @param dPath Pointer to the buffer to reallocate
2389  * @param nPath Size of dPath buffer.
2390  * @param nStaticBuffer Size of original static buffer.
2391  *
2392  * @return True on success, false on memory allocation failure.
2393  *
2394  * @remarks NOTE: that the pointer to the buffer to reallocate, may, or may
2395  * not, be a pointer to a static buffer. We must NOT try to free a
2396  * static buffer and we MUST free a non-static buffer.
2397  */
2398 static bool getBiggerBuffer(RexxCallContext *c, char **dPath, size_t *nPath, size_t nStaticBuffer)
2399 {
2400  if ( *nPath != nStaticBuffer )
2401  {
2402  free(*dPath);
2403  }
2404 
2405  *nPath *= 2;
2406  *dPath = (char *)malloc(*nPath * sizeof(char));
2407 
2408  if ( *dPath == NULL )
2409  {
2411  return false;
2412  }
2413 
2414  return true;
2415 }
2416 
2417 /**
2418  * This is a SysFileTree specific function.
2419  *
2420  * Test if struct stat.st_mode field is a file for the purpose of SysFileTree.
2421  *
2422  * @param m
2423  *
2424  * @return True if a file, otherwise
2425  */
2426 inline bool isAcceptableFile(mode_t m)
2427 {
2428  return (S_ISREG(m) || // if it is a file
2429  S_ISCHR(m) || // or a device special
2430  S_ISBLK(m) || // file
2431  S_ISSOCK(m) || // or a socket
2432  S_ISLNK(m) || // or a symbolic link
2433  S_ISFIFO(m)); // or a FIFO
2434 }
2435 
2436 inline char typeOfEntry(mode_t m)
2437 {
2438  if( S_ISLNK(m) )
2439  {
2440  return 'l'; // symbolic link
2441  }
2442  else if ( S_ISBLK(m) )
2443  {
2444  return 'b'; // block device
2445  }
2446  else if ( S_ISCHR(m) )
2447  {
2448  return 'c'; // character device
2449  }
2450  else if ( S_ISDIR(m) )
2451  {
2452  return 'd'; // directory
2453  }
2454  else if ( S_ISFIFO(m) )
2455  {
2456  return 'p'; // FIFO
2457  }
2458  else if ( S_ISSOCK(m) )
2459  {
2460  return 's'; // socket
2461  }
2462  else
2463  {
2464  return '-'; // regular file
2465  }
2466 }
2467 
2468 /**
2469  * This is a SysFileTree specific function.
2470  *
2471  * Find the next file in the directory pointed to by dir_handle that matches
2472  * fileSpec. The needed information is returnd in finfo and d_name.
2473  *
2474  * @param fileSpec Search specification.
2475  * @param path Current path we are searching.
2476  * @param dir_handle Directory handle.
2477  * @param finfo Returned file info buffer.
2478  * @param d_name Returned name of the file found
2479  * @param caseless Do case insensitive matching, or not.
2480  *
2481  * @return True if a file is found, outherwise false.
2482  *
2483  * @remarks The '?' glob character is not currently supported. Old notes said
2484  * add the implementation here.
2485  */
2486 bool linFindNextFile(RexxCallContext *c, const char *fileSpec, const char *path, DIR *dir_handle,
2487  struct stat *finfo, char **d_name, bool caseless)
2488 {
2489  char fullPath[IBUF_LEN];
2490  int len;
2491  char *dFullPath = fullPath;
2492  size_t nFullPath = IBUF_LEN;
2493 
2494  struct dirent *dir_entry = readdir(dir_handle);
2495  if( dir_entry == NULL )
2496  {
2497  return false;
2498  }
2499 
2500  do
2501  {
2502  len = snprintf(dFullPath, nFullPath, "%s%s", path, dir_entry->d_name);
2503  if ( len >= (int)nFullPath )
2504  {
2505  if ( ! getBiggerBuffer(c, &dFullPath, &nFullPath, IBUF_LEN) )
2506  {
2508  if ( nFullPath != IBUF_LEN )
2509  {
2510  free(dFullPath);
2511  }
2512  return false;
2513  }
2514  }
2515 
2516  lstat(dFullPath, finfo);
2517 
2518  if ( isAcceptableFile(finfo->st_mode) )
2519  {
2520  // If this is a caseless search, we compare an upper cased copy of
2521  // the entry name. fileSpec has already been upper cased at a
2522  // higher level.
2523  if ( caseless )
2524  {
2525  char dup_d_name[IBUF_LEN];
2526  char *pDest = dup_d_name;
2527  char *pSrc = dir_entry->d_name;
2528 
2529  for ( ; *pSrc; pDest++, pSrc++ )
2530  {
2531  *pDest = toupper(*pSrc);
2532  }
2533  *pDest = '\x0';
2534 
2535  if ( fnmatch(fileSpec, dup_d_name, FNM_NOESCAPE | FNM_PATHNAME | FNM_PERIOD ) == 0 )
2536  {
2537  *d_name = dir_entry->d_name;
2538 
2539  if ( nFullPath != IBUF_LEN )
2540  {
2541  free(dFullPath);
2542  }
2543  return true;
2544  }
2545  }
2546  else
2547  {
2548  if ( fnmatch(fileSpec, dir_entry->d_name, FNM_NOESCAPE | FNM_PATHNAME | FNM_PERIOD) == 0 )
2549  {
2550  *d_name = dir_entry->d_name;
2551 
2552  if ( nFullPath != IBUF_LEN )
2553  {
2554  free(dFullPath);
2555  }
2556  return true;
2557  }
2558  }
2559  }
2560  dir_entry = readdir(dir_handle);
2561  }
2562  while( dir_entry != NULL );
2563 
2564  if ( nFullPath != IBUF_LEN )
2565  {
2566  free(dFullPath);
2567  }
2568 
2569  return false;
2570 }
2571 
2572 /**
2573  * This is a SysFileTree specific function.
2574  *
2575  * Find the next directory in the directory pointed to by dir_handle that
2576  * matches fileSpec. The needed information is returnd in finfo and d_name.
2577  *
2578  * @param fileSpec Search specification.
2579  * @param path Current path we are searching.
2580  * @param dir_handle Directory handle.
2581  * @param finfo Returned file info buffer.
2582  * @param d_name Returned name of the directory found.
2583  * @param caseless Do case insensitive matching, or not.
2584  *
2585  * @return True if a directory is found, outherwise false.
2586  *
2587  * @remarks The '?' glob character is not currently supported. Old notes said
2588  * add the implementation here.
2589  */
2590 bool linFindNextDir(RexxCallContext *c, const char *fileSpec, const char *path, DIR *dir_handle,
2591  struct stat *finfo, char **d_name, bool caseless)
2592 {
2593  char fullPath[FOUNDFILE_BUF_LEN];
2594  char *dFullPath = fullPath;
2595  size_t nFullPath = FOUNDFILE_BUF_LEN;
2596 
2597  struct dirent *dir_entry = readdir(dir_handle);
2598  if( dir_entry == NULL )
2599  {
2600  return false;
2601  }
2602 
2603  do
2604  {
2605  int len = snprintf(dFullPath, nFullPath, "%s%s", path, dir_entry->d_name);
2606  if ( len >= (int)nFullPath )
2607  {
2608  if ( nFullPath != FOUNDFILE_BUF_LEN )
2609  {
2610  free(dFullPath);
2611  }
2612  nFullPath = neededSize(len, nFullPath);
2613 
2614  dFullPath = (char *)malloc(nFullPath * sizeof(char));
2615  if ( dFullPath == NULL )
2616  {
2618  return false;
2619  }
2620 
2621  snprintf(dFullPath, sizeof fullPath, "%s%s", path, dir_entry->d_name);
2622  }
2623 
2624  lstat(fullPath, finfo);
2625 
2626  if ( S_ISDIR(finfo->st_mode) )
2627  {
2628  // If this is a caseless search, we compare an upper cased copy of
2629  // the entry name. fileSpec has already been upper cased at a
2630  // higher level.
2631  if ( caseless )
2632  {
2633  char dup_d_name[IBUF_LEN];
2634  char *pDest = dup_d_name;
2635  char *pSrc = dir_entry->d_name;
2636 
2637  for ( ; *pSrc; pDest++, pSrc++ )
2638  {
2639  *pDest = toupper(*pSrc);
2640  }
2641  *pDest = '\x0';
2642 
2643  if ( fnmatch(fileSpec, dup_d_name, FNM_NOESCAPE | FNM_PATHNAME | FNM_PERIOD ) == 0 )
2644  {
2645  *d_name = dir_entry->d_name;
2646  return true;
2647  }
2648  }
2649  else
2650  {
2651  if ( fnmatch(fileSpec, dir_entry->d_name, FNM_NOESCAPE | FNM_PATHNAME | FNM_PERIOD) == 0 )
2652  {
2653  *d_name = dir_entry->d_name;
2654  return true;
2655  }
2656  }
2657  }
2658  dir_entry = readdir(dir_handle);
2659  }
2660  while( dir_entry != NULL );
2661 
2662  return false;
2663 }
2664 
2665 /**
2666  * This is a SysFileTree specific function.
2667  *
2668  * Determines the options by converting the character based argument to the
2669  * correct set of flags.
2670  *
2671  * @param c
2672  * @param opts
2673  * @param pOpts
2674  *
2675  * @return bool
2676  */
2677 static bool goodOpts(RexxCallContext *c, CSTRING opts, uint32_t *pOpts)
2678 {
2679  uint32_t options = *pOpts;
2680 
2681  while ( *opts )
2682  {
2683  switch( toupper(*opts) )
2684  {
2685  case 'S': // recurse into subdirectories
2686  options |= RECURSE;
2687  break;
2688 
2689  case 'O': // only return names
2690  options |= NAME_ONLY;
2691  break;
2692 
2693  case 'T': // use short time format, ignored if L is used
2694  options |= EDITABLE_TIME;
2695  break;
2696 
2697  case 'L': // use long time format
2698  options |= LONG_TIME;
2699  break;
2700 
2701  case 'F': // include only files
2702  options &= ~DO_DIRS;
2703  options |= DO_FILES;
2704  break;
2705 
2706  case 'D': // include only directories
2707  options |= DO_DIRS;
2708  options &= ~DO_FILES;
2709  break;
2710 
2711  case 'B': // include both files and directories
2712  options |= DO_DIRS;
2713  options |= DO_FILES;
2714  break;
2715 
2716  case 'I': // case insensitive?
2717  options |= CASELESS;
2718  break;
2719 
2720  default: // error, unknown option
2721  return false;
2722  }
2723  opts++;
2724  }
2725 
2726  *pOpts = options;
2727  return true;
2728 }
2729 
2730 /**
2731  * This is a SysFileTree specific helper function.
2732  *
2733  * Checks the validity of the options argument to SysFileTree, sets the default
2734  * options, and converts the character based argument to the proper set of
2735  * flags.
2736  *
2737  * @param context
2738  * @param opts
2739  * @param options
2740  * @param argPos
2741  *
2742  * @return bool
2743  */
2744 static bool getOptionsFromArg(RexxCallContext *context, CSTRING opts, uint32_t *options, size_t argPos)
2745 {
2746  *options = DO_FILES | DO_DIRS;
2747 
2748  if ( argumentExists(argPos) )
2749  {
2750  if ( strlen(opts) == 0 )
2751  {
2752  nullStringException(context->threadContext, "SysFileTree", argPos);
2753  return false;
2754  }
2755 
2756  if ( ! goodOpts(context, opts, options) )
2757  {
2758  badSFTOptsException(context->threadContext, argPos, opts);
2759  return false;
2760  }
2761  }
2762 
2763  return true;
2764 }
2765 
2766 
2767 /**
2768  * Checks the file specification as input by the user for validity and expands /
2769  * redoes the value if needed. For example if the the specification starts with
2770  * the ~ character, the file spec is expanded to include the full path to the
2771  * home directory.
2772  *
2773  * @param context Call context we are operating in.
2774  *
2775  * @param fSpec The file specification as passed by the user.
2776  * Can not be the empty string and must be 494 chars or less.
2777  *
2778  * @param fileSpec Buffer to contain the expanded file specification. At this
2779  * time the buffer is 4096 in length, which seems more than
2780  * sufficient. However, we check the length when copying into
2781  * it anyway.
2782  *
2783  * @param bufLen Length of fileSpec buffer.
2784  *
2785  * @param argPos Argument position, use for raising conditions.
2786  *
2787  * @return True if no error, otherwise false. False is only returned if an
2788  * exception has been raised.
2789  *
2790  * @remarks The fileSpec buffer is IBUF_LEN, or 4096 bytes.
2791  * Theoretically, if fSpec starts with the tilde, '~', the string
2792  * could be too long after expanding the home directoy.
2793  */
2794 static bool getFileSpecFromArg(RexxCallContext *context, CSTRING fSpec, char *fileSpec, size_t bufLen, size_t argPos)
2795 {
2796 
2797  size_t len = strlen(fSpec);
2798  if ( len == 0 )
2799  {
2800  nullStringException(context->threadContext, "SysFileTree", argPos);
2801  return false;
2802  }
2803  if ( len >= bufLen - 1) // take into account that a trailing "*" may be appended
2804  {
2805  stringTooLongException(context->threadContext, "SysFileTree", argPos, bufLen - 1, len);
2806  return false;
2807  }
2808  strcpy(fileSpec, fSpec);
2809 
2810  // If filespec is '*' then use './ *'
2811  if ( len == 1 && fileSpec[0] == '*' )
2812  {
2813  strcpy(fileSpec, "./*");
2814  }
2815 
2816  // If fileSpec ends in '/' then append '*'
2817  if ( fileSpec[len - 1] == '/' )
2818  {
2819  strcat(fileSpec, "*");
2820  }
2821 
2822  // If filespec begins with '~' generate the absolute path.
2823  if ( fileSpec[0] == '~' )
2824  {
2825  char *temp = resolve_tilde(fileSpec);
2826  if ( ! temp )
2827  {
2829  return false;
2830  }
2831 
2832  if ( strlen(temp) >= bufLen )
2833  {
2834  stringTooLongException(context->threadContext, "SysFileTree", argPos, bufLen, strlen(temp));
2835  free(temp);
2836  return false;
2837  }
2838 
2839  strcpy(fileSpec, temp);
2840  free(temp);
2841  }
2842 
2843  return true;
2844 }
2845 
2846 /**
2847  * Used to retrieve the file name portion of the search file specification the
2848  * user sent to SysFileTree.
2849  *
2850  * @param c
2851  * @param fileSpec
2852  * @param treeData
2853  * @param lastSlashPos
2854  *
2855  * @return True on success, false on a memory allocation error.
2856  */
2857 static bool getFileNameSegment(RexxCallContext *c, char *fileSpec, RXTREEDATA *treeData, int *lastSlashPos)
2858 {
2859  size_t l; // Temporay var for length calculations.
2860  int slashPos = 0; // Position of last slash in fileSpec.
2861  int len = strlen(fileSpec);
2862 
2863  // Get maximum position of last '/' and then step back through fileSpec
2864  // until we are at the beginning of fileSpec, or at the last '/' character.
2865  slashPos = len - 1;
2866  do
2867  {
2868  slashPos--;
2869  }
2870  while( fileSpec[slashPos] != '/' && slashPos >= 0 );
2871 
2872  if ( fileSpec[slashPos] == '/' )
2873  {
2874  // We found a slash, if there are characters after the slash, they are
2875  // the file name portion. Otherwise, juse use wildcards.
2876  if ( fileSpec[slashPos + 1] != '\0' )
2877  {
2878  l = strlen(&fileSpec[slashPos + 1]) + 1;
2879  if ( l > treeData->nFNameSpec )
2880  {
2881  if ( ! increaseBuffer(c, l, treeData, FILESPEC_BUFFER) )
2882  {
2883  return false;
2884  }
2885  }
2886  strcpy(treeData->dFNameSpec, &fileSpec[slashPos + 1]);
2887  }
2888  else
2889  {
2890  strcpy(treeData->dFNameSpec, "*");
2891  }
2892  }
2893  else
2894  {
2895  // no '/', fileSpec is just a file name.
2896 
2897  l = strlen(&fileSpec[slashPos + 1]) + 1;
2898  if ( l > treeData->nFNameSpec )
2899  {
2900  if ( ! increaseBuffer(c, l, treeData, FILESPEC_BUFFER) )
2901  {
2902  return false;
2903  }
2904  }
2905  strcpy(treeData->dFNameSpec, &fileSpec[slashPos + 1]);
2906  }
2907 
2908  *lastSlashPos = slashPos;
2909  return true;
2910 }
2911 
2912 /**
2913  *
2914  *
2915  * @param c
2916  * @param fileSpec
2917  * @param path
2918  * @param pathLen
2919  * @param lastSlashPos
2920  *
2921  * @return bool
2922  */
2923 static bool getPathSegment(RexxCallContext *c, char *fileSpec, char **path, size_t *pathLen, int lastSlashPos)
2924 {
2925  char savedPath[FOUNDFILE_BUF_LEN]; // Used to save current directory and return to it.
2926  char *dPath = *path;
2927  size_t nPath = *pathLen;
2928 
2929  if ( fileSpec[lastSlashPos] != '/' )
2930  {
2931  // We have no slash in fileSpec, so it can not be a relative or full
2932  // path name. We resolve to the current directory.
2933  while ( getcwd(dPath, nPath) == NULL )
2934  {
2935  // The buffer is not big enough, reallocate a bigger buffer.
2936  if ( ! getBiggerBuffer(c, &dPath, &nPath, *pathLen) )
2937  {
2938  return false;
2939  }
2940  }
2941 
2942  if ( strlen(dPath) + 1 > nPath )
2943  {
2944  // The buffer is not big enough to concatenate a slash, reallocate
2945  // a bigger buffer.
2946  if ( ! getBiggerBuffer(c, &dPath, &nPath, *pathLen) )
2947  {
2948  return false;
2949  }
2950  }
2951 
2952  strcat(dPath, "/");
2953  }
2954  else
2955  {
2956  // There is a slash in fileSpec, so it is a relative or full path.
2957  // Copy the path out.
2958  size_t l = lastSlashPos + 1 + 1;
2959  if ( l > nPath )
2960  {
2961  nPath = neededSize(l, nPath);
2962  dPath = (char *)malloc(nPath * sizeof(char));
2963 
2964  if ( dPath == NULL )
2965  {
2967  return false;
2968  }
2969  }
2970  strncpy(dPath, fileSpec, lastSlashPos + 1);
2971 
2972  *(dPath + lastSlashPos + 1) = '\0'; // Terminate the string
2973 
2974  // At this point path could be a relative path. We try to resolve it to
2975  // a full path by saving the current directory, doing a chdir() which
2976  // will resolve a relative path, and then copying that current directory
2977  // into path. If we can not save the current directory or change
2978  // directory we just keep the relative path.
2979  if ( getcwd(savedPath, FOUNDFILE_BUF_LEN) != NULL )
2980  {
2981  if ( chdir(dPath) == 0 )
2982  {
2983  while ( getcwd(dPath, nPath) == NULL )
2984  {
2985  // The buffer is not big enough, reallocate a bigger buffer.
2986  if ( ! getBiggerBuffer(c, &dPath, &nPath, *pathLen) )
2987  {
2988  // Back to current directory.
2989  int ignore = chdir(savedPath);
2990  return false;
2991  }
2992  }
2993 
2994  if ( strlen(dPath) + 1 > nPath )
2995  {
2996  // The buffer is not big enough to concatenate a slash,
2997  // reallocate a bigger buffer.
2998  if ( ! getBiggerBuffer(c, &dPath, &nPath, *pathLen) )
2999  {
3000  return false;
3001  }
3002  }
3003 
3004  if ( lastSlashPos > 0 )
3005  {
3006  strcat(dPath, "/");
3007  }
3008 
3009  // Back to current directory.
3010  int ignore = chdir(savedPath);
3011  }
3012  }
3013  }
3014 
3015  if ( nPath != *pathLen )
3016  {
3017  *pathLen = nPath;
3018  *path = dPath;
3019  }
3020 
3021  return true;
3022 }
3023 
3024 /**
3025  * This is a SysFileTree() specific function..
3026  *
3027  * This function expands the file spec passed in to the function into its full
3028  * path name. The full path name is then split into the path portion and the
3029  * file name portion. The path portion is returned in path and the file name
3030  * portion is returned in the RXTREEDATA structure.
3031  *
3032  * The path portion will end with the '\' character.
3033  *
3034  * @param c Call context we are operating in.
3035  *
3036  * @param fSpec File specification to search for.
3037  *
3038  * @param path Pointer to the buffer for the returned full path of
3039  * expanded file specification. This buffer may be
3040  * reallocated if it is not big enough
3041  *
3042  * @param pathLen Pointer to the size of the passed in path buffer. This
3043  * value will be reset if the path buffer is reallocated.
3044  *
3045  * @param treeData The file name portion of the full path is copied into the
3046  * dFNameSpec field in this struct.
3047  *
3048  *
3049  * @return True on success, false on error. The only error would be if memory
3050  * allocation failed, in which case a condition has been raised. It
3051  * seems highly unlikely that this function could fail.
3052  *
3053  * @remarks Both the path and the file name destination buffers are checked to
3054  * be sure they are big enough. If either is not big enough, a larger
3055  * buffer is allocated.
3056  */
3057 
3058 static bool getPath(RexxCallContext *c, char *fileSpec, char **path, size_t *pathLen, RXTREEDATA *treeData)
3059 {
3060  int lastSlashPos; // Position of last slash in fileSpec.
3061 
3062  // If fileSpec is exactly "." or ".." then change it to "*" or "../*"
3063  if ( strcmp(fileSpec, ".") == 0 )
3064  {
3065  strcpy(fileSpec, "./*");
3066  }
3067  else if ( strcmp(fileSpec, "..") == 0 )
3068  {
3069  strcpy(fileSpec, "../*");
3070  }
3071 
3072  // Get the file name segment of fileSpec.
3073  if ( ! getFileNameSegment(c, fileSpec, treeData, &lastSlashPos) )
3074  {
3075  return false;
3076  }
3077 
3078  // Now resolve to the fully qualified path name.
3079  if ( ! getPathSegment(c, fileSpec, path, pathLen, lastSlashPos) )
3080  {
3081  return false;
3082  }
3083 
3084  return true;
3085 }
3086 
3087 
3088 /**
3089  * This is a SysFileTree() specific function..
3090  *
3091  * Formats a found file entry according to the specified options and adds it to
3092  * the stem containing the found files.
3093  *
3094  * @param c Call context we are operating in.
3095  *
3096  * @param treeData Struct with data related to finding the files.
3097  *
3098  * @param options Options specifying how the found file line is to be
3099  * formatted.
3100  *
3101  * @param finfo File info structure.
3102  *
3103  * @return False on error, otherwise true.
3104  *
3105  * @remarks If the options specify name only, we just need to copy over the
3106  * found file name. Otherwise we need to format a line with the time
3107  * stamp and file attributes, plus the found file name.
3108  *
3109  * We use the buffers in the treeData structure to format the found
3110  * file line. snprintf() is used to prevent and detect buffer
3111  * overflows. We start off trying to use the large static buffers in
3112  * the struct, each time a buffer is found to be too small in size, it
3113  * is doubled in size by dynamically allocated memory.. Code at the
3114  * top level detects and frees any allocated memory.
3115  *
3116  * Note that we can count the characters used for both the time data
3117  * and attribute data, so we know the buffers are large enough.
3118  *
3119  * If the file search is a very deep recursion in the host file
3120  * system, a very large number of String objects may be created in the
3121  * single Call context of SysFileTree. A reference to each created
3122  * object is saved in a hash table to protect it from garbage
3123  * collection, which can lead to a very large hash table. To prevent
3124  * the creation of a very large hash table, we create a temp object,
3125  * pass that object to the interpreter, and then tell the interpreter
3126  * the object no longer needs to be protected in this call context.
3127  */
3128 bool formatFile(RexxCallContext *c, RXTREEDATA *treeData, uint32_t options, struct stat *finfo)
3129 {
3130  struct tm *timestamp;
3131  char tp;
3132 
3133  if ( options & NAME_ONLY )
3134  {
3135  if ( treeData->nFoundFileLine < treeData->nFoundFile )
3136  {
3137  if ( ! increaseBuffer(c, treeData->nFoundFile, treeData, FOUNDFILELINE_BUFFER) )
3138  {
3139  return false;
3140  }
3141  }
3142  strcpy(treeData->foundFileLine, treeData->foundFile);
3143  }
3144  else
3145  {
3146 #ifdef AIX
3147  struct tm stTimestamp;
3148  timestamp = localtime_r(&(finfo->st_mtime), &stTimestamp);
3149 #else
3150  timestamp = localtime(&(finfo->st_mtime));
3151 #endif
3152  if ( options & LONG_TIME )
3153  {
3154 
3155  snprintf(treeData->fileTime, sizeof treeData->fileTime, "%4d-%02d-%02d %02d:%02d:%02d %10lu ",
3156  timestamp->tm_year + 1900,
3157  timestamp->tm_mon + 1,
3158  timestamp->tm_mday,
3159  timestamp->tm_hour,
3160  timestamp->tm_min,
3161  timestamp->tm_sec,
3162  (unsigned long)finfo->st_size);
3163  }
3164  else
3165  {
3166  if ( options & EDITABLE_TIME )
3167  {
3168  snprintf(treeData->fileTime, sizeof treeData->fileTime, "%02d/%02d/%02d/%02d/%02d %10lu ",
3169  (timestamp->tm_year) % 100,
3170  timestamp->tm_mon + 1,
3171  timestamp->tm_mday,
3172  timestamp->tm_hour,
3173  timestamp->tm_min,
3174  (unsigned long)finfo->st_size);
3175  }
3176  else
3177  {
3178  snprintf(treeData->fileTime, sizeof treeData->fileTime, "%2d/%02d/%02d %2d:%02d%c %10lu ",
3179  timestamp->tm_mon+1,
3180  timestamp->tm_mday,
3181  timestamp->tm_year % 100,
3182  timestamp->tm_hour < 13 ? timestamp->tm_hour : timestamp->tm_hour - 12,
3183  timestamp->tm_min,
3184  (timestamp->tm_hour < 12 || timestamp->tm_hour == 24) ? 'a' : 'p',
3185  (unsigned long)finfo->st_size);
3186  }
3187  }
3188  tp = typeOfEntry(finfo->st_mode);
3189 
3190  snprintf(treeData->fileAttr, sizeof treeData->fileAttr, "%c%c%c%c%c%c%c%c%c%c ",
3191  tp,
3192  (finfo->st_mode & S_IREAD) ? 'r' : '-',
3193  (finfo->st_mode & S_IWRITE) ? 'w' : '-',
3194  (finfo->st_mode & S_IEXEC) ? 'x' : '-',
3195  (finfo->st_mode & S_IRGRP) ? 'r' : '-',
3196  (finfo->st_mode & S_IWGRP) ? 'w' : '-',
3197  (finfo->st_mode & S_IXGRP) ? 'x' : '-',
3198  (finfo->st_mode & S_IROTH) ? 'r' : '-',
3199  (finfo->st_mode & S_IWOTH) ? 'w' : '-',
3200  (finfo->st_mode & S_IXOTH) ? 'x' : '-');
3201 
3202  // Now format the complete line.
3203  int len = snprintf(treeData->dFoundFileLine, treeData->nFoundFileLine, "%s%s%s",
3204  treeData->fileTime, treeData->fileAttr, treeData->dFoundFile);
3205  if ( len >= (int)treeData->nFoundFileLine )
3206  {
3207  size_t need = treeData->nFoundFile + strlen(treeData->fileTime) + strlen(treeData->fileAttr) + 1;
3208  if ( ! increaseBuffer(c, need, treeData, FOUNDFILELINE_BUFFER) )
3209  {
3210  return false;
3211  }
3212 
3213  // The buffer is now guaranteed to be big enough.
3214  snprintf(treeData->dFoundFileLine, need, "%s%s%s",
3215  treeData->fileTime, treeData->fileAttr, treeData->dFoundFile);
3216  }
3217  }
3218 
3219  // Place found file line in the stem.
3220  RexxStringObject t = c->String(treeData->foundFileLine);
3221 
3222  treeData->count++;
3223  c->SetStemArrayElement(treeData->files, treeData->count, t);
3224  c->ReleaseLocalReference(t);
3225 
3226  return true;
3227 }
3228 
3229 
3230 /**
3231  * Finds all files matching treeData->fNameSpec starting path, recursing into
3232  * sub-directories if requested.
3233  *
3234  * @param c Call context we are operating in.
3235  *
3236  * @param path Path to the directory to start in.
3237  *
3238  * @param treeData Struct containing data pertaining to the search and used to
3239  * return the found files.
3240  *
3241  * @param options Options defining the search. Some comination of the
3242  * following.
3243  *
3244  * RECURSE - Indicates that function should search
3245  * all child subdirectories recursively.
3246  * DO_DIRS - Indicates that directories should be
3247  * included in the search.
3248  * DO_FILES - Indicates that files should be included
3249  * in the search.
3250  * NAME_ONLY - Indicates that the output should be
3251  * restricted to matched file names only.
3252  * EDITABLE_TIME - Indicates time and date fields should
3253  * be output as one timestamp.
3254  * LONG_TIME - Indicates time and date fields should
3255  * be output as one long formatted timestamp
3256  * CASELESS - Indicates do a case insensitive check for
3257  * file names.
3258  *
3259  * @return True on no error, otherwise false.
3260  *
3261  * @remarks The IBM recursive find file passed in fileSpec as the first
3262  * argument, but never used it. Instead it used fNameSpec in
3263  * RXTREEDATA. So, that argument is eliminated here and fNameSpec is
3264  * used.
3265  *
3266  * snprintf() note:
3267  *
3268  * snprintf() doe not write more than size bytes (including the
3269  * terminating null byte ('\0')). If the output was truncated due to
3270  * this limit then the return value is the number of characters
3271  * (excluding the terminating null byte) which would have been written
3272  * to the final string if enough space had been available. Thus, a
3273  * return value of size or more means that the output was truncated.
3274  *
3275  * Therefore, unlike windows, there is no possibility of having an
3276  * unterminated string. If we detect that the buffers in use are too
3277  * small, rather than truncate data, we allocate dynamic memory that
3278  * is big enough. Actually we allocate a buffer twice as big as the
3279  * current buffer.
3280  *
3281  * We start with big static buffers, so we need to keep track of
3282  * whether memory was allocated or not. This is done by tracking the
3283  * size of the buffers. If a buffer size does not equal the original
3284  * size of the static buffer, then it needs to be freed.
3285  */
3286 static bool recursiveFindFile(RexxCallContext *c, const char *path, RXTREEDATA *treeData, uint32_t options)
3287 {
3288  DIR *dir_handle; // Directory handle.
3289  struct stat finfo; // File information.
3290  char *fileName; // Found file name returned here.
3291  int len; // snprintf return to check buffer overflow.
3292  bool caseless = options & CASELESS;
3293 
3294  // First, process all of the normal files, saving directories for last. *
3295 
3296  dir_handle = opendir(path);
3297  if ( dir_handle == NULL )
3298  {
3299  // Old IBM code returned VALID_ROUTINE here. I think it should probably
3300  // be an error though. I.e., it should be false.
3301  return true;
3302  }
3303 
3304  // If processing files, and have some files, get all of them.
3305  if ( (options & DO_FILES) && linFindNextFile(c, treeData->dFNameSpec, path, dir_handle, &finfo, &fileName, caseless) )
3306  {
3307  do
3308  {
3309  // Build the full name.
3310  len = snprintf(treeData->dFoundFile, treeData->nFoundFile, "%s%s", path, fileName);
3311  if ( len >= (int)treeData->nFoundFile )
3312  {
3313  if ( ! increaseBuffer(c, len + 1, treeData, FOUNDFILE_BUFFER) )
3314  {
3315  closedir(dir_handle);
3316  return false;
3317  }
3318 
3319  // We are guarenteed the buffer is big enough.
3320  snprintf(treeData->dFoundFile, len+1, "%s%s", path, fileName);
3321  }
3322 
3323  if ( ! formatFile(c, treeData, options, &finfo) )
3324  {
3325  closedir(dir_handle);
3326  return false;
3327  }
3328  }
3329  while ( linFindNextFile(c, treeData->dFNameSpec, path, dir_handle, &finfo, &fileName, caseless) );
3330  }
3331 
3332  // Reset the directory handle, (rewinddir doesn't work.)
3333  closedir(dir_handle);
3334  dir_handle = opendir(path);
3335 
3336  if ( dir_handle == NULL )
3337  {
3338  return true;
3339  }
3340 
3341  // If processing directories, and have some directories, get all of them.
3342  if ( (options & DO_DIRS) && linFindNextDir(c, treeData->dFNameSpec, path, dir_handle, &finfo, &fileName, caseless) )
3343  {
3344  do
3345  {
3346  // Skip dot directories.
3347  if ( strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0 )
3348  {
3349  continue;
3350  }
3351 
3352  // Build the full name.
3353  len = snprintf(treeData->dFoundFile, treeData->nFoundFile, "%s%s", path, fileName);
3354  if ( len >= (int)treeData->nFoundFile )
3355  {
3356  if ( ! increaseBuffer(c, len + 1, treeData, FOUNDFILE_BUFFER) )
3357  {
3358  closedir(dir_handle);
3359  return false;
3360  }
3361 
3362  // We are guarenteed the buffer is big enough.
3363  snprintf(treeData->dFoundFile, len+1, "%s%s", path, fileName);
3364  }
3365 
3366  if ( ! formatFile(c, treeData, options, &finfo) )
3367  {
3368  closedir(dir_handle);
3369  return false;
3370  }
3371  }
3372  while ( linFindNextDir(c, treeData->dFNameSpec, path, dir_handle, &finfo, &fileName, caseless) );
3373 
3374  }
3375 
3376  // Reset the directory handle, (rewinddir doesn't work.)
3377  closedir(dir_handle);
3378  dir_handle = opendir(path);
3379  if ( dir_handle == NULL )
3380  {
3381  return true;
3382  }
3383 
3384  // Do we need to recurse?
3385  if ( options & RECURSE )
3386  {
3387  // Used to create a new directory name.
3388  char tmpDirName[FOUNDFILE_BUF_LEN];
3389  char *dTmpDirName = tmpDirName;
3390  size_t nTmpDirName = FOUNDFILE_BUF_LEN;
3391 
3392  // No need for caseless when matching a star.
3393  if ( linFindNextDir(c, "*", path, dir_handle, &finfo, &fileName, 0) )
3394  {
3395  do
3396  {
3397  // Skip dot directories.
3398  if ( strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0 )
3399  {
3400  continue;
3401  }
3402 
3403  // Build the new directory name and search the next level.
3404  len = snprintf(dTmpDirName, nTmpDirName, "%s%s/", path, fileName);
3405  if ( len >= (int)nTmpDirName )
3406  {
3407  if ( ! getBiggerBuffer(c, &dTmpDirName, &nTmpDirName, FOUNDFILE_BUF_LEN) )
3408  {
3409  closedir(dir_handle);
3410  return false;
3411  }
3412 
3413  // We are guarenteed the buffer is big enough.
3414  snprintf(dTmpDirName, len+1, "%s%s/", path, fileName);
3415  }
3416 
3417  if ( ! recursiveFindFile(c, dTmpDirName, treeData, options) )
3418  {
3419  closedir(dir_handle);
3420  if ( nTmpDirName != FOUNDFILE_BUF_LEN )
3421  {
3422  free(dTmpDirName);
3423  }
3424  return false;
3425  }
3426  }
3427  while ( linFindNextDir(c, "*", path, dir_handle, &finfo, &fileName, 0) );
3428  }
3429 
3430  if ( nTmpDirName != FOUNDFILE_BUF_LEN )
3431  {
3432  free(dTmpDirName);
3433  }
3434  }
3435 
3436  closedir(dir_handle);
3437  return true;
3438 }
3439 
3440 /**
3441  * Intializes the RXTREEDATA struct at start up of SysFileTree()
3442  *
3443  * @param treeData
3444  * @param files
3445  */
3446 void inline initTreeData(RXTREEDATA *treeData, RexxStemObject files)
3447 {
3448  treeData->files = files;
3449 
3450  treeData->dFNameSpec = treeData->fNameSpec;
3451  treeData->nFNameSpec = FNAMESPEC_BUF_LEN;
3452 
3453  treeData->dFoundFile = treeData->foundFile;
3454  treeData->nFoundFile = FOUNDFILE_BUF_LEN;
3455 
3456  treeData->dFoundFileLine = treeData->foundFileLine;
3458 }
3459 
3460 /**
3461  * Frees any allocated memory in the RXTREEDATA struct, if needed, on return
3462  * from SysFileTree()
3463  *
3464  * @param treeData
3465  */
3466 void inline uninitTreeData(RXTREEDATA *treeData)
3467 {
3468  if ( treeData->nFNameSpec > FNAMESPEC_BUF_LEN )
3469  {
3470  free(treeData->dFNameSpec);
3471  }
3472  if ( treeData->nFoundFile > FOUNDFILE_BUF_LEN )
3473  {
3474  free(treeData->dFoundFile);
3475  }
3476  if ( treeData->nFoundFileLine > FOUNDFILELINE_BUF_LEN )
3477  {
3478  free(treeData->dFoundFileLine);
3479  }
3480 }
3481 
3482 
3483 
3484 /**
3485  * SysFileTree() implementation. Searches for files in a directory tree.
3486  *
3487  * @param fSpec [required] The search pattern, may contain glob characters.
3488  * E.g., .sh
3489  *
3490  * @param files [required] A stem to contain the returned results.
3491  *
3492  * @param opts [optional] Any combination of the following:
3493  *
3494  * 'B' - Search for files and directories.
3495  * 'D' - Search for directories only.
3496  * 'F' - Search for files only.
3497  * 'O' - Only output file names.
3498  * 'S' - Recursively scan subdirectories.
3499  * 'T' - Combine time & date fields into one.
3500  * 'L' - Long time format
3501  * 'I' - Case Insensitive search.
3502  *
3503  * @param targetAttr [optional] Target attribute mask. This is not used in
3504  * the Unix version of SysFileTree(), was never used. But,
3505  * the doc never noted that fact. It is left here to avoid
3506  * breaking any program that may have used it.
3507  *
3508  * @param newAttr [optional] New attribute mask. This is not used in the
3509  * Unix version of SysFileTree(), was never used. But, the
3510  * doc never noted that fact. It is left here to avoid
3511  * breaking any program that may have used it.
3512  *
3513  * @return 0 on success, non-zero on error. For all errors a condition is
3514  * raised.
3515  *
3516  *
3517  */
3518 RexxRoutine5(uint32_t, SysFileTree, CSTRING, fSpec, RexxStemObject, files, OPTIONAL_CSTRING, opts,
3519  OPTIONAL_CSTRING, targetAttr, OPTIONAL_CSTRING, newAttr)
3520 {
3521  char fileSpec[FNAMESPEC_BUF_LEN]; // File specification to look for.
3522  char path[FOUNDFILE_BUF_LEN]; // Path to search along.
3523  uint32_t result = 1; // Return value, 1 is an error.
3524  RXTREEDATA treeData = {0}; // Struct containing data about found files.
3525  uint32_t options = 0;
3526 
3527  initTreeData(&treeData, files);
3528 
3529  char *dPath = path;
3530  size_t nPath = FOUNDFILE_BUF_LEN;
3531 
3532  if ( ! getFileSpecFromArg(context, fSpec, fileSpec, FNAMESPEC_BUF_LEN, 1) )
3533  {
3534  goto done_out;
3535  }
3536 
3537  if ( ! getOptionsFromArg(context, opts, &options, 3) )
3538  {
3539  goto done_out;
3540  }
3541 
3542  if ( ! getPath(context, fileSpec, &dPath, &nPath, &treeData) )
3543  {
3544  goto done_out;
3545  }
3546 
3547  // If caseless, upper case file name portion now
3548  if ( options & CASELESS )
3549  {
3550  char *p = treeData.fNameSpec;
3551  for ( ; *p; ++p )
3552  {
3553  *p = toupper(*p);
3554  }
3555  }
3556 
3557  // The old RecursiveFindFile pulls fileSpec from treeData and never uses
3558  // fileSpec. fileSpec and treeData.fNameSpec are not equivalent.
3559 
3560  if ( recursiveFindFile(context, dPath, &treeData, options) )
3561  {
3562 
3563  context->SetStemArrayElement(treeData.files, 0, context->WholeNumber(treeData.count));
3564  result = 0;
3565  }
3566 
3567  if ( nPath != FOUNDFILE_BUF_LEN )
3568  {
3569  free(dPath);
3570  }
3571 
3572 done_out:
3573 
3574  uninitTreeData(&treeData);
3575  return result;
3576 }
3577 
3578 
3579 
3580 /*************************************************************************
3581 * Function: SysTempFileName *
3582 * *
3583 * Syntax: call SysTempFileName template [,filler] *
3584 * *
3585 * Params: template - Description of filespec desired. For example: *
3586 * C:\TEMP\FILE.??? *
3587 * filler - A character which when found in template will be *
3588 * replaced with random digits until a unique file *
3589 * or directory is found. The default character *
3590 * is '?'. *
3591 * *
3592 * Return: other - Unique file/directory name. *
3593 * '' - No more files exist given specified template. *
3594 *************************************************************************/
3595 
3596 size_t RexxEntry SysTempFileName(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3597 {
3598  char filler; /* filler character */
3599  char numstr[6];
3600  size_t num, max;
3601  char *array;
3602  char *dir; /* the directory */
3603  char *file; /* the file prefix */
3604  char *tmp; /* temporary */
3605  int x, j, i;
3606  j = 0; /* get a copy of the argument */
3607  i = 0;
3608  max = 1;
3609 
3610  if (numargs < 1 || /* validate arguments */
3611  numargs > 2 ||
3612  !RXVALIDSTRING(args[0]) ||
3613  args[0].strlength > 512)
3614  return INVALID_ROUTINE;
3615 
3616  if (numargs == 2 && /* get filler character */
3617  !RXNULLSTRING(args[1])) {
3618  if (args[1].strlength != 1) /* must be one character */
3619  return INVALID_ROUTINE;
3620  filler = args[1].strptr[0];
3621  }
3622  else
3623  filler = '?';
3624  /* get the file id */
3625  dir = (char*) malloc(args[0].strlength+1);
3626  if (dir == NULL){ /* if something went wrong */
3627  BUILDRXSTRING(retstr, ERROR_NOMEM);
3628  return VALID_ROUTINE;
3629  }
3630 
3631  strcpy(dir, args[0].strptr); /* copy the string */
3632 
3633  tmp = dir; /* set temporary */
3634 
3635 /* search for filter *********/
3636 
3637  for (x = 0; tmp[x] != 0; x++)
3638  {
3639  if (tmp[x] == filler)
3640  {
3641  max = max *10;
3642  j++;
3643  }
3644  }
3645 
3646  if (j)
3647  {
3648  srand(time(0));
3649  num = rand();
3650  num = num % max;
3651 
3652  switch (j)
3653  {
3654  case 1:
3655  snprintf(numstr, sizeof numstr, "%01u", (int)num);
3656  break;
3657  case 2:
3658  snprintf(numstr, sizeof numstr, "%02u", (int)num);
3659  break;
3660  case 3:
3661  snprintf(numstr, sizeof numstr, "%03u", (int)num);
3662  break;
3663  case 4:
3664  snprintf(numstr, sizeof numstr, "%04u", (int)num);
3665  break;
3666  case 5:
3667  snprintf(numstr, sizeof numstr, "%05u", (int)num);
3668  break;
3669  default:
3670  return INVALID_ROUTINE; /* raise error condition */
3671  } /* for compatibility */
3672 
3673  for (x = 0; tmp[x] !=0; x++)
3674  {
3675  if (tmp[x] == filler)
3676  {
3677  tmp[x] = numstr[i++];
3678  }
3679  }
3680  } /* if we need the filler */
3681 
3682  while(*tmp != 0 ) /* lets start at the end */
3683  {
3684  tmp++;
3685  }
3686 
3687  while((*tmp != '/') && (*tmp != '\\') && (tmp > dir))
3688  {
3689  --tmp;
3690  }
3691 
3692  if(tmp == dir)
3693  { /* directory string is '' or '/' */
3694  if(*dir == '\\'){
3695  file = dir+1;
3696  array = tempnam(NULL,file); /* call system routine */
3697  }
3698  else if(*dir == '/'){
3699  file = dir+1;
3700  array = tempnam("/",file); /* call system routine */
3701  }
3702  else{
3703  file = dir;
3704  array = tempnam(NULL,file);/* call system routine */
3705  }
3706  }
3707  else
3708  { /* directory string exists */
3709  file = tmp+1; /* set filename prefix */
3710  *tmp = '\0'; /* terminate directory string */
3711  array = tempnam(dir,file);/* call system routine */
3712  }
3713 
3714  if(strlen(array) > 255)
3715  {
3716  free((char *)retstr->strptr);
3717  retstr->strptr = (char *) malloc(strlen(array) + 1);
3718  }
3719 
3720  strcpy(retstr->strptr, array);
3721  /* correct string length */
3722  retstr->strlength = strlen(retstr->strptr);
3723 
3724  free (array);
3725  free(dir); /* free local string */
3726 
3727  return VALID_ROUTINE;
3728 }
3729 
3730 
3731 /*************************************************************************
3732 * Function: SysSetPriority *
3733 * *
3734 * Syntax: result = SysSetPriority(Class, Level) *
3735 * *
3736 * Params: Class - The priority class (0-4) *
3737 * Level - Amount to change (-31 to +31) *
3738 * (lower to higher priority) *
3739 * Return: 0 for correct execution *
3740 * 304 for ERROR_INVALID_PDELTA *
3741 * 307 for ERROR_INVALID_PCLASS *
3742 * derived from: *
3743 * result - return code from DosSetPriority *
3744 * *
3745 *************************************************************************/
3746 
3747 size_t RexxEntry SysSetPriority(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3748 {
3749  int pclass; /* priority class */
3750  int level; /* priority level */
3751  RexxReturnCode rc; /* creation return code */
3752 
3753  if (numargs != 2 || /* must have two */
3754  !RXVALIDSTRING(args[0])) /* first is omitted */
3755  return INVALID_ROUTINE; /* raise error condition */
3756  /* get class of change */
3757 
3758  if(!string2int(args[0].strptr,&pclass) || /* set the value for pclass */
3759  !string2int(args[1].strptr,&level)) /* set the value for level */
3760  return INVALID_ROUTINE;
3761 
3762  if (pclass == 0){ /* class 0 -> no change */
3763  rc = 0; /* no error */
3764  }
3765  /* change the priority */
3766  /* change according to delta */
3767  else if (((size_t)pclass > 0) && ((size_t)pclass <= 4)){
3768  int pid; /* PID */
3769  pid = getpid(); /* current PID */
3770 
3771  int priority; /* Priority */
3772  /* current priority */
3773  priority = getpriority(PRIO_PROCESS, getpid());
3774 
3775  /* Set new priority */
3776  setpriority(PRIO_PROCESS, getpid(),-level);
3777  rc = 0;
3778  }
3779 
3780  else{
3781  rc = 307;
3782  return INVALID_ROUTINE; /* raise error condition */
3783  }
3784 
3785  snprintf(retstr->strptr, DEFRXSTRING, "%d", rc); /* format the return code */
3786  retstr->strlength = strlen(retstr->strptr);
3787 
3788  return VALID_ROUTINE; /* good completion */
3789 }
3790 
3791 
3792 
3793 /*************************************************************************
3794 * Function: SysGetMessage *
3795 * *
3796 * Syntax: call SysGetMessage msgnum [,file] [,str1]...[,str9] *
3797 * *
3798 * Params: file - Name of message file to get message from. *
3799 * Default is OSO001.MSG. *
3800 * msgnum - Number of message being queried. *
3801 * str1 ... str9 - Insertion strings. For messages which *
3802 * contain %1, %2, etc, the str1, str2, etc *
3803 * strings will be inserted (if given). *
3804 * *
3805 * Return: The message with the inserted strings (if given). *
3806 * Note: The set number ist always 1. Therefore the interface *
3807 * remains the same as in OS/2 and Win. *
3808 * Reason: keep portability *
3809 *************************************************************************/
3810 
3811 size_t RexxEntry SysGetMessage(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
3812 {
3813  int msgnum; /* Message number to get */
3814  int setnum = 1; /* Set number (const 1) */
3815 #if defined( HAVE_CATOPEN )
3816  nl_catd catalog; /* catalog handle */
3817 #endif
3818  /* default error msg */
3819  const char default_message[] = {"Error: Message catalog not open !\0"};
3820  /* msg not found msg */
3821  const char not_found_message[] = {"Error: Message not found !\0"};
3822  /* insertion error msg */
3823  const char error_insertions[] = {"Error: Unable to generate message \
3824  (wrong insertions)\0"};
3825  /* cat not found msg */
3826  const char cat_not_found_message[] = {"Error: Message catalog not found !\0"};
3827 
3828  const char * msg; /* msg retrieved from catalog */
3829  int icount; /* number of insertions */
3830  int msg_length = 0; /* length of the return msg */
3831  const char * msgfile; /* name of the message file */
3832  char * temp;
3833  int count = 0; /* number of '%s' in the msg */
3834 
3835 
3836  if (numargs < 1 || numargs > 11 || /* validate arguments */
3837  !RXVALIDSTRING(args[0]))
3838  return INVALID_ROUTINE; /* exit with error */
3839 
3840  /* get message number */
3841  if (!string2int(args[0].strptr, &msgnum) || msgnum < 0)
3842  return INVALID_ROUTINE; /* exit with error */
3843 
3844  /* Get message file name. */
3845  /* Use "rexx.cat if not */
3846  /* given */
3847  if (numargs >= 2 && RXVALIDSTRING(args[1]))
3848  msgfile = args[1].strptr; /* use provided message file */
3849  else
3850  msgfile = REXXMESSAGEFILE;
3851 
3852 #if defined( HAVE_SETLOCALE )
3853  setlocale(LC_ALL, "en_US");
3854 #endif
3855 
3856 #if defined( HAVE_CATOPEN )
3857  /* open the catalog */
3858  if((catalog = catopen(msgfile, NL_CAT_LOCALE)) == (nl_catd)-1){
3859  retstr->strptr = (char *)malloc(strlen(cat_not_found_message)+1);
3860  strcpy(retstr->strptr, cat_not_found_message);
3861  retstr->strlength = strlen(cat_not_found_message);
3862  return VALID_ROUTINE;
3863  }
3864 
3865  /* retrieve msg from catalog */
3866  msg = catgets(catalog, setnum, (int)msgnum, default_message);
3867 
3868  if(*msg == '\0') /* if empty string returned */
3869  msg = not_found_message; /* it means msg not found */
3870 
3871  /* set number of insertions */
3872  if (numargs >= 2)
3873  icount = numargs-2;
3874  else
3875  icount = 0;
3876 
3877  /* calculate length of the return message */
3878  for(int j=2; j < icount+2; j++)
3879  msg_length += args[j].strlength;
3880  msg_length += strlen(msg);
3881  msg_length -= icount*2;
3882 
3883  /* alloc needed space for the return message (add 100 for default msgs) */
3884  if(!(retstr->strptr = (char *)malloc(msg_length+100))){
3885  BUILDRXSTRING(retstr, ERROR_NOMEM);
3886  catclose(catalog);
3887  return VALID_ROUTINE;
3888  }
3889 
3890  /* check for too much '%s' in the message */
3891  temp = const_cast<char *>(msg);
3892  /* replace all &1..&9 with %s */
3893  while((temp = strstr(temp, "&"))){
3894  if(isdigit(*(temp+1))){ /* replace &1..&9 ? */
3895  *(temp++) = '%';
3896  *(temp++) = 's'; /* %s expected */
3897  // count++;
3898  }
3899  else
3900  temp++;
3901  }
3902  /* now look for number of replacement variables */
3903  temp = const_cast<char *>(msg); /* reset temp pointer */
3904  while((temp = strstr(temp,"%s"))){ /* search for the %s */
3905  count ++; /* increment counter */
3906  temp += 2; /* jump over %s */
3907  }
3908  if(count > icount)
3909  icount = 10; /* go to error case */
3910 
3911  /* generate full message with insertions */
3912  switch(icount){
3913  case(1):{
3914  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr) != msg_length)
3915  strcpy(retstr->strptr, error_insertions);
3916  break;
3917  }
3918  case(2):{
3919  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3920  args[3].strptr) != msg_length)
3921  strcpy(retstr->strptr, error_insertions);
3922  break;
3923  }
3924  case(3):{
3925  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3926  args[3].strptr,
3927  args[4].strptr) != msg_length)
3928  strcpy(retstr->strptr, error_insertions);
3929  break;
3930  }
3931  case(4):{
3932  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3933  args[3].strptr,
3934  args[4].strptr,
3935  args[5].strptr) != msg_length)
3936  strcpy(retstr->strptr, error_insertions);
3937  break;
3938  }
3939  case(5):{
3940  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3941  args[3].strptr,
3942  args[4].strptr,
3943  args[5].strptr,
3944  args[6].strptr) != msg_length)
3945  strcpy(retstr->strptr, error_insertions);
3946  break;
3947  }
3948  case(6):{
3949  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3950  args[3].strptr,
3951  args[4].strptr,
3952  args[5].strptr,
3953  args[6].strptr,
3954  args[7].strptr) != msg_length)
3955  strcpy(retstr->strptr, error_insertions);
3956  break;
3957  }
3958  case(7):{
3959  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3960  args[3].strptr,
3961  args[4].strptr,
3962  args[5].strptr,
3963  args[6].strptr,
3964  args[7].strptr,
3965  args[8].strptr) != msg_length)
3966  strcpy(retstr->strptr, error_insertions);
3967  break;
3968  }
3969  case(8):{
3970  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3971  args[3].strptr,
3972  args[4].strptr,
3973  args[5].strptr,
3974  args[6].strptr,
3975  args[7].strptr,
3976  args[8].strptr,
3977  args[9].strptr) != msg_length)
3978  strcpy(retstr->strptr, error_insertions);
3979  break;
3980  }
3981  case(9):{
3982  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[2].strptr,
3983  args[3].strptr,
3984  args[4].strptr,
3985  args[5].strptr,
3986  args[6].strptr,
3987  args[7].strptr,
3988  args[8].strptr,
3989  args[9].strptr,
3990  args[10].strptr) != msg_length)
3991  strcpy(retstr->strptr, error_insertions);
3992  break;
3993  }
3994  case(10):{
3995  strcpy(retstr->strptr, error_insertions); /* error case */
3996  break;
3997  }
3998  default:{
3999  strcpy(retstr->strptr, msg);
4000  break;
4001  }
4002  }
4003  retstr->strlength = strlen(retstr->strptr);/* insert the length */
4004 
4005  catclose(catalog); /* close the catalog */
4006  return VALID_ROUTINE; /* no error on call */
4007 #else
4008  /* catopen not supported */
4009  const char cat_not_supported_message[] = {"Error: Message catalog (catopen) not supported !\0"};
4010 
4011  retstr->strptr = (char *)malloc(strlen(cat_not_supported_message)+1);
4012  strcpy(retstr->strptr, cat_not_supported_message);
4013  retstr->strlength = strlen(cat_not_supported_message);
4014  return VALID_ROUTINE;
4015 #endif
4016 }
4017 
4018 
4019 
4020 /*************************************************************************
4021 * Function: SysGetMessageX *
4022 * *
4023 * Syntax: call SysGetMessageX setnum, msgnum [,file] [,str1]...[,str9]*
4024 * *
4025 * Params: file - Name of message file to get message from. *
4026 * Default is OSO001.MSG. *
4027 * msgnum - Number of message being queried. *
4028 * str1 ... str9 - Insertion strings. For messages which *
4029 * contain %1, %2, etc, the str1, str2, etc *
4030 * strings will be inserted (if given). *
4031 * setnum - set number in the catalog *
4032 * *
4033 * Return: The message with the inserted strings (if given). *
4034 * Note: This is a special Unix only version of SysGetMessage which *
4035 * supports the selection of a set in the msg catalog. *
4036 *************************************************************************/
4037 
4038 size_t RexxEntry SysGetMessageX(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4039 {
4040  int msgnum; /* Message number to get */
4041  int setnum; /* Set number */
4042 #if defined( HAVE_CATOPEN )
4043  nl_catd catalog; /* catalog handle */
4044 #endif
4045  /* default error msg */
4046  const char default_message[] = {"Error: Message catalog not open !\0"};
4047  /* msg not found msg */
4048  const char not_found_message[] = {"Error: Message not found !\0"};
4049  /* insertion error msg */
4050  const char error_insertions[] = {"Error: Unable to generate message \
4051  (wrong insertions)\0"};
4052  /* cat not found msg */
4053  const char cat_not_found_message[] = {"Error: Message catalog not found !\0"};
4054 
4055  char * msg; /* msg retrieved from catalog */
4056  int icount; /* number of insertions */
4057  int msg_length = 0; /* length of the return msg */
4058  const char * msgfile; /* name of the message file */
4059  char * temp;
4060  int count = 0; /* number of '%s' in the msg */
4061 
4062 
4063  if (numargs < 1 || numargs > 12 || /* validate arguments */
4064  !RXVALIDSTRING(args[0]) ||
4065  !RXVALIDSTRING(args[1]))
4066  return INVALID_ROUTINE; /* exit with error */
4067 
4068  /* get set number */
4069  if (!string2int(args[0].strptr, &setnum) || setnum < 0)
4070  return INVALID_ROUTINE; /* exit with error */
4071 
4072  /* get message number */
4073  if (!string2int(args[1].strptr, &msgnum) || msgnum < 0)
4074  return INVALID_ROUTINE; /* exit with error */
4075 
4076  /* Get message file name. */
4077  /* Use "rexx.cat if not */
4078  /* given */
4079  if (numargs >= 3 && RXVALIDSTRING(args[1]))
4080  msgfile = args[2].strptr; /* use provided message file */
4081  else
4082  msgfile = REXXMESSAGEFILE;
4083 
4084 #if defined( HAVE_CATOPEN )
4085  /* open the catalog */
4086  if((catalog = catopen(msgfile, NL_CAT_LOCALE)) == (nl_catd)-1){
4087  retstr->strptr = (char *)malloc(strlen(cat_not_found_message)+1);
4088  strcpy(retstr->strptr, cat_not_found_message);
4089  retstr->strlength = strlen(cat_not_found_message);
4090  return VALID_ROUTINE;
4091  }
4092 
4093  /* retrieve msg from catalog */
4094  msg = catgets(catalog, setnum, msgnum, default_message);
4095 
4096  if(*msg == '\0') /* if empty string returned */
4097  msg = const_cast<char *>(not_found_message); /* it means msg not found */
4098 
4099  /* set number of insertions */
4100  if (numargs >= 3)
4101  icount = numargs-3;
4102  else
4103  icount = 0;
4104 
4105  /* calculate length of the return message */
4106  for(int j=3; j < icount+3; j++)
4107  msg_length += args[j].strlength;
4108  msg_length += strlen(msg);
4109  msg_length -= icount*2;
4110 
4111  /* alloc needed space for the return message (add 100 for default msgs) */
4112  if(!(retstr->strptr = (char *)malloc(msg_length+100))){
4113  BUILDRXSTRING(retstr, ERROR_NOMEM);
4114  catclose(catalog);
4115  return VALID_ROUTINE;
4116  }
4117 
4118  /* check for to much '%s' in the message */
4119  temp = msg;
4120  /* replace all &1..&9 with %s */
4121  while((temp = strstr(temp, "&"))){
4122  if(isdigit(*(temp+1))){ /* replace &1..&9 ? */
4123  *(temp++) = '%';
4124  *(temp++) = 's'; /* %s expected */
4125  }
4126  else
4127  temp++;
4128  }
4129  /* now look for number of replacement variables */
4130  temp = msg; /* reset temp pointer */
4131  while((temp = strstr(temp,"%s"))){ /* search for the %s */
4132  count++; /* increment counter */
4133  temp += 2; /* jump over %s */
4134  }
4135 
4136  if(count > icount)
4137  icount = 10; /* go to error case */
4138 
4139  /* generate full message with insertions */
4140  switch(icount){
4141  case(1):{
4142  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr) != msg_length)
4143  strcpy(retstr->strptr, error_insertions);
4144  break;
4145  }
4146  case(2):{
4147  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4148  args[4].strptr) != msg_length)
4149  strcpy(retstr->strptr, error_insertions);
4150  break;
4151  }
4152  case(3):{
4153  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4154  args[4].strptr,
4155  args[5].strptr) != msg_length)
4156  strcpy(retstr->strptr, error_insertions);
4157  break;
4158  }
4159  case(4):{
4160  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4161  args[4].strptr,
4162  args[5].strptr,
4163  args[6].strptr) != msg_length)
4164  strcpy(retstr->strptr, error_insertions);
4165  break;
4166  }
4167  case(5):{
4168  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4169  args[4].strptr,
4170  args[5].strptr,
4171  args[6].strptr,
4172  args[7].strptr) != msg_length)
4173  strcpy(retstr->strptr, error_insertions);
4174  break;
4175  }
4176  case(6):{
4177  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4178  args[4].strptr,
4179  args[5].strptr,
4180  args[6].strptr,
4181  args[7].strptr,
4182  args[8].strptr) != msg_length)
4183  strcpy(retstr->strptr, error_insertions);
4184  break;
4185  }
4186  case(7):{
4187  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4188  args[4].strptr,
4189  args[5].strptr,
4190  args[6].strptr,
4191  args[7].strptr,
4192  args[8].strptr,
4193  args[9].strptr) != msg_length)
4194  strcpy(retstr->strptr, error_insertions);
4195  break;
4196  }
4197  case(8):{
4198  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4199  args[4].strptr,
4200  args[5].strptr,
4201  args[6].strptr,
4202  args[7].strptr,
4203  args[8].strptr,
4204  args[9].strptr,
4205  args[10].strptr) != msg_length)
4206  strcpy(retstr->strptr, error_insertions);
4207  break;
4208  }
4209  case(9):{
4210  if(snprintf(retstr->strptr, DEFRXSTRING, msg,args[3].strptr,
4211  args[4].strptr,
4212  args[5].strptr,
4213  args[6].strptr,
4214  args[7].strptr,
4215  args[8].strptr,
4216  args[9].strptr,
4217  args[10].strptr,
4218  args[11].strptr) != msg_length)
4219  strcpy(retstr->strptr, error_insertions);
4220  break;
4221  }
4222  case(10):{
4223  strcpy(retstr->strptr, error_insertions); /* error case */
4224  break;
4225  }
4226  default:{
4227  strcpy(retstr->strptr, msg);
4228  break;
4229  }
4230  }
4231  retstr->strlength = strlen(retstr->strptr);/* insert the length */
4232 
4233  catclose(catalog); /* close the catalog */
4234  return VALID_ROUTINE; /* no error on call */
4235 #else
4236  /* catopen not supported */
4237  const char cat_not_supported_message[] = {"Error: Message catalog (catopen) not supported !\0"};
4238 
4239  retstr->strptr = (char *)malloc(strlen(cat_not_supported_message)+1);
4240  strcpy(retstr->strptr, cat_not_supported_message);
4241  retstr->strlength = strlen(cat_not_supported_message);
4242  return VALID_ROUTINE;
4243 #endif
4244 }
4245 
4246 
4247 
4248 /*************************************************************************
4249 * Function: SysGetKey *
4250 * *
4251 * Syntax: call SysGetKey [echo] *
4252 * *
4253 * Params: echo - Either of the following: *
4254 * 'ECHO' - Echo the inputted key (default). *
4255 * 'NOECHO' - Do not echo the inputted key. *
4256 * *
4257 * Return: The key striked. *
4258 *************************************************************************/
4259 
4260 size_t RexxEntry SysGetKey(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4261 {
4262  bool echo = true; /* Set to false if we */
4263  /* shouldn't echo */
4264  if (numargs > 1) /* too many arguments */
4265  return INVALID_ROUTINE; /* raise an error */
4266 
4267  if (numargs == 1) { /* validate arguments */
4268  if (!strcasecmp(args[0].strptr, "NOECHO"))
4269  echo = false;
4270  else if (strcasecmp(args[0].strptr, "ECHO"))
4271  return INVALID_ROUTINE; /* Invalid option */
4272  }
4273 
4274  getkey(retstr->strptr,echo); /* call the complicated part */
4275  retstr->strlength = strlen(retstr->strptr); /* format string */
4276 
4277  return VALID_ROUTINE; /* no error on call */
4278 }
4279 
4280 
4281 #ifdef AIX_DISABLED
4282 /*************************************************************************
4283 * Function: SysAddFuncPkg *
4284 * *
4285 * Description: Function to enable load of old CREXX libraries for AIX. *
4286 * The Path must be fully qualified in REXX procedure, *
4287 * it is used to select the old >load< function call. *
4288 * *
4289 * Syntax: call SysAddFuncPkg ( /Fully_qualified_path/Package_name ) *
4290 * *
4291 * Params: none *
4292 * *
4293 * Return: null string *
4294 *************************************************************************/
4295 
4296 size_t RexxEntry SysAddFuncPkg(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4297 {
4298  RexxReturnCode rc = NULL;
4299  int j; /* Counter */
4300  size_t arglength; /* length of the count */
4301  char * argstring; /* input sleep time */
4302  RXFUNCBLOCK *funcblock; /* Base for function blocks */
4303  PRXINITFUNCPKG InitFunc; /* Pointer returned from load */
4304 
4305  retstr->strlength = 0; /* set return value */
4306  /* check arguments */
4307  if (numargs != 1) /* Must have one argument */
4308  return INVALID_ROUTINE;
4309 
4310  argstring = args[0].strptr; /* point to the string */
4311  arglength = args[0].strlength; /* get length of string */
4312  if ( (arglength == 0 ) || /* if null string */
4313  (arglength >= MAXNAME) || /* or too long */
4314  (!strchr( argstring, '/' )) )
4315  return INVALID_ROUTINE; /* not valid */
4316 
4317  if (!(InitFunc = (PRXINITFUNCPKG)load(argstring,0,NULL)))
4318  { /* loadAndInit? for load */
4319  if ( InitFunc == NULL ) {
4320  fprintf(stderr, " *** Unable to load library %s !\nError message: errno = %d;",\
4321  argstring, errno);
4322  perror(" REXXUTIL");
4323  rc = 1;
4324  }
4325  }
4326  if ( rc == NULL ) {
4327  /* Call the initialization routine for the library (which should */
4328  /* be the function pointer returned to us by load). */
4329  rc = (*InitFunc)(&funcblock);
4330  if (rc) { /* If routine indicates error,*/
4331  /* tell the user. */
4332  fprintf(stderr,"*** Library load routine gave error %d.\n",rc);
4333  return(rc); /* don't load anything */
4334  } /* endif */
4335 
4336  /* Now run through the array of func blocks, adding them to the */
4337  /* list of external functions or subcommand handlers. Note that */
4338  /* we use the external function types in all cases, but since */
4339  /* the only thing affected is the function pointers, there's no */
4340  /* problem - the RXFUNCBLOCK and RXSUBCOMBLOCK types are */
4341  /* otherwise identical. */
4342  for (j=0; funcblock[j].name != NULL; j++) {
4343 #ifdef ORXLD_DEBUG
4344  fprintf(stderr,"REXXUTIL: PKGFunction %s \n", funcblock[j].name);
4345 #endif
4346  if ( funcblock[j].function && (rc == NULL) ) {
4347  RexxRegisterFunctionDll( funcblock[j].name,
4348  argstring, funcblock[j].name);
4349  }
4350  }
4351  }
4352  return VALID_ROUTINE;
4353 }
4354 
4355 /*************************************************************************
4356 * Function: SysAddCmdPkg like SysAddFuncPkg *
4357 * *
4358 *************************************************************************/
4359 size_t RexxEntry SysAddCmdPkg(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4360 {
4361  return SysAddFuncPkg(
4362  name, /* Function name */
4363  numargs, /* Number of arguments */
4364  args, /* Argument array */
4365  queuename, /* Current queue */
4366  retstr ); /* Return RXSTRING */
4367 
4368 }
4369 
4370 /*************************************************************************
4371 * Function: SysDropFuncPkg *
4372 * *
4373 * Description: Function to enable load of old CREXX libraries for AIX. *
4374 * It is used for the old >load< function call for the *
4375 * list of function to be dropped from REXX. *
4376 * The Path must not be fully qualified in REXX procedure. *
4377 * *
4378 * Syntax: call SysDropFuncPkg *
4379 * *
4380 * Return: NO_UTIL_ERROR - Successful. *
4381 *************************************************************************/
4382 
4383 size_t RexxEntry SysDropFuncPkg(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4384 {
4385  RexxReturnCode rc = NULL;
4386  int j; /* Counter */
4387  size_t arglength; /* length of the count */
4388  char * argstring; /* input sleep time */
4389  RXFUNCBLOCK *funcblock; /* Base for function blocks */
4390  PRXINITFUNCPKG InitFunc; /* Pointer returned from load */
4391 
4392  retstr->strlength = 0; /* set return value */
4393  /* check arguments */
4394  if (numargs != 1) /* Must have one argument */
4395  return INVALID_ROUTINE;
4396 
4397  argstring = args[0].strptr; /* point to the string */
4398  arglength = args[0].strlength; /* get length of string */
4399  if ( (arglength == 0 ) || /* if null string */
4400  (arglength >= MAXNAME) ) /* or too long */
4401  return INVALID_ROUTINE; /* not valid */
4402 
4403  if (!(InitFunc = (PRXINITFUNCPKG)load(argstring,0,NULL)))
4404  { /* loadAndInit? for load */
4405  if ( InitFunc == NULL ) {
4406  fprintf(stderr, " *** Unable to drop library %s !\nError message: errno = %d;",\
4407  argstring, errno);
4408  perror(" REXXUTIL");
4409  rc = 1;
4410  }
4411  }
4412  if ( rc == NULL ) {
4413  /* Call the initialization routine for the library (which should */
4414  /* be the function pointer returned to us by load). */
4415  rc = (*InitFunc)(&funcblock);
4416  if (rc) { /* If routine indicates error,*/
4417  /* tell the user. */
4418  fprintf(stderr," *** Library drop routine gave error %d.\n",rc);
4419  return(rc); /* don't load anything */
4420  } /* endif */
4421 
4422  /* Now run through the array of func blocks, adding them to the */
4423  /* list of external functions or subcommand handlers. Note that */
4424  /* we use the external function types in all cases, but since */
4425  /* the only thing affected is the function pointers, there's no */
4426  /* problem - the RXFUNCBLOCK and RXSUBCOMBLOCK types are */
4427  /* otherwise identical. */
4428  for (j=0; funcblock[j].name != NULL; j++) {
4429 #ifdef ORXLD_DEBUG
4430  fprintf(stderr,"REXXUTIL: PKGFunction %s \n", funcblock[j].name);
4431 #endif
4432  if ( funcblock[j].function && (rc == NULL) ) {
4433  RexxDeregisterFunction( funcblock[j].name );
4434  }
4435  } /* endfor */
4436  }
4437  return VALID_ROUTINE; /* no error on call */
4438 }
4439 
4440 /*************************************************************************
4441 * Function: SysDropCmdPkg like SysDropFuncPkg *
4442 * *
4443 *************************************************************************/
4444 size_t RexxEntry SysDropCmdPkg(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4445 {
4446  return SysDropFuncPkg(
4447  name, /* Function name */
4448  numargs, /* Number of arguments */
4449  args, /* Argument array */
4450  queuename, /* Current queue */
4451  retstr ); /* Return RXSTRING */
4452 }
4453 #endif /* CREXX funcs */
4454 
4455 /*************************************************************************
4456 * Function: SysFork *
4457 * *
4458 * Description: Function to migrate CREXX for AIX procedures. *
4459 * *
4460 * Syntax: call SysFork() *
4461 * *
4462 * Return: Process_ID ( to parent child''s ID / to child the ID 0 ) *
4463 *************************************************************************/
4464 
4465 size_t RexxEntry SysFork(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4466 {
4467  retstr->strlength = 0; /* set return value */
4468  /* check arguments */
4469  if (numargs != 0) /* Must have no argument */
4470  return INVALID_ROUTINE;
4471  else
4472  {
4473  snprintf(retstr->strptr, DEFRXSTRING, "%d", fork());
4474  retstr->strlength = strlen(retstr->strptr);
4475  }
4476  return VALID_ROUTINE; /* no error on call */
4477 }
4478 
4479 /*************************************************************************
4480 * Function: SysWait *
4481 * *
4482 * Description: Function to migrate CREXX for AIX procedures. *
4483 * *
4484 * Syntax: call SysWait() *
4485 * *
4486 * Return: exit code of child *
4487 *************************************************************************/
4488 
4489 size_t RexxEntry SysWait(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4490 {
4491  int iStatus;
4492  retstr->strlength = 0; /* set return value */
4493  /* check arguments */
4494  if (numargs != 0) /* Must have no argument */
4495  return INVALID_ROUTINE;
4496  else
4497  {
4498  wait( &iStatus );
4499  snprintf(retstr->strptr, DEFRXSTRING, "%d", iStatus);
4500  retstr->strlength = strlen(retstr->strptr);
4501  }
4502  return VALID_ROUTINE; /* no error on call */
4503 }
4504 
4505 /*************************************************************************
4506 * Function: SysCreatePipe *
4507 * *
4508 * Description: Function to migrate CREXX for AIX procedures. *
4509 * Function creates an unnamed pipe *
4510 * *
4511 * Syntax: call SysCreatePipe( Blocking | Nonblocking ) *
4512 * *
4513 * Return: 'handle handle' ( handle for read and handle for write )*
4514 *************************************************************************/
4515 
4516 size_t RexxEntry SysCreatePipe(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4517 {
4518  int iStatus;
4519  int iaH[2];
4520  char cBlocking = 0;
4521  retstr->strlength = 0; /* set return value */
4522  /* check arguments ---------- */
4523  if (numargs > 1) /* More than one arg? */
4524  return INVALID_ROUTINE;
4525  if (numargs == 0 ) /* No arg? */
4526  cBlocking = 1; /* Default is blocking */
4527  /* One arg, first char is 'b'? */
4528  else if (args[0].strptr[0] == 'b' || args[0].strptr[0] == 'B')
4529  cBlocking = 1; /* Wants blocking */
4530  /* One arg, first char is 'n'? */
4531  else if (args[0].strptr[0] == 'n' || args[0].strptr[0] == 'N')
4532  cBlocking = 0; /* Wants non-blocking */
4533 
4534  if (pipe(iaH)) /* Create the pipe */
4535  {
4536  perror("*** ERROR: Creating pipe"); /* pipe creation failed */
4537  return VALID_ROUTINE; /* no error on call */
4538  }
4539  if (!cBlocking) /* Non-blocking? */
4540  {
4541  /* Get file status flags ---------- */
4542  iStatus = fcntl(iaH[0], F_GETFL, NULL);
4543  iStatus |= O_NONBLOCK; /* Turn on NONBLOCK flag */
4544  /* Does set work? ----------------- */
4545  if (fcntl(iaH[0], F_SETFL, iStatus) == -1) {
4546  perror("*** ERROR: Setting NONBLOCK flag"); /* No, tell user */
4547  close(iaH[0]); close(iaH[1]); /* Close pipes */
4548  return VALID_ROUTINE; /* no error on call */
4549  }
4550  }
4551  snprintf(retstr->strptr, DEFRXSTRING, "%d %d", iaH[0], iaH[1]);/* Create return string */
4552  retstr->strlength = strlen(retstr->strptr);
4553 
4554  return VALID_ROUTINE; /* no error on call */
4555 }
4556 
4557 /*************************************************************************
4558 * Function: SysDumpVariables *
4559 * *
4560 * Syntax: result = SysDumpVariables([filename]) *
4561 * *
4562 * Params: filename - name of the file where variables are appended to *
4563 * (dump is written to stdout if omitted) *
4564 * *
4565 * Return: 0 - dump completed OK *
4566 * -1 - failure during dump *
4567 *************************************************************************/
4568 
4569 size_t RexxEntry SysDumpVariables(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4570 {
4571  RexxReturnCode rc; /* Ret code */
4572  SHVBLOCK shvb;
4573  int handle;
4574  bool fCloseFile = false;
4575 
4576  if ( (numargs > 1) || /* wrong number of arguments? */
4577  ((numargs > 0) && !RXVALIDSTRING(args[0])) )
4578  return INVALID_ROUTINE; /* raise error condition */
4579 
4580  if (numargs > 0)
4581  {
4582  /* open output file for append */
4583  fCloseFile = true;
4584 
4585  handle = open(args[0].strptr, O_WRONLY | O_APPEND | O_CREAT, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH);
4586  if(handle < 0)
4587  return INVALID_ROUTINE; /* raise error condition */
4588  }
4589  else
4590  handle = STDOUT_FILENO;
4591 
4592  do
4593  {
4594  /* prepare request block */
4595  shvb.shvnext = NULL;
4596  shvb.shvname.strptr = NULL; /* let REXX allocate the memory */
4597  shvb.shvname.strlength = 0;
4598  shvb.shvnamelen = 0;
4599  shvb.shvvalue.strptr = NULL; /* let REXX allocate the memory */
4600  shvb.shvvalue.strlength = 0;
4601  shvb.shvvaluelen = 0;
4602  shvb.shvcode = RXSHV_NEXTV;
4603  shvb.shvret = 0;
4604 
4605  rc = RexxVariablePool(&shvb);
4606 
4607  if (rc == RXSHV_OK)
4608  {
4609  ssize_t ignore; // avoid warning: ignoring return value of 'ssize_t write(int, const void*, size_t)'
4610 
4611  ignore = write(handle, "Name=", strlen("Name="));
4612  ignore = write(handle, shvb.shvname.strptr, shvb.shvname.strlength);
4613  ignore = write(handle, ", Value='", 9);
4614  ignore = write(handle, shvb.shvvalue.strptr,shvb.shvvalue.strlength);
4615  ignore = write(handle, "'\n", 2);
4616 
4617  /* free memory allocated by REXX */
4618  RexxFreeMemory((void *)shvb.shvname.strptr);
4619  RexxFreeMemory((void *)shvb.shvvalue.strptr);
4620 
4621  /* leave loop if this was the last var */
4622  if (shvb.shvret & RXSHV_LVAR)
4623  break;
4624  }
4625  } while (rc == RXSHV_OK);
4626 
4627  if (fCloseFile)
4628  close(handle); /* close the file */
4629 
4630  if (rc != RXSHV_LVAR)
4631  RETVAL(-1)
4632  else
4633  RETVAL(0)
4634 }
4635 
4636 /*************************************************************************
4637 * Function: SysGetFileDateTime *
4638 * *
4639 * Syntax: result = SysGetFileDateTime(filename [,timesel]) *
4640 * Params: filename - name of the file to query *
4641 * timesel - What filetime to query: Access/Write *
4642 * *
4643 * Return: -1 - file date/time query failed *
4644 * other - date and time as YYYY-MM-DD HH:MM:SS *
4645 *************************************************************************/
4646 
4647 size_t RexxEntry SysGetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4648 {
4649  struct stat64 buf;
4650  struct tm *newtime;
4651  const char *dir_buf = NULL; /* full directory path */
4652  bool fOk = true;
4653  bool alloc_Flag = false;
4654 
4655  if ( (numargs < 1) || (numargs > 2) ||
4656  ((numargs == 2) && !RXVALIDSTRING(args[1])) )
4657  return INVALID_ROUTINE; /* raise error condition */
4658 
4659  if(*(args[0].strptr) == '~')
4660  {
4661  dir_buf = resolve_tilde(args[0].strptr);
4662  alloc_Flag = true;
4663  }
4664  else
4665  {
4666  dir_buf = args[0].strptr;
4667  }
4668 
4669  if (stat64(dir_buf, &buf) < 0)
4670  {
4671  fOk = false;
4672  }
4673 
4674  if(fOk)
4675  {
4676  if (numargs > 1)
4677  {
4678  switch (args[1].strptr[0])
4679  {
4680  case 'a':
4681  case 'A':
4682  newtime = localtime(&(buf.st_atime));
4683  break;
4684  case 'w':
4685  case 'W':
4686  newtime = localtime(&(buf.st_mtime));
4687 
4688  break;
4689  default:
4690  return INVALID_ROUTINE;
4691  }
4692  }
4693  else
4694  newtime = localtime(&(buf.st_mtime));
4695 
4696  newtime->tm_year += 1900;
4697  newtime->tm_mon += 1;
4698 
4699  snprintf(retstr->strptr, DEFRXSTRING, "%4d-%02d-%02d %02d:%02d:%02d",
4700  newtime->tm_year,
4701  newtime->tm_mon,
4702  newtime->tm_mday,
4703  newtime->tm_hour,
4704  newtime->tm_min,
4705  newtime->tm_sec);
4706  retstr->strlength = strlen(retstr->strptr);
4707  }
4708  if( (dir_buf) && (alloc_Flag == true) )
4709  free((void *)dir_buf); /* free the buffer memory */
4710  if (!fOk)
4711  RETVAL(-1)
4712  else
4713  return VALID_ROUTINE;
4714 }
4715 
4716 /*************************************************************************
4717 * Function: SysSetFileDateTime *
4718 * *
4719 * Syntax: result = SysSetFileDateTime(filename [,newdate] [,newtime]) *
4720 * *
4721 * Params: filename - name of the file to update *
4722 * newdate - new date to set in format YYYY-MM-DD (YYYY>1800) *
4723 * newtime - new time to set in format HH:MM:SS *
4724 * *
4725 * Return: 0 - file date/time was updated correctly *
4726 * -1 - failure attribute update *
4727 *************************************************************************/
4728 
4729 size_t RexxEntry SysSetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4730 {
4731  bool fOk = true;
4732  struct utimbuf timebuf;
4733  struct tm *newtime;
4734  time_t ltime;
4735  struct stat64 buf;
4736  const char *dir_buf = NULL;
4737  bool alloc_Flag = false;
4738 
4739  /* we expect one to three parameters, if three parameters are */
4740  /* specified then the second may be omitted to set only a new time,*/
4741  /* if only one is specified then the file is set to current time */
4742 
4743  if ( (numargs < 1) || (numargs > 3) ||
4744  ((numargs == 2) && !RXVALIDSTRING(args[1])) ||
4745  ((numargs == 3) && !RXVALIDSTRING(args[2])) )
4746  return INVALID_ROUTINE; /* raise error condition */
4747 
4748  if(*(args[0].strptr) == '~')
4749  {
4750  dir_buf = resolve_tilde(args[0].strptr);
4751  alloc_Flag = true;
4752  }
4753  else
4754  {
4755  dir_buf = args[0].strptr;
4756  }
4757 
4758  if (stat64(dir_buf, &buf) < 0)
4759  {
4760  fOk = false;
4761  }
4762 
4763  if (numargs == 1)
4764  {
4765  time(&ltime);
4766  timebuf.modtime = ltime;
4767  if (utime(dir_buf, &timebuf) < 0)
4768  {
4769  fOk = false;
4770  }
4771  }
4772  else
4773  {
4774  newtime = localtime(&(buf.st_mtime));
4775  if ((numargs >= 2) && RXVALIDSTRING(args[1]))
4776  {
4777 
4778  /* parse new date */
4779  if (sscanf(args[1].strptr, "%4d-%2d-%2d", &newtime->tm_year,
4780  &newtime->tm_mon, &newtime->tm_mday) != 3)
4781  fOk = false;
4782  newtime->tm_year -= 1900;
4783  newtime->tm_mon -= 1;
4784  }
4785  if ((numargs == 3) && RXVALIDSTRING(args[2]))
4786  {
4787  /* parse new time */
4788  if (sscanf(args[2].strptr, "%2d:%2d:%2d", &newtime->tm_hour,
4789  &newtime->tm_min, &newtime->tm_sec) != 3)
4790  fOk = false;
4791  }
4792  ltime = mktime(newtime);
4793  timebuf.modtime = ltime;
4794  if (utime(dir_buf, &timebuf) < 0)
4795  {
4796  fOk = false;
4797  }
4798  }
4799 
4800  if( (dir_buf) && (alloc_Flag == true) )
4801  free((void *)dir_buf); /* free the buffer memory */
4802  if (fOk)
4803  RETVAL(0)
4804  else
4805  RETVAL(-1)
4806 }
4807 
4808 
4809 size_t RexxEntry RexxStemSort(const char *stemname, int order, int type,
4810  size_t start, size_t end, size_t firstcol, size_t lastcol);
4811 
4812 /*************************************************************************
4813 * Function: SysStemSort *
4814 * *
4815 * Syntax: result = SysStemSort(stem, order, type, start, end, *
4816 * firstcol, lastcol) *
4817 * *
4818 * Params: stem - name of stem to sort *
4819 * order - 'A' or 'D' for sort order *
4820 * type - 'C', 'I', 'N' for comparision type *
4821 * start - first index to sort *
4822 * end - last index to sort *
4823 * firstcol - first column to use as sort key *
4824 * lastcol - last column to use as sort key *
4825 * *
4826 * Return: 0 - sort was successful *
4827 * -1 - sort failed *
4828 *************************************************************************/
4829 
4830 size_t RexxEntry SysStemSort(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
4831 {
4832  char stemName[255];
4833  size_t first = 1;
4834  size_t last = SIZE_MAX;
4835  size_t firstCol = 0;
4836  size_t lastCol = SIZE_MAX;
4837  int sortType = SORT_CASESENSITIVE;
4838  int sortOrder = SORT_ASCENDING;
4839 
4840  if ( (numargs < 1) || (numargs > 7) || /* validate arguments */
4841  !RXVALIDSTRING(args[0]))
4842  return INVALID_ROUTINE;
4843 
4844  /* remember stem name */
4845  memset(stemName, 0, sizeof(stemName));
4846  strcpy(stemName, args[0].strptr);
4847  if (stemName[args[0].strlength-1] != '.')
4848  stemName[args[0].strlength] = '.';
4849 
4850  /* check other parameters */
4851  if ((numargs >= 2) && RXVALIDSTRING(args[1])) /* sort order */
4852  {
4853  switch (args[1].strptr[0])
4854  {
4855  case 'A':
4856  case 'a':
4857  sortOrder = SORT_ASCENDING;
4858  break;
4859  case 'D':
4860  case 'd':
4861  sortOrder = SORT_DECENDING;
4862  break;
4863  default:
4864  return INVALID_ROUTINE;
4865  } /* endswitch */
4866  } /* endif */
4867 
4868  if ((numargs >= 3) && RXVALIDSTRING(args[2])) /* sort type */
4869  {
4870  switch (args[2].strptr[0])
4871  {
4872  case 'C':
4873  case 'c':
4874  sortType = SORT_CASESENSITIVE;
4875  break;
4876  case 'I':
4877  case 'i':
4878  sortType = SORT_CASEIGNORE;
4879  break;
4880  default:
4881  return INVALID_ROUTINE;
4882  } /* endswitch */
4883  } /* endif */
4884 
4885  if ((numargs >= 4) && RXVALIDSTRING(args[3])) /* first element to sort */
4886  {
4887  if (!string2size_t(args[3].strptr, &first))
4888  return INVALID_ROUTINE;
4889  if (first == 0)
4890  return INVALID_ROUTINE;
4891  } /* endif */
4892 
4893  if ((numargs >= 5) && RXVALIDSTRING(args[4])) /* last element to sort */
4894  {
4895  if (!string2size_t(args[4].strptr, &last))
4896  return INVALID_ROUTINE;
4897  if (last < first)
4898  return INVALID_ROUTINE;
4899  } /* endif */
4900 
4901  if ((numargs >= 6) && RXVALIDSTRING(args[5])) /* first column to sort */
4902  {
4903  if (!string2size_t(args[5].strptr, &firstCol))
4904  return INVALID_ROUTINE;
4905  firstCol--;
4906  } /* endif */
4907 
4908  if ((numargs == 7) && RXVALIDSTRING(args[6])) /* last column to sort */
4909  {
4910  if (!string2size_t(args[6].strptr, &lastCol))
4911  return INVALID_ROUTINE;
4912  lastCol--;
4913  if (lastCol < firstCol)
4914  return INVALID_ROUTINE;
4915 
4916  } /* endif */
4917 
4918  /* the sorting is done in the interpreter */
4919  if (!RexxStemSort(stemName, sortOrder, sortType, first, last, firstCol, lastCol)) {
4920  snprintf(retstr->strptr, DEFRXSTRING, "-1");
4921  retstr->strlength = 2;
4922  return INVALID_ROUTINE;
4923  }
4924 
4925  snprintf(retstr->strptr, DEFRXSTRING, "0");
4926  retstr->strlength = 1;
4927  return VALID_ROUTINE;
4928 }
4929 
4930 
4931 /*************************************************************************
4932 * Function: SysStemDelete *
4933 * *
4934 * Syntax: result = SysStemDelete(stem, startitem [,itemcount]) *
4935 * *
4936 * Params: stem - name of stem where item will be deleted *
4937 * startitem - index of item to delete *
4938 * itemcount - number of items to delete if more than 1 *
4939 * *
4940 * Return: 0 - delete was successful *
4941 * -1 - delete failed *
4942 *************************************************************************/
4943 
4944 RexxRoutine3(int, SysStemDelete, RexxStemObject, toStem, stringsize_t, start, OPTIONAL_stringsize_t, count)
4945 
4946 {
4947  if (argumentOmitted(3))
4948  {
4949  count = 1;
4950  }
4951 
4952  stringsize_t items;
4953 
4954  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
4955  if (temp == NULLOBJECT || !context->StringSize(temp, &items))
4956  {
4957  context->InvalidRoutine();
4958  return 0;
4959  }
4960 
4961  // make sure the deletion site is within the bounds
4962  if (start + count - 1 > items)
4963  {
4964  context->InvalidRoutine();
4965  return 0;
4966  }
4967 
4968  stringsize_t index;
4969  /* now copy the remaining indices up front */
4970  for ( index = start; index + count <= items; index++)
4971  {
4972  // copy from the old index to the new index
4973  RexxObjectPtr value = context->GetStemArrayElement(toStem, index + count);
4974  // is this a sparse array?
4975  if (value == NULLOBJECT)
4976  {
4977  // return this as a failure
4978  return -1;
4979  }
4980  context->SetStemArrayElement(toStem, index, value);
4981  }
4982 
4983  /* now delete the items at the end */
4984  for (index = items - count + 1; index <= items; index++)
4985  {
4986  context->DropStemArrayElement(toStem, index);
4987  }
4988 
4989  context->SetStemArrayElement(toStem, 0, context->StringSize(items - count));
4990  return 0;
4991 }
4992 
4993 
4994 /*************************************************************************
4995 * Function: SysStemInsert *
4996 * *
4997 * Syntax: result = SysStemInsert(stem, position, value) *
4998 * *
4999 * Params: stem - name of stem where item will be inserted *
5000 * position - index where new item will be inserted *
5001 * value - new item value *
5002 * *
5003 * Return: 0 - insert was successful *
5004 * -1 - insert failed *
5005 *************************************************************************/
5006 
5007 RexxRoutine3(int, SysStemInsert, RexxStemObject, toStem, stringsize_t, position, RexxObjectPtr, newValue)
5008 {
5009  stringsize_t count;
5010 
5011  RexxObjectPtr temp = context->GetStemArrayElement(toStem, 0);
5012  if (temp == NULLOBJECT || !context->StringSize(temp, &count))
5013  {
5014  context->InvalidRoutine();
5015  return 0;
5016  }
5017 
5018  /* check wether new position is within limits */
5019  if (position == 0 || (position > count + 1))
5020  {
5021  context->InvalidRoutine();
5022  return 0;
5023  }
5024 
5025  for (size_t index = count; index >= position; index--)
5026  {
5027  // copy from the old index to the new index
5028  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5029  // is this a sparse array?
5030  if (value == NULLOBJECT)
5031  {
5032  // return this as a failure
5033  return -1;
5034  }
5035  context->SetStemArrayElement(toStem, index + 1, value);
5036  }
5037 
5038  // now set the new value and increase the count at stem.0
5039  context->SetStemArrayElement(toStem, position, newValue);
5040  context->SetStemArrayElement(toStem, 0, context->WholeNumber(count + 1));
5041  return 0;
5042 }
5043 
5044 
5045 /*************************************************************************
5046 * Function: SysStemCopy *
5047 * *
5048 * Syntax: result = SysStemCopy(fromstem, tostem, from, to, count *
5049 * [,insert]) *
5050 * *
5051 * Params: fromstem - name of source stem *
5052 * tostem - - name of target stem *
5053 * from - first index in source stem to copy *
5054 * to - position where items are copied/inserted in target stem*
5055 * count - number of items to copy/insert *
5056 * insert - 'I' to indicate insert instead of 'O' overwrite *
5057 * *
5058 * Return: 0 - stem copy was successful *
5059 * -1 - stem copy failed *
5060 *************************************************************************/
5061 
5062 RexxRoutine6(int, SysStemCopy, RexxStemObject, fromStem, RexxStemObject, toStem,
5063  OPTIONAL_stringsize_t, from, OPTIONAL_stringsize_t, to, OPTIONAL_stringsize_t, count,
5064  OPTIONAL_CSTRING, option)
5065 {
5066  bool inserting = false;
5067 
5068  /* get copy type */
5069  if (option != NULL)
5070  {
5071  switch (*option)
5072  {
5073  case 'I':
5074  case 'i':
5075  inserting = true;
5076  break;
5077  case 'O':
5078  case 'o':
5079  inserting = false;
5080  break;
5081  default:
5082  {
5083  context->InvalidRoutine();
5084  return 0;
5085  }
5086  }
5087  }
5088 
5089  stringsize_t fromCount;
5090 
5091  RexxObjectPtr temp = context->GetStemArrayElement(fromStem, 0);
5092  if (temp == NULLOBJECT || !context->StringSize(temp, &fromCount))
5093  {
5094  context->InvalidRoutine();
5095  return 0;
5096  }
5097 
5098  // default from location is the first element
5099  if (argumentOmitted(3))
5100  {
5101  from = 1;
5102  }
5103 
5104  if (argumentOmitted(4))
5105  {
5106  to = 1;
5107  }
5108 
5109  // was a count explicitly specified?
5110  if (argumentExists(5))
5111  {
5112  // this must be in range
5113  if ((count > (fromCount - from + 1)) || (fromCount == 0))
5114  {
5115  context->InvalidRoutine();
5116  return 0;
5117  }
5118  }
5119  else
5120  {
5121  // default is to copy everything from the starting position.
5122  count = fromCount - from + 1;
5123  }
5124 
5125  stringsize_t toCount = 0;
5126  // but if it is set, then use that value
5127  temp = context->GetStemArrayElement(toStem, 0);
5128  if (temp != NULLOBJECT && !context->StringSize(temp, &toCount))
5129  {
5130  context->InvalidRoutine();
5131  return 0;
5132  }
5133 
5134  // copying out of range? Error
5135  if (to > toCount + 1)
5136  {
5137  context->InvalidRoutine();
5138  return 0;
5139  }
5140 
5141  if (inserting)
5142  {
5143  /* if we are about to insert the items we have to make room */
5144  for (size_t index = toCount; index >= to; index--)
5145  {
5146  // copy from the old index to the new index
5147  RexxObjectPtr value = context->GetStemArrayElement(toStem, index);
5148  // is this a sparse array?
5149  if (value == NULLOBJECT)
5150  {
5151  // return this as a failure
5152  return -1;
5153  }
5154  context->SetStemArrayElement(toStem, index + count, value);
5155  }
5156 
5157 
5158  // set the new count value in the target
5159  toCount += count;
5160  context->SetStemArrayElement(toStem, 0, context->StringSize(toCount));
5161  }
5162  /* now do the actual copying from the source to target */
5163  for (size_t index = 0; index < count; index++)
5164  {
5165  // just retrieve and copy
5166  RexxObjectPtr value = context->GetStemArrayElement(fromStem, from + index);
5167  // is this a sparse array?
5168  if (value == NULLOBJECT)
5169  {
5170  // return this as a failure
5171  return -1;
5172  }
5173  context->SetStemArrayElement(toStem, to + index, value);
5174  }
5175 
5176  // do we need to update the size?
5177  if (to + count - 1 > toCount)
5178  {
5179  context->SetStemArrayElement(toStem, 0, context->StringSize(to + count - 1));
5180  }
5181  return 0;
5182 }
5183 
5184 
5185 /**************************************************************************
5186 * Function: SysQueryProcess *
5187 * *
5188 * Params: "PID" - (default) returns current process ID *
5189 * NEW: "PPID" - returns parent of current process ID *
5190 * NEW: "PGID" - returns group ID of current process *
5191 * NO "TID" - returns current thread ID *
5192 * YES "PPRIO" - returns current process priority *
5193 * NO "TPRIO" - returns current thread priority *
5194 * YES "PTIME" - returns current process times *
5195 * NO "TTIME" - returns current thread times *
5196 * NEW: "PMEM" - returns current process max memory size RSS *
5197 * NEW: "PSWAPS" - returns current process number of swaps out *
5198 * NEW: "PRCVDSIG" - returns current process received signals *
5199 ***************************************************************************/
5200 
5201 size_t RexxEntry SysQueryProcess(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5202 {
5203 
5204  unsigned int uiUsedCPUTime = 0;
5205  unsigned int uiUsedCPUmsec = 0;
5206  unsigned int uiUsedHours = 0;
5207  unsigned int uiUsedMinutes = 0;
5208  unsigned int uiUsedSeconds = 0;
5209  char timebuf[40];
5210  int iRc = 0;
5211  struct rusage struResUse;
5212  retstr->strlength = 0; /* set return value */
5213 
5214  if (numargs > 1) /* none or one argument accepted */
5215  return INVALID_ROUTINE; /* raise error condition */
5216 
5217  if ((numargs == 0) || (!strcasecmp(args[0].strptr, "PID")))
5218  {
5219  snprintf(retstr->strptr, DEFRXSTRING, "%d", getpid());
5220  retstr->strlength = strlen(retstr->strptr);
5221  return VALID_ROUTINE; /* no error on call */
5222  }
5223  else
5224  if (!strcasecmp(args[0].strptr, "PPID"))
5225  {
5226  snprintf(retstr->strptr, DEFRXSTRING, "%d", getppid());
5227  retstr->strlength = strlen(retstr->strptr);
5228  return VALID_ROUTINE; /* no error on call */
5229  }
5230  else
5231  if (!strcasecmp(args[0].strptr, "PGID"))
5232  {
5233  snprintf(retstr->strptr, DEFRXSTRING, "%d", getpgid(getpid()));
5234  retstr->strlength = strlen(retstr->strptr);
5235  return VALID_ROUTINE; /* no error on call */
5236  }
5237  else
5238  if (!strcasecmp(args[0].strptr, "PPRIO"))
5239  {
5240  snprintf(retstr->strptr, DEFRXSTRING, "%d", getpriority(PRIO_PROCESS, 0));
5241  retstr->strlength = strlen(retstr->strptr);
5242  return VALID_ROUTINE; /* no error on call */
5243  }
5244 
5245  /* ----------------------------------------------------------------- */
5246  /* Get process usage data and keep calls together at the end of */
5247  /* the function SysQueryProcess. */
5248  iRc = getrusage ( RUSAGE_SELF, &struResUse);
5249  if ( iRc )
5250  {
5251  snprintf(retstr->strptr, DEFRXSTRING, " System error; errno = %d", errno);
5252  retstr->strlength = strlen(retstr->strptr);
5253  return VALID_ROUTINE; /* no error on call */
5254  }
5255  if (!strcasecmp(args[0].strptr, "PTIME")) /* Calculate the used CPU time*/
5256  {
5257  uiUsedCPUmsec = (unsigned int) struResUse.ru_utime.tv_usec/1000;
5258  uiUsedCPUmsec += (unsigned int) struResUse.ru_stime.tv_usec/1000;
5259  if (uiUsedCPUmsec >= 1000 )
5260  {
5261  uiUsedCPUTime = uiUsedCPUmsec / 1000;
5262  uiUsedCPUmsec = uiUsedCPUmsec % 1000;
5263  }
5264  uiUsedCPUTime += (unsigned int) struResUse.ru_utime.tv_sec;
5265  uiUsedCPUTime += (unsigned int) struResUse.ru_stime.tv_sec;
5266  uiUsedHours = uiUsedCPUTime / 3600;
5267  uiUsedMinutes = uiUsedCPUTime / 60;
5268  if (uiUsedMinutes >= 60 ) uiUsedMinutes = uiUsedMinutes % 60;
5269  if (uiUsedCPUTime >= 60 ) uiUsedSeconds = uiUsedCPUTime % 60;
5270  else uiUsedSeconds = uiUsedCPUTime;
5271 
5272  snprintf(retstr->strptr, DEFRXSTRING, "CPU_Time Summary: %2d:%.2d:%.2d:%.3d Kernel:",
5273  uiUsedHours, uiUsedMinutes, uiUsedSeconds, uiUsedCPUmsec );
5274 
5275  uiUsedCPUmsec = (unsigned int) struResUse.ru_stime.tv_usec/1000;
5276  uiUsedCPUTime = (unsigned int) struResUse.ru_stime.tv_sec;
5277  uiUsedHours = uiUsedCPUTime / 3600;
5278  uiUsedMinutes = uiUsedCPUTime / 60;
5279  if (uiUsedMinutes >= 60 ) uiUsedMinutes = uiUsedMinutes % 60;
5280  if (uiUsedCPUTime >= 60 ) uiUsedSeconds = uiUsedCPUTime % 60;
5281  else uiUsedSeconds = uiUsedCPUTime;
5282 
5283  snprintf(timebuf, sizeof timebuf, " %2d:%.2d:%.2d:%.3d User:", uiUsedHours,
5284  uiUsedMinutes, uiUsedSeconds, uiUsedCPUmsec );
5285  strcat(retstr->strptr, timebuf);
5286 
5287  uiUsedCPUmsec = (unsigned int) struResUse.ru_utime.tv_usec/1000;
5288  uiUsedCPUTime = (unsigned int) struResUse.ru_utime.tv_sec;
5289  uiUsedHours = uiUsedCPUTime / 3600;
5290  uiUsedMinutes = uiUsedCPUTime / 60;
5291  if (uiUsedMinutes >= 60 ) uiUsedMinutes = uiUsedMinutes % 60;
5292  if (uiUsedCPUTime >= 60 ) uiUsedSeconds = uiUsedCPUTime % 60;
5293  else uiUsedSeconds = uiUsedCPUTime;
5294 
5295  snprintf(timebuf, sizeof timebuf, " %2d:%.2d:%.2d:%.3d", uiUsedHours,
5296  uiUsedMinutes, uiUsedSeconds, uiUsedCPUmsec );
5297  strcat(retstr->strptr, timebuf);
5298 
5299  retstr->strlength = strlen(retstr->strptr);
5300  return VALID_ROUTINE; /* no error on call */
5301  }
5302  else
5303  if (!strcasecmp(args[0].strptr, "PMEM")) /* Show max memory RSS used */
5304  {
5305  snprintf(retstr->strptr, DEFRXSTRING, "Max_Memory_RSS: %ld", struResUse.ru_maxrss);
5306  retstr->strlength = strlen(retstr->strptr);
5307  return VALID_ROUTINE; /* no error on call */
5308  }
5309  else
5310  if (!strcasecmp(args[0].strptr, "PSWAPS")) /* Memory has been swapped */
5311  {
5312  snprintf(retstr->strptr, DEFRXSTRING, "Memory_swaps: %ld", struResUse.ru_nswap);
5313  retstr->strlength = strlen(retstr->strptr);
5314  return VALID_ROUTINE; /* no error on call */
5315  }
5316  else
5317  if (!strcasecmp(args[0].strptr, "PRCVDSIG")) /* Process received signals*/
5318  {
5319  snprintf(retstr->strptr, DEFRXSTRING, "Received_signals: %ld", struResUse.ru_nsignals);
5320  retstr->strlength = strlen(retstr->strptr);
5321  return VALID_ROUTINE; /* no error on call */
5322  }
5323 
5324  return INVALID_ROUTINE; /* good completion */
5325 }
5326 
5327 /*************************************************************************
5328 * Function: SysGetErrortext *
5329 * *
5330 * Syntax: call SysGetErrortext errnumber *
5331 * *
5332 * Params: errnumber - error number to be described *
5333 * *
5334 * Return: Description or empty string *
5335 *************************************************************************/
5336 
5337 size_t RexxEntry SysGetErrortext(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5338 {
5339 
5340  int errnum = 0;
5341  char *errmsg = NULL;
5342 
5343  if (numargs != 1)
5344  /* If no args, then its an */
5345  /* incorrect call */
5346  return INVALID_ROUTINE;
5347 
5348  errnum = atoi(args[0].strptr);
5349  errmsg = strerror( errnum );
5350  if (errmsg == NULL )
5351  retstr->strptr[0] = '\0';
5352  else
5353  {
5354  if (strlen(errmsg) >= retstr->strlength)
5355  retstr->strptr = (char *) malloc(strlen(errmsg + 1));
5356  strcpy(retstr->strptr,errmsg);
5357  }
5358  retstr->strlength = strlen(retstr->strptr);
5359 
5360  return VALID_ROUTINE;
5361 }
5362 
5363 
5364 /*************************************************************************
5365 * Function: SysUtilVersion *
5366 * *
5367 * Syntax: Say SysUtilVersion *
5368 * *
5369 * Return: REXXUTIL.DLL Version *
5370 *************************************************************************/
5371 
5372 size_t RexxEntry SysUtilVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
5373 {
5374  if (numargs != 0) /* validate arg count */
5375  return INVALID_ROUTINE;
5376  /* format into the buffer */
5377  snprintf(retstr->strptr, DEFRXSTRING, "%d.%d.%d", ORX_VER, ORX_REL, ORX_MOD);
5378  retstr->strlength = strlen(retstr->strptr);
5379 
5380  return VALID_ROUTINE;
5381 }
5382 
5383 
5384 /*************************************************************************
5385 * Function: SysFileCopy *
5386 * *
5387 * Syntax: call SysFileCopy FROMfile TOfile *
5388 * *
5389 * Params: FROMfile - file to be copied. *
5390 * TOfile - target file of copy operation. *
5391 * *
5392 * Return: Return 0 if no error, or error code. *
5393 *************************************************************************/
5394 
5396 {
5397 public:
5398  AutoClose() : value(-1) {};
5399  AutoClose(int fd) : value(fd) {}
5400  ~AutoClose() { close(false); }
5401  AutoClose& operator=(int fd) { close(false); value=fd; return *this; }
5402  operator int() const { return value; }
5403  int operator==(int fd) { return value == fd; }
5404  int close(bool returnError=true);
5405 private:
5406  int value; // >= 0 if opened
5407 };
5408 
5409 // Returns 0 if no error, -1 otherwise (in this case, errno contains the error code)
5410 int AutoClose::close(bool returnError)
5411 {
5412  int closeStatus = 0;
5413  if (returnError)
5414  {
5415  if (value >= 0) closeStatus = ::close(value);
5416  }
5417  else
5418  {
5419  if (value >= 0)
5420  {
5421  int backup = errno;
5422  ::close(value);
5423  errno = backup;
5424  }
5425  }
5426  value = -1;
5427  return closeStatus;
5428 }
5429 
5431 {
5432 public:
5433  AutoFree() : value(NULL) {};
5434  AutoFree(char *p) : value(p) {}
5435  ~AutoFree() { if (value != NULL) free(value); value=NULL; }
5436  AutoFree& operator=(char *p) { if (value != NULL) free(value); value=p; return *this; }
5437  operator char *() const { return value; }
5438  int operator==(char *p) { return value == p; }
5439 private:
5440  char *value;
5441 };
5442 
5443 /*
5444  Remember : realpath will fail if the file does not exist.
5445  See normalizePathName in SysFileSystem.cpp.
5446  */
5447 bool SamePaths(CSTRING path1, CSTRING path2)
5448 {
5449  char actualpath1[PATH_MAX+1];
5450  char actualpath2[PATH_MAX+1];
5451  if (realpath(path1, actualpath1) == NULL) return false;
5452  if (realpath(path2, actualpath2) == NULL) return false;
5453  return strcmp(actualpath1, actualpath2) == 0;
5454 }
5455 
5456 /*
5457 Returns a new filename whose directory path is the same as filename's directory
5458 and that is not the same as the name of an existing file in this directory.
5459 Used to temporarily rename a file in place (i.e. in its directory).
5460 This new filename must be freed when no longer needed.
5461 */
5462 char *TemporaryFilename(CSTRING filename, int &errInfo)
5463 {
5464  errInfo = 0;
5465  AutoFree filenameCopy = strdup(filename); // dirname wants a writable string
5466  if (filenameCopy != NULL)
5467  {
5468  AutoFree directory = strdup(dirname(filenameCopy)); // Dup because not clear from standard if filenameCopy can be freed just after the call
5469  if (directory != NULL)
5470  {
5471  char *newFilename = tempnam(directory, NULL);
5472  if (newFilename != NULL) return newFilename;
5473  }
5474  }
5475  errInfo = errno;
5476  return NULL;
5477 }
5478 
5479 /*
5480 The symbolic links are dereferenced :
5481 - If the source file is a symbolic link, the actual file copied is the target of the symbolic link.
5482 - If the destination file already exists and is a symbolic link, the target of the symbolic link is overwritten by the source file.
5483 If the destination file does not exist then it is created with the access mode of the source file (if possible).
5484 */
5485 int CopyFile_DereferenceSymbolicLinks(CSTRING fromFile, CSTRING toFile, bool preserveTimestamps, bool preserveMode, bool *timestampsPreserved=NULL, bool *modePreserved=NULL)
5486 {
5487  // initialize output arguments
5488  if (timestampsPreserved != NULL) *timestampsPreserved = false;
5489  if (modePreserved != NULL) *modePreserved = false;
5490 
5491  if (SamePaths(fromFile, toFile)) return EEXIST; // did not find a better error code to return
5492 
5493  struct stat64 fromStat;
5494  if (stat64(fromFile, &fromStat) == -1) return errno;
5495  AutoClose fromHandle = open64(fromFile, O_RDONLY);
5496  if (fromHandle == -1) return errno;
5497 
5498  struct stat64 toStat;
5499  bool toFileCreated = (stat64(toFile, &toStat) == -1);
5500  AutoClose toHandle = open64(toFile, O_WRONLY | O_CREAT | O_TRUNC, 0666); // default access mode for the moment (like fopen)
5501  if (toHandle == -1) return errno;
5502 
5503  char buffer[IBUF_LEN];
5504  while(1)
5505  {
5506  int count = read(fromHandle, &buffer, IBUF_LEN);
5507  if (count == -1) return errno;
5508  if (count == 0) break; // EOF
5509  if (write(toHandle, buffer, count) == -1) return errno;
5510  }
5511 
5512  if (fromHandle.close() == -1) return errno;
5513  if (toHandle.close() == -1) return errno;
5514 
5515  if (preserveTimestamps)
5516  {
5517  struct utimbuf timebuf;
5518  timebuf.actime = fromStat.st_atime;
5519  timebuf.modtime = fromStat.st_mtime;
5520  if (utime(toFile, &timebuf) == 0)
5521  {
5522  if (timestampsPreserved != NULL) *timestampsPreserved = true;
5523  }
5524  }
5525 
5526  if (toFileCreated || preserveMode)
5527  {
5528  if (chmod(toFile, fromStat.st_mode) == 0)
5529  {
5530  if (modePreserved != NULL) *modePreserved = true;
5531  }
5532  }
5533  return 0;
5534 }
5535 
5536 /*
5537 The symbolic links are not dereferenced :
5538 - If the source file is a symbolic link, the actual file copied is the symbolic link itself.
5539 - If the destination file already exists and is a symbolic link, the target of the symbolic link is not overwritten by the source file.
5540 */
5541 int CopyFile_DontDereferenceSymbolicLinks(CSTRING fromFile, CSTRING toFile, bool force, bool preserveTimestamps, bool preserveMode, bool *timestampsPreserved=NULL, bool *modePreserved=NULL)
5542 {
5543  // initialize output arguments
5544  if (timestampsPreserved != NULL) *timestampsPreserved = false;
5545  if (modePreserved != NULL) *modePreserved = false;
5546 
5547  if (SamePaths(fromFile, toFile)) return EEXIST; // did not find a better error code to return
5548 
5549  struct stat64 fromStat;
5550  if (lstat64(fromFile, &fromStat) == -1) return errno;
5551  bool fromFileIsSymbolicLink = S_ISLNK(fromStat.st_mode);
5552 
5553  struct stat64 toStat;
5554  bool toFileExists = (lstat64(toFile, &toStat) == 0);
5555  bool toFileIsSymbolicLink = (toFileExists && S_ISLNK(toStat.st_mode));
5556 
5557  AutoFree toFileNewname;
5558  if (toFileExists && (fromFileIsSymbolicLink || toFileIsSymbolicLink))
5559  {
5560  // Must remove toFile when fromFile is a symbolic link, to let copy the link.
5561  // Must remove toFile when toFile is a symbolic link, to not follow the link.
5562  // But do that safely : first rename toFile, then do the copy and finally remove the renamed toFile.
5563  if (force == false) return EEXIST; // Not allowed by caller to remove it
5564  int errInfo;
5565  toFileNewname = TemporaryFilename(toFile, errInfo);
5566  if (errInfo != 0) return errInfo;
5567  }
5568 
5569  if (fromFileIsSymbolicLink)
5570  {
5571  off_t pathSize = fromStat.st_size; // The length in bytes of the pathname contained in the symbolic link
5572  AutoFree pathBuffer = (char *)malloc(pathSize+1);
5573  if (pathBuffer == NULL) return errno;
5574  if (readlink(fromFile, pathBuffer, pathSize) == -1) return errno;
5575  pathBuffer[pathSize] = '\0';
5576  if (toFileNewname != NULL && rename(toFile, toFileNewname) == -1) return errno;
5577  if (symlink(pathBuffer, toFile) == -1)
5578  {
5579  int errInfo = errno;
5580  // Undo the renaming
5581  if (toFileNewname != NULL) rename(toFileNewname, toFile);
5582  return errInfo;
5583  }
5584  // Note : there is no API to preserve the timestamps and mode of a symbolic link
5585  }
5586  else
5587  {
5588  if (toFileNewname != NULL && rename(toFile, toFileNewname) == -1) return errno;
5589  int errInfo = CopyFile_DereferenceSymbolicLinks(fromFile, toFile, preserveTimestamps, preserveMode, timestampsPreserved, modePreserved);
5590  if (errInfo != 0)
5591  {
5592  // Undo the renaming
5593  if (toFileNewname != NULL) rename(toFileNewname, toFile);
5594  return errInfo;
5595  }
5596  }
5597  // The copy has been done, now can remove the backup
5598  if (toFileNewname != NULL) unlink(toFileNewname);
5599  return 0;
5600 
5601 }
5602 
5603 RexxRoutine2(int, SysFileCopy, CSTRING, fromFile, CSTRING, toFile)
5604 {
5605  return CopyFile_DereferenceSymbolicLinks(fromFile, toFile, true, false);
5606  // Note : no error returned if timestamps not preserved
5607 }
5608 
5609 /*************************************************************************
5610 * Function: SysFileMove *
5611 * *
5612 * Syntax: call SysFileMove FROMfile TOfile *
5613 * *
5614 * Params: FROMfile - file to be moved. *
5615 * TOfile - target file of move operation. *
5616 * *
5617 * Return: Return code from MoveFile() function. *
5618 *************************************************************************/
5619 
5620 int MoveFile(CSTRING fromFile, CSTRING toFile)
5621 {
5622  // rename does not return an error if both files resolve to the same file
5623  // but we want to return an error in this case, to follow the behavior of mv
5624  if (SamePaths(fromFile, toFile)) return EEXIST; // did not find a better error code to return
5625  if (rename(fromFile, toFile) == 0) return 0; // move done
5626  if (errno != EXDEV) return errno; // move ko, no fallbak
5627 
5628  // Before trying the fallback copy+unlink, ensure that we can unlink fromFile.
5629  // If we can rename it in place, then we can unlink it.
5630  int errInfo;
5631  AutoFree fromFileNewname = TemporaryFilename(fromFile, errInfo);
5632  if (errInfo != 0) return errInfo;
5633  if (rename(fromFile, fromFileNewname) == -1) return errno;
5634  // Good, here we know we can unlink fromFile, undo the rename
5635  if (rename(fromFileNewname, fromFile) == -1) return errno; // should not happen, but...
5636 
5637  // The files are on different file systems and the implementation does not support that.
5638  // Try to copy then unlink
5639  int copyStatus = CopyFile_DontDereferenceSymbolicLinks(fromFile, toFile, false, true, true); // 3rd arg=false : dont't force (good choice ?)
5640  if (copyStatus != 0) return copyStatus; // fallback ko
5641  // Note : no error returned if timestamps or mode not preserved
5642 
5643  // copy to is ok, now unlink from
5644  // in case of error, don't remove toFile, it's a bad idea !
5645  return unlink(fromFile);
5646 }
5647 
5648 RexxRoutine2(int, SysFileMove, CSTRING, fromFile, CSTRING, toFile)
5649 {
5650  return MoveFile(fromFile, toFile);
5651 }
5652 
5653 /*************************************************************************
5654 * Function: SysIsFile *
5655 * *
5656 * Syntax: call SysIsFile file *
5657 * *
5658 * Params: file - file to check existance of. *
5659 * *
5660 * Return: Logical. *
5661 *************************************************************************/
5662 
5663 RexxRoutine1(logical_t, SysIsFile, CSTRING, filename)
5664 {
5665  struct stat64 finfo; /* return buf for the finfo */
5666 
5667  int rc = stat64(filename, &finfo); /* read the info about it */
5668  // check the flag settings for a regular file
5669  return rc == 0 && (S_ISREG(finfo.st_mode) || S_ISBLK(finfo.st_mode));
5670 }
5671 
5672 /*************************************************************************
5673 * Function: SysIsFileDirectory *
5674 * *
5675 * Syntax: call SysIsFileDirectory dir *
5676 * *
5677 * Params: dir - dir to check existance of. *
5678 * *
5679 * Return: Logical. *
5680 *************************************************************************/
5681 
5682 RexxRoutine1(logical_t, SysIsFileDirectory, CSTRING, filename)
5683 {
5684  struct stat64 finfo; /* return buf for the finfo */
5685 
5686  int rc = stat64(filename, &finfo); /* read the info about it */
5687  return rc == 0 && S_ISDIR(finfo.st_mode);
5688 }
5689 
5690 /*************************************************************************
5691 * Function: SysIsFileLink *
5692 * *
5693 * Syntax: call SysIsFileLink file *
5694 * *
5695 * Params: file - file to check if it is a Link (Alias). *
5696 * *
5697 * Return: Logical. *
5698 *************************************************************************/
5699 
5700 RexxRoutine1(logical_t, SysIsFileLink, CSTRING, filename)
5701 {
5702  struct stat64 finfo; /* return buf for the finfo */
5703 
5704  int rc = lstat64(filename, &finfo); /* read the info about it */
5705  return rc == 0 && S_ISLNK(finfo.st_mode);
5706 }
5707 
5708 /*************************************************************************
5709 * Function: SysFileExists *
5710 * *
5711 * Syntax: call SysFileExists *
5712 * *
5713 * Params: dir - file or dir to check existance of *
5714 * *
5715 * Return: Logical. *
5716 *************************************************************************/
5717 
5718 RexxRoutine1(logical_t, SysFileExists, CSTRING, filename)
5719 {
5720  struct stat64 finfo; /* return buf for the finfo */
5721 
5722  return stat64(filename, &finfo) == 0; /* read the info about it */
5723 }
5724 
5725 
5726 #ifdef XX__cplusplus
5727 }
5728 #endif
5729 
5730 
5731 // now build the actual entry list
5733 {
5734  REXX_TYPED_ROUTINE(SysCreateMutexSem, SysCreateMutexSem),
5735  REXX_TYPED_ROUTINE(SysOpenMutexSem, SysOpenMutexSem),
5736  REXX_TYPED_ROUTINE(SysCloseMutexSem, SysCloseMutexSem),
5737  REXX_TYPED_ROUTINE(SysRequestMutexSem, SysRequestMutexSem),
5738  REXX_TYPED_ROUTINE(SysReleaseMutexSem, SysReleaseMutexSem),
5739  REXX_TYPED_ROUTINE(SysCreateEventSem, SysCreateEventSem),
5740  REXX_TYPED_ROUTINE(SysOpenEventSem, SysOpenEventSem),
5741  REXX_TYPED_ROUTINE(SysCloseEventSem, SysCloseEventSem),
5742  REXX_TYPED_ROUTINE(SysResetEventSem, SysResetEventSem),
5743  REXX_TYPED_ROUTINE(SysPostEventSem, SysPostEventSem),
5744  REXX_TYPED_ROUTINE(SysWaitEventSem, SysWaitEventSem),
5746  REXX_TYPED_ROUTINE(SysAddRexxMacro, SysAddRexxMacro),
5747  REXX_TYPED_ROUTINE(SysDropRexxMacro, SysDropRexxMacro),
5748  REXX_TYPED_ROUTINE(SysReorderRexxMacro, SysReorderRexxMacro),
5749  REXX_TYPED_ROUTINE(SysQueryRexxMacro, SysQueryRexxMacro),
5750  REXX_TYPED_ROUTINE(SysClearRexxMacroSpace, SysClearRexxMacroSpace),
5751  REXX_TYPED_ROUTINE(SysLoadRexxMacroSpace, SysLoadRexxMacroSpace),
5752  REXX_TYPED_ROUTINE(SysSaveRexxMacroSpace, SysSaveRexxMacroSpace),
5753 #ifdef AIX_DISABLED
5754  REXX_CLASSIC_ROUTINE(SysAddFuncPkg, SysAddFuncPkg),
5755  REXX_CLASSIC_ROUTINE(SysAddCmdPkg, SysAddCmdPkg),
5756  REXX_CLASSIC_ROUTINE(SysDropFuncPkg, SysDropFuncPkg),
5757  REXX_CLASSIC_ROUTINE(SysDropCmdPkg, SysDropCmdPkg),
5758 #endif
5766  REXX_TYPED_ROUTINE(SysFileTree, SysFileTree),
5772 #ifdef LINUX
5773  REXX_CLASSIC_ROUTINE(SysLinVer, SysLinVer),
5774 #endif
5778  REXX_TYPED_ROUTINE(SysSleep, SysSleep),
5784  REXX_TYPED_ROUTINE(SysStemDelete, SysStemDelete),
5785  REXX_TYPED_ROUTINE(SysStemInsert, SysStemInsert),
5786  REXX_TYPED_ROUTINE(SysStemCopy, SysStemCopy),
5790  REXX_TYPED_ROUTINE(SysFileCopy, SysFileCopy),
5791  REXX_TYPED_ROUTINE(SysFileMove, SysFileMove),
5792  REXX_TYPED_ROUTINE(SysIsFile, SysIsFile),
5793  REXX_TYPED_ROUTINE(SysIsFileDirectory, SysIsFileDirectory),
5794  REXX_TYPED_ROUTINE(SysIsFileLink, SysIsFileLink),
5795  REXX_TYPED_ROUTINE(SysFileExists, SysFileExists),
5797 };
5798 
5800 {
5802  REXX_INTERPRETER_4_0_0, // anything after 4.0.0 will work
5803  "REXXUTIL", // name of the package
5804  "4.0", // package information
5805  NULL, // no load/unload functions
5806  NULL,
5807  rexxutil_routines, // the exported functions
5808  NULL // no methods in this package
5809 };
5810 
5811 // package loading stub.
#define MAXNAME
RexxReturnCode RexxEntry RexxVariablePool(PSHVBLOCK pshvblock)
int operator==(int fd)
int close(bool returnError=true)
AutoClose(int fd)
AutoClose & operator=(int fd)
AutoFree & operator=(char *p)
AutoFree(char *p)
int operator==(char *p)
int type
Definition: cmdparse.cpp:1888
#define argumentExists(i)
Definition: oorexxapi.h:3777
#define REXX_INTERPRETER_4_0_0
Definition: oorexxapi.h:216
#define REXX_CLASSIC_ROUTINE(n, e)
Definition: oorexxapi.h:192
#define argumentOmitted(i)
Definition: oorexxapi.h:3778
#define REXX_LAST_ROUTINE()
Definition: oorexxapi.h:193
#define REXX_TYPED_ROUTINE(n, e)
Definition: oorexxapi.h:191
#define STANDARD_PACKAGE_HEADER
Definition: oorexxapi.h:230
#define Rexx_Error_Invalid_argument_range
Definition: oorexxerrors.h:469
#define Rexx_Error_Incorrect_call_user_defined
Definition: oorexxerrors.h:397
#define Rexx_Error_Invalid_argument_number
Definition: oorexxerrors.h:464
#define Rexx_Error_System_service_user_defined
Definition: oorexxerrors.h:453
#define Rexx_Error_Incorrect_call_null
Definition: oorexxerrors.h:382
RexxReturnCode REXXENTRY RexxDeregisterFunction(CONSTANT_STRING)
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 RexxRegisterFunctionDll(CONSTANT_STRING, CONSTANT_STRING, CONSTANT_STRING)
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)
#define RXNULLSTRING(r)
Definition: rexx.h:177
int RexxReturnCode
Definition: rexx.h:73
RexxReturnCode REXXENTRY RexxClearMacroSpace(void)
size_t stringsize_t
Definition: rexx.h:228
#define RexxEntry
Definition: rexx.h:235
RexxReturnCode REXXENTRY RexxDropMacro(CONSTANT_STRING)
struct _RexxStemObject * RexxStemObject
Definition: rexx.h:139
#define RXMACRO_SEARCH_BEFORE
Definition: rexxapidefs.h:220
#define RXSHV_NEXTV
Definition: rexxapidefs.h:103
#define RXSHV_BADN
Definition: rexxapidefs.h:116
#define RXMACRO_SEARCH_AFTER
Definition: rexxapidefs.h:221
#define RXSHV_SET
Definition: rexxapidefs.h:97
#define RXSHV_OK
Definition: rexxapidefs.h:112
#define RXSHV_LVAR
Definition: rexxapidefs.h:114
const char * strptr
Definition: rexx.h:163
size_t strlength
Definition: rexx.h:162
const char * scan
size_t strlength
Definition: rexx.h:157
char * strptr
Definition: rexx.h:158
Definition: oorexxapi.h:242
Definition: oorexxapi.h:177
size_t shvvaluelen
Definition: rexx.h:209
CONSTANT_RXSTRING shvname
Definition: rexx.h:206
unsigned char shvret
Definition: rexx.h:211
unsigned char shvcode
Definition: rexx.h:210
RXSTRING shvvalue
Definition: rexx.h:207
size_t shvnamelen
Definition: rexx.h:208
struct _SHVBLOCK * shvnext
Definition: rexx.h:205
struct _SORT_MEM * pNext
size_t ulRemaining
char * pNextBlock
size_t ulItems
RexxThreadContext * threadContext
Definition: oorexxapi.h:2200
sem_t * handle
char ibuf[IBUF_LEN]
char varname[MAX]
SHVBLOCK shvb
char stemname[MAX]
char fileTime[FILETIME_BUF_LEN]
char fNameSpec[FNAMESPEC_BUF_LEN]
size_t nFoundFile
size_t nFoundFileLine
char foundFile[FOUNDFILE_BUF_LEN]
RexxStemObject files
char * dFoundFile
char * dFNameSpec
char * dFoundFileLine
char foundFileLine[FOUNDFILELINE_BUF_LEN]
char fileAttr[FILEATTR_BUF_LEN]
size_t nFNameSpec
struct semid_ds * buf
unsigned short * array
#define DEFRXSTRING
#define MAX_DIGITS
#define stty(a, b)
#define MAX_READ
#define SEM_WAIT_PERIOD
size_t RexxEntry SysSearchPath(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
const char * mystrstr(const char *haystack, const char *needle, size_t hlen, size_t nlen, bool sensitive)
#define MAX_LINE_LEN
#define CH_NL
size_t RexxEntry SysVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
int OpenFile(const char *file, GetFileData *filedata)
struct termios in_orig
int CopyFile_DereferenceSymbolicLinks(CSTRING fromFile, CSTRING toFile, bool preserveTimestamps, bool preserveMode, bool *timestampsPreserved=NULL, bool *modePreserved=NULL)
bool string2size_t(const char *string, size_t *number)
#define BUILDRXSTRING(t, s)
#define CH_EOF
RexxPackageEntry rexxutil_package_entry
OOREXX_GET_PACKAGE(rexxutil)
size_t RexxEntry SysCreatePipe(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
static void stringTooLongException(RexxThreadContext *c, CSTRING funcName, size_t pos, size_t len, size_t realLen)
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
#define LONG_TIME
size_t RexxEntry SysSetPriority(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysWait(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define MAX
void uninitTreeData(RXTREEDATA *treeData)
size_t RexxEntry SysGetMessage(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
struct _GetFileData GetFileData
static bool getFileNameSegment(RexxCallContext *c, char *fileSpec, RXTREEDATA *treeData, int *lastSlashPos)
bool string2int(const char *string, int *number)
#define DO_DIRS
struct RxSemData RXSEMDATA
#define FNAMESPEC_BUF_LEN
RexxRoutineEntry rexxutil_routines[]
char typeOfEntry(mode_t m)
size_t RexxEntry SysDumpVariables(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
bool linFindNextFile(RexxCallContext *c, const char *fileSpec, const char *path, DIR *dir_handle, struct stat *finfo, char **d_name, bool caseless)
#define VALID_ROUTINE
int get_next_path(char **ppenv, char *path_buf)
struct RxTreeData RXTREEDATA
int MoveFile(CSTRING fromFile, CSTRING toFile)
#define gtty(a, b)
static void badSFTOptsException(RexxThreadContext *c, size_t pos, CSTRING actual)
#define ERROR_NOMEM
size_t RexxEntry SysGetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
static bool recursiveFindFile(RexxCallContext *c, const char *path, RXTREEDATA *treeData, uint32_t options)
void strupr(char *string)
int getkey(char *ret, bool echo)
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)
bool linFindNextDir(RexxCallContext *c, const char *fileSpec, const char *path, DIR *dir_handle, struct stat *finfo, char **d_name, bool caseless)
#define FOUNDFILE_BUF_LEN
#define SORT_ASCENDING
void CloseFile(GetFileData *filedata)
#define NO_UTIL_ERROR
#define CH_CR
#define SORT_CASEIGNORE
size_t RexxEntry SysGetErrortext(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define DO_FILES
struct _SORT_MEM * PSORTMEM
struct RxStemData RXSTEMDATA
RexxRoutine0(int, SysClearRexxMacroSpace)
size_t RexxEntry SysDropFuncs(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define FOUNDFILELINE_BUF_LEN
static bool goodOpts(RexxCallContext *c, CSTRING opts, uint32_t *pOpts)
#define SORT_CASESENSITIVE
#define EDITABLE_TIME
#define ENVIRONMENT_ONLY
void nullStringException(RexxThreadContext *c, CSTRING fName, size_t pos)
size_t RexxEntry SysMkDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
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 SysUtilVersion(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry RexxStemSort(const char *stemname, int order, int type, size_t start, size_t end, size_t firstcol, size_t lastcol)
int CopyFile_DontDereferenceSymbolicLinks(CSTRING fromFile, CSTRING toFile, bool force, bool preserveTimestamps, bool preserveMode, bool *timestampsPreserved=NULL, bool *modePreserved=NULL)
size_t RexxEntry SysQueryProcess(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
char * TemporaryFilename(CSTRING filename, int &errInfo)
size_t RexxEntry SysFileSearch(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define REXXMESSAGEFILE
#define FILETIME_BUF_LEN
int GetLine(char *line, size_t size, GetFileData *filedata)
#define RECURSE
struct _SORT_MEM SORTMEM
bool isAcceptableFile(mode_t m)
RexxRoutine3(int, SysAddRexxMacro, CSTRING, name, CSTRING, file, OPTIONAL_CSTRING, option)
RexxRoutine1(int, SysSleep, RexxStringObject, delay)
#define SORT_DECENDING
#define RETVAL(retc)
int SearchPath(int SearchFlag, const char *path, const char *filename, char *buf, size_t buf_size)
#define CURRENT_DIR_FIRST
char * resolve_tilde(const char *)
#define INVALID_ROUTINE
static bool getPath(RexxCallContext *c, char *fileSpec, char **path, size_t *pathLen, RXTREEDATA *treeData)
bool formatFile(RexxCallContext *c, RXTREEDATA *treeData, uint32_t options, struct stat *finfo)
bool SamePaths(CSTRING path1, CSTRING path2)
#define restore_tty(a)
static bool getFileSpecFromArg(RexxCallContext *context, CSTRING fSpec, char *fileSpec, size_t bufLen, size_t argPos)
static bool getBiggerBuffer(RexxCallContext *c, char **dPath, size_t *nPath, size_t nStaticBuffer)
void restore_terminal(int signal)
size_t RexxEntry SysSetFileDateTime(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
TreeDataBuffers
@ FILESPEC_BUFFER
@ FOUNDFILELINE_BUFFER
@ FOUNDFILE_BUFFER
#define NAME_ONLY
#define CASELESS
static bool getOptionsFromArg(RexxCallContext *context, CSTRING opts, uint32_t *options, size_t argPos)
size_t RexxEntry SysRmDir(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysTempFileName(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
static bool increaseBuffer(RexxCallContext *c, size_t need, RXTREEDATA *treeData, TreeDataBuffers whichBuffer)
RexxRoutine2(int, SysReorderRexxMacro, CSTRING, name, CSTRING, option)
void outOfMemoryException(RexxThreadContext *c)
size_t RexxEntry SysFileDelete(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
size_t RexxEntry SysFork(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
void initTreeData(RXTREEDATA *treeData, RexxStemObject files)
static bool getPathSegment(RexxCallContext *c, char *fileSpec, char **path, size_t *pathLen, int lastSlashPos)
#define ERROR_FILEOPEN
size_t RexxEntry SysGetMessageX(const char *name, size_t numargs, CONSTRXSTRING args[], const char *queuename, PRXSTRING retstr)
#define FILEATTR_BUF_LEN
int ReadNextBuffer(GetFileData *filedata)
char line[LINEBUFSIZE]
#define isnan(x)
UINT_PTR uintptr_t
#define SIZE_MAX
unsigned int uint32_t
SSIZE_T ssize_t
static uint32_t newAttr(int32_t *mask, uint32_t attr)