Subversion Repositories SvarDOS

Rev

Rev 500 | Rev 506 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 500 Rev 505
1
/* This file is part of the SvarCOM project and is published under the terms
1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
2
 * of the MIT license.
3
 *
3
 *
4
 * Copyright (C) 2021 Mateusz Viste
4
 * Copyright (C) 2021 Mateusz Viste
5
 *
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
8
 * to deal in the Software without restriction, including without limitation
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
 * and/or sell copies of the Software, and to permit persons to whom the
10
 * and/or sell copies of the Software, and to permit persons to whom the
11
 * Software is furnished to do so, subject to the following conditions:
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
12
 *
13
 * The above copyright notice and this permission notice shall be included in
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
14
 * all copies or substantial portions of the Software.
15
 *
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
 * DEALINGS IN THE SOFTWARE.
22
 * DEALINGS IN THE SOFTWARE.
23
 */
23
 */
24
 
24
 
25
#include <i86.h>
25
#include <i86.h>
26
#include <dos.h>
26
#include <dos.h>
27
#include <stdio.h>
27
#include <stdio.h>
28
#include <stdlib.h>
28
#include <stdlib.h>
29
#include <string.h>
29
#include <string.h>
30
 
30
 
31
#include <process.h>
31
#include <process.h>
32
 
32
 
33
#include "cmd.h"
33
#include "cmd.h"
-
 
34
#include "doserr.h"
34
#include "env.h"
35
#include "env.h"
35
#include "helpers.h"
36
#include "helpers.h"
36
#include "redir.h"
37
#include "redir.h"
37
#include "rmodinit.h"
38
#include "rmodinit.h"
38
#include "sayonara.h"
39
#include "sayonara.h"
39
 
40
 
40
#include "rmodcore.h" /* rmod binary inside a BUFFER array */
41
#include "rmodcore.h" /* rmod binary inside a BUFFER array */
41
 
42
 
42
struct config {
43
struct config {
43
  unsigned char flags; /* command.com flags, as defined in rmodinit.h */
44
  unsigned char flags; /* command.com flags, as defined in rmodinit.h */
44
  char *execcmd;
45
  char *execcmd;
45
  unsigned short envsiz;
46
  unsigned short envsiz;
46
};
47
};
47
 
48
 
48
/* max length of the cmdline storage (bytes) - includes also max length of
49
/* max length of the cmdline storage (bytes) - includes also max length of
49
 * line loaded from a BAT file (no more than 255 bytes!) */
50
 * line loaded from a BAT file (no more than 255 bytes!) */
50
#define CMDLINE_MAXLEN 255
51
#define CMDLINE_MAXLEN 255
51
 
52
 
52
 
53
 
53
/* sets guard values at a few places in memory for later detection of
54
/* sets guard values at a few places in memory for later detection of
54
 * overflows via memguard_check() */
55
 * overflows via memguard_check() */
55
static void memguard_set(char *cmdlinebuf) {
56
static void memguard_set(char *cmdlinebuf) {
56
  BUFFER[sizeof(BUFFER) - 1] = 0xC7;
57
  BUFFER[sizeof(BUFFER) - 1] = 0xC7;
57
  cmdlinebuf[CMDLINE_MAXLEN] = 0xC7;
58
  cmdlinebuf[CMDLINE_MAXLEN] = 0xC7;
58
}
59
}
59
 
60
 
60
 
61
 
61
/* checks for valguards at specific memory locations, returns 0 on success */
62
/* checks for valguards at specific memory locations, returns 0 on success */
62
static int memguard_check(unsigned short rmodseg, char *cmdlinebuf) {
63
static int memguard_check(unsigned short rmodseg, char *cmdlinebuf) {
63
  /* check RMOD signature (would be overwritten in case of stack overflow */
64
  /* check RMOD signature (would be overwritten in case of stack overflow */
64
  static char msg[] = "!! MEMORY CORRUPTION ## DETECTED !!";
65
  static char msg[] = "!! MEMORY CORRUPTION ## DETECTED !!";
65
  unsigned short far *rmodsig = MK_FP(rmodseg, 0x100 + 6);
66
  unsigned short far *rmodsig = MK_FP(rmodseg, 0x100 + 6);
66
  if (*rmodsig != 0x2019) {
67
  if (*rmodsig != 0x2019) {
67
    msg[22] = '1';
68
    msg[22] = '1';
68
    outputnl(msg);
69
    outputnl(msg);
69
    printf("rmodseg = %04X ; *rmodsig = %04X\r\n", rmodseg, *rmodsig);
70
    printf("rmodseg = %04X ; *rmodsig = %04X\r\n", rmodseg, *rmodsig);
70
    return(1);
71
    return(1);
71
  }
72
  }
72
  /* check last BUFFER byte */
73
  /* check last BUFFER byte */
73
  if (BUFFER[sizeof(BUFFER) - 1] != 0xC7) {
74
  if (BUFFER[sizeof(BUFFER) - 1] != 0xC7) {
74
    msg[22] = '2';
75
    msg[22] = '2';
75
    outputnl(msg);
76
    outputnl(msg);
76
    return(2);
77
    return(2);
77
  }
78
  }
78
  /* check last cmdlinebuf byte */
79
  /* check last cmdlinebuf byte */
79
  if (cmdlinebuf[CMDLINE_MAXLEN] != 0xC7) {
80
  if (cmdlinebuf[CMDLINE_MAXLEN] != 0xC7) {
80
    msg[22] = '3';
81
    msg[22] = '3';
81
    outputnl(msg);
82
    outputnl(msg);
82
    return(3);
83
    return(3);
83
  }
84
  }
84
  /* all good */
85
  /* all good */
85
  return(0);
86
  return(0);
86
}
87
}
87
 
88
 
88
 
89
 
