Subversion Repositories SvarDOS

Rev

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

Rev 949 Rev 957
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 */
-
 
319
#define DELETE_STDIN_FILE 1
-
 
320
#define CALL_FLAG         2
-
 
321
 
318
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod, struct redir_data *redir, unsigned char delete_stdin_file) {
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) {
319
  char *cmdfile = buff + 512;
323
  char *cmdfile = buff + 512;
320
  const char far *pathptr;
324
  const char far *pathptr;
321
  int lookup;
325
  int lookup;
322
  unsigned short i;
326
  unsigned short i;
323
  const char *ext;
327
  const char *ext;
324
  char *cmd = buff + 1024;
328
  char *cmd = buff + 1024;
325
  const char *cmdtail;
329
  const char *cmdtail;
326
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
330
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
327
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
331
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
328
  _Packed struct {
332
  _Packed struct {
329
    unsigned short envseg;
333
    unsigned short envseg;
330
    unsigned long cmdtail;
334
    unsigned long cmdtail;
331
    unsigned long fcb1;
335
    unsigned long fcb1;
332
    unsigned long fcb2;
336
    unsigned long fcb2;
333
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
337
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
334
 
338
 
335
  /* find cmd and cmdtail */
339
  /* find cmd and cmdtail */
336
  i = 0;
340
  i = 0;
337
  cmdtail = cmdline;
341
  cmdtail = cmdline;
338
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
342
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
339
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
343
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
340
    cmd[i++] = *cmdtail;
344
    cmd[i++] = *cmdtail;
341
    cmdtail++;
345
    cmdtail++;
342
  }
346
  }
343
  cmd[i] = 0;
347
  cmd[i] = 0;
344
 
348
 
345
  /* is this a command in curdir? */
349
  /* is this a command in curdir? */
346
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
350
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
347
  if (lookup == 0) {
351
  if (lookup == 0) {
348
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
352
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
349
    goto RUNCMDFILE;
353
    goto RUNCMDFILE;
350
  } else if (lookup == -2) {
354
  } else if (lookup == -2) {
351
    /* puts("NOT FOUND"); */
355
    /* puts("NOT FOUND"); */
352
    return;
356
    return;
353
  }
357
  }
354
 
358
 
355
  /* try matching something in PATH */
359
  /* try matching something in PATH */
356
  pathptr = env_lookup_val(envseg, "PATH");
360
  pathptr = env_lookup_val(envseg, "PATH");
357
 
361
 
358
  /* try each path in %PATH% */
362
  /* try each path in %PATH% */
359
  while (pathptr) {
363
  while (pathptr) {
360
    for (i = 0;; i++) {
364
    for (i = 0;; i++) {
361
      buff[i] = *pathptr;
365
      buff[i] = *pathptr;
362
      if ((buff[i] == 0) || (buff[i] == ';')) break;
366
      if ((buff[i] == 0) || (buff[i] == ';')) break;
363
      pathptr++;
367
      pathptr++;
364
    }
368
    }
365
    buff[i] = 0;
369
    buff[i] = 0;
366
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
370
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
367
    if (lookup == 0) goto RUNCMDFILE;
371
    if (lookup == 0) goto RUNCMDFILE;
368
    if (lookup == -2) return;
372
    if (lookup == -2) return;
369
    if (*pathptr == ';') {
373
    if (*pathptr == ';') {
370
      pathptr++;
374
      pathptr++;
371
    } else {
375
    } else {
372
      break;
376
      break;
373
    }
377
    }
374
  }
378
  }
375
 
379
 
376
  /* 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) */
377
  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];
378
  buff[128 + i] = 0;
382
  buff[128 + i] = 0;
379
  if ((i < 9) && (link_computefname(buff, buff + 128, envseg) == 0)) {
383
  if ((i < 9) && (link_computefname(buff, buff + 128, envseg) == 0)) {
380
    /* 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 */
381
    i = 0;
385
    i = 0;
382
    _asm {
386
    _asm {
383
      push ax
387
      push ax
384
      push bx
388
      push bx
385
      push cx
389
      push cx
386
      push dx
390
      push dx
387
 
391
 
388
      mov ax, 0x3d00  /* DOS 2+ - OPEN EXISTING FILE, READ-ONLY */
392
      mov ax, 0x3d00  /* DOS 2+ - OPEN EXISTING FILE, READ-ONLY */
389
      mov dx, buff    /* file name */
393
      mov dx, buff    /* file name */
390
      int 0x21
394
      int 0x21
391
      jc ERR_FOPEN
395
      jc ERR_FOPEN
392
      /* file handle in AX, read from file now */
396
      /* file handle in AX, read from file now */
393
      mov bx, ax      /* file handle */
397
      mov bx, ax      /* file handle */
394
      mov ah, 0x3f    /* Read from file via handle bx */
398
      mov ah, 0x3f    /* Read from file via handle bx */
395
      mov cx, 128     /* up to 128 bytes */
399
      mov cx, 128     /* up to 128 bytes */
396
      /* mov dx, buff */ /* dest buffer (already set) */
400
      /* mov dx, buff */ /* dest buffer (already set) */
397
      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 */
398
      jc ERR_READ
402
      jc ERR_READ
399
      mov i, ax
403
      mov i, ax
400
      ERR_READ:
404
      ERR_READ:
401
      mov ah, 0x3e    /* close file handle in BX */
405
      mov ah, 0x3e    /* close file handle in BX */
402
      int 0x21
406
      int 0x21
403
      ERR_FOPEN:
407
      ERR_FOPEN:
404
 
408
 
405
      pop dx
409
      pop dx
406
      pop cx
410
      pop cx
407
      pop bx
411
      pop bx
408
      pop ax
412
      pop ax
409
    }
413
    }
410
 
414
 
411
    /* did I read anything? */
415
    /* did I read anything? */
412
    if (i != 0) {
416
    if (i != 0) {
413
      buff[i] = 0;
417
      buff[i] = 0;
414
      /* 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
415
       * link file using a text editor */
419
       * link file using a text editor */
416
      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++);
417
      buff[i] = 0;
421
      buff[i] = 0;
418
      /* lookup check */
422
      /* lookup check */
419
      if (buff[0] != 0) {
423
      if (buff[0] != 0) {
420
        lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
424
        lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
421
        if (lookup == 0) goto RUNCMDFILE;
425
        if (lookup == 0) goto RUNCMDFILE;
422
      }
426
      }
423
    }
427
    }
424
  }
