Reginald is a free, open-source REXX interpreter, based upon Regina, a GNU-licensed REXX interpreter originally written by Anders Christensen.

Reginald was originally based upon the Regina 0.08f sources, with bug fixes, improvements, and new features added to the original code. The original code has been reworked so significantly (nearly every function in the original sources has changed), and so many new features added, that it really is a new interpreter. In order to avoid conflicts with the original version of Regina, I've named my version Reginald (ie, Regina's younger brother). Reginald will peacefully co-exist with Regina. You do not need to remove one from your system in order to use the other one. You can even run Regina and Reginald simultaneously, running the same REXX script even, if you want to try them out side by side. (But note that Reginald has its own script launcher. The Regina frontend, Regina.exe, does not work with Reginald).

So what's the deal with Reginald? Well, as mentioned, it does have a number of bug fixes, improvements, and new features. Among the most important is the fact that, due to a complete redesign of some fundamental parts of the sources, Reginald does not leak memory, even when called by a host program using the REXXSAA API. (Due to a design issue with the original sources, Regina can leak memory. For further information, read my Where's my RAM going? article). It also uses less memory while running, thus impacting system performance less. It's more configurable by a Win32 enduser. It better adheres to Microsoft's standards for Win32 certification. It has many new features/functions useful to Win32 users which will be discussed below. It has a fairly easy way to allow direct access to operating system functions, and functions in other add-on packages not designed specifically for REXX. And it comes in an easily installable and useable package.

Reginald comes in a self-installing package for Win32. REGINALD.EXE is the installer. By simply running this executable, it installs the entire Reginald package. This package includes:

REGINALD.DLL -- The Reginald interpreter in DLL form.
REGINALD.HLP -- Online help for REXX errors.
RXLAUNCH.EXE -- A REXX Script Launcher to run REXX scripts.
RXLAUNCH.HLP -- An online instruction manual for RXLAUNCH.
RXADMIN.EXE -- An Administrative Tool for setting up the Interpreter.
RXADMIN.HLP -- An online instruction manual for RXADMIN.

Additionally, the installer creates Desktop icons for all of the components (with informative names such as "REXX Script Launcher" for the Script Launcher), sets up file associations so that the enduser can simply click upon the icon for a REXX script (whose name ends in .rex) to start it running, and even installs an uninstall utility that cleanly removes the entire package, including cleaning out any Reginald registry entries. (Reginald will be listed in Control Panel's "Add/Remove Programs" utility).

So after simply double-clicking the icon for REGINALD.EXE, an enduser has installed REXX upon his computer, and he's ready to start running REXX scripts by double-clicking upon their icons. (As you'll discover later, he doesn't even need to deal with a command line interface). Also, Reginald will be setup to make it easy for other programs to utilize REXX as their macro language. (A separate developer's document details how to properly utilize Reginald so that everything runs smoothly, easily, and transparently for the Win32 user).

To aid in my goal of creating a package that can be easily used by any Win32 user/developer, Reginald's Script Launcher is designed even to make it possible to create an auto-run CDROM that contains a REXX script. The launcher is smart enough to recognize when Reginald is not yet installed, and will therefore first run REGINALD.EXE to install Reginald. Then, it will run your REXX script on the CDROM. What you need to do is create an autorun.inf (ie, plain text) file that contains the following text:

open=rxlaunch.exe yourscriptname

where yourscriptname is the name of your script (optionally enclosed in quotes if its name contains embedded spaces).