89
/* parses command line the hard way (directly from PSP) */
90
/* parses command line the hard way (directly from PSP) */
90
static void parse_argv(struct config *cfg) {
91
static void parse_argv(struct config *cfg) {
91
  const unsigned char *cmdlinelen = (void *)0x80;
92
  const unsigned char *cmdlinelen = (void *)0x80;
92
  char *cmdline = (void *)0x81;
93
  char *cmdline = (void *)0x81;
93
 
94
 
94
  memset(cfg, 0, sizeof(*cfg));
95
  memset(cfg, 0, sizeof(*cfg));
95
 
96
 
96
  /* set a NULL terminator on cmdline */
97
  /* set a NULL terminator on cmdline */
97
  cmdline[*cmdlinelen] = 0;
98
  cmdline[*cmdlinelen] = 0;
98
 
99
 
99
  while (*cmdline != 0) {
100
  while (*cmdline != 0) {
100
 
101
 
101
    /* skip over any leading spaces */
102
    /* skip over any leading spaces */
102
    if (*cmdline == ' ') {
103
    if (*cmdline == ' ') {
103
      cmdline++;
104
      cmdline++;
104
      continue;
105
      continue;
105
    }
106
    }
106
 
107
 
107
    if (*cmdline != '/') {
108
    if (*cmdline != '/') {
108
      output("Invalid parameter: ");
109
      output("Invalid parameter: ");
109
      outputnl(cmdline);
110
      outputnl(cmdline);
110
      goto SKIP_TO_NEXT_ARG;
111
      goto SKIP_TO_NEXT_ARG;
111
    }
112
    }
112
 
113
 
113
    /* got a slash */
114
    /* got a slash */
114
    cmdline++;  /* skip the slash */
115
    cmdline++;  /* skip the slash */
115
    switch (*cmdline) {
116
    switch (*cmdline) {
116
      case 'c': /* /C = execute command and quit */
117
      case 'c': /* /C = execute command and quit */
117
      case 'C':
118
      case 'C':
118
        cfg->flags |= FLAG_EXEC_AND_QUIT;
119
        cfg->flags |= FLAG_EXEC_AND_QUIT;
119
        /* FALLTHRU */
120
        /* FALLTHRU */
120
      case 'k': /* /K = execute command and keep running */
121
      case 'k': /* /K = execute command and keep running */
121
      case 'K':
122
      case 'K':
122
        cfg->execcmd = cmdline + 1;
123
        cfg->execcmd = cmdline + 1;
123
        return;
124
        return;
124
 
125
 
125
      case 'd': /* /D = skip autoexec.bat processing */
126
      case 'd': /* /D = skip autoexec.bat processing */
126
      case 'D':
127
      case 'D':
127
        cfg->flags |= FLAG_SKIP_AUTOEXEC;
128
        cfg->flags |= FLAG_SKIP_AUTOEXEC;
128
        break;
129
        break;
129
 
130
 
130
      case 'e': /* preset the initial size of the environment block */
131
      case 'e': /* preset the initial size of the environment block */
131
      case 'E':
132
      case 'E':
132
        cmdline++;
133
        cmdline++;
133
        if (*cmdline == ':') cmdline++; /* could be /E:size */
134
        if (*cmdline == ':') cmdline++; /* could be /E:size */
134
        atous(&(cfg->envsiz), cmdline);
135
        atous(&(cfg->envsiz), cmdline);
135
        if (cfg->envsiz < 64) cfg->envsiz = 0;
136
        if (cfg->envsiz < 64) cfg->envsiz = 0;
136
        break;
137
        break;
137
 
138
 
138
      case 'p': /* permanent shell (can't exit + run autoexec.bat) */
139
      case 'p': /* permanent shell (can't exit + run autoexec.bat) */
139
      case 'P':
140
      case 'P':
140
        cfg->flags |= FLAG_PERMANENT;
141
        cfg->flags |= FLAG_PERMANENT;
141
        break;
142
        break;
142
 
143
 
143
      case '?':
144
      case '?':
144
        outputnl("Starts the SvarCOM command interpreter");
145
        outputnl("Starts the SvarCOM command interpreter");
145
        outputnl("");
146
        outputnl("");
146
        outputnl("COMMAND /E:nnn [/[C|K] [/P] [/D] command]");
147
        outputnl("COMMAND /E:nnn [/[C|K] [/P] [/D] command]");
147
        outputnl("");
148
        outputnl("");
148
        outputnl("/D      Skip AUTOEXEC.BAT processing (makes sense only with /P)");
149
        outputnl("/D      Skip AUTOEXEC.BAT processing (makes sense only with /P)");
149
        outputnl("/E:nnn  Sets the environment size to nnn bytes");
150
        outputnl("/E:nnn  Sets the environment size to nnn bytes");
150
        outputnl("/P      Makes the new command interpreter permanent and run AUTOEXEC.BAT");
151
        outputnl("/P      Makes the new command interpreter permanent and run AUTOEXEC.BAT");
151
        outputnl("/C      Executes the specified command and returns");
152
        outputnl("/C      Executes the specified command and returns");
152
        outputnl("/K      Executes the specified command and continues running");
153
        outputnl("/K      Executes the specified command and continues running");
153
        exit(1);
154
        exit(1);
154
        break;
155
        break;
155
 
156
 
156
      default:
157
      default:
157
        output("Invalid switch: /");
158
        output("Invalid switch: /");
158
        outputnl(cmdline);
159
        outputnl(cmdline);
159
        break;
160
        break;
160
    }
161
    }
161
 
162
 
162
    /* move to next argument or quit processing if end of cmdline */
163
    /* move to next argument or quit processing if end of cmdline */
163
    SKIP_TO_NEXT_ARG:
164
    SKIP_TO_NEXT_ARG:
164
    while ((*cmdline != 0) && (*cmdline != ' ') && (*cmdline != '/')) cmdline++;
165
    while ((*cmdline != 0) && (*cmdline != ' ') && (*cmdline != '/')) cmdline++;
165
  }
166
  }
166
}
167
}
167
 
168
 