428
  }
425
 
429
 
426
  /* all failed (ie. executable file not found) */
430
  /* all failed (ie. executable file not found) */
427
  return;
431
  return;
428
 
432
 
429
  RUNCMDFILE:
433
  RUNCMDFILE:
430
 
434
 
431
  /* special handling of batch files */
435
  /* special handling of batch files */
432
  if ((ext != NULL) && (imatch(ext, "bat"))) {
436
  if ((ext != NULL) && (imatch(ext, "bat"))) {
433
    /* free the bat-context linked list, if present, and replace it with a new entry */
-
 
434
    while (rmod->bat != NULL) {
-
 
435
      struct batctx far *victim = rmod->bat;
437
    struct batctx far *newbat;
436
      rmod->bat = rmod->bat->parent;
-
 
437
      rmod_ffree(victim);
-
 
438
    }
438
 
439
    rmod->bat = rmod_fmalloc(sizeof(struct batctx), rmod->rmodseg, "SVBATCTX");
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
      outputnl("INTERNAL ERR: OUT OF MEMORY");
441
      rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
442
      return;
442
      if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
443
    }
443
    }
444
    _fmemset(rmod->bat, 0, sizeof(struct batctx));
-
 
445
 
444
 
-
 
445
    /* if bat is not called via a CALL, then free the bat-context linked list */
-
 
446
    if ((flags & CALL_FLAG) == 0) {
-
 
447
      while (rmod->bat != NULL) {
446
    /* copy truename of the bat file to rmod buff */
448
        struct batctx far *victim = rmod->bat;
447
    _fstrcpy(rmod->bat->fname, cmdfile);
449
        rmod->bat = rmod->bat->parent;
-
 
450
        rmod_ffree(victim);
-
 
451
      }
-
 
452
    }
-
 
453
    /* allocate a new bat context */
-
 
454
    newbat = rmod_fcalloc(sizeof(struct batctx), rmod->rmodseg, "SVBATCTX");
-
 
455
    if (newbat == NULL) {
-
 
456
      nls_outputnl_doserr(8); /* insufficient memory */
-
 
457
      return;
-
 
458
    }
448
 
459
 
-
 
460
    /* fill the newly allocated batctx structure */
-
 
461
    _fstrcpy(newbat->fname, cmdfile); /* truename of the BAT file */
449
    /* explode args of the bat file and store them in rmod buff */
462
    /* explode args of the bat file and store them in rmod buff */
450
    cmd_explode(buff, cmdline, NULL);
463
    cmd_explode(buff, cmdline, NULL);
451
    _fmemcpy(rmod->bat->argv, buff, sizeof(rmod->bat->argv));
464
    _fmemcpy(newbat->argv, buff, sizeof(newbat->argv));
-
 
465
 
-
 
466
    /* push the new bat to the top of rmod's linked list */
-
 
467
    newbat->parent = rmod->bat;
-
 
468
    rmod->bat = newbat;
452
 
469
 
453
    /* reset the 'next line to execute' counter */
-
 
454
    rmod->bat->nextline = 0;
-
 
455
    /* remember the echo flag (in case bat file disables echo) */
-
 
456
    rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
-
 
457
    if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
-
 
458
    return;
470
    return;
459
  }
471
  }
460
 
472
 
461
  /* copy full filename to execute, along with redirected files (if any) */
473
  /* copy full filename to execute, along with redirected files (if any) */
462
  _fstrcpy(rmod_execprog, cmdfile);
474
  _fstrcpy(rmod_execprog, cmdfile);
463
 
475
 
464
  /* copy stdin file if a redirection is needed */
476
  /* copy stdin file if a redirection is needed */
465
  if (redir->stdinfile) {
477
  if (redir->stdinfile) {
466
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDINFILE);
478
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDINFILE);
467
    char far *delstdin = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDIN_DEL);
479
    char far *delstdin = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDIN_DEL);
468
    _fstrcpy(farptr, redir->stdinfile);
480
    _fstrcpy(farptr, redir->stdinfile);
