Subversion Repositories SvarDOS

Rev

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

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