168
 
169
 
169
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
170
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
170
static void build_and_display_prompt(char *buff, unsigned short envseg) {
171
static void build_and_display_prompt(char *buff, unsigned short envseg) {
171
  char *s = buff;
172
  char *s = buff;
172
  /* locate the prompt variable or use the default pattern */
173
  /* locate the prompt variable or use the default pattern */
173
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
174
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
174
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
175
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
175
  /* build the prompt string based on pattern */
176
  /* build the prompt string based on pattern */
176
  for (; *fmt != 0; fmt++) {
177
  for (; *fmt != 0; fmt++) {
177
    if (*fmt != '$') {
178
    if (*fmt != '$') {
178
      *s = *fmt;
179
      *s = *fmt;
179
      s++;
180
      s++;
180
      continue;
181
      continue;
181
    }
182
    }
182
    /* escape code ($P, etc) */
183
    /* escape code ($P, etc) */
183
    fmt++;
184
    fmt++;
184
    switch (*fmt) {
185
    switch (*fmt) {
185
      case 'Q':  /* $Q = = (equal sign) */
186
      case 'Q':  /* $Q = = (equal sign) */
186
      case 'q':
187
      case 'q':
187
        *s = '=';
188
        *s = '=';
188
        s++;
189
        s++;
189
        break;
190
        break;
190
      case '$':  /* $$ = $ (dollar sign) */
191
      case '$':  /* $$ = $ (dollar sign) */
191
        *s = '$';
192
        *s = '$';
192
        s++;
193
        s++;
193
        break;
194
        break;
194
      case 'T':  /* $t = current time */
195
      case 'T':  /* $t = current time */
195
      case 't':
196
      case 't':
196
        s += sprintf(s, "00:00"); /* TODO */
197
        s += sprintf(s, "00:00"); /* TODO */
197
        break;
198
        break;
198
      case 'D':  /* $D = current date */
199
      case 'D':  /* $D = current date */
199
      case 'd':
200
      case 'd':
200
        s += sprintf(s, "1985-07-29"); /* TODO */
201
        s += sprintf(s, "1985-07-29"); /* TODO */
201
        break;
202
        break;
202
      case 'P':  /* $P = current drive and path */
203
      case 'P':  /* $P = current drive and path */
203
      case 'p':
204
      case 'p':
204
        _asm {
205
        _asm {
205
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
206
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
206
          int 0x21
207
          int 0x21
207
          mov bx, s
208
          mov bx, s
208
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
209
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
209
        }
210
        }
210
        *s += 'A';
211
        *s += 'A';
211
        s++;
212
        s++;
212
        *s = ':';
213
        *s = ':';
213
        s++;
214
        s++;
214
        *s = '\\';
215
        *s = '\\';
215
        s++;
216
        s++;
216
        _asm {
217
        _asm {
217
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
218
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
218
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
219
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
219
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
220
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
220
          int 0x21
221
          int 0x21
221
        }
222
        }
222
        while (*s != 0) s++; /* move ptr forward to end of pathname */
223
        while (*s != 0) s++; /* move ptr forward to end of pathname */
223
        break;
224
        break;
224
      case 'V':  /* $V = DOS version number */
225
      case 'V':  /* $V = DOS version number */
225
      case 'v':
226
      case 'v':
226
        s += sprintf(s, "VER"); /* TODO */
227
        s += sprintf(s, "VER"); /* TODO */
227
        break;
228
        break;
228
      case 'N':  /* $N = current drive */
229
      case 'N':  /* $N = current drive */
229
      case 'n':
230
      case 'n':
230
        _asm {
231
        _asm {
231
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
232
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
232
          int 0x21
233
          int 0x21
233
          mov bx, s
234
          mov bx, s
234
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
235
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
235
        }
236
        }
236
        *s += 'A';
237
        *s += 'A';
237
        s++;
238
        s++;
238
        break;
239
        break;
239
      case 'G':  /* $G = > (greater-than sign) */
240
      case 'G':  /* $G = > (greater-than sign) */
240
      case 'g':
241
      case 'g':
241
        *s = '>';
242
        *s = '>';
242
        s++;
243
        s++;
243
        break;
244
        break;
244
      case 'L':  /* $L = < (less-than sign) */
245
      case 'L':  /* $L = < (less-than sign) */
245
      case 'l':
246
      case 'l':
246
        *s = '<';
247
        *s = '<';
247
        s++;
248
        s++;
248
        break;
249
        break;
249
      case 'B':  /* $B = | (pipe) */
250
      case 'B':  /* $B = | (pipe) */
250
      case 'b':
251
      case 'b':
251
        *s = '|';
252
        *s = '|';
252
        s++;
253
        s++;
253
        break;
254
        break;
254
      case 'H':  /* $H = backspace (erases previous character) */
255
      case 'H':  /* $H = backspace (erases previous character) */
255
      case 'h':
256
      case 'h':
256
        *s = '\b';
257
        *s = '\b';
257
        s++;
258
        s++;
258
        break;
259
        break;
259
      case 'E':  /* $E = Escape code (ASCII 27) */
260
      case 'E':  /* $E = Escape code (ASCII 27) */
260
      case 'e':
261
      case 'e':
261
        *s = 27;
262
        *s = 27;
262
        s++;
263
        s++;
263
        break;
264
        break;
264
      case '_':  /* $_ = CR+LF */
265
      case '_':  /* $_ = CR+LF */
265
        *s = '\r';
266
        *s = '\r';
266
        s++;
267
        s++;
267
        *s = '\n';
268
        *s = '\n';
268
        s++;
269
        s++;
269
        break;
270
        break;
270
    }
271
    }
271
  }
272
  }
272
  *s = 0;
273
  *s = 0;
273
  output(buff);
274
  output(buff);
274
}
275
}
275
 
276
 
276
 
277
 
