Subversion Repositories SvarDOS

Rev

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

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