MUX-AE handler
	-- 2000/12/10 ska

I. Overview

The MUX-AE handler allows TSRs and alike programs to install extensions
to COMMAND, hence this function is called "Installable Commands".

Note: The innerworking of the MUX-AE interface has been revised heavily
by Eduardo Casino, mail appended.

There are two subfunctions (quote from RBIL):

--------l-2FAE00-----------------------------
INT 2F U - DOS 3.3+ internal - INSTALLABLE COMMAND - INSTALLATION CHECK
        AX = AE00h
        DX = magic value FFFFh
        CH = FFh
        CL = length of command line tail (4DOS v4.0)
        DS:BX -> command line buffer (see #02977)
        DS:SI -> command name buffer (see #02978)
        DI = 0000h (4DOS v4.0)
Return: AL = FFh if this command is a TSR extension to COMMAND.COM
        AL = 00h if the command should be executed as usual
Notes:  This call provides a mechanism for TSRs to install permanent
          extensions to the command repertoire of COMMAND.COM.  It appears
          that COMMAND.COM makes this call before executing the current
          command line, and does not execute it itself if the return is FFh.
        APPEND hooks this call, to allow subsequent APPEND commands to
          execute without re-running APPEND
SeeAlso: AX=AE01h

Format of COMMAND.COM command line buffer:
Offset  Size    Description     (Table 02977)
 00h    BYTE    max length of command line, as in INT 21/AH=0Ah
 01h    BYTE    count of bytes to follow, excluding terminating 0Dh
      N BYTEs   command line text, terminated by 0Dh

Format of command name buffer:
Offset  Size    Description     (Table 02978)
 00h    BYTE    length of command name
 01h  N BYTEs   uppercased command name (blank-padded to 11 chars by 4DOS v4)
--------l-2FAE01-----------------------------
INT 2F U - DOS 3.3+ internal - INSTALLABLE COMMAND - EXECUTE
        AX = AE01h
        DX = magic value FFFFh
        CH = 00h
        CL = length of command name (4DOS v4.0)
        DS:BX -> command line buffer (see #02977)
        DS:SI -> command name buffer (see #02978)
Return: DS:SI buffer updated
          if length byte is nonzero, the following bytes contain the uppercase
          internal command to execute and the command line buffer contains the
          command's parameters (the first DS:[SI] bytes are ignored)
Notes:  this call requests execution of the command which a previous call to
          AX=AE00h indicated was resident
        APPEND hooks this call

===

FreeCOM implements the functionality as FEATURE_INSTALLABLE_COMMANDS
and performs the actions as described as follows.

II. Order of Execution

This pseudocode of how FreeCOM interpretes a parsed command line
shall demonstrate the internals and differences from other implementations.

1. Expand environment variables

2. Extract any redirections and decompose pipes
	in result the command line buffer DS:BX does not contain any
	">", "<" or ">>" metacommands nor does a pipe appears
	in the buffer -- this may be a violation of compatibly as the
	example in Undocumented DOS by Schulmann contains code handle
	redirections, thus, it opens the possibility that an Installed
	Command would be allowed to kill pending commands of a pipe.

3. Extract the first command name (first non-blank word fully constructed
out of characters permisable in filenames, but excluding colon, dot,
and backslash).

4. If found a word, pass it through MUX-AE-00, on success MUX-AE-01 is called.
	If now BYTE DS:[SI] == 0, the interpretation of the command stops.

5. Search the internal commands list for the word left in STRING DS:[SI+1].
	If found, it is executed with the command line arguments starting at
	STRING DS:[BX+2+(DS:[SI])]. Note: DS:[SI] is the length of the command
	on return of MUX-AE-01.
	Then the interpretation of commands stops.

	-> RBIL does not mention any minimum length requirements for the
	   STRING DS:[SI] buffer. FreeCOM passes a full
  	   BUFFER_SIZE_MUX_AE (currently 255 bytes) buffer to MUX-AE.

6. Steps 4 and 5 are repeated until MUX-AE-00 returns false.

7. The original command line is tried as either drive change ("?:" syntax)
	or external command. Note: Any change of both the command name buffer
	(DS:[SI]) and command name buffer (DS:[BX}) are ignored.

III. Incompatiblies

- In earlier versions FreeCOM used the command line buffer (DS:[BX])
	to execute an external command, if applicable; thusm allowing an
	extension to re-write the command line. This is NO LONGER so, for
	compatibly with MS COMMAND.

- FreeCOM always uses BYTE DS:[BX+1] to determine the length of
	STRING DS:[BX+2]. If an Installed Command wants to change the
	arguments, this byte must be updated.

- FreeCOM supports the 4dos v4 extensions mentioned by RBIL.

- As FreeCOM has no meaning of "transient" portion, the segment
	registers simply point to the strings. DS == ES always.
	This may vary in future!

- If an extension rewrites the command, FreeCOM is to call MUX-AE again.
	This might lead to an infinite loop. In order to prevent such situation,
	FreeCOM calls the MUX-AE chain only a limited number of times, which
	can be customized through the MUX_AE_MAX_REPEAT_CALL setting of
	CONFIG.H.

IV. Side definitions

- The buffer DS:BX will always be larger than 12 bytes:
	  11 bytes (as 4dos v4 blank padded)
	+  1 byte (length byte)
	+  1 byte ('\0' byte at 13th position)

- The string at DS:[BX+2] is terminated by '\xd' and '\0' on entry to
	MUX-AE-00.
	There will always be a '\0' character at DS:[BX+2+[DS:BX]],
	thus the byte following the last byte, which is part of the string.

- If at any time BYTE DS:[BX+1] becomes greater than or equal to BYTE DS:[BX],
	FreeCOM aborts due to possible memory hazard.

V. Installable Command samples

The TOOLS subdirectory contains several samples of Installable Commands.
They shall verify a working MUX-AE interface and are not meant as
something useful, hence, they are simple and display a lot of debugging
information.

LOAD_ICD.C implements the loader.
ICMD_*.NAS implement various samples. They are NASM sources and must be
compiled using this command:
C> NASM -f bin ICMD_#.NAS
In result a ICMD_#.ICD is created.

The format of such sample is:
1. The MUX-entry point is the very first byte of the file.
2. A validation area is located at the end of the file:
	begin of file --> entry point
	code
	BYTE Pascal string of the name of Installable Command
	WORD offset within file to DWORD of where to write the original
		value of INT-2F to in order to be able to chain the interrupt
	WORD offset within file to write the name to (see above)
	BYTE value 0 (zero)
	.... comment <<is displayed while loading>>
	.... Installable Command Image ID (see LOAD_ICD.C)	

During the load and installation of the Installable Command by LOAD_ICD
the filename without path nor extension is placed as Pascal string
at the position mentioned within the validation record. The last byte
of the name is the last byte of the residently installed image of the
Installed Command, too.

The name is displayed within debug messages and is the name of the
newly installed internal command. Because LOAD_ICD updates the name
during the load of the Installable Command, the same Installable
Command can be installed with different names by copying the file
to a different file.

VI. Examples

NOTE: This section is out-dated!

1) The simplest form is an Installable Command that performs any action
	itself.
-> MUX-AE-00 returns AL == 0xFF
-> MUX-AE-01 performs the action and returns DS:[SI] == 0

2) Map an Installable Command into another internal command.
-> MUX-AE-00 returns AL == 0xFF
-> MUX-AE-01 modifies Pascal string of command name at DS:[SI]

3) Map an Installable Command into an external command
-> MUX-AE-00 returns AL == 0xFF
-> MUX-AE-01 modifies Pascal string of command name at DS:[SI]
Note: DS:[BX+2] must be checked that it contains no path delimiter
character, that are colon ':', backslash '\\', nor dot '.'. Otherwise
the remaining command line tail will be appended to the command name
and will complete the name passed back within DS:[SI]. Internal as well
as Installable Commands are separated by such characters!
E.g.: Installable Command "GO" and maps to "WALK"
C> GO\THERE
--> GO rewrites the command "GO" into "WALK" and then appends the
remaining command line tail and, in result, performs:
C> WALK\THERE

To overcome this problem GO must rewrite the command line tail, too,
by inserting a blank at position DS:[BX+DS:[SI]]. (DS:[SI] is the
length of the original command name, 2 in this example.)
-->
C> WALK \THERE

4) Disable an internal command.
-> MUX-AE-00 returns AL == 0xFF
-> MUX-AE-01 returns DS:[SI] == 0
The Installable Command must be named equal to the internal command,
e.g.: Installable Command "DEL" will disable the internal command
DEL and any external one, too.

