Subversion Repositories SvarDOS

Rev

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

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