Subversion Repositories SvarDOS

Rev

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

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