VI. Clarifying mail by Eduardo Casino

Hello,

El jue, 10-06-2004 a las 15:25, FreeCOM escribi:


>> there is another testing release of FreeCOM that should fix the MUX-AE 
>> issue. Would you please test it?
>> http://freedos.sourceforge.net/freecom/packages/TESTING/


It works perfectly with FD APPEND, but...


>> It has, however, one known incompatibly with MS DOS:
>> 
>> It occurs to me that MUX-AE is called recursively (or repeatedly) when 
>> MUX-AE-00 returns 0xFF (true), but MUX-AE-01 returns with DS:[SI] != 0 
>> (aka TSR re-wrote the command, but did not executed it itself).
>> FreeCOM does not do this, but calls the MUX-AE pair only one time. Do 
>> you know something more about this?


I've written a group of silly test programs to check this and the
conclusions are:

When returning from MUX-AE-01 and DS:[SI] != 0, MS COMMAND.COM, again,
checks DS:[SI+1] and:
* If it is an internal command, executes the internal command without
calling again MUX-AE.
* If it is not an internal command, calls MUX-AE-00 to check if it is an
extension. In this case, executes the extension using MUX-AE-01.
* If it is not an extension, no matter what the content of DS:[SI+1] is,
tries to load and execute the _typed_ command from disk and with its
_original_ arguments, no matter if the extension has modified the
command line buffer.

