jls@killer.DALLAS.TX.US (Jerome Schneider) (11/14/88)
COMMAND.COM PATCHES for MKS Here are two patches to PC-DOS 3.3 COMMAND.COM to better allow the MKS toolkit to co-exist with command.com and with many application programs that shell to DOS or another program via command.com. The first patch improves functionality when an MKS /etc/passwd file entry specifies command.com as the login shell. By adding a /L (or -l) option to command.com for "login", the root directory can be searched for an AUTOEXEC.BAT file to execute before responding to console commands. The second patch eliminates the "Specified COMMAND search path bad" error from programs that call command.com (via the system() or EXEC functions). Many applications written in MSC 4.x or 5.x use the system() library call that fails to examine the switch char (- or /) before issuing a hard-wired exec on ($COMSPEC /c "string to be executed"). (As for the legitimacy of hacking DOS files, it seems that once you have modified COMMAND.COM with the MKS patch script to disable conflicting internal commands, you already have produced a non-standard copy. Just be sure to make these patches to a *copy* :-) of command.com.) SOME BACKGROUND ON COMMAND.COM OPTIONS AND FLAGS. Several undocumented features are present in PC-DOS 3.1 - 3.3 command.com. A partial list of the command line options includes the following: /p - Makes this copy of command.com permanent in memory by trapping the "exit" signal. The autoexec.bat file, if any, is run before accepting commands from the console. A new environment, containing only PATH= and COMSPEC= entries is built. /d - When present with the /p flag to make the shell permanent, /d means "Don't" execute autoexec.bat when starting shell. (I can find no application that uses this option.) /f - Always issue a "Fail" response by replying automatically to critical errors at the Abort, Retry, Ignore message. (This seems to work erratically on anything except internal command file errors, so I never use it.) /c "str" - spawns a sub-shell of command.com to execute the command in string "str", which can be either an internal command or an EXE, COM, or BAT file. If /p or /d options have been requested, they are cancelled by this option. /e:nnn - establishes the default environment size when starting up a /p (permanent) command.com. If present when spawning a sub-shell and if nnn is greater than the passed environment, a new environment nnn bytes (nnn paragraphs in DOS 3.1, 3.2) is created and the passed environment is copied into it. <path> - Any string not preceded by the current switch char (- or /) is examined to determine whether it is a legal path name or a valid dos device. If it is a legal path, a search is made to determine if the file command.com exists in the named directory. If either of these tests fails, the ubiquitous "Specified COMMAND search directory bad" error is displayed and a new environment with just PATH= and COMSPEC= entries is created. If, however, a command.com was found, the COMSPEC entry is modified to point to the new command.com. (It was probably sloppy programming that allowed this option to even be used in any shell spawn except the /p (permanent) type.) <device> - If the path doesn't point to a legal directory, the path name is checked (via ioctrl() test ISDEV) for a legal device, such as CON, COM1, AUX, etc. If a valid device, the command.com shell uses that device as the standard in/out. Note that the options are shown with a "/" switch character, but command.com can properly detect a '-' switch char, if set, on the option flags. ADDING A LOGIN OPTION TO COMMAND.COM The MKS toolkit login command works very nicely for "login" to a specific applications after setting a HOME directory and running ~/profile.ksh at /bin/sh startup. Some applications, for reasons of memory requirements, incompatibility with "-" switch char or "/" file path character, may need to start up the DOS command.com as an /etc/passwd login shell instead of ksh. Because login initializes some of the environment variables using "/" in the file paths, simply invoking command.com from an /etc/passwd line will not allow us to run AUTOEXEC.BAT (to redefine variables or the switch char, for example), before responding to the console for commands. It is tempting to try "/p" in the /etc/passwd entry to force command.com to execute /AUTOEXEC.BAT, but because conventional DOS login shells have no parent process for the root COMMAND.COM to exit to, the /p option traps all "exit" commands (SIGTERM) to keep COMMAND.COM "permanent" in memory. This prohibits any exit back to the MKS inittab to respawn a new login. The patch below allows command.com to be used as a login shell by adding a new /L ( or -l) login option to provide a search of the root directory for an AUTOEXEC.BAT file to execute before responding to console commands. To add this feature to COMMAND.COM, we need two things. First, we must add an option flag, consisting of an upper or lower case L immediately preceded by an occurrence of the current SWITCH char (a - or /), to the command.com option parser. Second, we must locate the code that modifies the undocumented memory location holding the AUTOEXEC.BAT execute flag. Because the rarely used option flag "/d" already exists in COMMAND.COM, it can be modified to test for "L" instead of "D". Also, since the existing /d code already alters the "autoexec" byte when the /d flag is found, we only need to change the value inserted into the byte. The legal values for the autoexec byte are: 0xff - the initial value when command.com loaded. Any non-zero value means do not run autoexec.bat when starting shell. 0x01 - When the undocumented "/d" flag is encountered, indicate that the "don't" flag was found. Do not run autoexec.bat. 0x00 - If zero, the autoexec.bat file, if any, should be run. By locating the code that sets the flag to 0x01 when the /d flag is parsed, it is rather easy, then, to change the option test from D to L and to change the execute flag value to 0x00. This will insure that the default (0xff) value will not be altered except when the /L is detected. -------------------------- PATCH #1 --------------------------- For IBM's PC-DOS 3.3 (my copy is 25,307 bytes dated 3/17/87). Use debug (on a copy, of course) to unassemble the following: debug command.com -u f9d fa9 xxxx:0F9D 3A064D16 CMP AL,[164D] ; is option a "d"? xxxx:0FA1 7507 JNZ 0FAA ; skip if not, else xxxx:0FA3 C6062E1601 MOV BYTE PTR [162E],01 ; set 0x01 in flag xxxx:0FA8 EBA6 JMP 0F50 ; and continue scan. If your display matches this, change the /d flag byte from 01 to 00: -e fa7 xxxx:0FA7 01.00 and confirm the change: -u fa3 fa7 xxxx:0FA3 C6062E1600 MOV BYTE PTR [162E],00 ; set 0x00 in flag Now, lets confirm the value of the "d" option held in memory by: -d 164c 164e xxxx:1640 ..... 70 63 61 .... pdc ; the p, d, c options If this matches, then lets change the option flag from "d" to "l": -e 164d 60C9:164D 64.6c ; change byte at 164d from 64 to 6C. -d 164c 164e ; and confirm the change. xxxx:1640 ..... 70 6C 63 .... plc ; d becomes l At this point, write the changed command.com back to disk and quit: -w Writing 62DB bytes -q Your patched version of command.com should now run /AUTOEXEC.BAT first when called with a -l or /L option. You may wish to create a temporary autoexec.bat (in the root dir /) to test this. Remember that although login changes to the $HOME directory before starting command.com, the autoexec.bat file is only looked for in the DOS root (/) directory. I use the /autoexec.bat like a global initialize file (similar to the /etc/profile.ksh) that is executed by all passwd entries with command.com as the shell. I use the following as the LAST line of my /autoexec.bat: IF EXIST PROFILE.BAT PROFILE This tests for a profile.bat in the $HOME directory and executes it if one is found. This combination gives me a global/local init paradigm for those applications using command.com as a login shell. Remember that if your profile.bat or autoexec.bat will set a lot of environment variables, add an appropriate "-e:nnnn" to the passwd entry to reserve a larger environment for the login. Finally, for those who are using a different version of command.com, I think most 3.x versions of PC or MS DOS are similar but the locations of the flag test and the option chars will vary. For PC-DOS 3.1, the execute flag code is at xxxx:0df2 to xxxx:0dfe, and the "d" byte is at 1337. For other versions, I would suggest you first locate the "pdcA" string by using debug's search option on command.com: -s 100 2000 70,64,63 ; search 100 to 2000 for "pdc" xxxx:1336 -d 1336 : display any data found by search xxxx:1330 .. 70 64-63 41 00 ... pdcA In this case, the "d" (64) is at offset 1337 hex. Remember this value and then search for any references to this address. Because Intel stores offsets in reverse byte order, the search should be similar to: -s 100 2000 37,13 ; search from 100 to 2000 for 1337 xxxx:0df4 ; a reference to 1337 found at this addr. Unassemble starting about 2 bytes BEFORE this and you should find code similar to the following. The MOV BYTE PTR [xxxx], 01 is confirmation: xxxx:0DF2 3A063713 CMP AL,[1337] xxxx:0DF6 7507 JNZ 0DFF xxxx:0DF8 C6061C1301 MOV BYTE PTR [131C],01 xxxx:0DFD EBA6 JMP 0DA5 If this doesn't work, your version is probably too radically different for this patch. In summary, the two changes for PC-DOS 3.1 command.com are: -e 1337 xxxx:1337 64.6c ; change "d" to "l" -e dfc xxxx:0DFC 01.00 ; change execute flag to 00 -w Writing 5AAA bytes -q In summary, the two changes for PC-DOS 3.2 command.com are: -e 149d xxxx:149D 64.6c -e 3ea xxxx:0E3A 01.00 -w Writing 5CEF bytes -q ----------------------- end PATCH #1 ----------------------------- DUAL SWITCH CHAR FOR COMMAND.COM This patch allows programs that call command.com via the system() or EXEC functions to work properly with either a -c or /c for the switch character. This should cure most problems when spawning command.com from within an application, and eliminate the empty environment and the "Specified COMMAND search path bad" errors. The problem results when the switch char is set to "-" AND command.com is invoked from within a program using a hard-wired exec call: $COMSPEC /c "string" Command.com interprets the "/c" (and any identifiers in the "string") as a possible <path> or <device> options rather than as a sub-shell call "-c" to execute "string" and return. The fix is to allow both "/" and "-" to be recognized as a valid switch character in the parsing of command.com options. The only trade-off is that specifying a legitimate command search path from the login or root (i.e. SHELL= COMMAND.COM C:/PATH1/PATH2/..../PATHN -L ) must include a drive letter (C:, for example) in the search path. (A leading "/" in the search path would be interpreted as a switch char parameter if the drive letter was missing.) The actual patch method is to replace the specific memory references to "white space blank" and the DOS switch character flag with hard- coded tests. If done properly, the new code just overlays the old code with no room to spare. -------------------------- PATCH #2 --------------------------- For IBM's PC-DOS 3.3 (my copy is 25,307 bytes dated 3/17/87), Use debug (on a copy, of course) and unassemble the following: debug command.com -u f53 f66 xxxx:0F53 AC LODSB ; load next char from cmnd line xxxx:0F54 3A064B16 CMP AL,[164B] ; test if whitespace ( space) xxxx:0F58 74F6 JZ 0F50 ; yes, it was a space. xxxx:0F5A 3C09 CMP AL,09 ; test if tab xxxx:0F5C 74F2 JZ 0F50 ; yest, it was a tab. xxxx:0F5E 3A06170D CMP AL,[0D17] ; is it current DOS switch char xxxx:0F62 7403 JZ 0F67 ; switch char found, get option xxxx:0F64 E9E000 JMP 1047 ; not switch char, rescan line. If this matches your version, assemble new code over the existing tests for white space and switch char. (Do not type the comments, though): -a f54 ; start assembling at location 0f54 xxxx:0F54 cmp al,20 ; See if char is a blank (space) xxxx:0F56 jz 0f50 ; Skip if it is. xxxx:0F58 cmp al,09 ; See if char is a tab. xxxx:0F5A jz 0f50 ; Skip if it is a tab. xxxx:0F5C cmp al,2d ; See if char is a "-" switch char xxxx:0F5E jz 0f67 ; Jump to examine next char if it is. xxxx:0F60 cmp al,2f ; See if char is a "/" switch char. xxxx:0F62 ; end assembly with a blank line. Verify the patched code by unassembling again: -u f53 f66 xxxx:0F53 AC LODSB xxxx:0F54 3C20 CMP AL,20 xxxx:0F56 74F8 JZ 0F50 xxxx:0F58 3C09 CMP AL,09 xxxx:0F5A 74F4 JZ 0F50 xxxx:0F5C 3C2D CMP AL,2D xxxx:0F5E 7407 JZ 0F67 xxxx:0F60 3C2F CMP AL,2F xxxx:0F62 7403 JZ 0F67 xxxx:0F64 E9E000 JMP 1047 Write the patched command.com back to disk and exit from debug: -w Writing 62DB bytes -q Your patched version of command.com should now work properly when invoked from within applications that hard-wire the switch char to /c, without regard to the current switch char. Again, for versions other than IBM PC-DOS 3.3, locating the section of code that tests for whitespace and switch chars in the command line parser is required. The best starting point is the address in the previous patch that changed the "pdcA" d option flag. The "whitespace blank" for testing is (usually) the byte BEFORE the "plcA" string. Using PC-DOS 3.1 as an example, these bytes are found by searching for the " pld" string: -s 100 2000 20,70,6C,63 ; search 100 to 2000 for " plc" xxxx:1335 -d 1335 : display any data found by search 637E:1330 .. 20 70 6C-63 41 00 ... plcA In this case, the " " (20) is at offset 1335 hex. Remember this value and then search for any references to this address. Because Intel stores offsets in reverse byte order, the debug search should be similar to: -s 100 2000 35,13 ; search from 100 to 2000 for 1335 xxxx:0DAB ; This is probably the area, but, xxxx:0E62 ; This looks similar, but doesn't fit. xxxx:0ED6 ; ditto xxxx:0EE5 Unassemble each of these locations, starting about 4 bytes BEFORE each address. You are looking for code similar to the following: -u 0da7 xxxx:0DA7 49 DEC CX ; count-- length of option line. xxxx:0DA8 AC LODSB ; Load next char into AL xxxx:0DA9 3A063513 CMP AL,[1335] ; This is the test for " " xxxx:0DAD 74F6 JZ 0DA5 ; jump to continue scan xxxx:0DAF 3C09 CMP AL,09 ; This is the test for TAB xxxx:0DB1 74F2 JZ 0DA5 ; jump to continue scan xxxx:0DB3 3A06E70B CMP AL,[0BE7] ; Is char the current sw char. xxxx:0DB7 7403 JZ 0DBC ; Jump $+5 to parse sw char. xxxx:0DB9 E99C00 JMP 0E58 ; Jump, char is something else. xxxx:0DBC E331 JCXZ 0DEF ; Are there any chars after / The replacement code should overlay the bytes starting just after the LODSB and must not clobber the JMP xxxx. Note that the jump addresses vary with each version, so I can't really give portable patch code. In general, the first two JZ addresses stay the same. The third JZ in the original code is used in the last two JZ's of the patch. ----- original code -------- ----------- patched code ------- :0DA7 49 DEC CX :0DA7 49 DEC CX :0DA8 AC LODSB :0DA8 AC LODSB :0DA9 3A063513 CMP AL,[1335] :0DA9 3C20 CMP AL,20 :0DAD 74F6 JZ aaaa :0DAB 74F8 JZ aaaa :0DAF 3C09 CMP AL,09 :0DAD 3C09 CMP AL,09 :0DB1 74F2 JZ aaaa :0DAF 74F4 JZ aaaa :0DB3 3A06E70B CMP AL,[0BE7] :0DB1 3C2D CMP AL,2D :0DB7 7403 JZ bbbb :0DB3 7407 JZ bbbb :0DB5 3C2F CMP AL,2F :0DB7 7403 JZ bbbb :0DB9 E99C00 JMP 0E58 :0DB9 E9E000 JMP 0E58 :0DBC E331 JCXZ 0DEF :0DBC E331 JCXZ 0DEF For PC-DOS 3.1 command.com, the added code is: -a da9 xxxx:0DA9 3C20 CMP AL,20 xxxx:0DAB 74F8 JZ 0DA5 xxxx:0DAD 3C09 CMP AL,09 xxxx:0DAF 74F4 JZ 0DA5 xxxx:0DB1 3C2D CMP AL,2D xxxx:0DB3 7407 JZ 0DBC xxxx:0DB5 3C2F CMP AL,2F xxxx:0DB7 7403 JZ 0DBC -w For PC-DOS 3.21 command.com, the added code is: -a de7 xxxx:0DE7 3C20 CMP AL,20 xxxx:0DE9 74F8 JZ 0DE3 xxxx:0DEB 3C09 CMP AL,09 xxxx:0DED 74F4 JZ 0DE3 xxxx:0DEF 3C2D CMP AL,2D xxxx:0DF1 7407 JZ 0DFA xxxx:0DF3 3C2F CMP AL,2F xxxx:0DF5 7403 JZ 0DFA -w Writing 5CEF bytes Note: the size of the environment of the spawned 3.1/3.2 command.com limits the alteration of variables if the passed environment is larger than the minimum size built into command.com. Because the -e:nnnn parameter can not be easily added to the invocation, providing a larger environment must be done by patching command.com. Microsoft supplies a utility "SETENV" that modifies most versions to default to a larger minimum size. Also, various PD hacks exist for this, so I won't elaborate here. ----------------------------- end of PATCH #2 -------------------------- ONE UNRESOLVED SWITCH CHAR "-" BUG: There remains one problem with using command.com and the MKS toolkit when the switch char is set to "-". To demonstrate the problem, execute the following line using command.com C:> \BIN\ECHO THIS STRING IS PRINTED BY THE MKS ECHO CMND. When a leading path using "\" characters is parsed by command.com, an "EXEC failure" error is generated. The error only occurs when the switch char is "-" and DOS-style path names are used. This is a problem when ksh spawns command.com to process a batch file and the batch file has execution requests with the old delimiters. (It is reasonable to expect such .BAT files exist for applications that must run under command.com with the switch char set to /.) I have traced through this enough to determine that command.com is trying to exec the file with the ASCIZ name generated thusly: "/path_specifier/\bin\echo ......" Apparently, the leading "\" is not recognized as a path component, so command prepends $PATH strings to the command. If anyone has spelunked through this, how about some email. I plan to nail this down when time permits and will post a patch when (and if) available. Hope this stuff helps -- I'm _really_ in love with the toolkit (or do I just hate vanilla DOS and command.com?). .SIGNATURE=$(cat /dev/null)