469
    if (delete_stdin_file) {
481
    if (flags & DELETE_STDIN_FILE) {
470
      *delstdin = redir->stdinfile[0];
482
      *delstdin = redir->stdinfile[0];
471
    } else {
483
    } else {
472
      *delstdin = 0;
484
      *delstdin = 0;
473
    }
485
    }
474
  }
486
  }
475
 
487
 
476
  /* same for stdout file */
488
  /* same for stdout file */
477
  if (redir->stdoutfile) {
489
  if (redir->stdoutfile) {
478
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTFILE);
490
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTFILE);
479
    unsigned short far *farptr16 = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTAPP);
491
    unsigned short far *farptr16 = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTAPP);
480
    _fstrcpy(farptr, redir->stdoutfile);
492
    _fstrcpy(farptr, redir->stdoutfile);
481
    /* openflag */
493
    /* openflag */
482
    *farptr16 = redir->stdout_openflag;
494
    *farptr16 = redir->stdout_openflag;
483
  }
495
  }
484
 
496
 
485
  /* copy cmdtail to rmod's PSP and compute its len */
497
  /* copy cmdtail to rmod's PSP and compute its len */
486
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
498
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
487
  rmod_cmdtail[i] = '\r';
499
  rmod_cmdtail[i] = '\r';
488
  rmod_cmdtail[-1] = i;
500
  rmod_cmdtail[-1] = i;
489
 
501
 
490
  /* set up rmod to execute the command */
502
  /* set up rmod to execute the command */
491
 
503
 
492
  ExecParam->envseg = envseg;
504
  ExecParam->envseg = envseg;
493
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
505
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
494
  ExecParam->fcb1 = 0; /* TODO farptr */
506
  ExecParam->fcb1 = 0; /* TODO farptr */
495
  ExecParam->fcb2 = 0; /* TODO farptr */
507
  ExecParam->fcb2 = 0; /* TODO farptr */
496
  exit(0); /* let rmod do the job now */
508
  exit(0); /* let rmod do the job now */
497
}
509
}
498
 
510
 
499
 
511
 
500
static void set_comspec_to_self(unsigned short envseg) {
512
static void set_comspec_to_self(unsigned short envseg) {
501
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
513
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
502
  char far *myenv = MK_FP(*psp_envseg, 0);
514
  char far *myenv = MK_FP(*psp_envseg, 0);
503
  unsigned short varcount;
515
  unsigned short varcount;
504
  char buff[256] = "COMSPEC=";
516
  char buff[256] = "COMSPEC=";
505
  char *buffptr = buff + 8;
517
  char *buffptr = buff + 8;
506
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
518
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
507
  while (*myenv != 0) {
519
  while (*myenv != 0) {
508
    /* consume a NULL-terminated string */
520
    /* consume a NULL-terminated string */
509
    while (*myenv != 0) myenv++;
521
    while (*myenv != 0) myenv++;
510
    /* move to next string */
522
    /* move to next string */
511
    myenv++;
523
    myenv++;
512
  }
524
  }
513
  /* get next word, if 1 then EXEPATH follows */
525
  /* get next word, if 1 then EXEPATH follows */
514
  myenv++;
526
  myenv++;
515
  varcount = *myenv;
527
  varcount = *myenv;
516
  myenv++;
528
  myenv++;
517
  varcount |= (*myenv << 8);
529
  varcount |= (*myenv << 8);
518
  myenv++;
530
  myenv++;
519
  if (varcount != 1) return; /* NO EXEPATH FOUND */
531
  if (varcount != 1) return; /* NO EXEPATH FOUND */
520
  while (*myenv != 0) {
532
  while (*myenv != 0) {
521
    *buffptr = *myenv;
533
    *buffptr = *myenv;
522
    buffptr++;
534
    buffptr++;
523
    myenv++;
535
    myenv++;
524
  }
536
  }
525
  *buffptr = 0;
537
  *buffptr = 0;
526
  /* printf("EXEPATH: '%s'\r\n", buff); */
538
  /* printf("EXEPATH: '%s'\r\n", buff); */
527
  env_setvar(envseg, buff);
539
  env_setvar(envseg, buff);
528
}
540
}
529
 
541
 
530
 
542
 
531
/* wait for user input */
543
/* wait for user input */
532
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
544
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
533
  _asm {
545
  _asm {
534
    push ax
546
    push ax
535
    push bx
547
    push bx
536
    push cx
548
    push cx
537
    push dx
549
    push dx
538
    push ds
550
    push ds
539
 
551
 
540
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
552
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
541
    mov ax, 0x4800
553
    mov ax, 0x4800
542
    int 0x2f
554
    int 0x2f
543
    mov bl, al /* save doskey status in BL */
555
    mov bl, al /* save doskey status in BL */
544
 
556
 
545
    /* set up buffered input to inpseg:inpoff */
557
    /* set up buffered input to inpseg:inpoff */
546
    mov ax, inpseg
558
    mov ax, inpseg
547
    push ax
559
    push ax
548
    pop ds
560
    pop ds
549
    mov dx, inpoff
561
    mov dx, inpoff
550
 
562
 
551
    /* execute either DOS input or DOSKEY */
563
    /* execute either DOS input or DOSKEY */
552
    test bl, bl /* zf set if no DOSKEY present */
564
    test bl, bl /* zf set if no DOSKEY present */
553
    jnz DOSKEY
565
    jnz DOSKEY
554
 
566
 
555
    mov ah, 0x0a
567
    mov ah, 0x0a
556
    int 0x21
568
    int 0x21
557
    jmp short DONE
569
    jmp short DONE
558
 
570
 
559
    DOSKEY:
571
    DOSKEY:
560
    mov ax, 0x4810
572
    mov ax, 0x4810
561
    int 0x2f
573
    int 0x2f
562
 
574
 
563
    DONE:
575
    DONE:
564
    /* terminate command with a CR/LF */
576
    /* terminate command with a CR/LF */
565
    mov ah, 0x02 /* display character in dl */
577
    mov ah, 0x02 /* display character in dl */
566
    mov dl, 0x0d
578
    mov dl, 0x0d
567
    int 0x21
579
    int 0x21
568
    mov dl, 0x0a
580
    mov dl, 0x0a
569
    int 0x21
581
    int 0x21
570
 
582
 
571
    pop ds
583
    pop ds
572
    pop dx
584
    pop dx
573
    pop cx
585
    pop cx
574
    pop bx
586
    pop bx
575
    pop ax
587
    pop ax
576
  }
588
  }