277
/* tries locating executable fname in path and fille res with result. returns 0 on success,
278
/* tries locating executable fname in path and fille res with result. returns 0 on success,
278
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
279
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
279
 * format is filled the offset where extension starts in fname (-1 if not found) */
280
 * format is filled the offset where extension starts in fname (-1 if not found) */
280
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
281
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
281
  unsigned short lastbslash = 0xffff;
282
  unsigned short lastbslash = 0xffff;
282
  unsigned short i, len;
283
  unsigned short i, len;
283
  unsigned char explicitpath = 0;
284
  unsigned char explicitpath = 0;
284
 
285
 
285
  /* does the original fname had an explicit path prefix or explicit ext? */
286
  /* does the original fname had an explicit path prefix or explicit ext? */
286
  *extptr = NULL;
287
  *extptr = NULL;
287
  for (i = 0; fname[i] != 0; i++) {
288
  for (i = 0; fname[i] != 0; i++) {
288
    switch (fname[i]) {
289
    switch (fname[i]) {
289
      case ':':
290
      case ':':
290
      case '\\':
291
      case '\\':
291
        explicitpath = 1;
292
        explicitpath = 1;
292
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
293
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
293
        break;
294
        break;
294
      case '.':
295
      case '.':
295
        *extptr = fname + i + 1;
296
        *extptr = fname + i + 1;
296
        break;
297
        break;
297
    }
298
    }
298
  }
299
  }
299
 
300
 
300
  /* normalize filename */
301
  /* normalize filename */
301
  if (file_truename(fname, res) != 0) return(-2);
302
  if (file_truename(fname, res) != 0) return(-2);
302
 
303
 
303
  /* printf("truename: %s\r\n", res); */
304
  /* printf("truename: %s\r\n", res); */
304
 
305
 
305
  /* figure out where the command starts and if it has an explicit extension */
306
  /* figure out where the command starts and if it has an explicit extension */
306
  for (len = 0; res[len] != 0; len++) {
307
  for (len = 0; res[len] != 0; len++) {
307
    switch (res[len]) {
308
    switch (res[len]) {
308
      case '?':   /* abort on any wildcard character */
309
      case '?':   /* abort on any wildcard character */
309
      case '*':
310
      case '*':
310
        return(-2);
311
        return(-2);
311
      case '\\':
312
      case '\\':
312
        lastbslash = len;
313
        lastbslash = len;
313
        break;
314
        break;
314
    }
315
    }
315
  }
316
  }
316
 
317
 
317
  /* printf("lastbslash=%u\r\n", lastbslash); */
318
  /* printf("lastbslash=%u\r\n", lastbslash); */
318
 
319
 
319
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
320
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
320
  if (!explicitpath) {
321
  if (!explicitpath) {
321
    if (path != NULL) {
322
    if (path != NULL) {
322
      i = strlen(path);
323
      i = strlen(path);
323
    } else {
324
    } else {
324
      i = 0;
325
      i = 0;
325
    }
326
    }
326
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
327
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
327
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
328
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
328
    if (i != 0) {
329
    if (i != 0) {
329
      memmove(res, path, i);
330
      memmove(res, path, i);
330
      res[i - 1] = '\\';
331
      res[i - 1] = '\\';
331
    }
332
    }
332
  }
333
  }
333
 
334
 
334
  /* if no extension was initially provided, try matching COM, EXE, BAT */
335
  /* if no extension was initially provided, try matching COM, EXE, BAT */
335
  if (*extptr == NULL) {
336
  if (*extptr == NULL) {
336
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
337
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
337
    len = strlen(res);
338
    len = strlen(res);
338
    for (i = 0; ext[i] != NULL; i++) {
339
    for (i = 0; ext[i] != NULL; i++) {
339
      strcpy(res + len, ext[i]);
340
      strcpy(res + len, ext[i]);
340
      /* printf("? '%s'\r\n", res); */
341
      /* printf("? '%s'\r\n", res); */
341
      *extptr = ext[i] + 1;
342
      *extptr = ext[i] + 1;
342
      if (file_getattr(res) >= 0) return(0);
343
      if (file_getattr(res) >= 0) return(0);
343
    }
344
    }
344
  } else { /* try finding it as-is */
345
  } else { /* try finding it as-is */
345
    /* printf("? '%s'\r\n", res); */
346
    /* printf("? '%s'\r\n", res); */
346
    if (file_getattr(res) >= 0) return(0);
347
    if (file_getattr(res) >= 0) return(0);
347
  }
348
  }
348
 
349
 
349
  /* not found */
350
  /* not found */
350
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
351
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
351
  return(-1);
352
  return(-1);
352
}
353
}
353
 
354
 
354
 
355
 
355
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
356
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
356
  char *cmdfile = buff + 512;
357
  char *cmdfile = buff + 512;
357
  const char far *pathptr;
358
  const char far *pathptr;
358
  int lookup;
359
  int lookup;
359
  unsigned short i;
360
  unsigned short i;
360
  const char *ext;
361
  const char *ext;
361
  char *cmd = buff + 256;
362
  char *cmd = buff + 256;
362
  const char *cmdtail;
363
  const char *cmdtail;
363
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
364
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
364
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
365
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
365
  _Packed struct {
366
  _Packed struct {
366
    unsigned short envseg;
367
    unsigned short envseg;
367
    unsigned long cmdtail;
368
    unsigned long cmdtail;
368
    unsigned long fcb1;
369
    unsigned long fcb1;
369
    unsigned long fcb2;
370
    unsigned long fcb2;
370
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
371
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
371
 
372
 
372
  /* find cmd and cmdtail */
373
  /* find cmd and cmdtail */
373
  i = 0;
374
  i = 0;
374
  cmdtail = cmdline;
375
  cmdtail = cmdline;
375
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
376
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
376
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
377
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
377
    cmd[i++] = *cmdtail;
378
    cmd[i++] = *cmdtail;
378
    cmdtail++;
379
    cmdtail++;
379
  }
380
  }
380
  cmd[i] = 0;
