Subversion Repositories SvarDOS

Rev

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

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