577
}
589
}
578
 
590
 
579
 
591
 
580
/* fetches a line from batch file and write it to buff (NULL-terminated),
592
/* fetches a line from batch file and write it to buff (NULL-terminated),
581
 * increments rmod counter and returns 0 on success. */
593
 * increments rmod counter and returns 0 on success. */
582
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
594
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
583
  unsigned short i;
595
  unsigned short i;
584
  unsigned short batname_seg = FP_SEG(rmod->bat->fname);
596
  unsigned short batname_seg = FP_SEG(rmod->bat->fname);
585
  unsigned short batname_off = FP_OFF(rmod->bat->fname);
597
  unsigned short batname_off = FP_OFF(rmod->bat->fname);
586
  unsigned short filepos_cx = rmod->bat->nextline >> 16;
598
  unsigned short filepos_cx = rmod->bat->nextline >> 16;
587
  unsigned short filepos_dx = rmod->bat->nextline & 0xffff;
599
  unsigned short filepos_dx = rmod->bat->nextline & 0xffff;
588
  unsigned char blen = 0;
600
  unsigned char blen = 0;
589
  unsigned short errv = 0;
601
  unsigned short errv = 0;
590
 
602
 
591
  /* open file, jump to offset filpos, and read data into buff.
603
  /* open file, jump to offset filpos, and read data into buff.
592
   * result in blen (unchanged if EOF or failure). */
604
   * result in blen (unchanged if EOF or failure). */
593
  _asm {
605
  _asm {
594
    push ax
606
    push ax
595
    push bx
607
    push bx
596
    push cx
608
    push cx
597
    push dx
609
    push dx
598
 
610
 
599
    /* open file (read-only) */
611
    /* open file (read-only) */
600
    mov bx, 0xffff        /* preset BX to 0xffff to detect error conditions */
612
    mov bx, 0xffff        /* preset BX to 0xffff to detect error conditions */
601
    mov dx, batname_off
613
    mov dx, batname_off
602
    mov ax, batname_seg
614
    mov ax, batname_seg
603
    push ds     /* save DS */
615
    push ds     /* save DS */
604
    mov ds, ax
616
    mov ds, ax
605
    mov ax, 0x3d00
617
    mov ax, 0x3d00
606
    int 0x21    /* handle in ax on success */
618
    int 0x21    /* handle in ax on success */
607
    pop ds      /* restore DS */
619
    pop ds      /* restore DS */
608
    jc ERR
620
    jc ERR
609
    mov bx, ax  /* save handle to bx */
621
    mov bx, ax  /* save handle to bx */
610
 
622
 
611
    /* jump to file offset CX:DX */
623
    /* jump to file offset CX:DX */
612
    mov ax, 0x4200
624
    mov ax, 0x4200
613
    mov cx, filepos_cx
625
    mov cx, filepos_cx
614
    mov dx, filepos_dx
626
    mov dx, filepos_dx
615
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
627
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
616
    jc ERR
628
    jc ERR
617
 
629
 
618
    /* read the line into buff */
630
    /* read the line into buff */
619
    mov ah, 0x3f
631
    mov ah, 0x3f
620
    xor ch, ch
632
    xor ch, ch
621
    mov cl, buffmaxlen
633
    mov cl, buffmaxlen
622
    mov dx, buff
634
    mov dx, buff
623
    int 0x21 /* CF clear on success, AX=number of bytes read */
635
    int 0x21 /* CF clear on success, AX=number of bytes read */
624
    jc ERR
636
    jc ERR
625
    mov blen, al
637
    mov blen, al
626
    jmp CLOSEANDQUIT
638
    jmp CLOSEANDQUIT
627
 
639
 
628
    ERR:
640
    ERR:
629
    mov errv, ax
641
    mov errv, ax
630
 
642
 
631
    CLOSEANDQUIT:
643
    CLOSEANDQUIT:
632
    /* close file (if bx contains a handle) */
644
    /* close file (if bx contains a handle) */
633
    cmp bx, 0xffff
645
    cmp bx, 0xffff
634
    je DONE
646
    je DONE
635
    mov ah, 0x3e
647
    mov ah, 0x3e
636
    int 0x21
648
    int 0x21
637
 
649
 
638
    DONE:
650
    DONE:
639
    pop dx
651
    pop dx
640
    pop cx
652
    pop cx
641
    pop bx
653
    pop bx
642
    pop ax
654
    pop ax
643
  }
655
  }
