Subversion Repositories SvarDOS

Rev

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

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