Then, you put that AUTORUN.INF, RXLAUNCH.EXE, REGINALD.EXE, and your REXX script into the root directory of the CDROM. You have now created a CDROM that will autorun your REXX script, even if REXX is not yet installed upon the system. The first time that the user inserts the CDROM in his drive, he will be prompted to install Reginald, and then after Reginald installs, your REXX script will automatically run. The next time he inserts the CDROM, your REXX script will automatically run as it normally would (ie, the user won't be prompted to install Reginald again, since it is already installed) with no startup speed penalties incurred.

Incidentally, the Reginald installer itself is also smart enough to recognize when Reginald is already installed, and does proper version-checking so that older versions do not overwrite newer versions, and has the option to not re-install Reginald if a suitable version is already installed. So, if a suitable version of Reginald is already installed, your REXX script autoruns the very first time without even needing to prompt the user to install REXX.

To the best of my knowledge, all severe memory leaks have been fixed. (My work with the original Regina sources initially started as a project to fully document those sources. In doing this, I learned a great deal about the intricacies of Regina's design, and uncovered a number of bugs which I have fixed in my version, as well as brainstorming various improvements to the code). If you have problems with memory usage under Regina, you should find that Reginald greatly alleviates this problem.

Reginald is a bit more "run-time configurable" than Regina. What I mean by run-time configurable is that Reginald offers an easy way to configure more default settings than Regina. Some Regina configurable defaults are only available by recompiling the Regina sources. For example, want to have the interpreter come up by default with the LINES() built-in function returning an actual line count (as opposed to just "1" if there are lines in the stream)? Well, you have to recompile the Regina sources. Not so with Reginald. Reginald's Administrative Tool lets anyone configure those options at any time, and then save his settings in the Windows registry.

And unlike Regina, Reginald uses the Windows registry to save settings, rather than using Environment variables (which are problematic under Windows since they are per-process settings, and are "lost" after a process ends. This is one reason why people using Regina under Win32 often have trouble getting Regina to "remember" the path to their REXX scripts, and thus discover that Regina seems to constantly forget where you keep your REXX scripts). Using Reginald's GUI Administrative Tool, any enduser can easily set all options that a script could set with the OPTIONS instruction, plus the settings for NUMERIC FUZZ, NUMERIC DIGITS, TRACE, and other such settings. He can also set all of the paths for his REXX scripts, and save those paths permanently.

Furthermore, by using the registry, this means that if an administrator utilizes the multiple user features of Win95/98/NT/2000/XP, then each user can have his own settings for Reginald, including his own script paths, automatically setup for him when he logs in.

Reginald's script launcher also has a few advantages over Regina's frontend. First of all, you no longer have to hassle with redundant quoting of arguments passed to your REXX script when running the Script Launcher from a batch file. For example, let's say that you want to run a script named "blort.rexx" and pass it the 3 arguments of "one", "this is argument two", and "the third one". You would invoke the Script Launcher from the command line (or a batch file) as so:

rxlaunch.exe blort.rexx one "this is argument two" 'the third one'

As you can see, to pass an argument that has imbedded spaces, you need to quote the argument. You can use either double quotes or single quotes, as shown above. But you don't need to use redundant quote characters. Reginald's script launcher does not "eat" quote characters, unlike Regina. This has been a big problem with trying to set up a file association with Regina. The file association that Reginald sets up works with REXX scripts whose names have embedded spaces.

Furthermore, if you run the Script Launcher without even a script name, it will open a File Dialog so that you can pick out the REXX script to run. (By default, it will open in the first script path that you set with the Administrative Tool). What this means is that, if an enduser simply clicks upon the desktop icon for Reginald's Script Launcher, he can pick out a REXX script to run using the familiar Windows File Dialog. He will not be presented with a blank Command Prompt window that sits there and appears to be doing nothing to an enduser.

The Script Launcher also supports drag and drop. Drag the icon for any REXX script over the icon for the Script Launcher and drop it onto Script Launcher's icon to run the REXX script.

When Script Launcher is running a REXX script, the name of the script is shown in the title of the display window. (Regina simply displays "Regina" as its window title). So, if the enduser is running multiple scripts, he can tell which window is running which script.

If an error occurs in running the script and the script terminates, Script Launcher won't automatically close down the display window. Instead, it will keep it open so that the user can actually read the error message, until such time as he manually closes the window. (Of course, if the script exits without any errors, then Script Launcher closes down the window automatically). The fact that Regina automatically closes down its window when a script terminates, even when an error has occured, results in the enduser perhaps missing important error messages (unless he runs Regina from an already open Command Prompt window -- something that Windows endusers do not want to have to do any more), and scratching his head wondering why the script did not seem to do anything.

Reginald has a built-in graphical debugger. It has its own window in which it displays your original source lines. And you can set breakpoints just by clicking on any source line, and "run" the script to that point. As each source line is interpreted, the debugger highlights that source line, so you can easily see where you are in the script. The debugger has buttons you can click upon to Run, Step, or Redo. And there is a separate area to show you the value of variables as they are interpreted. There is also a box into which you can type any REXX instruction to be interpreted on-the-fly. Some of the things you can do with this are:

You can jump to any label in the script simply by typing SIGNAL followed by the label name. You can also end debugging (but continue running the script) just by typing a RETURN instruction. Or you can abort the currently running script by typing EXIT. (If the script was launched by another script, you will be returned to the parent script. Whatever you typed after EXIT will be returned to that parent script).

Reginald's graphical debugger makes it vastly easier to debug a REXX script than using Regina's TRACE modes.

Further improvements that I've made to the Reginald interpreter include:

You create/query/set/delete the value of registry keys using the VALUE() built-in function. To do this, use "WIN32" for the Environment name. For example, to query and print the value of "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Personal", you would do:

SAY VALUE("Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Personal", , "WIN32")

(This would normally SAY "C:\MY DOCUMENTS". Quite useful).

An INTERPRET string may contain a RETURN or EXIT statement. For example, the following is legal:

INTERPRET "SAY 'This is the end';EXIT 0"

The above statement would end the script at the EXIT statement. If you had used a RETURN instead, then the INTERPRET would have ended there. For example, you can abort an INTERPRET string part-way through by using a RETURN as so:

INTERPRET "IF WantToEnd = 'YES' THEN RETURN;SAY 'Continuing...'"

Although an INTERPRET string itself cannot contain a label, you can do a SIGNAL to jump to some label in the script.

Reginald immediately triggers the HALT condition when the user presses CTRL-C while the REXX script is executing a PULL statement (ie, while waiting for the user's input).

Reginald allows your script to utilize a new MSGBOX OPTION. Whenever a SYNTAX error occurs (and your script isn't trapping SYNTAX), instead of the message being dumped to the console window, it pops up in a message box. This box has a "Help" button which, when you click upon it, will open up an online manual that explains what that specific error is likely caused by, and remedies to fix it. The message box is a bit easier on the eyes than seeing some lengthy line haphazardly spewed across a console window (and likely character-wrapped at some odd point). The information is laid out in a more orderly fashion, with different pieces of information in different areas of the message box.

Better yet, any REXX script can turn on this pop-up error message box simply by 'OPTIONS MSGBOX'. It doesn't matter what host is used to launch the script. It will work with any program.

Reginald implements CALL being used with any condition, including NOVALUE, SYNTAX, etc.

Reginald implements Object Rexx's DO OVER statement.

Reginald allows a child script to optionally be able to access (ie, query/set) any of the variables in the parent script.

Reginald allows a parent script to trap conditions that are not trapped in another REXX script that the parent has called (ie, a child script). Take an example of what happens if you write "call on halt; call sub" and there is a HALT signal during the execution of the subroutine.

What happens upon a given condition depends upon whether the sub and/or the caller have trapped that condition.

If neither has trapped HALT, then the whole thing terminates right there with an error message (either printed to the console window, or with OPTIONS MSGBOX, in a message box).

If the sub is trapping HALT, then the sub's HALT handler is executed, regardless of whether or not the caller is also trapping HALT. (ie, The caller never becomes informed of a HALT unless the sub is specifically written to somehow inform him of such). SIGL and CONDITION('D') are how they would normally be set.

If the sub is not trapping HALT, but the caller is trapping HALT, then the sub is aborted immediately. It returns no value to the caller, and the caller's HALT handler is executed. SIGL is set to the line number in the caller where he made the call to sub(). (ie, It's like how Object Rexx does it). But the error description returned by CONDITION('D') reflects my new error message which informs him of where the error really occurred and what it really was.

Let me give you some code examples to illustrate (ie, the ones that I've used to test Reginald):

/* Here's the REXX subroutine we'll be calling. It's name is abort().
   All it does is loop forever until the user initiates a HALT. And
   since HALT is not trapped, this script aborts then.
SAY "Press CTRL-C to abort..."
Now consider if we run the following script:
/* Test if caller can trap an untrapped HALT in a subroutine */
val = 'Good'
val = abort()
SAY "ERROR: Caller did not trap the untrapped HALT in subroutine"
EXIT "Bad"
SAY "Caller successfully trapped the untrapped HALT in subroutine"
SAY "ERROR: SYNTAX handled before previous HALT"
EXIT "Bad"
This results in RexxStart() returning RX_START_OK and the return value is "Good" (ie, the aborted call to the sub does not change the value of 'val'). You never see the message "ERROR: Caller did not trap the untrapped HALT in subroutine". You do see the message "Caller successfully trapped the untrapped HALT in subroutine".

Now consider running the following script:

CALL abort()
SAY "ERROR: Untrapped HALT in subroutine did not terminate caller"
EXIT "Bad"
This causes the REXX interpreter to abort the whole thing. It displays the message 'Error 4 in "abort" at line 6: Program interrupted', and then RexxStart() returns with RX_SCRIPT_ERROR and the return string is nul (ie, strptr and strlength fields are 0). You never see the message "ERROR: Untrapped HALT in subroutine did not terminate caller". Now let's take your example and make it into a complete script:
/* Test if caller can trap a untrapped HALT in a subroutine via CALL
SAY "Caller successfully trapped the untrapped HALT in subroutine"
This results in RexxStart() returning RX_START_OK and the return value is "Good". You see the messages 'Caller successfully trapped the untrapped HALT in subroutine', and 'Script "abort" failed at line 6: SIGINT'. The latter message is my new error message format returned from CONDITION('D') when a caller's condition has been raised due to some untrapped condition in the sub. Note that you have the name of the sub in which the condition was raised, the line number in the sub where the condition occurred, and the specific error message which is simply "SIGINT" in this case. SIGL is set to the line # where the caller made his call to sub()) (ie, line 3).

One really nice thing about this is that a programmer can put all of his "clean up code" in his top level script, slap a SIGNAL ON SYNTAX and SIGNAL ON HALT in it, and whenever the user closes down his program (which includes closing the console window in MS Windows -- that generates HALT), he gets to clean up everything in one place. He doesn't need to pepper every subroutine he calls with SIGNAL ON SYNTAX and SIGNAL ON HALT in order to prevent the interpreter from yanking the rug out from under him.

Reginald's Script Launcher does not open a console window if the script never outputs/inputs anything to the standard output (for example by using SAY, PULL, LINEOUT(), LINEIN(), CHAROUT(), etc). So, if you're going to be using some add-on to create your own graphical user interface, such as REXX Dialog, then the user never will see some useless console window hanging around.

Reginald's OPTIONS LABEL can be used to test for duplicate labels in a script.

Reginald supports a NULL environment (meaning that any function return that isn't assigned to a variable or used in an expression is not automatically passed to the shell). So, you don't get those "command not recognized" error messages in the console window if you forget to use the return from a function.

Reginald supports auto-loading function libraries, so you don't have to RXFUNCADD() for every add-on you use.

A number of Regina's built-in functions have been improved.

I have fixed the STATE() builtin function, so that it really does verify if a file exists (and returns 0 if it does).

The DIRECTORY() function now raises the SYNTAX condition if DIRECTORY() fails to query or change the directory properly. You can retrieve an accurate error message with CONDITION('D'). Regina leaves you in the dark whether it fails and why, and simply returns an empty string if there's an error.

The slightly similiar CHDIR() command will not raise any condition if it fails (ie, it returns a '0' for failure), but will set an informative error message that you can retrieve with the RXFUNCERRMSG() built-in function.

The GETENV() built-in function will raise NOTREADY if you've trapped that condition, and the environment variable doesn't exist.

The STREAM() FSTAT command has quite a few different options. It will correctly return the size of a large drive, or the free space on one, for example.

Reginald's LINES() and CHARS() work on the default input stream, so you can use them to check if the user has typed anything into the console window.

The RANDOM() built-in function is made more random.

The SLEEP() built-in can take a fractional amount of seconds. For example, to sleep for 250 milliseconds, you can do:

CALL SLEEP("0.25")

The BEEP() built-in has been fixed so that it can properly be called without any args. Furthermore, it has been enhanced to play WAVE files. If the first argument is not a number, it is assumed to be the name of a WAVE file to play. For example, to play the WAVE file named CHORD.WAV in the "C:\WINDOWS\MEDIA" folder:


This will play that WAVE file once. If you supply a second argument (ie, the duration), this will indicate how many times you wish the WAVE file to be repeated. For example, to play that WAVE file 3 times:


Normally, Reginald halts your script while the WAVE file is playing. But if you pass a duration of 0, then this is a special case. As soon as the WAVE file starts playing, Reginald returns from your call to BEEP() (ie, asyncronous playback) so that your script can go on to do other things while the WAVE is playing. When the WAVE finishes playing once, then playback will automatically end. But, you can prematurely stop the playback at any point by passing a nul WAVE name as so:


Indeed, if you call BEEP() again, to play another WAVE file, without first stopping the previously playing, asyncronous WAVE, then the second WAVE file will not play.

The FILESPEC() built-in has an additional command 'E' to return only the file's extension. For example:

SAY FILESPEC("myfile.txt", "E")

displays .txt.

For very large hard drives or large files under Windows, Regina's STREAM('C','FSTAT') could report an incorrect size. Reginald fixes this.

Reginald trims any leading and trailing spaces off of the names of streams (so you don't have to use STRIP()) on stream names before calling any stream functions. This is true of QUALIFY() as well. Additionally, QUALIFY() replaces any directory separator characters in a filename per the operating system. For example, under Windows, any '/' character is replaced with '\' character. (STREAM's 'QUERY EXISTS' does likewise). So, you can use this as a way to resolve filename discrepancies upon various platforms without needing to query the OS yourself and monkey with the filename accordingly.

Reginald sets the NOTREADY condition (if trapped) when a file doesn't open with STREAM(). But it also returns a proper error string so that you don't need to trap NOTREADY in order to detect when STREAM() fails to open a file. For an error, Reginald's STREAM() returns the string "ERROR: xxx" where xxx is the error number from the operating system. Regina returns a nul string instead, for an error. That made it impossible to detect an error if the script wasn't handling NOTREADY. So too, when you use STREAM() to close a stream, and there's a problem, Reginald returns that "ERROR: xxx" string.

Furthermore, anytime there is an error that would normally trigger the NOTREADY condition, when you do a STREAM('D') (or CONDITION('D')), you get back an error message from the operating system that specifically tells you what the problem is with that stream. With Regina, many times it just gives you a generic "File error" message that doesn't really tell you much about why the call failed. So, Reginald's stream API is a bit more robust in terms of error reporting.

The CONDITION() built-in function has an additional command 'M' which displays a pop-up message box containing any error message associated with the last raised condition. This message box also displays the source line in error. The message box also has a Help button, which when clicked, opens up an online help book to a page that describes the error in more detail and suggests possible remedies. If the condition was a NOTREADY condition, then CONDITION('M') will display a very specific error message from the operating system (instead of Regina's generic "File error" message).

Reginald has a number of useful, new built-in functions. Many of these do things that you would use shell commands such as 'dir', 'copy', 'del', etc to do. But the advantages are that Reginald's new built-ins are faster, make it easier for you to handle errors, and do not require a console window open.

DIR() can be used to create or delete a directory. This can create an entire directory tree, for example, DIR('C:\MyDir\MySub') will create both MyDir and MySub if they don't exist.

COPYFILE() copies one or more files. You can use wildcards for pattern matching, for example COPYFILE('*.bat', '*.bak') copies all files in the current directory that end with .bat, renaming them with a .bak extension.

DELETEFILE() deletes one or more files. You can use wildcards for pattern matching, for example DELETEFILE('*.bak') deletes all files in the current directory that end with .bak.

MOVEFILE() moves one or more files from one location to another. When the file remains on the same drive, this essentially renames a file. You can use wildcards for pattern matching.

EDITNAME() transforms a filename based upon a template containing wildcards.

LOADTEXT() reads all of the lines of a text file into a stem variable.

DRIVEINFO() queries information about a drive such as its free space.

DRIVEMAP() queries the drives by type, such as listing all CDROM drives.

MATCHNAME() tests for the existence of a certain file or directory, perhaps with certain attributes. Alternately, it can enumerate all of the files in a given directory, perhaps filtering only those that match a certain template containing wildcards, or which have certain attributes.

PATH() gets a full pathname from a filename, queries the current directory, sets the current directory, and/or splits a pathname into separate elements.

SEARCHPATH() locates a file, or queries path environment variables, or returns the path to special directories such as the current user's desktop, or the Program Files directory.

VALUEIN() reads in a numeric value from a binary (ie, non-text) file.

VALUEOUT() writes out numeric values to a binary (ie, non-text) file (ie, in non-text format).

EXPAND() replaces tab characters with spaces, or vice versa.

ITERATEEXE() launches another non-REXX program a multiple number of times, implementing filename pattern matching. Also, can print files, or execute other "actions" upon files.

Reginald's FUNCDEF function lets you easily register, and then directly call, any function in the operating system, or other add-on libraries not specifically designed to support REXX. This opens up REXX to lots of support written for other languages. You can now use that support, or go directly to the operating system to "roll your own code" for what you need to do. You're not limited to using only those built-in functions, or functions in DLLs that are specifically written for REXX.

There is good news aplenty for developers who are writing (non-REXX) programs which use the REXXSAA API. My main interest in working with Regina concerned addressing some deficiencies I saw in that API. Reginald's REXXSAA API has been vastly cleaned up and improved. I have finished adding some missing API's, such as RexxRegisterExitDll() and RexxRegisterSubcomDll() and added some extra features, such as the ability to "autoload" function libraries (ie, so that individual scripts do not need to register the functions in those libraries), autoloading Environments (which a script can itself load), and autoloading Exit Handlers (so that an app developer can create a DLL full of Exit Handlers that get transparently utilized by any program which uses Reginald's REXXSAA API).

Indeed, many of the new features in Reginald's Script Launcher are made possible due to the improvements I made to Reginald's REXXSAA API. For example, you can now create your own console and Reginald will automatically recognize it and use it for input/output. You don't need to hassle with Exit Handlers, even if you've written a "Windowed" (ie, non-console) Windows program. And if you choose to open your own console, Reginald's SAY and PULL commands will transparently work with it. (Regina's don't). Furthermore, Reginald's HALT handling has been improved to accomodate Windowed apps.

The way that the REXXSAA API handles error conditions has been made much more consistent. For example, with Regina, if you were to call RexxVariablePool() and it failed due to a memory error, it would raise a SYNTAX condition in the script or write an error message to stderr. And yet if it failed because of some other problem, it would do neither. Such inconsistent behavior typically results in either the script and the host program both trying to deal with the problem, or both of them missing the problem. In either case, the user typically gets confused by redundant error messages/handling, or doesn't get informed at all. With Reginald, there is now a consistent, and well-documented approach toward handling errors in the REXXSAA API, so your program knows exactly when/where/how to deal with those errors. Indeed, Reginald's error handling has been very closely scrutinized so that it corrects possible scenarios where the interpreter could get bogged down in endless recursion while dealing with error handling. I'm a stickler for error handling and resource management, and I have expended a great deal of time trying to ensure that Reginald doesn't leak memory, and that its error handling does truly deal with every possible error situation. (Regina's memory leaks are particularly hard on the developer using the REXXSAA API. Read my story on How I got involved with Regina's sources for a more detailed explanation).

When a REXX script calls another script like a subroutine, Reginald first searches its macro table for that name, and executes any macro it finds with that name. This means that any macros you "install" via Reginald can be utilized by any script you launch. Furthermore, Reginald now provides a means to query if a macro has been installed, and to delete a macro from Reginald's internal macro table.

Reginald implements the RXMSQ Exit Handlers. It also implements the RXHLTCLR Exit Handler.

Reginald implements multiple internal queues and the RXQUEUE() command, but does not yet implement external queues a la Regina 2.2. (I think that the method Mark choose, which assesses internal queues through sockets, is unnecessary overhead, and that a better, more efficient design can be implemented. This is something that I'll be working on, and external queue support will be a transparent add-on to Reginald so that if you don't need external queue support, you don't take the speed and memory penalty). Anyway, RXSOCK is more versatile when you need to exchange data with other computers, and I've completed a freely available, improved version of RXSOCK for WIN32, also available on this site.

One good thing that Reginald does which has not been properly implemented in Regina -- The queue name arg passed to any external function accurately reflects what is the name of the currently selected queue. So too, RexxVariablePool() will accurately fetch the current queue's name.

Later on, I want to write up some good documentation for endusers and developers, to help ensure that REXX gets utilized more under Win32. Right now, I think that it is vastly underutilized, especially given the fact that it is freely available, and there is a great need for Windows programs to offer better scripting/macro support than they typically do. I have always felt that clear, explicit documentation is an essential element to the success of a development language. Before developers can support something, they have to know that it is out there, what it specifically can do for them and how, and they need to be convinced that it can be easily, cheaply, and quickly used to increase the value of their software.

It will involve quite a bit of time and work to create such a resource for Rexx developers/endusers. But, as you can now judge from the extent of the changes to the Regina sources in order to create Reginald, a great deal of time and effort has already been spent upon realizing what I am happy to be able to offer today.

Please try out Reginald, and let me know if something isn't working for you. Hopefully, you can put it to good use, and help promote Rexx to Win32 users.

To download Reginald (or other useful REXX related items), go to the Software for Rexx Users page.

Jeff Glatt