644
 
656
 
645
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
657
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
646
 
658
 
647
  if (errv != 0) nls_outputnl_doserr(errv);
659
  if (errv != 0) nls_outputnl_doserr(errv);
648
 
660
 
649
  /* on EOF - abort processing the bat file */
661
  /* on EOF - abort processing the bat file */
650
  if (blen == 0) goto OOPS;
662
  if (blen == 0) goto OOPS;
651
 
663
 
652
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
664
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
653
   * I support all CR/LF, CR- and LF-terminated batch files */
665
   * I support all CR/LF, CR- and LF-terminated batch files */
654
  for (i = 0; i < blen; i++) {
666
  for (i = 0; i < blen; i++) {
655
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
667
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
656
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->bat->nextline += 1;
668
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->bat->nextline += 1;
657
      break;
669
      break;
658
    }
670
    }
659
  }
671
  }
660
  buff[i] = 0;
672
  buff[i] = 0;
661
  rmod->bat->nextline += i + 1;
673
  rmod->bat->nextline += i + 1;
662
 
674
 
663
  return(0);
675
  return(0);
664
 
676
 
665
  OOPS:
677
  OOPS:
666
  rmod->bat->fname[0] = 0;
678
  rmod->bat->fname[0] = 0;
667
  rmod->bat->nextline = 0;
679
  rmod->bat->nextline = 0;
668
  return(-1);
680
  return(-1);
669
}
681
}
670
 
682
 
671
 
683
 
672
/* replaces %-variables in a BAT line with resolved values:
684
/* replaces %-variables in a BAT line with resolved values:
673
 * %PATH%       -> replaced by the contend of the PATH env variable
685
 * %PATH%       -> replaced by the contend of the PATH env variable
674
 * %UNDEFINED%  -> undefined variables are replaced by nothing ("")
686
 * %UNDEFINED%  -> undefined variables are replaced by nothing ("")
675
 * %NOTCLOSED   -> NOTCLOSED
687
 * %NOTCLOSED   -> NOTCLOSED
676
 * %1           -> first argument of the batch file (or nothing if no arg) */
688
 * %1           -> first argument of the batch file (or nothing if no arg) */
677
static void batpercrepl(char *res, unsigned short ressz, const char *line, const struct rmod_props far *rmod, unsigned short envseg) {
689
static void batpercrepl(char *res, unsigned short ressz, const char *line, const struct rmod_props far *rmod, unsigned short envseg) {
678
  unsigned short lastperc = 0xffff;
690
  unsigned short lastperc = 0xffff;
679
  unsigned short reslen = 0;
691
  unsigned short reslen = 0;
680
 
692
 
681
  if (ressz == 0) return;
693
  if (ressz == 0) return;
682
  ressz--; /* reserve one byte for the NULL terminator */
694
  ressz--; /* reserve one byte for the NULL terminator */
683
 
695
 
684
  for (; (reslen < ressz) && (*line != 0); line++) {
696
  for (; (reslen < ressz) && (*line != 0); line++) {
685
    /* if not a percent, I don't care */
697
    /* if not a percent, I don't care */
686
    if (*line != '%') {
698
    if (*line != '%') {
687
      res[reslen++] = *line;
699
      res[reslen++] = *line;
688
      continue;
700
      continue;
689
    }
701
    }
690
 
702
 
691
    /* *** perc char handling *** */
703
    /* *** perc char handling *** */
692
 
704
 
693
    /* closing perc? */
705
    /* closing perc? */
694
    if (lastperc != 0xffff) {
706
    if (lastperc != 0xffff) {
695
      /* %% is '%' */
707
      /* %% is '%' */
696
      if (lastperc == reslen) {
708
      if (lastperc == reslen) {
697
        res[reslen++] = '%';
709
        res[reslen++] = '%';
698
      } else {   /* otherwise variable name */
710
      } else {   /* otherwise variable name */
699
        const char far *ptr;
711
        const char far *ptr;
700
        res[reslen] = 0;
712
        res[reslen] = 0;
701
        reslen = lastperc;
713
        reslen = lastperc;
702
        ptr = env_lookup_val(envseg, res + reslen);
714
        ptr = env_lookup_val(envseg, res + reslen);
703
        if (ptr != NULL) {
715
        if (ptr != NULL) {
704
          while ((*ptr != 0) && (reslen < ressz)) {
716
          while ((*ptr != 0) && (reslen < ressz)) {
705
            res[reslen++] = *ptr;
717
            res[reslen++] = *ptr;
706
            ptr++;
718
            ptr++;
707
          }
719
          }
708
        }
720
        }
709
      }
721
      }
710
      lastperc = 0xffff;
722
      lastperc = 0xffff;
711
      continue;
723
      continue;
712
    }
724
    }
713
 
725
 
714
    /* digit? (bat arg) */
726
    /* digit? (bat arg) */
715
    if ((line[1] >= '0') && (line[1] <= '9')) {
727
    if ((line[1] >= '0') && (line[1] <= '9')) {
716
      unsigned short argid = line[1] - '0';
728
      unsigned short argid = line[1] - '0';
717
      unsigned short i;
729
      unsigned short i;
718
      const char far *argv = "";
730
      const char far *argv = "";
719
      if ((rmod != NULL) && (rmod->bat != NULL)) argv = rmod->bat->argv;
731
      if ((rmod != NULL) && (rmod->bat != NULL)) argv = rmod->bat->argv;
720
 
732
 
721
      /* locate the proper arg */
733
      /* locate the proper arg */
722
      for (i = 0; i != argid; i++) {
734
      for (i = 0; i != argid; i++) {
723
        /* if string is 0, then end of list reached */
735
        /* if string is 0, then end of list reached */
724
        if (*argv == 0) break;
736
        if (*argv == 0) break;
725
        /* jump to next arg */
737
        /* jump to next arg */
726
        while (*argv != 0) argv++;
738
        while (*argv != 0) argv++;
727
        argv++;
739
        argv++;
728
      }
740
      }
729
 
741
 
730
      /* copy the arg to result */
742
      /* copy the arg to result */
731
      for (i = 0; (argv[i] != 0) && (reslen < ressz); i++) {
743
      for (i = 0; (argv[i] != 0) && (reslen < ressz); i++) {
732
        res[reslen++] = argv[i];
744
        res[reslen++] = argv[i];
733
      }
745
      }
734
      line++;  /* skip the digit */
746
      line++;  /* skip the digit */
735
      continue;
747
      continue;
736
    }
748
    }
737
 
749
 
738
    /* opening perc */
750
    /* opening perc */
739
    lastperc = reslen;
751
    lastperc = reslen;
740
 
752
 
741
  }
753
  }
742
 
754
 
743
  res[reslen] = 0;
755
  res[reslen] = 0;
744
}
756
}
745
 
