Rev 577 | Rev 957 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 Mateusz Viste
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <i86.h>
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include "cmd.h"
#include "env.h"
#include "helpers.h"
#include "redir.h"
#include "rmodinit.h"
#include "sayonara.h"
#include "rmodcore.h" /* rmod binary inside a BUFFER array */
/* this version byte is used to tag RMOD so I can easily make sure that
* the RMOD struct I find in memory is one that I know. Should the version
* mismatch, then it would likely mean that SvarCOM has been upgraded and
* RMOD should not be accessed as its structure might no longer be in sync
* with what I think it is.
* *** INCREMENT THIS AT EACH NEW SVARCOM RELEASE! *** */
#define BYTE_VERSION 3
struct config {
unsigned char flags; /* command.com flags, as defined in rmodinit.h */
char *execcmd;
unsigned short envsiz;
};
/* max length of the cmdline storage (bytes) - includes also max length of
* line loaded from a BAT file (no more than 255 bytes!) */
#define CMDLINE_MAXLEN 255
/* sets guard values at a few places in memory for later detection of
* overflows via memguard_check() */
static void memguard_set(char *cmdlinebuf) {
BUFFER[sizeof(BUFFER) - 1] = 0xC7;
cmdlinebuf[CMDLINE_MAXLEN] = 0xC7;
}
/* checks for valguards at specific memory locations, returns 0 on success */
static int memguard_check(unsigned short rmodseg, char *cmdlinebuf) {
/* check RMOD signature (would be overwritten in case of stack overflow */
static char msg[] = "!! MEMORY CORRUPTION ## DETECTED !!";
unsigned short far *rmodsig = MK_FP(rmodseg, 0x100 + 6);
unsigned char far *rmod = MK_FP(rmodseg, 0);
if (*rmodsig != 0x2019) {
msg[22] = '1';
goto FAIL;
}
/* check last BUFFER byte */
if (BUFFER[sizeof(BUFFER) - 1] != 0xC7) {
msg[22] = '2';
goto FAIL;
}
/* check last cmdlinebuf byte */
if (cmdlinebuf[CMDLINE_MAXLEN] != 0xC7) {
msg[22] = '3';
goto FAIL;
}
/* check rmod exec buf */
if (rmod[RMOD_OFFSET_EXECPROG + 127] != 0) {
msg[22] = '4';
goto FAIL;
}
/* check rmod exec stdin buf */
if (rmod[RMOD_OFFSET_STDINFILE + 127] != 0) {
msg[22] = '5';
goto FAIL;
}
/* check rmod exec stdout buf */
if (rmod[RMOD_OFFSET_STDOUTFILE + 127] != 0) {
msg[22] = '6';
goto FAIL;
}
/* else all good */
return(0);
/* error handling */
FAIL:
outputnl(msg);
return(1);
}
/* parses command line the hard way (directly from PSP) */
static void parse_argv(struct config *cfg) {
const unsigned char *cmdlinelen = (void *)0x80;
char *cmdline = (void *)0x81;
memset(cfg, 0, sizeof(*cfg));
/* set a NULL terminator on cmdline */
cmdline[*cmdlinelen] = 0;
while (*cmdline != 0) {
/* skip over any leading spaces */
if (*cmdline == ' ') {
cmdline++;
continue;
}
if (*cmdline != '/') {
output("Invalid parameter: ");
outputnl(cmdline);
goto SKIP_TO_NEXT_ARG;
}
/* got a slash */
cmdline++; /* skip the slash */
switch (*cmdline) {
case 'c': /* /C = execute command and quit */
case 'C':
cfg->flags |= FLAG_EXEC_AND_QUIT;
/* FALLTHRU */
case 'k': /* /K = execute command and keep running */
case 'K':
cfg->execcmd = cmdline + 1;
return;
case 'd': /* /D = skip autoexec.bat processing */
case 'D':
cfg->flags |= FLAG_SKIP_AUTOEXEC;
break;
case 'e': /* preset the initial size of the environment block */
case 'E':
cmdline++;
if (*cmdline == ':') cmdline++; /* could be /E:size */
atous(&(cfg->envsiz), cmdline);
if (cfg->envsiz < 64) cfg->envsiz = 0;
break;
case 'p': /* permanent shell (can't exit + run autoexec.bat) */
case 'P':
cfg->flags |= FLAG_PERMANENT;
break;
case '?':
outputnl("Starts the SvarCOM command interpreter");
outputnl("");
outputnl("COMMAND /E:nnn [/[C|K] [/P] [/D] command]");
outputnl("");
outputnl("/D Skip AUTOEXEC.BAT processing (makes sense only with /P)");
outputnl("/E:nnn Sets the environment size to nnn bytes");
outputnl("/P Makes the new command interpreter permanent and run AUTOEXEC.BAT");
outputnl("/C Executes the specified command and returns");
outputnl("/K Executes the specified command and continues running");
exit(1);
break;
default:
output("Invalid switch: /");
outputnl(cmdline);
break;
}
/* move to next argument or quit processing if end of cmdline */
SKIP_TO_NEXT_ARG:
while ((*cmdline != 0) && (*cmdline != ' ') && (*cmdline != '/')) cmdline++;
}
}
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
static void build_and_display_prompt(char *buff, unsigned short envseg) {
char *s = buff;
/* locate the prompt variable or use the default pattern */
const char far *fmt = env_lookup_val(envseg, "PROMPT");
if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
/* build the prompt string based on pattern */
for (; *fmt != 0; fmt++) {
if (*fmt != '$') {
*s = *fmt;
s++;
continue;
}
/* escape code ($P, etc) */
fmt++;
switch (*fmt) {
case 'Q': /* $Q = = (equal sign) */
case 'q':
*s = '=';
s++;
break;
case '$': /* $$ = $ (dollar sign) */
*s = '$';
s++;
break;
case 'T': /* $t = current time */
case 't':
s += sprintf(s, "00:00"); /* TODO */
break;
case 'D': /* $D = current date */
case 'd':
s += sprintf(s, "1985-07-29"); /* TODO */
break;
case 'P': /* $P = current drive and path */
case 'p':
_asm {
mov ah, 0x19 /* DOS 1+ - GET CURRENT DRIVE */
int 0x21
mov bx, s
mov [bx], al /* AL = drive (00 = A:, 01 = B:, etc */
}
*s += 'A';
s++;
*s = ':';
s++;
*s = '\\';
s++;
_asm {
mov ah, 0x47 /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
xor dl,dl /* DL = drive number (00h = default, 01h = A:, etc) */
mov si, s /* DS:SI -> 64-byte buffer for ASCIZ pathname */
int 0x21
jc DONE /* leave path empty on error */
/* move s ptr forward to end (0-termintor) of pathname */
NEXTBYTE:
mov si, s
cmp byte ptr [si], 0
je DONE
inc s
jmp NEXTBYTE
DONE:
}
break;
case 'V': /* $V = DOS version number */
case 'v':
s += sprintf(s, "VER"); /* TODO */
break;
case 'N': /* $N = current drive */
case 'n':
_asm {
mov ah, 0x19 /* DOS 1+ - GET CURRENT DRIVE */
int 0x21
mov bx, s
mov [bx], al /* AL = drive (00 = A:, 01 = B:, etc */
}
*s += 'A';
s++;
break;
case 'G': /* $G = > (greater-than sign) */
case 'g':
*s = '>';
s++;
break;
case 'L': /* $L = < (less-than sign) */
case 'l':
*s = '<';
s++;
break;
case 'B': /* $B = | (pipe) */
case 'b':
*s = '|';
s++;
break;
case 'H': /* $H = backspace (erases previous character) */
case 'h':
*s = '\b';
s++;
break;
case 'E': /* $E = Escape code (ASCII 27) */
case 'e':
*s = 27;
s++;
break;
case '_': /* $_ = CR+LF */
*s = '\r';
s++;
*s = '\n';
s++;
break;
}
}
*s = 0;
output(buff);
}
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod, struct redir_data *redir, unsigned char delete_stdin_file) {
char *cmdfile = buff + 512;
const char far *pathptr;
int lookup;
unsigned short i;
const char *ext;
char *cmd = buff + 1024;
const char *cmdtail;
char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
_Packed struct {
unsigned short envseg;
unsigned long cmdtail;
unsigned long fcb1;
unsigned long fcb2;
} far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
/* find cmd and cmdtail */
i = 0;
cmdtail = cmdline;
while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
cmd[i++] = *cmdtail;
cmdtail++;
}
cmd[i] = 0;
/* is this a command in curdir? */
lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
if (lookup == 0) {
/* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
goto RUNCMDFILE;
} else if (lookup == -2) {
/* puts("NOT FOUND"); */
return;
}
/* try matching something in PATH */
pathptr = env_lookup_val(envseg, "PATH");
/* try each path in %PATH% */
while (pathptr) {
for (i = 0;; i++) {
buff[i] = *pathptr;
if ((buff[i] == 0) || (buff[i] == ';')) break;
pathptr++;
}
buff[i] = 0;
lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
if (lookup == 0) goto RUNCMDFILE;
if (lookup == -2) return;
if (*pathptr == ';') {
pathptr++;
} else {
break;
}
}
/* last chance: is it an executable link? (trim extension from cmd first) */
for (i = 0; (cmd[i] != 0) && (cmd[i] != '.') && (i < 9); i++) buff[128 + i] = cmd[i];
buff[128 + i] = 0;
if ((i < 9) && (link_computefname(buff, buff + 128, envseg) == 0)) {
/* try opening the link file (if it exists) and read it into buff */
i = 0;
_asm {
push ax
push bx
push cx
push dx
mov ax, 0x3d00 /* DOS 2+ - OPEN EXISTING FILE, READ-ONLY */
mov dx, buff /* file name */
int 0x21
jc ERR_FOPEN
/* file handle in AX, read from file now */
mov bx, ax /* file handle */
mov ah, 0x3f /* Read from file via handle bx */
mov cx, 128 /* up to 128 bytes */
/* mov dx, buff */ /* dest buffer (already set) */
int 0x21 /* read up to 256 bytes from file and write to buff */
jc ERR_READ
mov i, ax
ERR_READ:
mov ah, 0x3e /* close file handle in BX */
int 0x21
ERR_FOPEN:
pop dx
pop cx
pop bx
pop ax
}
/* did I read anything? */
if (i != 0) {
buff[i] = 0;
/* trim buff at first \n or \r, just in case someone fiddled with the
* link file using a text editor */
for (i = 0; (buff[i] != 0) && (buff[i] != '\r') && (buff[i] != '\n'); i++);
buff[i] = 0;
/* lookup check */
if (buff[0] != 0) {
lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
if (lookup == 0) goto RUNCMDFILE;
}
}
}
/* all failed (ie. executable file not found) */
return;
RUNCMDFILE:
/* special handling of batch files */
if ((ext != NULL) && (imatch(ext, "bat"))) {
/* free the bat-context linked list, if present, and replace it with a new entry */
while (rmod->bat != NULL) {
struct batctx far *victim = rmod->bat;
rmod->bat = rmod->bat->parent;
rmod_ffree(victim);
}
rmod->bat = rmod_fmalloc(sizeof(struct batctx), rmod->rmodseg, "SVBATCTX");
if (rmod->bat == NULL) {
outputnl("INTERNAL ERR: OUT OF MEMORY");
return;
}
_fmemset(rmod->bat, 0, sizeof(struct batctx));
/* copy truename of the bat file to rmod buff */
_fstrcpy(rmod->bat->fname, cmdfile);
/* explode args of the bat file and store them in rmod buff */
cmd_explode(buff, cmdline, NULL);
_fmemcpy(rmod->bat->argv, buff, sizeof(rmod->bat->argv));
/* reset the 'next line to execute' counter */
rmod->bat->nextline = 0;
/* remember the echo flag (in case bat file disables echo) */
rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
return;
}
/* copy full filename to execute, along with redirected files (if any) */
_fstrcpy(rmod_execprog, cmdfile);
/* copy stdin file if a redirection is needed */
if (redir->stdinfile) {
char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDINFILE);
char far *delstdin = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDIN_DEL);
_fstrcpy(farptr, redir->stdinfile);
if (delete_stdin_file) {
*delstdin = redir->stdinfile[0];
} else {
*delstdin = 0;
}
}
/* same for stdout file */
if (redir->stdoutfile) {
char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTFILE);
unsigned short far *farptr16 = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTAPP);
_fstrcpy(farptr, redir->stdoutfile);
/* openflag */
*farptr16 = redir->stdout_openflag;
}
/* copy cmdtail to rmod's PSP and compute its len */
for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
rmod_cmdtail[i] = '\r';
rmod_cmdtail[-1] = i;
/* set up rmod to execute the command */
ExecParam->envseg = envseg;
ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
ExecParam->fcb1 = 0; /* TODO farptr */
ExecParam->fcb2 = 0; /* TODO farptr */
exit(0); /* let rmod do the job now */
}
static void set_comspec_to_self(unsigned short envseg) {
unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
char far *myenv = MK_FP(*psp_envseg, 0);
unsigned short varcount;
char buff[256] = "COMSPEC=";
char *buffptr = buff + 8;
/* who am i? look into my own environment, at the end of it should be my EXEPATH string */
while (*myenv != 0) {
/* consume a NULL-terminated string */
while (*myenv != 0) myenv++;
/* move to next string */
myenv++;
}
/* get next word, if 1 then EXEPATH follows */
myenv++;
varcount = *myenv;
myenv++;
varcount |= (*myenv << 8);
myenv++;
if (varcount != 1) return; /* NO EXEPATH FOUND */
while (*myenv != 0) {
*buffptr = *myenv;
buffptr++;
myenv++;
}
*buffptr = 0;
/* printf("EXEPATH: '%s'\r\n", buff); */
env_setvar(envseg, buff);
}
/* wait for user input */
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
_asm {
push ax
push bx
push cx
push dx
push ds
/* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
mov ax, 0x4800
int 0x2f
mov bl, al /* save doskey status in BL */
/* set up buffered input to inpseg:inpoff */
mov ax, inpseg
push ax
pop ds
mov dx, inpoff
/* execute either DOS input or DOSKEY */
test bl, bl /* zf set if no DOSKEY present */
jnz DOSKEY
mov ah, 0x0a
int 0x21
jmp short DONE
DOSKEY:
mov ax, 0x4810
int 0x2f
DONE:
/* terminate command with a CR/LF */
mov ah, 0x02 /* display character in dl */
mov dl, 0x0d
int 0x21
mov dl, 0x0a
int 0x21
pop ds
pop dx
pop cx
pop bx
pop ax
}
}
/* fetches a line from batch file and write it to buff (NULL-terminated),
* increments rmod counter and returns 0 on success. */
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
unsigned short i;
unsigned short batname_seg = FP_SEG(rmod->bat->fname);
unsigned short batname_off = FP_OFF(rmod->bat->fname);
unsigned short filepos_cx = rmod->bat->nextline >> 16;
unsigned short filepos_dx = rmod->bat->nextline & 0xffff;
unsigned char blen = 0;
unsigned short errv = 0;
/* open file, jump to offset filpos, and read data into buff.
* result in blen (unchanged if EOF or failure). */
_asm {
push ax
push bx
push cx
push dx
/* open file (read-only) */
mov bx, 0xffff /* preset BX to 0xffff to detect error conditions */
mov dx, batname_off
mov ax, batname_seg
push ds /* save DS */
mov ds, ax
mov ax, 0x3d00
int 0x21 /* handle in ax on success */
pop ds /* restore DS */
jc ERR
mov bx, ax /* save handle to bx */
/* jump to file offset CX:DX */
mov ax, 0x4200
mov cx, filepos_cx
mov dx, filepos_dx
int 0x21 /* CF clear on success, DX:AX set to cur pos */
jc ERR
/* read the line into buff */
mov ah, 0x3f
xor ch, ch
mov cl, buffmaxlen
mov dx, buff
int 0x21 /* CF clear on success, AX=number of bytes read */
jc ERR
mov blen, al
jmp CLOSEANDQUIT
ERR:
mov errv, ax
CLOSEANDQUIT:
/* close file (if bx contains a handle) */
cmp bx, 0xffff
je DONE
mov ah, 0x3e
int 0x21
DONE:
pop dx
pop cx
pop bx
pop ax
}
/* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
if (errv != 0) nls_outputnl_doserr(errv);
/* on EOF - abort processing the bat file */
if (blen == 0) goto OOPS;
/* find nearest \n to inc batch offset and replace \r by NULL terminator
* I support all CR/LF, CR- and LF-terminated batch files */
for (i = 0; i < blen; i++) {
if ((buff[i] == '\r') || (buff[i] == '\n')) {
if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->bat->nextline += 1;
break;
}
}
buff[i] = 0;
rmod->bat->nextline += i + 1;
return(0);
OOPS:
rmod->bat->fname[0] = 0;
rmod->bat->nextline = 0;
return(-1);
}
/* replaces %-variables in a BAT line with resolved values:
* %PATH% -> replaced by the contend of the PATH env variable
* %UNDEFINED% -> undefined variables are replaced by nothing ("")
* %NOTCLOSED -> NOTCLOSED
* %1 -> first argument of the batch file (or nothing if no arg) */
static void batpercrepl(char *res, unsigned short ressz, const char *line, const struct rmod_props far *rmod, unsigned short envseg) {
unsigned short lastperc = 0xffff;
unsigned short reslen = 0;
if (ressz == 0) return;
ressz--; /* reserve one byte for the NULL terminator */
for (; (reslen < ressz) && (*line != 0); line++) {
/* if not a percent, I don't care */
if (*line != '%') {
res[reslen++] = *line;
continue;
}
/* *** perc char handling *** */
/* closing perc? */
if (lastperc != 0xffff) {
/* %% is '%' */
if (lastperc == reslen) {
res[reslen++] = '%';
} else { /* otherwise variable name */
const char far *ptr;
res[reslen] = 0;
reslen = lastperc;
ptr = env_lookup_val(envseg, res + reslen);
if (ptr != NULL) {
while ((*ptr != 0) && (reslen < ressz)) {
res[reslen++] = *ptr;
ptr++;
}
}
}
lastperc = 0xffff;
continue;
}
/* digit? (bat arg) */
if ((line[1] >= '0') && (line[1] <= '9')) {
unsigned short argid = line[1] - '0';
unsigned short i;
const char far *argv = "";
if ((rmod != NULL) && (rmod->bat != NULL)) argv = rmod->bat->argv;
/* locate the proper arg */
for (i = 0; i != argid; i++) {
/* if string is 0, then end of list reached */
if (*argv == 0) break;
/* jump to next arg */
while (*argv != 0) argv++;
argv++;
}
/* copy the arg to result */
for (i = 0; (argv[i] != 0) && (reslen < ressz); i++) {
res[reslen++] = argv[i];
}
line++; /* skip the digit */
continue;
}
/* opening perc */
lastperc = reslen;
}
res[reslen] = 0;
}
int main(void) {
static struct config cfg;
static unsigned short far *rmod_envseg;
static unsigned short far *lastexitcode;
static struct rmod_props far *rmod;
static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
static char *cmdline;
static struct redir_data redirprops;
static enum cmd_result cmdres;
static unsigned short i; /* general-purpose variable for short-lived things */
static unsigned char delete_stdin_file;
rmod = rmod_find(BUFFER_len);
if (rmod == NULL) {
/* look at command line parameters (in case env size if set there) */
parse_argv(&cfg);
rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
if (rmod == NULL) {
outputnl("ERROR: rmod_install() failed");
return(1);
}
/* copy flags to rmod's storage (and enable ECHO) */
rmod->flags = cfg.flags | FLAG_ECHOFLAG;
/* printf("rmod installed at %Fp\r\n", rmod); */
rmod->version = BYTE_VERSION;
} else {
/* printf("rmod found at %Fp\r\n", rmod); */
/* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
* die asap, because the command has been executed already, so I no longer
* have a purpose in life */
if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
/* */
if (rmod->version != BYTE_VERSION) {
outputnl("SVARCOM VERSION CHANGED. SYSTEM HALTED. PLEASE REBOOT YOUR COMPUTER.");
_asm {
HALT:
hlt
jmp HALT
}
}
}
/* install a few guardvals in memory to detect some cases of overflows */
memguard_set(cmdlinebuf);
rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
/* make COMPSEC point to myself */
set_comspec_to_self(*rmod_envseg);
/* {
unsigned short envsiz;
unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
envsiz = *sizptr;
envsiz *= 16;
printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod->rmodseg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
}*/
/* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
* but skip this check if /D was also passed */
if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
}
do {
/* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->bat == NULL)) outputnl("");
SKIP_NEWLINE:
/* memory check */
memguard_check(rmod->rmodseg, cmdlinebuf);
/* preset cmdline to point at the dedicated buffer */
cmdline = cmdlinebuf;
/* (re)load translation strings if needed */
nls_langreload(BUFFER, *rmod_envseg);
/* load awaiting command, if any (used to run piped commands) */
if (rmod->awaitingcmd[0] != 0) {
_fstrcpy(cmdline, rmod->awaitingcmd);
rmod->awaitingcmd[0] = 0;
delete_stdin_file = 1;
goto EXEC_CMDLINE;
} else {
delete_stdin_file = 0;
}
/* skip user input if I have a command to exec (/C or /K) */
if (cfg.execcmd != NULL) {
cmdline = cfg.execcmd;
cfg.execcmd = NULL;
goto EXEC_CMDLINE;
}
/* if batch file is being executed -> fetch next line */
if (rmod->bat != NULL) {
if (getbatcmd(BUFFER, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
struct batctx far *victim = rmod->bat;
rmod->bat = rmod->bat->parent;
rmod_ffree(victim);
/* end of batch? then restore echo flag as it was before running the bat file */
if (rmod->bat == NULL) {
rmod->flags &= ~FLAG_ECHOFLAG;
if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
}
continue;
}
/* %-decoding of variables (%PATH%, %1, %%...), result in cmdline */
batpercrepl(cmdline, CMDLINE_MAXLEN, BUFFER, rmod, *rmod_envseg);
/* skip any leading spaces */
while (*cmdline == ' ') cmdline++;
/* output prompt and command on screen if echo on and command is not
* inhibiting it with the @ prefix */
if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
build_and_display_prompt(BUFFER, *rmod_envseg);
outputnl(cmdline);
}
/* skip the @ prefix if present, it is no longer useful */
if (cmdline[0] == '@') cmdline++;
} else {
/* interactive mode: display prompt (if echo enabled) and wait for user
* command line */
if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
/* collect user input */
cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
/* copy it to local cmdline */
if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
}
/* if nothing entered, loop again (but without appending an extra CR/LF) */
if (cmdline[0] == 0) goto SKIP_NEWLINE;
/* I jump here when I need to exec an initial command (/C or /K) */
EXEC_CMDLINE:
/* move pointer forward to skip over any leading spaces */
while (*cmdline == ' ') cmdline++;
/* update rmod's ptr to COMPSPEC so it is always up to date */
rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
/* handle redirections (if any) */
i = redir_parsecmd(&redirprops, cmdline, rmod->awaitingcmd, *rmod_envseg);
if (i != 0) {
nls_outputnl_doserr(i);
rmod->awaitingcmd[0] = 0;
continue;
}
/* try matching (and executing) an internal command */
cmdres = cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER), &redirprops, delete_stdin_file);
if ((cmdres == CMD_OK) || (cmdres == CMD_FAIL)) {
/* internal command executed */
continue;
} else if (cmdres == CMD_CHANGED) { /* cmdline changed, needs to be reprocessed */
goto EXEC_CMDLINE;
} else if (cmdres == CMD_NOTFOUND) {
/* this was not an internal command, try matching an external command */
run_as_external(BUFFER, cmdline, *rmod_envseg, rmod, &redirprops, delete_stdin_file);
/* perhaps this is a newly launched BAT file */
if ((rmod->bat != NULL) && (rmod->bat->nextline == 0)) goto SKIP_NEWLINE;
/* run_as_external() does not return on success, if I am still alive then
* external command failed to execute */
outputnl("Bad command or file name");
continue;
}
/* I should never ever land here */
outputnl("INTERNAL ERR: INVALID CMDRES");
} while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
sayonara(rmod);
return(0);
}