unix/ExternalFunctions.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 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 AIX Support aixextf.c */
40 /* */
41 /* AIX specific external function lookup and AIX built-in functions. */
42 /* */
43 /******************************************************************************/
44 
45 /******************************************************************************/
46 /* aixextf.c - Methods to resolve external function calls. */
47 /* */
48 /* C methods: */
49 /* sysDirectory- Method for the DIRECTORY BIF */
50 /* SysExternalFunction- Method for searching/invoking an external function */
51 /* */
52 /* Internal routines: */
53 /* ExecExternalSearch - Search for and execute a REXX program from disk. */
54 /* MacroSpaceSearch - Search for and execute a function in REXX macrospace.*/
55 /* RegExternalFunction - Search for and execute a registered external */
56 /* function. */
57 /******************************************************************************/
58 #include <stdio.h> /* Get printf, FILE type, etc. */
59 #include <string.h> /* Get strcpy, strcat, etc. */
60 #include <ctype.h> /* Get toupper */
61 #include <unistd.h> /* get getcwd routine/environ */
62 #include <limits.h> /* Get PATH_MAX */
63 #include <sys/ioctl.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 #include <sys/types.h>
67 #include <pwd.h>
68 
69 #include "RexxCore.h" /* global REXX definitions */
70 #include "StringClass.hpp"
71 #include "ArrayClass.hpp"
72 #include "RexxActivity.hpp"
73 #include "RexxActivation.hpp"
74 #include "MethodClass.hpp"
75 #include "SourceFile.hpp"
76 #include "RexxInternalApis.h" /* Get private REXXAPI API's */
77 #include "ActivityManager.hpp"
78 #include "ProtectedObject.hpp"
79 #include "StringUtil.hpp"
80 #include "SystemInterpreter.hpp"
81 #include "PackageManager.hpp"
82 #include "BufferClass.hpp"
83 
84 
85 #define CMDBUFSIZE 1024 /* Max size of executable cmd */
86 #if defined(AIX)
87 #define CMDDEFNAME "/bin/ksh" /* Default unix sys cmd handler */
88 #define COMSPEC "ksh" /* unix cmd handler env name */
89 #define SYSENV "ksh" /* Default AIX cmd environment */
90 #elif defined(OPSYS_SUN)
91 #define CMDDEFNAME "/bin/sh" /* Default unix sys cmd handler */
92 #define COMSPEC "sh" /* unix cmd handler env name */
93 #define SYSENV "sh" /* Default LINUX cmd environment */
94 #else
95 #define CMDDEFNAME "/bin/bash" /* Default unix sys cmd handler */
96 #define COMSPEC "bash" /* unix cmd handler env name */
97 #define SYSENV "bash" /* Default AIX cmd environment */
98 #endif
99 
100 #define DRVNUM 0x40 /* drive number subtractor */
101 #define DIRLEN 256 /* length of a directory */
102 #define FULLSEG 65536L /* ^4K constant */
103  /* FILESPEC function options */
104 #define FILESPEC_PATH 'P'
105 #define FILESPEC_NAME 'N'
106 #define FILESPEC_LOCATION 'L'
107 #define FILESPEC_EXTENSION 'E'
108 #define FILESPEC_DRIVE 'D'
109 
110 #define KIOCSOUND 0x4B2F /* start sound generation (0 for off) */
111 
112 typedef struct _ENVENTRY { /* setlocal/endlocal structure */
113  size_t size; /* size of the saved memory */
115 
116 int putflag = 0; /* static or dynamic env memory */
117 
118 /*********************************************************************/
119 /* */
120 /* Subroutine Name: sysBeep */
121 /* */
122 /* Descriptive Name: BEEP function */
123 /* */
124 /* Function: sounds the speaker if possible, else flash */
125 /* the screen */
126 /*********************************************************************/
127 
128 RexxRoutine2(CSTRING, sysBeep, OPTIONAL_wholenumber_t, Frequency, OPTIONAL_wholenumber_t, Duration)
129 {
130  /* console beep for Unix */
131  printf("\a");
132  return ""; /* always returns a null */
133 }
134 
135 
136 /********************************************************************
137 * Function: resolve_tilde(path) *
138 * *
139 * Purpose: Resolves path names including '~'. *
140 * *
141 * RC: Returns the absolute path in new allocated space. *
142 * *
143 *********************************************************************/
144 char *resolve_tilde(const char *path)
145 {
146  const char * st;
147  const char *home_dir = NULL; /* home directory path */
148  char *dir_buf = NULL; /* full directory path */
149  const char * slash;
150  char username[100];
151  struct passwd *ppwd;
152 
153  st = path;
154  /* if no user name */
155  if (*(st) == '~' && (*(st+1) == '\0' || *(st+1) == '/'|| *(st+1) == ' ' ))
156  {
157  if (*(st+1) == '/')
158  { /* if there is a path */
159  st +=2; /* jump over '~/' */
160  /* get home directory path */
161  home_dir = getenv("HOME"); /* from the environment */
162  if (!home_dir) /* if no home dir info */
163  return(0);
164  /* get space for the buf */
165  size_t size = strlen(home_dir)+strlen(st)+2;
166  dir_buf = (char *)malloc(size);
167  if (!dir_buf)
168  return(0);
169  /* merge the strings */
170  snprintf(dir_buf, size, "%s/%s", home_dir, st);
171  return dir_buf;
172  }
173  else
174  {
175  /* get home directory path */
176  home_dir = getenv("HOME"); /* from the environment */
177  /* get space for the buf */
178  size_t size = strlen(home_dir)+2;
179  dir_buf = (char *)malloc(size);
180  if (!dir_buf)
181  return(0);
182  snprintf(dir_buf, size, "%s/", home_dir);
183  return dir_buf;
184  }
185  }
186  else if (*(st) == '~')
187  { /* cmd is '~username...' */
188  st++; /* jump over '~' */
189  slash = strchr(st,'/'); /* search for '/' */
190  if (!slash)
191  { /* if no '/' */
192  /* rest of string is username */
193  ppwd = getpwnam(st); /* get info about the user */
194  /* get space for the buf */
195  if (ppwd == NULL)
196  { /* no user */
197  return NULL; /* nothing happend */
198  }
199  size_t size = strlen(ppwd->pw_dir)+2;
200  dir_buf = (char *)malloc(size);
201  if (!dir_buf)
202  return NULL;
203  /* merge the strings */
204  snprintf(dir_buf, size, "%s/", ppwd->pw_dir);
205  }
206  else
207  { /* there is a slash */
208  /* copy the username into a */
209  /* local buffer; 100 bytes */
210  /* should be big enough */
211  /* fixes bug 1695834 */
212  memcpy(username, st, slash-st);
213  username[slash-st] = '\0';
214  ppwd = getpwnam(username); /* get info about the user */
215  slash++; /* step over the slash */
216  /* get space for the buf */
217  size_t size = strlen(ppwd->pw_dir)+strlen(slash)+2;
218  dir_buf = (char *)malloc(size);
219  if (!dir_buf)
220  return NULL;
221  /* merge the strings */
222  snprintf(dir_buf, size, "%s/%s", ppwd->pw_dir, slash);
223  }
224  return dir_buf; /* directory change to */
225  }
226  return NULL;
227 }
228 
229 /****************************************************************************/
230 /* sysDirectory */
231 /****************************************************************************/
232 RexxRoutine1(RexxStringObject, sysDirectory, OPTIONAL_CSTRING, dir)
233 {
234  RexxReturnCode rc;
235  char *rdir; /* resolved path */
236 
237  rc = 0;
238  if (dir != NO_CSTRING) /* if new directory is not null, */
239  {
240  if (*dir == '~')
241  {
242  rdir = resolve_tilde(dir);
243  rc = chdir(rdir);
244  free(rdir);
245  }
246  else
247  {
248  rc = chdir(dir); /* change to the new directory */
249  }
250  }
251 
252  // if we couldn't change the directory, return a null string
253  if (rc != 0)
254  {
255  return context->NullString();
256  }
257  else
258  {
259  // get the current working directory and return it
260  char temp[PATH_MAX + 3];
262  return context->NewStringFromAsciiz(temp);
263  }
264 }
265 
266 
267 /*****************************************************************************/
268 /* sysFilespec */
269 /*****************************************************************************/
270 RexxRoutine2(RexxStringObject, sysFilespec, CSTRING, option, CSTRING, name)
271 {
272  const char *endPtr = name + strlen(name); // point to last character
273  const char *pathEnd = strrchr(name, '/'); // find the last slash in name
274  // get the end of the path portion (if any)
275  const char *pathStart = name;
276  // note that pathend is one character past the end of the path.
277  // this means the length is easily calculated as pathEnd - pathStart,
278  // even in the cases where there is no patch portion
279  pathEnd = pathEnd == NULL ? pathStart : pathEnd + 1;
280  // this one needs a little adjustment for the case where this is all name
281  const char *nameStart = pathEnd == name ? name : pathEnd;
282 
283  switch (toupper(*option)) /* process each option */
284  {
285  case FILESPEC_PATH: /* extract the path */
286  {
287  return context->String(pathStart, pathEnd - pathStart);
288  }
289 
290  case FILESPEC_NAME: /* extract the file name */
291  { /* everything to right of slash */
292  return context->String(nameStart, endPtr - nameStart);
293  }
294 
295  case FILESPEC_DRIVE: /* extract the drive name */
296  { /* compatibility to windows, no drive */
297  return context->NullString();
298  }
299 
300  case FILESPEC_LOCATION: /* extract the file name */
301  { /* everything to left of slash */
302  return context->String(name, pathEnd - name);
303  }
304 
305  case FILESPEC_EXTENSION: // extract the file extension
306  {
307  // find the position of the last dot
308  const char *lastDot = strrchr(name, '.');
309 
310  if (lastDot >= nameStart)
311  {
312  // we don't extract the period
313  lastDot++;
314  return context->String(lastDot, endPtr - lastDot);
315  }
316  else
317  {
318  return context->NullString(); // nothing found, return the empty string
319  }
320 
321  }
322  default: /* unknown option */
323  {
324  char optionChar[2];
325  optionChar[0] = *option;
326  optionChar[1] = '\0';
327 
328  RexxArrayObject subs = context->Array(context->String("FILESPEC"), (RexxStringObject)OREF_positional, context->WholeNumberToObject(1),
329  context->String("ELNP"), context->String(optionChar));
330  /* raise an error */
331  context->RaiseException(Rexx_Error_Incorrect_call_list, subs);
332  return NULLOBJECT;
333  }
334  }
335 }
336 
337 
338 /******************************************************************************/
339 /* Name: SysExternalFunction */
340 /* */
341 /* Notes: Handles searching for and executing an external function. The */
342 /* search order is: */
343 /* 1) Macro-space pre-order functions */
344 /* 2) Registered external functions */
345 /* 3) REXX programs with same extension (if applicable) */
346 /* 4) REXX programs with default extension */
347 /* 5) Macro-space post-order functions */
348 /******************************************************************************/
350  RexxActivation * activation, /* Current Activation */
351  RexxActivity * activity, /* activity in use */
352  RexxString * target, /* Name of external function */
353  RexxObject ** arguments, /* Argument array */
354  size_t argcount, /* count of positional arguments */
355  size_t named_argcount, /* count of named arguments */
356  RexxString * calltype, /* Type of call */
357  ProtectedObject &result)
358 {
359  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_PREORDER, result))
360  {
361  return true;
362  }
363  /* no luck try for a registered func */
364  if (PackageManager::callNativeRoutine(activity, target, arguments, argcount, named_argcount, result))
365  {
366  return true;
367  }
368  /* have activation do the call */
369  if (activation->callExternalRexx(target, arguments, argcount, named_argcount, calltype, result))
370  {
371  return true;
372  }
373  /* function. If still not found, */
374  /* then raise an error */
375  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_POSTORDER, result))
376  {
377  return true;
378  }
379 
380  return false;
381 }
382 
383 
384 /**
385  * Push a new environment for the SysSetLocal() BIF.
386  *
387  * @param context The current activation context.
388  *
389  * @return Returns TRUE if the environment was successfully pushed.
390  */
392 {
393  RexxObject *Current = buildEnvlist(); /* build the new save block */
394  if (Current == OREF_NULL) /* if unsuccessful return zero */
395  {
396  return TheFalseObject;
397  }
398  else
399  {
400  /* Have Native Actiovation */
401  context->pushEnvironment(Current); /* update environemnt list */
402  return TheTrueObject; /* this returns one */
403  }
404 }
405 
406 /**
407  * Pop an environment for the SysEndLocal() BIF.
408  *
409  * @param context The current activation context.
410  *
411  * @return Always returns FALSE. This is a NOP on Windows.
412  */
414 {
415  RexxBuffer *Current = (RexxBuffer *)context->popEnvironment();/* block, if ixisted. */
416  if (TheNilObject == Current) /* nothing saved? */
417  {
418  return TheFalseObject; /* return failure value */
419  }
420  else
421  {
422  /* restore everything */
423  restoreEnvironment(Current->getData());
424  return TheTrueObject; /* this worked ok */
425  }
426 }
427 
428 
429 /*********************************************************************/
430 /* */
431 /* Subroutine Name: BuildEnvlist */
432 /* */
433 /* Descriptive Name: Build saved environment block */
434 /* */
435 /* Function: Builds a block containing all of the */
436 /* environment variables, the current drive */
437 /* and the current directory. */
438 /* */
439 /*********************************************************************/
440 
442 {
443  RexxBuffer *newBuffer; /* Buffer object to hold env */
444  char **Environment; /* environment pointer */
445  size_t size = 0; /* size of the new buffer */
446  char *curr_dir; /* current directory */
447  char *New; /* starting address of buffer */
448  Environment = getEnvironment(); /* get the ptr to the environ */
449 
450  for (;*Environment != NULL;Environment++)
451  {
452  size += strlen(*Environment); /* calculate the size for all */
453  size++; /* environment variables+'\0' */
454  } /* now get current dir */
455  if (!size)
456  {
457  return OREF_NULL; /* no envrionment ! */
458  }
459  if (!(curr_dir=(char *)malloc(PATH_MAX + 3)))/* malloc storage for cwd*/
460  {
462  }
463 
464  // start with a copy of the current working directory
466  size += strlen(curr_dir); /* add the space for curr dir */
467  size++; /* and its terminating '\0' */
468  size += sizeof(size_t); /* this is for the size itself*/
469  /* Now we have the size for */
470  /* allocating the new buffer */
471  newBuffer = new_buffer(size); /* let's do it */
472  /* Get starting address of buf*/
473  New = newBuffer->getData();
474  ((ENVENTRY*)New)->size = size; /* first write the size */
475  New += sizeof(size_t); /* update the pointer */
476  /* now write the curr dir */
477  memcpy(New,curr_dir,strlen(curr_dir));
478  New += strlen(curr_dir); /* update the pointer */
479  memcpy(New,"\0",1); /* write the terminator */
480  New++; /* update the pointer */
481  Environment = getEnvironment(); /* reset to begin of environ */
482  /* Loop through environment */
483  /* and copy all entries to the*/
484  /* buffer, each terminating */
485  /* with '\0' */
486  for (;*Environment != NULL;Environment++)
487  {
488  /* copy the entry */
489  memcpy(New,*Environment,strlen(*Environment));
490  New += strlen(*Environment); /* update the pointer */
491  memcpy(New,"\0",1); /* write the terminator */
492  New++; /* update the pointer */
493  }
494  free(curr_dir); /* free curr dir buffer */
495  return newBuffer; /* return the pointer */
496 }
497 
498 
499 /*********************************************************************/
500 /* */
501 /* Subroutine Name: RestoreEnvironment */
502 /* */
503 /* Descriptive Name: restores environment saved by Setlocal() */
504 /* */
505 /* Function: restores the environment variables, current */
506 /* directory and drive. */
507 /* */
508 /*********************************************************************/
509 
511  void *CurrentEnv) /* saved environment */
512 {
513  char *current; /* ptr to saved environment */
514  size_t size; /* size of the saved space */
515  size_t length; /* string length */
516  char *begin; /* begin of saved space */
517  char **Environment; /* environment pointer */
518 
519  char *del = NULL; /* ptr to old unused memory */
520  char *Env_Var_String; /* enviornment entry */
521  char namebufsave[256],namebufcurr[256];
522  char *np;
523  int i;
524 
525  Environment = getEnvironment(); /* get the current environment*/
526 
527  begin = current = (char *)CurrentEnv;/* get the saved space */
528  size = ((ENVENTRY*)current)->size; /* first read out the size */
529  current += sizeof(size_t); /* update the pointer */
530  if (chdir(current) == -1) /* restore the curr dir */
531  {
532  char msg[1024];
533  snprintf(msg, sizeof msg, "Error restoring current directory: %s", current);
535  }
536  current += strlen(current); /* update the pointer */
537  current++; /* jump over '\0' */
538  if (!putflag)
539  { /* first change in the */
540  /* environment ? */
541  /* copy all entries to dynamic memory */
542  /*for all entries in the env */
543  for (;*Environment != NULL;Environment++)
544  {
545  length = strlen(*Environment)+1; /* get the size of the string */
546  /* and alloc space for it */
547  Env_Var_String = (char *)malloc(length);
548  memcpy(Env_Var_String,*Environment,length);/* copy the string */
549  putenv(Env_Var_String); /* and make it part of env */
550  }
551  putflag = 1; /* prevent do it again */
552  }
553  /* Loop through the saved env */
554  /* entries and restore them */
555  for (;(size_t)(current-begin)<size;current+=(strlen(current)+1))
556  {
557  Environment = getEnvironment(); /* get the environment */
558  del = NULL;
559  np = current;
560  /* extract the the name */
561  /* from the saved enviroment */
562  for (i=0;(*np!='=')&&(i<255);np++,i++)
563  {
564  memcpy(&(namebufsave[i]),np,1); /* copy the character */
565  }
566  memcpy(&(namebufsave[i]),"\0",1); /* copy the terminator */
567  /* find the entry in the env */
568  for (;*Environment != NULL;Environment++)
569  {
570  np = *Environment;
571  /* extract the the name */
572  /* from the current env */
573  for (i=0;(*np!='=')&&(i<255);np++,i++)
574  {
575  memcpy(&(namebufcurr[i]),np,1);/* copy the character */
576  }
577  memcpy(&(namebufcurr[i]),"\0",1);/* copy the terminator */
578 
579  if (!strcmp(namebufsave,namebufcurr))
580  {/* have a match ? */
581  del = *Environment; /* remember it for deletion */
582  break; /* found, so get out of here */
583  }
584  }
585  if (putenv(current) == -1)
586  {
587  reportException(Error_System_service_service, "Error restoring environment variable");
588  }
589  if (del) /* if there was an old entry */
590  {
591  free(del); /* free it */
592  }
593  }
594 }
void reportException(wholenumber_t error)
RexxBuffer * new_buffer(size_t s)
#define MS_PREORDER
#define MS_POSTORDER
#define OREF_NULL
Definition: RexxCore.h:61
#define TheTrueObject
Definition: RexxCore.h:196
#define TheNilObject
Definition: RexxCore.h:191
#define TheFalseObject
Definition: RexxCore.h:195
#define Error_System_service
#define Error_System_service_service
static bool callNativeRoutine(RexxActivity *activity, RexxString *name, RexxObject **arguments, size_t argcount, size_t named_argcount, ProtectedObject &result)
bool callExternalRexx(RexxString *, RexxObject **, size_t, size_t, RexxString *, ProtectedObject &)
void pushEnvironment(RexxObject *)
bool callMacroSpaceFunction(RexxString *, RexxObject **, size_t, size_t, RexxString *, int, ProtectedObject &)
RexxObject * popEnvironment()
virtual char * getData()
static RexxObject * buildEnvlist()
static bool invokeExternalFunction(RexxActivation *, RexxActivity *, RexxString *, RexxObject **, size_t, size_t, RexxString *, ProtectedObject &)
static RexxObject * popEnvironment(RexxActivation *context)
static RexxObject * pushEnvironment(RexxActivation *context)
static void restoreEnvironment(void *CurrentEnv)
static void getCurrentWorkingDirectory(char *)
#define Rexx_Error_Incorrect_call_list
Definition: oorexxerrors.h:401
const char * CSTRING
Definition: rexx.h:78
struct _RexxStringObject * RexxStringObject
Definition: rexx.h:128
struct _RexxArrayObject * RexxArrayObject
Definition: rexx.h:130
#define NULLOBJECT
Definition: rexx.h:147
int RexxReturnCode
Definition: rexx.h:73
#define NO_CSTRING
Definition: rexx.h:146
#define FILESPEC_NAME
RexxRoutine1(RexxStringObject, sysDirectory, OPTIONAL_CSTRING, dir)
#define FILESPEC_DRIVE
RexxRoutine2(CSTRING, sysBeep, OPTIONAL_wholenumber_t, Frequency, OPTIONAL_wholenumber_t, Duration)
char * resolve_tilde(const char *path)
#define FILESPEC_LOCATION
struct _ENVENTRY ENVENTRY
#define FILESPEC_PATH
#define FILESPEC_EXTENSION
char ** getEnvironment()