757
 
746
 
758
 
-
 
759
 
747
int main(void) {
760
int main(void) {
748
  static struct config cfg;
761
  static struct config cfg;
749
  static unsigned short far *rmod_envseg;
762
  static unsigned short far *rmod_envseg;
750
  static unsigned short far *lastexitcode;
763
  static unsigned short far *lastexitcode;
751
  static struct rmod_props far *rmod;
764
  static struct rmod_props far *rmod;
752
  static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
765
  static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
753
  static char *cmdline;
766
  static char *cmdline;
754
  static struct redir_data redirprops;
767
  static struct redir_data redirprops;
755
  static enum cmd_result cmdres;
768
  static enum cmd_result cmdres;
756
  static unsigned short i; /* general-purpose variable for short-lived things */
769
  static unsigned short i; /* general-purpose variable for short-lived things */
757
  static unsigned char delete_stdin_file;
770
  static unsigned char flags;
758
 
771
 
759
  rmod = rmod_find(BUFFER_len);
772
  rmod = rmod_find(BUFFER_len);
760
  if (rmod == NULL) {
773
  if (rmod == NULL) {
761
    /* look at command line parameters (in case env size if set there) */
774
    /* look at command line parameters (in case env size if set there) */
762
    parse_argv(&cfg);
775
    parse_argv(&cfg);
763
    rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
776
    rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
764
    if (rmod == NULL) {
777
    if (rmod == NULL) {
765
      outputnl("ERROR: rmod_install() failed");
778
      outputnl("ERROR: rmod_install() failed");
766
      return(1);
779
      return(1);
767
    }
780
    }
768
    /* copy flags to rmod's storage (and enable ECHO) */
781
    /* copy flags to rmod's storage (and enable ECHO) */
769
    rmod->flags = cfg.flags | FLAG_ECHOFLAG;
782
    rmod->flags = cfg.flags | FLAG_ECHOFLAG;
770
    /* printf("rmod installed at %Fp\r\n", rmod); */
783
    /* printf("rmod installed at %Fp\r\n", rmod); */
771
    rmod->version = BYTE_VERSION;
784
    rmod->version = BYTE_VERSION;
772
  } else {
785
  } else {
773
    /* printf("rmod found at %Fp\r\n", rmod); */
786
    /* printf("rmod found at %Fp\r\n", rmod); */
774
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
787
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
775
     * die asap, because the command has been executed already, so I no longer
788
     * die asap, because the command has been executed already, so I no longer
776
     * have a purpose in life */
789
     * have a purpose in life */
777
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
790
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
778
    /* */
791
    /* */
779
    if (rmod->version != BYTE_VERSION) {
792
    if (rmod->version != BYTE_VERSION) {
780
      outputnl("SVARCOM VERSION CHANGED. SYSTEM HALTED. PLEASE REBOOT YOUR COMPUTER.");
793
      outputnl("SVARCOM VERSION CHANGED. SYSTEM HALTED. PLEASE REBOOT YOUR COMPUTER.");
781
      _asm {
794
      _asm {
782
        HALT:
795
        HALT:
783
        hlt
796
        hlt
784
        jmp HALT
797
        jmp HALT
785
      }
798
      }
786
    }
799
    }
787
  }
800
  }
788
 
801
 
789
  /* install a few guardvals in memory to detect some cases of overflows */
802
  /* install a few guardvals in memory to detect some cases of overflows */
790
  memguard_set(cmdlinebuf);
803
  memguard_set(cmdlinebuf);
791
 
804
 
792
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
805
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
793
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
806
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
794
 
807
 
795
  /* make COMPSEC point to myself */
808
  /* make COMPSEC point to myself */
796
  set_comspec_to_self(*rmod_envseg);
809
  set_comspec_to_self(*rmod_envseg);
797
 
810
 
798
/*  {
811
/*  {
799
    unsigned short envsiz;
812
    unsigned short envsiz;
800
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
813
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
801
    envsiz = *sizptr;
814
    envsiz = *sizptr;
802
    envsiz *= 16;
815
    envsiz *= 16;
803
    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);
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);
804
  }*/
817
  }*/
805
 
818
 
806
  /* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
819
  /* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
807
   * but skip this check if /D was also passed */
820
   * but skip this check if /D was also passed */
808
  if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
821
  if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
809
    if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
822
    if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
810
  }
