Subversion Repositories SvarDOS

Rev

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

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