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