823
  }
811
 
824
 
812
  do {
825
  do {
813
    /* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
826
    /* 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("");
827
    if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->bat == NULL)) outputnl("");
815
 
828
 
816
    SKIP_NEWLINE:
829
    SKIP_NEWLINE:
817
 
830
 
818
    /* memory check */
831
    /* memory check */
819
    memguard_check(rmod->rmodseg, cmdlinebuf);
832
    memguard_check(rmod->rmodseg, cmdlinebuf);
820
 
833
 
821
    /* preset cmdline to point at the dedicated buffer */
834
    /* preset cmdline to point at the dedicated buffer */
822
    cmdline = cmdlinebuf;
835
    cmdline = cmdlinebuf;
823
 
836
 
824
    /* (re)load translation strings if needed */
837
    /* (re)load translation strings if needed */
825
    nls_langreload(BUFFER, *rmod_envseg);
838
    nls_langreload(BUFFER, *rmod_envseg);
826
 
839
 
827
    /* load awaiting command, if any (used to run piped commands) */
840
    /* load awaiting command, if any (used to run piped commands) */
828
    if (rmod->awaitingcmd[0] != 0) {
841
    if (rmod->awaitingcmd[0] != 0) {
829
      _fstrcpy(cmdline, rmod->awaitingcmd);
842
      _fstrcpy(cmdline, rmod->awaitingcmd);
830
      rmod->awaitingcmd[0] = 0;
843
      rmod->awaitingcmd[0] = 0;
831
      delete_stdin_file = 1;
844
      flags |= DELETE_STDIN_FILE;
832
      goto EXEC_CMDLINE;
845
      goto EXEC_CMDLINE;
833
    } else {
846
    } else {
834
      delete_stdin_file = 0;
847
      flags &= ~DELETE_STDIN_FILE;
835
    }
848
    }
836
 
849
 
837
    /* skip user input if I have a command to exec (/C or /K) */
850
    /* skip user input if I have a command to exec (/C or /K) */
838
    if (cfg.execcmd != NULL) {
851
    if (cfg.execcmd != NULL) {
839
      cmdline = cfg.execcmd;
852
      cmdline = cfg.execcmd;
840
      cfg.execcmd = NULL;
853
      cfg.execcmd = NULL;
841
      goto EXEC_CMDLINE;
854
      goto EXEC_CMDLINE;
842
    }
855
    }
843
 
856
 
844
    /* if batch file is being executed -> fetch next line */
857
    /* if batch file is being executed -> fetch next line */
845
    if (rmod->bat != NULL) {
858
    if (rmod->bat != NULL) {
846
      if (getbatcmd(BUFFER, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
859
      if (getbatcmd(BUFFER, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
847
        struct batctx far *victim = rmod->bat;
860
        struct batctx far *victim = rmod->bat;
848
        rmod->bat = rmod->bat->parent;
861
        rmod->bat = rmod->bat->parent;
849
        rmod_ffree(victim);
862
        rmod_ffree(victim);
850
        /* end of batch? then restore echo flag as it was before running the bat file */
863
        /* end of batch? then restore echo flag as it was before running the (first) bat file */
851
        if (rmod->bat == NULL) {
864
        if (rmod->bat == NULL) {
852
          rmod->flags &= ~FLAG_ECHOFLAG;
865
          rmod->flags &= ~FLAG_ECHOFLAG;
853
          if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
866
          if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
854
        }
867
        }
855
        continue;
868
        continue;
856
      }
869
      }
857
      /* %-decoding of variables (%PATH%, %1, %%...), result in cmdline */
870
      /* %-decoding of variables (%PATH%, %1, %%...), result in cmdline */
858
      batpercrepl(cmdline, CMDLINE_MAXLEN, BUFFER, rmod, *rmod_envseg);
871
      batpercrepl(cmdline, CMDLINE_MAXLEN, BUFFER, rmod, *rmod_envseg);
859
      /* skip any leading spaces */
872
      /* skip any leading spaces */
860
      while (*cmdline == ' ') cmdline++;
873
      while (*cmdline == ' ') cmdline++;
861
      /* output prompt and command on screen if echo on and command is not
874
      /* output prompt and command on screen if echo on and command is not
862
       * inhibiting it with the @ prefix */
875
       * inhibiting it with the @ prefix */
863
      if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
876
      if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
864
        build_and_display_prompt(BUFFER, *rmod_envseg);
877
        build_and_display_prompt(BUFFER, *rmod_envseg);
865
        outputnl(cmdline);
878
        outputnl(cmdline);
866
      }
879
      }
867
      /* skip the @ prefix if present, it is no longer useful */
880
      /* skip the @ prefix if present, it is no longer useful */
868
      if (cmdline[0] == '@') cmdline++;
881
      if (cmdline[0] == '@') cmdline++;
869
    } else {
882
    } else {
870
      /* interactive mode: display prompt (if echo enabled) and wait for user
883
      /* interactive mode: display prompt (if echo enabled) and wait for user
871
       * command line */
884
       * command line */
872
      if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
885
      if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
873
      /* collect user input */
886
      /* collect user input */
874
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
887
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
875
      /* copy it to local cmdline */
888
      /* copy it to local cmdline */
876
      if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
889
      if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
877
      cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
890
      cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
878
    }
891
    }
879
 
892
 
880
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
893
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
881
    if (cmdline[0] == 0) goto SKIP_NEWLINE;
894
    if (cmdline[0] == 0) goto SKIP_NEWLINE;
882
 
895
 
883
    /* I jump here when I need to exec an initial command (/C or /K) */
896
    /* I jump here when I need to exec an initial command (/C or /K) */
884
    EXEC_CMDLINE:
897
    EXEC_CMDLINE:
885
 
898
 
886
    /* move pointer forward to skip over any leading spaces */
899
    /* move pointer forward to skip over any leading spaces */
887
    while (*cmdline == ' ') cmdline++;
900
    while (*cmdline == ' ') cmdline++;
888
 
901
 
889
    /* update rmod's ptr to COMPSPEC so it is always up to date */
902
    /* update rmod's ptr to COMPSPEC so it is always up to date */
890
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
903
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
891
 
904
 
892
    /* handle redirections (if any) */
905
    /* handle redirections (if any) */
893
    i = redir_parsecmd(&redirprops, cmdline, rmod->awaitingcmd, *rmod_envseg);
906
    i = redir_parsecmd(&redirprops, cmdline, rmod->awaitingcmd, *rmod_envseg);
894
    if (i != 0) {
907
    if (i != 0) {
895
      nls_outputnl_doserr(i);
908
      nls_outputnl_doserr(i);
896
      rmod->awaitingcmd[0] = 0;
909
      rmod->awaitingcmd[0] = 0;
897
      continue;
910
      continue;
898
    }
911
    }
899
 
912
 
900
    /* try matching (and executing) an internal command */
913
    /* try matching (and executing) an internal command */
901
    cmdres = cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER), &redirprops, delete_stdin_file);