Example 1: Extension to DIR
Imagine that this extension just prints "I'm DIR!" and then:
Case a) DS:[SI] unmodified. The internal DIR command is executed (you
get the directory listing.) This is just the same as the next case.
Case b) DS:[SI] contains 4, "ECHO" (or any other internal command). As
this is an internal command, it is executed.
Case c) DS:[SI] contains 3, "BAR" (or anything that it is not an
internal command). MUX-AE-00 is called and, if the "BAR" extension
exists, it is executed with MUX-AE-01. If it doesn't, the shell tries to
execute DIR from disk (DIR.BAT, DIR.COM or DIR.EXE)

Example 2: New extension "FOO"
In this case, it prints "I'm FOO!" and then:
Case a) DS:[SI] unmodified. The shell looks for the internal command
"FOO". As it does not exist, calls MUX-AE-00 and finds that it is an
extension, so it executes it using MUX-AE-01 and enters an endless loop.
This is, in fact, the same as case c.
Case b) The same as for example 1.
Case c) The same as for example 1, but now it tries to execute "FOO"
(FOO.BAT, FOO.COM or FOO.EXE)

For all cases in both examples, the shell uses the arguments in the
command line buffer (ignoring the first _updated_ DS:[SI] bytes), except
if command is not an extension in case c.

With FreeCOM 0.82pl3q two incompatibilities (or differences in
implementation ;-)) remain:

The first one is what you comment about FreeCOM calling the MUX-AE pair
only once. This prevents the looping problem, that's true, but
extensions should not allow this to happen. And if an extension tries to
execute another extension on exit, it won't work with FreeCOM, because
it won't call the MUX-AE functions again.

The second difference is that FreeCOM, at exit of the MUX-AE-01, uses
the arguments in the _updated_ command line buffer when it executes the
typed command from disk (when the command name buffer contains something
that it is not an internal command nor an extension) instead of the
_original_ command line, like MS-DOS does.  

These two programs show the incompatibilities:
http <<norobot>> perso.wanadoo.es/samelborp/test1.asm
http <<>norobot> perso.wanadoo.es/samelborp/test2.asm

test1.asm - Installs two extensions: FOO and BAR. FOO prints "Hello, I
am FOO!" and, on exit, calls BAR. BAR prints "Hello, I am BAR!" and
terminates". With MS-DOS, if you type "FOO" you see this:

C:\> FOO
Hello, I am FOO!
Hello, I am BAR!

With FreeCOM, you get:

C:\> FOO
Hello, I'm FOO!
Bad command or filename - "FOO".


test2.asm - Installs the extension "MORE". It prints "Hello, I am MORE!"
and, on exit, modifies the command name buffer with 8, "XXXXXXXX" and
the command line buffer with 11, "MORE XX.TXT", 13 . If you have a
TEST.TXT file with the line "This is a test.", in MS-DOS you get:

C:\> MORE TEST.TXT
Hello, I am MORE!
This is a test.

(MORE is executed with the original arguments)

With FreeCOM you get:

C:\> MORE TEST.TXT
Hello, I am MORE!
MORE: XX.TXT: No such file

(MORE is executed with the modified arguments)


WARNING: The programs are horrible, quick and dirty hacks with the only
purpose of showing the incompatibilities. Please, do not use them
against me  ;-) 

Regards,
Eduardo.


