Subversion Repositories SvarDOS

Rev

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

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