914
    cmdres = cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER), &redirprops, flags & DELETE_STDIN_FILE);
902
    if ((cmdres == CMD_OK) || (cmdres == CMD_FAIL)) {
915
    if ((cmdres == CMD_OK) || (cmdres == CMD_FAIL)) {
903
      /* internal command executed */
916
      /* internal command executed */
904
      continue;
917
      continue;
905
    } else if (cmdres == CMD_CHANGED) { /* cmdline changed, needs to be reprocessed */
918
    } else if (cmdres == CMD_CHANGED) { /* cmdline changed, needs to be reprocessed */
906
      goto EXEC_CMDLINE;
919
      goto EXEC_CMDLINE;
-
 
920
    } else if (cmdres == CMD_CHANGED_BY_CALL) { /* cmdline changed *specifically* by CALL */
-
 
921
      /* the distinction is important since it changes the way batch files are processed */
-
 
922
      flags |= CALL_FLAG;
-
 
923
      goto EXEC_CMDLINE;
907
    } else if (cmdres == CMD_NOTFOUND) {
924
    } else if (cmdres == CMD_NOTFOUND) {
908
      /* this was not an internal command, try matching an external command */
925
      /* this was not an internal command, try matching an external command */
909
      run_as_external(BUFFER, cmdline, *rmod_envseg, rmod, &redirprops, delete_stdin_file);
926
      run_as_external(BUFFER, cmdline, *rmod_envseg, rmod, &redirprops, flags);
-
 
927
      flags &= ~CALL_FLAG; /* reset callflag to make sure it is processed only once */
-
 
928
 
910
      /* perhaps this is a newly launched BAT file */
929
      /* is it a newly launched BAT file? */
911
      if ((rmod->bat != NULL) && (rmod->bat->nextline == 0)) goto SKIP_NEWLINE;
930
      if ((rmod->bat != NULL) && (rmod->bat->nextline == 0)) goto SKIP_NEWLINE;
912
      /* run_as_external() does not return on success, if I am still alive then
931
      /* run_as_external() does not return on success, if I am still alive then
913
       * external command failed to execute */
932
       * external command failed to execute */
914
      outputnl("Bad command or file name");
933
      outputnl("Bad command or file name");
915
      continue;
934
      continue;
916
    }
935
    }
917
 
936
 
918
    /* I should never ever land here */
937
    /* I should never ever land here */
919
    outputnl("INTERNAL ERR: INVALID CMDRES");
938
    outputnl("INTERNAL ERR: INVALID CMDRES");
920
 
939
 
921
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
940
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
922
 
941
 
923
  sayonara(rmod);
942
  sayonara(rmod);
924
  return(0);
943
  return(0);
925
}
944
}
926
 
945