381
  cmd[i] = 0;
381
 
382
 
382
  /* is this a command in curdir? */
383
  /* is this a command in curdir? */
383
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
384
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
384
  if (lookup == 0) {
385
  if (lookup == 0) {
385
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
386
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
386
    goto RUNCMDFILE;
387
    goto RUNCMDFILE;
387
  } else if (lookup == -2) {
388
  } else if (lookup == -2) {
388
    /* puts("NOT FOUND"); */
389
    /* puts("NOT FOUND"); */
389
    return;
390
    return;
390
  }
391
  }
391
 
392
 
392
  /* try matching something in PATH */
393
  /* try matching something in PATH */
393
  pathptr = env_lookup_val(envseg, "PATH");
394
  pathptr = env_lookup_val(envseg, "PATH");
394
  if (pathptr == NULL) return;
395
  if (pathptr == NULL) return;
395
 
396
 
396
  /* try each path in %PATH% */
397
  /* try each path in %PATH% */
397
  for (;;) {
398
  for (;;) {
398
    for (i = 0;; i++) {
399
    for (i = 0;; i++) {
399
      buff[i] = *pathptr;
400
      buff[i] = *pathptr;
400
      if ((buff[i] == 0) || (buff[i] == ';')) break;
401
      if ((buff[i] == 0) || (buff[i] == ';')) break;
401
      pathptr++;
402
      pathptr++;
402
    }
403
    }
403
    buff[i] = 0;
404
    buff[i] = 0;
404
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
405
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
405
    if (lookup == 0) break;
406
    if (lookup == 0) break;
406
    if (lookup == -2) return;
407
    if (lookup == -2) return;
407
    if (*pathptr == ';') {
408
    if (*pathptr == ';') {
408
      pathptr++;
409
      pathptr++;
409
    } else {
410
    } else {
410
      return;
411
      return;
411
    }
412
    }
412
  }
413
  }
413
 
414
 
414
  RUNCMDFILE:
415
  RUNCMDFILE:
415
 
416
 
416
  /* special handling of batch files */
417
  /* special handling of batch files */
417
  if ((ext != NULL) && (imatch(ext, "bat"))) {
418
  if ((ext != NULL) && (imatch(ext, "bat"))) {
418
    /* copy truename of the bat file to rmod buff */
419
    /* copy truename of the bat file to rmod buff */
419
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
420
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
420
    rmod->batfile[i] = 0;
421
    rmod->batfile[i] = 0;
421
    /* copy args of the bat file to rmod buff */
422
    /* copy args of the bat file to rmod buff */
422
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
423
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
423
    /* reset the 'next line to execute' counter */
424
    /* reset the 'next line to execute' counter */
424
    rmod->batnextline = 0;
425
    rmod->batnextline = 0;
425
    /* remember the echo flag (in case bat file disables echo) */
426
    /* remember the echo flag (in case bat file disables echo) */
426
    rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
427
    rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
427
    if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
428
    if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
428
    return;
429
    return;
429
  }
430
  }
430
 
431
 
431
  /* copy full filename to execute */
432
  /* copy full filename to execute */
432
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
433
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
433
  rmod_execprog[i] = 0;
434
  rmod_execprog[i] = 0;
434
 
435
 
435
  /* copy cmdtail to rmod's PSP and compute its len */
436
  /* copy cmdtail to rmod's PSP and compute its len */
436
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
437
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
437
  rmod_cmdtail[i] = '\r';
438
  rmod_cmdtail[i] = '\r';
438
  rmod_cmdtail[-1] = i;
439
  rmod_cmdtail[-1] = i;
439
 
440
 
440
  /* set up rmod to execute the command */
441
  /* set up rmod to execute the command */
441
 
442
 
442
  ExecParam->envseg = envseg;
443
  ExecParam->envseg = envseg;
443
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
444
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
444
  ExecParam->fcb1 = 0; /* TODO farptr */
445
  ExecParam->fcb1 = 0; /* TODO farptr */
445
  ExecParam->fcb2 = 0; /* TODO farptr */
446
  ExecParam->fcb2 = 0; /* TODO farptr */
446
  exit(0); /* let rmod do the job now */
447
  exit(0); /* let rmod do the job now */
447
}
448
}
448
 
449
 
449
 
450
 
450
static void set_comspec_to_self(unsigned short envseg) {
451
static void set_comspec_to_self(unsigned short envseg) {
451
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
452
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
452
  char far *myenv = MK_FP(*psp_envseg, 0);
453
  char far *myenv = MK_FP(*psp_envseg, 0);
453
  unsigned short varcount;
454
  unsigned short varcount;
454
  char buff[256] = "COMSPEC=";
455
  char buff[256] = "COMSPEC=";
455
  char *buffptr = buff + 8;
456
  char *buffptr = buff + 8;
456
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
457
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
457
  while (*myenv != 0) {
458
  while (*myenv != 0) {
458
    /* consume a NULL-terminated string */
459
    /* consume a NULL-terminated string */
459
    while (*myenv != 0) myenv++;
460
    while (*myenv != 0) myenv++;
460
    /* move to next string */
461
    /* move to next string */
461
    myenv++;
462
    myenv++;
462
  }
463
  }
463
  /* get next word, if 1 then EXEPATH follows */
464
  /* get next word, if 1 then EXEPATH follows */
464
  myenv++;
465
  myenv++;
465
  varcount = *myenv;
466
  varcount = *myenv;
466
  myenv++;
467
  myenv++;
467
  varcount |= (*myenv << 8);
468
  varcount |= (*myenv << 8);
468
  myenv++;
469
  myenv++;
469
  if (varcount != 1) return; /* NO EXEPATH FOUND */
470
  if (varcount != 1) return; /* NO EXEPATH FOUND */
470
  while (*myenv != 0) {
471
  while (*myenv != 0) {
471
    *buffptr = *myenv;
472
    *buffptr = *myenv;
472
    buffptr++;
473
    buffptr++;
473
    myenv++;
474
    myenv++;
474
  }
475
  }
475
  *buffptr = 0;
476
  *buffptr = 0;
476
  /* printf("EXEPATH: '%s'\r\n", buff); */
477
  /* printf("EXEPATH: '%s'\r\n", buff); */
477
  env_setvar(envseg, buff);
478
  env_setvar(envseg, buff);
478
}
479
}
479
 
