Subversion Repositories SvarDOS

Rev

Rev 485 | Rev 491 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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