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  dir_buf = (char *)malloc(strlen(home_dir)+strlen(st)+2);
166  if (!dir_buf)
167  return(0);
168  /* merge the strings */
169  sprintf(dir_buf, "%s/%s", home_dir, st);
170  return dir_buf;
171  }
172  else
173  {
174  /* get home directory path */
175  home_dir = getenv("HOME"); /* from the environment */
176  /* get space for the buf */
177  dir_buf = (char *)malloc(strlen(home_dir)+2);
178  if (!dir_buf)
179  return(0);
180  sprintf(dir_buf, "%s/", home_dir);
181  return dir_buf;
182  }
183  }
184  else if (*(st) == '~')
185  { /* cmd is '~username...' */
186  st++; /* jump over '~' */
187  slash = strchr(st,'/'); /* search for '/' */
188  if (!slash)
189  { /* if no '/' */
190  /* rest of string is username */
191  ppwd = getpwnam(st); /* get info about the user */
192  /* get space for the buf */
193  if (ppwd == NULL)
194  { /* no user */
195  return NULL; /* nothing happend */
196  }
197  dir_buf = (char *)malloc(strlen(ppwd->pw_dir)+2);
198  if (!dir_buf)
199  return NULL;
200  /* merge the strings */
201  sprintf(dir_buf, "%s/", ppwd->pw_dir);
202  }
203  else
204  { /* there is a slash */
205  /* copy the username into a */
206  /* local buffer; 100 bytes */
207  /* should be big enough */
208  /* fixes bug 1695834 */
209  memcpy(username, st, slash-st);
210  username[slash-st] = '\0';
211  ppwd = getpwnam(username); /* get info about the user */
212  slash++; /* step over the slash */
213  /* get space for the buf */
214  dir_buf = (char *)malloc(strlen(ppwd->pw_dir)+strlen(slash)+2);
215  if (!dir_buf)
216  return NULL;
217  /* merge the strings */
218  sprintf(dir_buf, "%s/%s", ppwd->pw_dir, slash);
219  }
220  return dir_buf; /* directory change to */
221  }
222  return NULL;
223 }
224 
225 /****************************************************************************/
226 /* sysDirectory */
227 /****************************************************************************/
228 RexxRoutine1(RexxStringObject, sysDirectory, OPTIONAL_CSTRING, dir)
229 {
230  RexxReturnCode rc;
231  char *rdir; /* resolved path */
232 
233  rc = 0;
234  if (dir != NO_CSTRING) /* if new directory is not null, */
235  {
236  if (*dir == '~')
237  {
238  rdir = resolve_tilde(dir);
239  rc = chdir(rdir);
240  free(rdir);
241  }
242  else
243  {
244  rc = chdir(dir); /* change to the new directory */
245  }
246  }
247 
248  // if we couldn't change the directory, return a null string
249  if (rc != 0)
250  {
251  return context->NullString();
252  }
253  else
254  {
255  // get the current working directory and return it
256  char temp[PATH_MAX + 3];
258  return context->NewStringFromAsciiz(temp);
259  }
260 }
261 
262 
263 /*****************************************************************************/
264 /* sysFilespec */
265 /*****************************************************************************/
266 RexxRoutine2(RexxStringObject, sysFilespec, CSTRING, option, CSTRING, name)
267 {
268  const char *endPtr = name + strlen(name); // point to last character
269  const char *pathEnd = strrchr(name, '/'); // find the last slash in name
270  // get the end of the path portion (if any)
271  const char *pathStart = name;
272  // note that pathend is one character past the end of the path.
273  // this means the length is easily calculated as pathEnd - pathStart,
274  // even in the cases where there is no patch portion
275  pathEnd = pathEnd == NULL ? pathStart : pathEnd + 1;
276  // this one needs a little adjustment for the case where this is all name
277  const char *nameStart = pathEnd == name ? name : pathEnd;
278 
279  switch (toupper(*option)) /* process each option */
280  {
281  case FILESPEC_PATH: /* extract the path */
282  {
283  return context->String(pathStart, pathEnd - pathStart);
284  }
285 
286  case FILESPEC_NAME: /* extract the file name */
287  { /* everything to right of slash */
288  return context->String(nameStart, endPtr - nameStart);
289  }
290 
291  case FILESPEC_DRIVE: /* extract the drive name */
292  { /* compatibility to windows, no drive */
293  return context->NullString();
294  }
295 
296  case FILESPEC_LOCATION: /* extract the file name */
297  { /* everything to left of slash */
298  return context->String(name, pathEnd - name);
299  }
300 
301  case FILESPEC_EXTENSION: // extract the file extension
302  {
303  // find the position of the last dot
304  const char *lastDot = strrchr(name, '.');
305 
306  if (lastDot >= nameStart)
307  {
308  // we don't extract the period
309  lastDot++;
310  return context->String(lastDot, endPtr - lastDot);
311  }
312  else
313  {
314  return context->NullString(); // nothing found, return the empty string
315  }
316 
317  }
318  default: /* unknown option */
319  {
320  char optionChar[2];
321  optionChar[0] = *option;
322  optionChar[1] = '\0';
323 
324  RexxArrayObject subs = context->Array(context->String("FILESPEC"), (RexxStringObject)OREF_positional, context->WholeNumberToObject(1),
325  context->String("ELNP"), context->String(optionChar));
326  /* raise an error */
327  context->RaiseException(Rexx_Error_Incorrect_call_list, subs);
328  return NULLOBJECT;
329  }
330  }
331 }
332 
333 
334 /******************************************************************************/
335 /* Name: SysExternalFunction */
336 /* */
337 /* Notes: Handles searching for and executing an external function. The */
338 /* search order is: */
339 /* 1) Macro-space pre-order functions */
340 /* 2) Registered external functions */
341 /* 3) REXX programs with same extension (if applicable) */
342 /* 4) REXX programs with default extension */
343 /* 5) Macro-space post-order functions */
344 /******************************************************************************/
346  RexxActivation * activation, /* Current Activation */
347  RexxActivity * activity, /* activity in use */
348  RexxString * target, /* Name of external function */
349  RexxObject ** arguments, /* Argument array */
350  size_t argcount, /* count of positional arguments */
351  size_t named_argcount, /* count of named arguments */
352  RexxString * calltype, /* Type of call */
353  ProtectedObject &result)
354 {
355  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_PREORDER, result))
356  {
357  return true;
358  }
359  /* no luck try for a registered func */
360  if (PackageManager::callNativeRoutine(activity, target, arguments, argcount, named_argcount, result))
361  {
362  return true;
363  }
364  /* have activation do the call */
365  if (activation->callExternalRexx(target, arguments, argcount, named_argcount, calltype, result))
366  {
367  return true;
368  }
369  /* function. If still not found, */
370  /* then raise an error */
371  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_POSTORDER, result))
372  {
373  return true;
374  }
375 
376  return false;
377 }
378 
379 
380 /**
381  * Push a new environment for the SysSetLocal() BIF.
382  *
383  * @param context The current activation context.
384  *
385  * @return Returns TRUE if the environment was successfully pushed.
386  */
388 {
389  RexxObject *Current = buildEnvlist(); /* build the new save block */
390  if (Current == OREF_NULL) /* if unsuccessful return zero */
391  {
392  return TheFalseObject;
393  }
394  else
395  {
396  /* Have Native Actiovation */
397  context->pushEnvironment(Current); /* update environemnt list */
398  return TheTrueObject; /* this returns one */
399  }
400 }
401 
402 /**
403  * Pop an environment for the SysEndLocal() BIF.
404  *
405  * @param context The current activation context.
406  *
407  * @return Always returns FALSE. This is a NOP on Windows.
408  */
410 {
411  RexxBuffer *Current = (RexxBuffer *)context->popEnvironment();/* block, if ixisted. */
412  if (TheNilObject == Current) /* nothing saved? */
413  {
414  return TheFalseObject; /* return failure value */
415  }
416  else
417  {
418  /* restore everything */
419  restoreEnvironment(Current->getData());
420  return TheTrueObject; /* this worked ok */
421  }
422 }
423 
424 
425 /*********************************************************************/
426 /* */
427 /* Subroutine Name: BuildEnvlist */
428 /* */
429 /* Descriptive Name: Build saved environment block */
430 /* */
431 /* Function: Builds a block containing all of the */
432 /* environment variables, the current drive */
433 /* and the current directory. */
434 /* */
435 /*********************************************************************/
436 
438 {
439  RexxBuffer *newBuffer; /* Buffer object to hold env */
440  char **Environment; /* environment pointer */
441  size_t size = 0; /* size of the new buffer */
442  char *curr_dir; /* current directory */
443  char *New; /* starting address of buffer */
444  Environment = getEnvironment(); /* get the ptr to the environ */
445 
446  for (;*Environment != NULL;Environment++)
447  {
448  size += strlen(*Environment); /* calculate the size for all */
449  size++; /* environment variables+'\0' */
450  } /* now get current dir */
451  if (!size)
452  {
453  return OREF_NULL; /* no envrionment ! */
454  }
455  if (!(curr_dir=(char *)malloc(PATH_MAX + 3)))/* malloc storage for cwd*/
456  {
458  }
459 
460  // start with a copy of the current working directory
462  size += strlen(curr_dir); /* add the space for curr dir */
463  size++; /* and its terminating '\0' */
464  size += sizeof(size_t); /* this is for the size itself*/
465  /* Now we have the size for */
466  /* allocating the new buffer */
467  newBuffer = new_buffer(size); /* let's do it */
468  /* Get starting address of buf*/
469  New = newBuffer->getData();
470  ((ENVENTRY*)New)->size = size; /* first write the size */
471  New += sizeof(size_t); /* update the pointer */
472  /* now write the curr dir */
473  memcpy(New,curr_dir,strlen(curr_dir));
474  New += strlen(curr_dir); /* update the pointer */
475  memcpy(New,"\0",1); /* write the terminator */
476  New++; /* update the pointer */
477  Environment = getEnvironment(); /* reset to begin of environ */
478  /* Loop through environment */
479  /* and copy all entries to the*/
480  /* buffer, each terminating */
481  /* with '\0' */
482  for (;*Environment != NULL;Environment++)
483  {
484  /* copy the entry */
485  memcpy(New,*Environment,strlen(*Environment));
486  New += strlen(*Environment); /* update the pointer */
487  memcpy(New,"\0",1); /* write the terminator */
488  New++; /* update the pointer */
489  }
490  free(curr_dir); /* free curr dir buffer */
491  return newBuffer; /* return the pointer */
492 }
493 
494 
495 /*********************************************************************/
496 /* */
497 /* Subroutine Name: RestoreEnvironment */
498 /* */
499 /* Descriptive Name: restores environment saved by Setlocal() */
500 /* */
501 /* Function: restores the environment variables, current */
502 /* directory and drive. */
503 /* */
504 /*********************************************************************/
505 
507  void *CurrentEnv) /* saved environment */
508 {
509  char *current; /* ptr to saved environment */
510  size_t size; /* size of the saved space */
511  size_t length; /* string length */
512  char *begin; /* begin of saved space */
513  char **Environment; /* environment pointer */
514 
515  char *del = NULL; /* ptr to old unused memory */
516  char *Env_Var_String; /* enviornment entry */
517  char namebufsave[256],namebufcurr[256];
518  char *np;
519  int i;
520 
521  Environment = getEnvironment(); /* get the current environment*/
522 
523  begin = current = (char *)CurrentEnv;/* get the saved space */
524  size = ((ENVENTRY*)current)->size; /* first read out the size */
525  current += sizeof(size_t); /* update the pointer */
526  if (chdir(current) == -1) /* restore the curr dir */
527  {
528  char msg[1024];
529  sprintf(msg, "Error restoring current directory: %s", current);
531  }
532  current += strlen(current); /* update the pointer */
533  current++; /* jump over '\0' */
534  if (!putflag)
535  { /* first change in the */
536  /* environment ? */
537  /* copy all entries to dynamic memory */
538  /*for all entries in the env */
539  for (;*Environment != NULL;Environment++)
540  {
541  length = strlen(*Environment)+1; /* get the size of the string */
542  /* and alloc space for it */
543  Env_Var_String = (char *)malloc(length);
544  memcpy(Env_Var_String,*Environment,length);/* copy the string */
545  putenv(Env_Var_String); /* and make it part of env */
546  }
547  putflag = 1; /* prevent do it again */
548  }
549  /* Loop through the saved env */
550  /* entries and restore them */
551  for (;(size_t)(current-begin)<size;current+=(strlen(current)+1))
552  {
553  Environment = getEnvironment(); /* get the environment */
554  del = NULL;
555  np = current;
556  /* extract the the name */
557  /* from the saved enviroment */
558  for (i=0;(*np!='=')&&(i<255);np++,i++)
559  {
560  memcpy(&(namebufsave[i]),np,1); /* copy the character */
561  }
562  memcpy(&(namebufsave[i]),"\0",1); /* copy the terminator */
563  /* find the entry in the env */
564  for (;*Environment != NULL;Environment++)
565  {
566  np = *Environment;
567  /* extract the the name */
568  /* from the current env */
569  for (i=0;(*np!='=')&&(i<255);np++,i++)
570  {
571  memcpy(&(namebufcurr[i]),np,1);/* copy the character */
572  }
573  memcpy(&(namebufcurr[i]),"\0",1);/* copy the terminator */
574 
575  if (!strcmp(namebufsave,namebufcurr))
576  {/* have a match ? */
577  del = *Environment; /* remember it for deletion */
578  break; /* found, so get out of here */
579  }
580  }
581  if (putenv(current) == -1)
582  {
583  reportException(Error_System_service_service, "Error restoring environment variable");
584  }
585  if (del) /* if there was an old entry */
586  {
587  free(del); /* free it */
588  }
589  }
590 }
void reportException(wholenumber_t error)
RexxBuffer * new_buffer(sizeB_t s)
#define MS_PREORDER
#define MS_POSTORDER
#define OREF_NULL
Definition: RexxCore.h:60
#define TheTrueObject
Definition: RexxCore.h:186
#define TheNilObject
Definition: RexxCore.h:181
#define TheFalseObject
Definition: RexxCore.h:185
#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:349
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()