480
 
480
 
481
 
481
/* wait for user input */
482
/* wait for user input */
482
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
483
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
483
  _asm {
484
  _asm {
484
    push ax
485
    push ax
485
    push bx
486
    push bx
486
    push cx
487
    push cx
487
    push dx
488
    push dx
488
    push ds
489
    push ds
489
 
490
 
490
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
491
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
491
    mov ax, 0x4800
492
    mov ax, 0x4800
492
    int 0x2f
493
    int 0x2f
493
    mov bl, al /* save doskey status in BL */
494
    mov bl, al /* save doskey status in BL */
494
 
495
 
495
    /* set up buffered input to inpseg:inpoff */
496
    /* set up buffered input to inpseg:inpoff */
496
    mov ax, inpseg
497
    mov ax, inpseg
497
    push ax
498
    push ax
498
    pop ds
499
    pop ds
499
    mov dx, inpoff
500
    mov dx, inpoff
500
 
501
 
501
    /* execute either DOS input or DOSKEY */
502
    /* execute either DOS input or DOSKEY */
502
    test bl, bl /* zf set if no DOSKEY present */
503
    test bl, bl /* zf set if no DOSKEY present */
503
    jnz DOSKEY
504
    jnz DOSKEY
504
 
505
 
505
    mov ah, 0x0a
506
    mov ah, 0x0a
506
    int 0x21
507
    int 0x21
507
    jmp short DONE
508
    jmp short DONE
508
 
509
 
509
    DOSKEY:
510
    DOSKEY:
510
    mov ax, 0x4810
511
    mov ax, 0x4810
511
    int 0x2f
512
    int 0x2f
512
 
513
 
513
    DONE:
514
    DONE:
514
    /* terminate command with a CR/LF */
515
    /* terminate command with a CR/LF */
515
    mov ah, 0x02 /* display character in dl */
516
    mov ah, 0x02 /* display character in dl */
516
    mov dl, 0x0d
517
    mov dl, 0x0d
517
    int 0x21
518
    int 0x21
518
    mov dl, 0x0a
519
    mov dl, 0x0a
519
    int 0x21
520
    int 0x21
520
 
521
 
521
    pop ds
522
    pop ds
522
    pop dx
523
    pop dx
523
    pop cx
524
    pop cx
524
    pop bx
525
    pop bx
525
    pop ax
526
    pop ax
526
  }
527
  }
527
}
528
}
528
 
529
 
529
 
530
 
530
/* fetches a line from batch file and write it to buff (NULL-terminated),
531
/* fetches a line from batch file and write it to buff (NULL-terminated),
531
 * increments rmod counter and returns 0 on success. */
532
 * increments rmod counter and returns 0 on success. */
532
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
533
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
533
  unsigned short i;
534
  unsigned short i;
534
  unsigned short batname_seg = FP_SEG(rmod->batfile);
535
  unsigned short batname_seg = FP_SEG(rmod->batfile);
535
  unsigned short batname_off = FP_OFF(rmod->batfile);
536
  unsigned short batname_off = FP_OFF(rmod->batfile);
536
  unsigned short filepos_cx = rmod->batnextline >> 16;
537
  unsigned short filepos_cx = rmod->batnextline >> 16;
537
  unsigned short filepos_dx = rmod->batnextline & 0xffff;
538
  unsigned short filepos_dx = rmod->batnextline & 0xffff;
538
  unsigned char blen = 0;
539
  unsigned char blen = 0;
-
 
540
  unsigned short errv = 0;
539
 
541
 
540
  /* open file, jump to offset filpos, and read data into buff.
542
  /* open file, jump to offset filpos, and read data into buff.
541
   * result in blen (unchanged if EOF or failure). */
543
   * result in blen (unchanged if EOF or failure). */
542
  _asm {
544
  _asm {
543
    push ax
545
    push ax
544
    push bx
546
    push bx
545
    push cx
547
    push cx
546
    push dx
548
    push dx
547
 
549
 
548
    /* open file (read-only) */
550
    /* open file (read-only) */
-
 
551
    mov bx, 0xffff        /* preset BX to 0xffff to detect error conditions */
549
    mov dx, batname_off
552
    mov dx, batname_off
550
    mov ax, batname_seg
553
    mov ax, batname_seg
551
    push ds     /* save DS */
554
    push ds     /* save DS */
552
    mov ds, ax
555
    mov ds, ax
553
    mov ax, 0x3d00
556
    mov ax, 0x3d00
554
    int 0x21    /* handle in ax on success */
557
    int 0x21    /* handle in ax on success */
555
    pop ds      /* restore DS */
558
    pop ds      /* restore DS */
556
    jc DONE
559
    jc ERR
557
    mov bx, ax  /* save handle to bx */
560
    mov bx, ax  /* save handle to bx */
558
 
561
 
559
    /* jump to file offset CX:DX */
562
    /* jump to file offset CX:DX */
560
    mov ax, 0x4200
563
    mov ax, 0x4200
561
    mov cx, filepos_cx
564
    mov cx, filepos_cx
562
    mov dx, filepos_dx
565
    mov dx, filepos_dx
563
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
566
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
564
    jc CLOSEANDQUIT
567
    jc ERR
565
 
568
 
566
    /* read the line into buff */
569
    /* read the line into buff */
567
    mov ah, 0x3f
570
    mov ah, 0x3f
568
    xor ch, ch
571
    xor ch, ch
569
    mov cl, buffmaxlen
572
    mov cl, buffmaxlen
570
    mov dx, buff
573
    mov dx, buff
571
    int 0x21 /* CF clear on success, AX=number of bytes read */
574
    int 0x21 /* CF clear on success, AX=number of bytes read */
572
    jc CLOSEANDQUIT
575
    jc ERR
573
    mov blen, al
576
    mov blen, al
-
 
577
    jmp CLOSEANDQUIT
-
 
578
 
-
 
579
    ERR:
-
 
580
    mov errv, ax
574
 
581
 
575
    CLOSEANDQUIT:
582
    CLOSEANDQUIT:
576
    /* close file (handle in bx) */
583
    /* close file (if bx contains a handle) */
-
 
584
    cmp bx, 0xffff
-
 
585
    je DONE
577
    mov ah, 0x3e
586
    mov ah, 0x3e
578
    int 0x21
587
    int 0x21
579
 
588
 
580
    DONE:
589
    DONE:
581
    pop dx
590
    pop dx
582
    pop cx
591
    pop cx
583
    pop bx
592
    pop bx
584
    pop ax
593
    pop ax
585
  }
594
  }
