windows/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 /* winextf.c - Methods to resolve external function calls. */
40 /* */
41 /* C methods: */
42 /* sysBeep - Method for the BEEP BIF */
43 /* sysSetLocal - Method for the SETLOCAL BIF */
44 /* sysEndLocal - Method for the ENDLOCAL BIF */
45 /* sysDirectory- Method for the DIRECTORY BIF */
46 /* SysExternalFunction- Method for searching/invoking an external function */
47 /* sysMessageBox - Method to pop up message box */
48 /* */
49 /******************************************************************************/
50 #include <stdio.h> /* Get printf, FILE type, etc. */
51 #include <string.h> /* Get strcpy, strcat, etc. */
52 #include <stdlib.h> /* Get system, max_path etc... */
53 #include <process.h>
54 #include <direct.h>
55 #include <windows.h>
56 #include <CommCtrl.h>
57 
58 #include "RexxCore.h" /* global REXX definitions */
59 #include "StringClass.hpp"
60 #include "ArrayClass.hpp"
61 #include "RexxActivity.hpp"
62 #include "RexxActivation.hpp"
63 #include "MethodClass.hpp"
64 #include "SourceFile.hpp"
65 #include "RexxInternalApis.h" /* Get private REXXAPI API's */
66 #include "ProtectedObject.hpp"
67 #include "StringUtil.hpp"
68 #include "PackageManager.hpp"
69 #include "SystemInterpreter.hpp"
70 
71 #define DIRLEN 256 /* length of a directory */
72 
73 #define MAX_FREQUENCY 32767
74 #define MIN_FREQUENCY 37
75 #define MAX_DURATION 60000
76 #define MIN_DURATION 0
77 
78  /* FILESPEC function options */
79 #define FILESPEC_DRIVE 'D'
80 #define FILESPEC_PATH 'P'
81 #define FILESPEC_NAME 'N'
82 #define FILESPEC_LOCATION 'L'
83 #define FILESPEC_EXTENSION 'E'
84 
85 typedef struct _ENVENTRY { /* setlocal/endlocal structure */
86  size_t DriveNumber; /* saved drive */
87  char Directory[DIRLEN]; /* saved current directory */
88  char *Environment; /* saved environment segment */
89  char Variables[1]; /* start of variable values */
91 
92 /*********************************************************************/
93 /* */
94 /* Subroutine Name: sysBeep */
95 /* */
96 /* Descriptive Name: BEEP function */
97 /* */
98 /* Function: sounds the speaker at frequency Hertz for */
99 /* specified duration (in milliseconds) */
100 /*********************************************************************/
101 
102 RexxRoutine2(CSTRING, sysBeep, wholenumber_t, Frequency, wholenumber_t, Duration)
103 {
104  /* out of range? */
105  if (Frequency > MAX_FREQUENCY || Frequency < MIN_FREQUENCY)
106  {
107  RexxArrayObject subs = context->NewArray(5);
108  context->ArrayAppend(subs, (RexxStringObject)OREF_positional);
109  context->ArrayAppend(subs, context->NewStringFromAsciiz("frequency"));
110  context->ArrayAppend(subs, context->WholeNumberToObject(MIN_FREQUENCY));
111  context->ArrayAppend(subs, context->WholeNumberToObject(MAX_FREQUENCY));
112  context->ArrayAppend(subs, context->WholeNumberToObject(Frequency));
113  context->RaiseException(Rexx_Error_Invalid_argument_range, subs);
114  return NULL;
115  }
116  /* out of range? */
117  if (Duration > MAX_DURATION || Duration < MIN_DURATION)
118  {
119  RexxArrayObject subs = context->NewArray(5);
120  context->ArrayAppend(subs, (RexxStringObject)OREF_positional);
121  context->ArrayAppend(subs, context->NewStringFromAsciiz("duration"));
122  context->ArrayAppend(subs, context->WholeNumberToObject(MIN_DURATION));
123  context->ArrayAppend(subs, context->WholeNumberToObject(MAX_DURATION));
124  context->ArrayAppend(subs, context->WholeNumberToObject(Duration));
125  context->RaiseException(Rexx_Error_Invalid_argument_range, subs);
126  return NULL;
127  }
128 
129  Beep((DWORD)Frequency, (DWORD)Duration); /* sound beep */
130  return ""; /* always returns a null */
131 }
132 
133 
134 /********************************************************************************************/
135 /* sysDirectory */
136 /********************************************************************************************/
137 RexxRoutine1(RexxStringObject, sysDirectory, OPTIONAL_CSTRING, dir)
138 {
139  char buffer[MAX_PATH+1];
140  int rc = 0;
141 
142  if (dir != NO_CSTRING)
143  {
144  if ((strlen(dir) == 2) && (dir[1] == ':'))
145  {
146  rc = _chdrive(toupper( dir[0] ) - 'A' + 1);
147  }
148  else
149  {
150  rc = _chdir(dir);
151  }
152  }
153  /* Return the current directory */
154  if (rc != 0 || _getcwd(buffer, MAX_PATH) == NULL)
155  {
156  return context->NullString();
157  }
158  else
159  {
160  return context->NewStringFromAsciiz(buffer);
161  }
162 }
163 
164 
165 /********************************************************************************************/
166 /* sysFilespec */
167 /********************************************************************************************/
168 RexxRoutine2(RexxStringObject, sysFilespec, CSTRING, option, CSTRING, name)
169 {
170  const char *endPtr = name + strlen(name); // point to last character
171  const char *pathEnd = strrchr(name, '\\'); // find the last backslash in name
172  const char *altPathEnd = strrchr(name, '/'); // 3.2.0 also looked for a forward slash, so handle that also
173  if (altPathEnd > pathEnd)
174  {
175  pathEnd = altPathEnd;
176  }
177  const char *driveEnd = strchr(name, ':'); // and first colon
178  // get the end of the path portion (if any)
179  const char *pathStart = driveEnd == NULL ? name : driveEnd + 1;
180  // note that pathend is one character past the end of the path.
181  // this means the length is easily calculated as pathEnd - pathStart,
182  // even in the cases where there is no patch portion
183  pathEnd = pathEnd == NULL ? pathStart : pathEnd + 1;
184  // this one needs a little adjustment for the case where this is all name
185  const char *nameStart = pathEnd == name ? name : pathEnd;
186 
187  switch (toupper(*option)) /* process each option */
188  {
189  case FILESPEC_PATH: /* extract the path */
190  {
191  return context->String(pathStart, pathEnd - pathStart);
192  }
193 
194  case FILESPEC_NAME: /* extract the file name */
195  { /* everything to right of slash */
196  return context->String(nameStart, endPtr - nameStart);
197  }
198 
199  case FILESPEC_LOCATION: /* extract the file name */
200  { /* everything to left of slash */
201  return context->String(name, pathEnd - name);
202  }
203 
204  case FILESPEC_DRIVE: /* extract the drive */
205  {
206  if (driveEnd != NULL) /* have a real string? */
207  {
208  return context->String(name, driveEnd + 1 - name);
209  }
210  else
211  {
212  return context->NullString(); // nothing found, return the empty string
213  }
214  }
215 
216  case FILESPEC_EXTENSION: // extract the file extension
217  {
218  // find the position of the last dot
219  const char *lastDot = strrchr(name, '.');
220 
221  if (lastDot >= nameStart)
222  {
223  // we don't extract the period
224  lastDot++;
225  return context->String(lastDot, endPtr - lastDot);
226  }
227  else
228  {
229  return context->NullString(); // nothing found, return the empty string
230  }
231 
232  }
233  default: /* unknown option */
234  {
235  char optionChar[2];
236  optionChar[0] = *option;
237  optionChar[1] = '\0';
238 
239  RexxArrayObject subs = context->Array(context->String("FILESPEC"), (RexxStringObject)OREF_positional, context->WholeNumberToObject(1),
240  context->String("DELNP"), context->String(optionChar));
241  /* raise an error */
242  context->RaiseException(Rexx_Error_Incorrect_call_list, subs);
243  return NULLOBJECT;
244  }
245  }
246 }
247 
248 
249 /******************************************************************************/
250 /* Name: SysExternalFunction */
251 /* */
252 /* Notes: Handles searching for and executing an external function. The */
253 /* search order is: */
254 /* 1) Macro-space pre-order functions */
255 /* 2) Registered external functions */
256 /* 3) REXX programs with same extension (if applicable) */
257 /* 4) REXX programs with default extension */
258 /* 5) Macro-space post-order functions */
259 /******************************************************************************/
261  RexxActivation * activation, /* Current Activation */
262  RexxActivity * activity, /* activity in use */
263  RexxString * target, /* Name of external function */
264  RexxObject ** arguments, /* Argument array */
265  size_t argcount, /* count of positional arguments */
266  size_t named_argcount, /* count of named arguments */
267  RexxString * calltype, /* Type of call */
268  ProtectedObject &result)
269 {
270  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_PREORDER, result))
271  {
272  return true;
273  }
274  /* no luck try for a registered func */
275  if (PackageManager::callNativeRoutine(activity, target, arguments, argcount, named_argcount, result))
276  {
277  return true;
278  }
279  /* have activation do the call */
280  if (activation->callExternalRexx(target, arguments, argcount, named_argcount, calltype, result))
281  {
282  return true;
283  }
284  /* function. If still not found, */
285  /* then raise an error */
286  if (activation->callMacroSpaceFunction(target, arguments, argcount, named_argcount, calltype, MS_POSTORDER, result))
287  {
288  return true;
289  }
290 
291  return false;
292 }
293 
294 
295 /*********************************************************************/
296 /* */
297 /* Subroutine Name: RestoreEnvironment */
298 /* */
299 /* Descriptive Name: restores environment saved by Setlocal() */
300 /* */
301 /* Function: restores the environment variables, current */
302 /* directory and drive. */
303 /* */
304 /*********************************************************************/
305 
306 void SystemInterpreter::restoreEnvironment(void *CurrentEnv)
307 {
308 }
309 
310 
311 /**
312  * Helper function for sysMessageBox(). Parses the other message box style
313  * argument and adds the styles specified.
314  *
315  * @param other The 'additional message box styles' argument to RxMessageBox().
316  * This can be more than one style keyword, separated by spaces.
317  *
318  * @param style Pointer to the MessageBox style flags collected so far. On
319  * return this will be augmented with the additional styles.
320  *
321  * @return True on no error, false if the user passed an unrecognizable keyword
322  * and a condition should be raised.
323  *
324  * @note Assumes other is not NULL.
325  */
326 static bool addMBStyle(CSTRING other, ULONG *style)
327 {
328  char *token;
329  char *str = strdup(other);
330  if ( ! str )
331  {
332  return false;
333  }
334 
335  ULONG extraFlag[] =
336  {
337  // Default button category. MB_DEFBUTTON1 is the default
338  MB_DEFBUTTON2,
339  MB_DEFBUTTON3,
340  MB_DEFBUTTON4,
341  // Modal category. MB_APPLMODAL is the default.
342  MB_SYSTEMMODAL,
343  MB_TASKMODAL,
344  // Miscellaneous catetgory. There is no default here. MB_SETFOREGROUND
345  // is already set. MB_SERVICE_NOTIFICATION_NT3X makes no sense since NT
346  // is not supported
347  MB_DEFAULT_DESKTOP_ONLY,
348  MB_RIGHT,
349  MB_RTLREADING,
350  MB_TOPMOST,
351  MB_SERVICE_NOTIFICATION
352  };
353 
354  const char *extra[] =
355  {
356  // Default button category.
357  "DEFBUTTON2",
358  "DEFBUTTON3",
359  "DEFBUTTON4",
360  // Modal category.
361  "SYSTEMMODAL",
362  "TASKMODAL",
363  // Miscellaneous catetgory.
364  "DEFAULTDESKTOP",
365  "RIGHT",
366  "RTLREADING",
367  "TOPMOST",
368  "SERVICENOTIFICATION"
369  };
370 
371  ULONG extraStyle = 0;
372  int i;
373  int count = sizeof(extra) / sizeof(const char *);
374 
375  token = strtok(str, " ");
376  while ( token != NULL )
377  {
378  for ( i = 0; i < count; i += 1 )
379  {
380  if ( !stricmp(token, extra[i]) )
381  {
382  extraStyle |= extraFlag[i];
383  break;
384  }
385  }
386 
387  if ( i == count )
388  {
389  // User sent a bad keyword.
390  free(str);
391  return false;
392  }
393 
394  token = strtok(NULL, " ");
395  }
396 
397  *style = *style | extraStyle;
398  free(str);
399  return true;
400 }
401 
402 /*********************************************************************/
403 /* */
404 /* Subroutine Name: RxMessageBox */
405 /* */
406 /* Descriptive Name: RxMessageBox function */
407 /* */
408 /* Function: pops up a PM message box when called from */
409 /* a PM session. */
410 /* Parameters: */
411 /* Input: */
412 /* Text = The message box text. */
413 /* Title = The message box title. */
414 /* Button = The message box button style. */
415 /* Icon = The message box icon style. */
416 /* Other = Additional message box styles. */
417 /* */
418 /*********************************************************************/
419 RexxRoutine5(int, sysMessageBox, CSTRING, text, OPTIONAL_CSTRING, title,
420  OPTIONAL_CSTRING, button, OPTIONAL_CSTRING, icon, OPTIONAL_CSTRING, other)
421 {
422  ULONG style; /* window style flags */
423  int maxCnt; /* Max loop count */
424  int index; /* table index */
425 
426  INITCOMMONCONTROLSEX ctrlex;
427  ctrlex.dwSize = sizeof(ctrlex);
428  ctrlex.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES;
429  InitCommonControlsEx(&ctrlex);
430 
431  PSZ Button_Styles[] = /* message box button styles */
432  {
433  "ABORTRETRYIGNORE",
434  "CANCELTRYCONTINUE",
435  "HELP",
436  "OK",
437  "OKCANCEL",
438  "RETRYCANCEL",
439  "YESNO",
440  "YESNOCANCEL"
441  };
442 
443  ULONG Button_Flags[] = /* message box button styles */
444  {
445  MB_ABORTRETRYIGNORE,
446  MB_CANCELTRYCONTINUE,
447  MB_HELP,
448  MB_OK,
449  MB_OKCANCEL,
450  MB_RETRYCANCEL,
451  MB_YESNO,
452  MB_YESNOCANCEL
453  };
454 
455  PSZ Icon_Styles[] = /* message box icon styles */
456  {
457  "EXCLAMATION",
458  "WARNING",
459  "INFORMATION",
460  "ASTERISK",
461  "QUESTION",
462  "STOP",
463  "ERROR",
464  "HAND",
465  "QUERY",
466  "NONE"
467  };
468 
469  ULONG Icon_Flags[] = /* message box icon styles */
470  {
471  MB_ICONEXCLAMATION,
472  MB_ICONWARNING,
473  MB_ICONINFORMATION,
474  MB_ICONASTERISK,
475  MB_ICONQUESTION,
476  MB_ICONSTOP,
477  MB_ICONERROR,
478  MB_ICONHAND,
479  MB_ICONQUESTION,
480  0
481  };
482 
483  // Always try to make the message box the foreground.
484  style = MB_SETFOREGROUND;
485 
486  if ( button == NULL )
487  {
488  // The default is an OK message box.
489  style |= MB_OK;
490  }
491  else
492  {
493  // Get the number of styles and search the button style table.
494  maxCnt = sizeof(Button_Styles) / sizeof(PSZ);
495  for ( index = 0; index < maxCnt; index++ )
496  {
497  if ( !stricmp(button, Button_Styles[index]) )
498  {
499  // Found a match. Only 1 button style can be used, so break.
500  style |= Button_Flags[index];
501  break;
502  }
503  }
504  if ( index == maxCnt )
505  {
506  // User specified an invalid button style word.
507  context->InvalidRoutine();
508  return 0;
509  }
510  }
511 
512  if ( icon != NULL )
513  {
514  // There is no default icon. The user can also explicitly specify no
515  // icon by using the NONE keyword.
516  maxCnt = sizeof(Icon_Styles)/sizeof(PSZ);
517  for ( index = 0; index < maxCnt; index += 1 )
518  {
519  if ( !stricmp(icon,Icon_Styles[index]) )
520  {
521  // Found a match. Only 1 icon stle can be used, so break.
522  style |= Icon_Flags[index];
523  break;
524  }
525  }
526  if ( index == maxCnt )
527  {
528  // User specified an invalid icon style word.
529  context->InvalidRoutine();
530  return 0;
531  }
532 
533  }
534 
535  if ( other != NULL )
536  {
537  if ( ! addMBStyle(other, &style) )
538  {
539  // User specified an invalid key word for one of the extra styles.
540  context->InvalidRoutine();
541  return 0;
542  }
543  }
544 
545  return MessageBox(NULL, text, title, style);
546 }
547 
548 
549 /**
550  * Push a new environment for the SysSetLocal() BIF.
551  *
552  * @param context The current activation context.
553  *
554  * @return Returns TRUE if the environment was successfully pushed.
555  */
557 {
558  return TheFalseObject;
559 }
560 
561 /**
562  * Pop an environment for the SysEndLocal() BIF.
563  *
564  * @param context The current activation context.
565  *
566  * @return Always returns FALSE. This is a NOP on Windows.
567  */
569 {
570  return TheFalseObject; /* return failure value */
571 }
#define MS_PREORDER
#define MS_POSTORDER
#define TheFalseObject
Definition: RexxCore.h:185
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 &)
bool callMacroSpaceFunction(RexxString *, RexxObject **, size_t, size_t, RexxString *, int, ProtectedObject &)
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)
#define Rexx_Error_Invalid_argument_range
Definition: oorexxerrors.h:419
#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
ssize_t wholenumber_t
Definition: rexx.h:230
#define NO_CSTRING
Definition: rexx.h:146
#define FILESPEC_NAME
#define MAX_FREQUENCY
static bool addMBStyle(CSTRING other, ULONG *style)
RexxRoutine1(RexxStringObject, sysDirectory, OPTIONAL_CSTRING, dir)
RexxRoutine2(CSTRING, sysBeep, wholenumber_t, Frequency, wholenumber_t, Duration)
#define FILESPEC_DRIVE
#define MAX_DURATION
#define DIRLEN
#define FILESPEC_LOCATION
RexxRoutine5(int, sysMessageBox, CSTRING, text, OPTIONAL_CSTRING, title, OPTIONAL_CSTRING, button, OPTIONAL_CSTRING, icon, OPTIONAL_CSTRING, other)
struct _ENVENTRY ENVENTRY
#define MIN_DURATION
#define MIN_FREQUENCY
#define FILESPEC_PATH
#define FILESPEC_EXTENSION