Subversion Repositories SvarDOS

Rev

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

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