586
 
595
 
587
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
596
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
588
 
597
 
-
 
598
  if (errv != 0) outputnl(doserr(errv));
-
 
599
 
589
  /* on EOF - abort processing the bat file */
600
  /* on EOF - abort processing the bat file */
590
  if (blen == 0) goto OOPS;
601
  if (blen == 0) goto OOPS;
591
 
602
 
592
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
603
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
593
   * I support all CR/LF, CR- and LF-terminated batch files */
604
   * I support all CR/LF, CR- and LF-terminated batch files */
594
  for (i = 0; i < blen; i++) {
605
  for (i = 0; i < blen; i++) {
595
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
606
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
596
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->batnextline += 1;
607
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->batnextline += 1;
597
      break;
608
      break;
598
    }
609
    }
599
  }
610
  }
600
  buff[i] = 0;
611
  buff[i] = 0;
601
  rmod->batnextline += i + 1;
612
  rmod->batnextline += i + 1;
602
 
613
 
603
  return(0);
614
  return(0);
604
 
615
 
605
  OOPS:
616
  OOPS:
606
  rmod->batfile[0] = 0;
617
  rmod->batfile[0] = 0;
607
  rmod->batnextline = 0;
618
  rmod->batnextline = 0;
608
  return(-1);
619
  return(-1);
609
}
620
}
610
 
621
 
611
 
622
 
612
int main(void) {
623
int main(void) {
613
  static struct config cfg;
624
  static struct config cfg;
614
  static unsigned short far *rmod_envseg;
625
  static unsigned short far *rmod_envseg;
615
  static unsigned short far *lastexitcode;
626
  static unsigned short far *lastexitcode;
616
  static struct rmod_props far *rmod;
627
  static struct rmod_props far *rmod;
617
  static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
628
  static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
618
  static char *cmdline;
629
  static char *cmdline;
619
 
630
 
620
  rmod = rmod_find(BUFFER_len);
631
  rmod = rmod_find(BUFFER_len);
621
  if (rmod == NULL) {
632
  if (rmod == NULL) {
622
    /* look at command line parameters (in case env size if set there) */
633
    /* look at command line parameters (in case env size if set there) */
623
    parse_argv(&cfg);
634
    parse_argv(&cfg);
624
    rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
635
    rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
625
    if (rmod == NULL) {
636
    if (rmod == NULL) {
626
      outputnl("ERROR: rmod_install() failed");
637
      outputnl("ERROR: rmod_install() failed");
627
      return(1);
638
      return(1);
628
    }
639
    }
629
    /* copy flags to rmod's storage (and enable ECHO) */
640
    /* copy flags to rmod's storage (and enable ECHO) */
630
    rmod->flags = cfg.flags | FLAG_ECHOFLAG;
641
    rmod->flags = cfg.flags | FLAG_ECHOFLAG;
631
    /* printf("rmod installed at %Fp\r\n", rmod); */
642
    /* printf("rmod installed at %Fp\r\n", rmod); */
632
  } else {
643
  } else {
633
    /* printf("rmod found at %Fp\r\n", rmod); */
644
    /* printf("rmod found at %Fp\r\n", rmod); */
634
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
645
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
635
     * die asap, because the command has been executed already, so I no longer
646
     * die asap, because the command has been executed already, so I no longer
636
     * have a purpose in life */
647
     * have a purpose in life */
637
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
648
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
638
  }
649
  }
639
 
650
 
640
  /* install a few guardvals in memory to detect some cases of overflows */
651
  /* install a few guardvals in memory to detect some cases of overflows */
641
  memguard_set(cmdlinebuf);
652
  memguard_set(cmdlinebuf);
642
 
653
 
643
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
654
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
644
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
655
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
645
 
656
 
646
  /* make COMPSEC point to myself */
657
  /* make COMPSEC point to myself */
647
  set_comspec_to_self(*rmod_envseg);
658
  set_comspec_to_self(*rmod_envseg);
648
 
659
 
649
/*  {
660
/*  {
650
    unsigned short envsiz;
661
    unsigned short envsiz;
651
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
662
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
652
    envsiz = *sizptr;
663
    envsiz = *sizptr;
653
    envsiz *= 16;
664
    envsiz *= 16;
654
    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);
665
    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);
655
  }*/
666
  }*/
656
 
667
 
657
  /* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
668
  /* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
658
   * but skip this check if /D was also passed */
669
   * but skip this check if /D was also passed */
659
  if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
670
  if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
660
    if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
671
    if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
661
  }
672
  }
662
 
673
 
