Subversion Repositories SvarDOS

Rev

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

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