Back to "execute" - as announced a while ago. As you probably know, shell scripts may take arguments, and quite like "real" shell commands, shell scripts also use a "template" that defines the type of the arguments, whether they are required or optional, whether they are switches, toggles and similar things. That's what the ".key" pseudo-command on the first line of a script does:
.KEY FROM/A/M,TO/A,CLONE/S
copy <FROM> <TO> <CLONE>
would be a simple wrapper to the "copy" command, where "FROM" is a mandatory argument, and multiple of them can be specified. "/A" means "mandatory", and "/M" means "one or more of them". "TO" is also a required argument, and "CLONE" is a switch.
Identical to binary commands, the letter behind the slash defines the type and requirements of the argument, and thus
.KEY YEAR/N
echo "We are in year "
would take a numeric argument, and refuse anything that is not a number.
That is how it is documented, and how arguments work. Execute uses the dos.library "ReadArgs()" function to parse off arguments, so all the nice "/" types come for free.
Unfortunately, however, none of the above examples work if you try them with the original v37 "Execute" command, and that is the one that came with workbench 3.1. Now, how come? Didn't I just say that "Execute" uses the dos.library "ReadArgs()", and that for this reason argument parsing should just work, because "ReadArgs()" surely works? Why doesn't it work for "Execute" then?
Well, this requires a bit of background. What ReadArgs() does is that it fills an array of string pointers, one for each argument. Or at least, this is what "rdargs()", the BCPL counterpart in Tripos did back then in the Kickstart 1.3 times. So the BCPL "Execute" from 1.3 did just work.
The problem is that kickstart 2.0 added a couple of new argument types. So for example, "/N" for "numeric argument" is new, and "/M" for "multiple arguments" is new. It did not exist in 1.3, and these two types were added by "arp", the amiga replacement project, and became later part of AmigaDos.
What "arp" also did is that it changed a bit the convention of how "ReadArgs()" works. So for example, for a "/N" argument, it is of course much more convenient to return the number directly instead of a string, so "ReadArgs()" fills in a pointer to a LONG instead of a pointer to a character array. This is certainly much more convenient than having to parse the number *again* in the program - ReadArgs() can do that already.
Similarly, for "/M" ReadArgs() does not leave a string pointer in its return vector, but rather, returns a pointer to a string array, i.e. a pointer to a pointer of characters. It has to, since "/M" may take multiple arguments on the console, so one string is not enough.
Unfortunately, nobody told "Execute" on the changed syntax, and nobody dared to touch the execute sources either. Thus, if the ".KEY" contains a "/N" argument, the v37 execute (the official 3.1 one, note that there is no v40 execute) expects a string pointer in the arguments, but just finds a pointer to a number there. And does strange and wonderful things, of which an "Enforcer hit" is probably the most harmless. Outch!
Hence, someone (the software distillery, namely) changed the interface of an Os function behind the "back of Execute", and thus broke a command. Or rather, they forgot to update Execute.
Unfortunately, this change means that now execute also needs to be aware of the type of arguments it has to parse off, and has to convert the numbers or multiple strings back to one single string, namely those that it requires for substitution of the parameters of the script, i.e. the things enclosed in angle brackets.
Hence, for V45 execute (and also V44), a couple of things unfortunately happen twice: First, ReadArgs() converts strings to numbers for /N arguments (because it always does), and "Execute" has to convert them back (for parameter substitution) such that the command in the script has to convert them back from string to number again.
There are more types like this: "/S" just leaves a boolean flag instead of a string, so "Execute" has to re-insert the name of the switch instead of the boolean value. The same goes for "/T", which is actually not really a toggle, but takes two values, "ON" or "OFF", or alternatively "YES" or "NO". The documentation is not quite appropriate.
"/M" returns a pointer to a string array, so "Execute" has to merge all the strings together.
And last but not least, do not forget that Execute also has to take care of proper quoting arguments with blanks in it - and escaping of the asterisk and the quote in the strings itself. We had this before.
So, it's actually a bit more complicated than it seems.