663
  do {
674
  do {
664
    /* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
675
    /* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
665
    if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->batfile[0] == 0)) outputnl("");
676
    if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->batfile[0] == 0)) outputnl("");
666
 
677
 
667
    SKIP_NEWLINE:
678
    SKIP_NEWLINE:
668
 
679
 
669
    /* cancel any redirections that may have been set up before */
680
    /* cancel any redirections that may have been set up before */
670
    redir_revert();
681
    redir_revert();
671
 
682
 
672
    /* memory check */
683
    /* memory check */
673
    memguard_check(rmod->rmodseg, cmdlinebuf);
684
    memguard_check(rmod->rmodseg, cmdlinebuf);
674
 
685
 
675
    /* preset cmdline to point at the dedicated buffer */
686
    /* preset cmdline to point at the dedicated buffer */
676
    cmdline = cmdlinebuf;
687
    cmdline = cmdlinebuf;
677
 
688
 
678
    /* (re)load translation strings if needed */
689
    /* (re)load translation strings if needed */
679
    nls_langreload(BUFFER, *rmod_envseg);
690
    nls_langreload(BUFFER, *rmod_envseg);
680
 
691
 
681
    /* skip user input if I have a command to exec (/C or /K) */
692
    /* skip user input if I have a command to exec (/C or /K) */
682
    if (cfg.execcmd != NULL) {
693
    if (cfg.execcmd != NULL) {
683
      cmdline = cfg.execcmd;
694
      cmdline = cfg.execcmd;
684
      cfg.execcmd = NULL;
695
      cfg.execcmd = NULL;
685
      goto EXEC_CMDLINE;
696
      goto EXEC_CMDLINE;
686
    }
697
    }
687
 
698
 
688
    /* if batch file is being executed -> fetch next line */
699
    /* if batch file is being executed -> fetch next line */
689
    if (rmod->batfile[0] != 0) {
700
    if (rmod->batfile[0] != 0) {
690
      if (getbatcmd(cmdline, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
701
      if (getbatcmd(cmdline, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
691
        /* restore echo flag as it was before running the bat file */
702
        /* restore echo flag as it was before running the bat file */
692
        rmod->flags &= ~FLAG_ECHOFLAG;
703
        rmod->flags &= ~FLAG_ECHOFLAG;
693
        if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
704
        if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
694
        continue;
705
        continue;
695
      }
706
      }
696
      /* skip any leading spaces */
707
      /* skip any leading spaces */
697
      while (*cmdline == ' ') cmdline++;
708
      while (*cmdline == ' ') cmdline++;
698
      /* output prompt and command on screen if echo on and command is not
709
      /* output prompt and command on screen if echo on and command is not
699
       * inhibiting it with the @ prefix */
710
       * inhibiting it with the @ prefix */
700
      if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
711
      if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
701
        build_and_display_prompt(BUFFER, *rmod_envseg);
712
        build_and_display_prompt(BUFFER, *rmod_envseg);
702
        outputnl(cmdline);
713
        outputnl(cmdline);
703
      }
714
      }
704
      /* skip the @ prefix if present, it is no longer useful */
715
      /* skip the @ prefix if present, it is no longer useful */
705
      if (cmdline[0] == '@') cmdline++;
716
      if (cmdline[0] == '@') cmdline++;
706
    } else {
717
    } else {
707
      /* interactive mode: display prompt (if echo enabled) and wait for user
718
      /* interactive mode: display prompt (if echo enabled) and wait for user
708
       * command line */
719
       * command line */
709
      if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
720
      if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
710
      /* collect user input */
721
      /* collect user input */
711
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
722
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
712
      /* copy it to local cmdline */
723
      /* copy it to local cmdline */
713
      if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
724
      if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
714
      cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
725
      cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
715
    }
726
    }
716
 
727
 
717
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
728
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
718
    if (cmdline[0] == 0) goto SKIP_NEWLINE;
729
    if (cmdline[0] == 0) goto SKIP_NEWLINE;
719
 
730
 
720
    /* I jump here when I need to exec an initial command (/C or /K) */
731
    /* I jump here when I need to exec an initial command (/C or /K) */
721
    EXEC_CMDLINE:
732
    EXEC_CMDLINE:
722
 
733
 
723
    /* move pointer forward to skip over any leading spaces */
734
    /* move pointer forward to skip over any leading spaces */
724
    while (*cmdline == ' ') cmdline++;
735
    while (*cmdline == ' ') cmdline++;
725
 
736
 
726
    /* update rmod's ptr to COMPSPEC so it is always up to date */
737
    /* update rmod's ptr to COMPSPEC so it is always up to date */
727
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
738
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
728
 
739
 
729
    /* handle redirections (if any) */
740
    /* handle redirections (if any) */
730
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
741
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
731
      outputnl("");
742
      outputnl("");
732
      continue;
743
      continue;
733
    }
744
    }
734
 
745
 
735
    /* try matching (and executing) an internal command */
746
    /* try matching (and executing) an internal command */
736
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER)) >= -1) {
747
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER)) >= -1) {
737
      /* internal command executed */
748
      /* internal command executed */
738
      redir_revert(); /* revert stdout (in case it was redirected) */
749
      redir_revert(); /* revert stdout (in case it was redirected) */
739
      continue;
750
      continue;
740
    }
751
    }
741
 
752
 
742
    /* if here, then this was not an internal command */
753
    /* if here, then this was not an internal command */
743
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
754
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
744
    /* perhaps this is a newly launched BAT file */
755
    /* perhaps this is a newly launched BAT file */
745
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
756
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
746
 
757
 
747
    /* revert stdout (so the err msg is not redirected) */
758
    /* revert stdout (so the err msg is not redirected) */
748
    redir_revert();
759
    redir_revert();
749
 
760
 
750
    /* run_as_external() does not return on success, if I am still alive then
761
    /* run_as_external() does not return on success, if I am still alive then
751
     * external command failed to execute */
762
     * external command failed to execute */
752
    outputnl("Bad command or file name");
763
    outputnl("Bad command or file name");
753
 
764
 
754
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
765
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
755
 
766
 
756
  sayonara(rmod);
767
  sayonara(rmod);
757
  return(0);
768
  return(0);
758
}
769
}
759
 
770