Subversion Repositories SvarDOS

Rev

Rev 1713 | Rev 1715 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
421 mateuszvis 1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
3
 *
1714 mateusz.vi 4
 * Copyright (C) 2021-2024 Mateusz Viste
421 mateuszvis 5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
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
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
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,
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
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
22
 * DEALINGS IN THE SOFTWARE.
23
 */
24
 
349 mateuszvis 25
#include <i86.h>
26
#include <dos.h>
27
#include <stdio.h>
350 mateuszvis 28
#include <stdlib.h>
349 mateuszvis 29
#include <string.h>
30
 
1001 mateusz.vi 31
#include "svarlang.lib/svarlang.h"
349 mateuszvis 32
 
352 mateuszvis 33
#include "cmd.h"
366 mateuszvis 34
#include "env.h"
352 mateuszvis 35
#include "helpers.h"
402 mateuszvis 36
#include "redir.h"
351 mateuszvis 37
#include "rmodinit.h"
448 mateuszvis 38
#include "sayonara.h"
349 mateuszvis 39
 
479 mateuszvis 40
#include "rmodcore.h" /* rmod binary inside a BUFFER array */
443 mateuszvis 41
 
572 mateuszvis 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! ***          */
1023 mateusz.vi 48
#define BYTE_VERSION 4
572 mateuszvis 49
 
50
 
349 mateuszvis 51
struct config {
449 mateuszvis 52
  unsigned char flags; /* command.com flags, as defined in rmodinit.h */
443 mateuszvis 53
  char *execcmd;
410 mateuszvis 54
  unsigned short envsiz;
443 mateuszvis 55
};
349 mateuszvis 56
 
490 mateuszvis 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!) */
59
#define CMDLINE_MAXLEN 255
349 mateuszvis 60
 
490 mateuszvis 61
 
62
/* sets guard values at a few places in memory for later detection of
63
 * overflows via memguard_check() */
500 mateuszvis 64
static void memguard_set(char *cmdlinebuf) {
490 mateuszvis 65
  BUFFER[sizeof(BUFFER) - 1] = 0xC7;
500 mateuszvis 66
  cmdlinebuf[CMDLINE_MAXLEN] = 0xC7;
490 mateuszvis 67
}
68
 
69
 
70
/* checks for valguards at specific memory locations, returns 0 on success */
500 mateuszvis 71
static int memguard_check(unsigned short rmodseg, char *cmdlinebuf) {
490 mateuszvis 72
  /* check RMOD signature (would be overwritten in case of stack overflow */
73
  static char msg[] = "!! MEMORY CORRUPTION ## DETECTED !!";
74
  unsigned short far *rmodsig = MK_FP(rmodseg, 0x100 + 6);
548 mateuszvis 75
  unsigned char far *rmod = MK_FP(rmodseg, 0);
76
 
490 mateuszvis 77
  if (*rmodsig != 0x2019) {
78
    msg[22] = '1';
548 mateuszvis 79
    goto FAIL;
490 mateuszvis 80
  }
548 mateuszvis 81
 
500 mateuszvis 82
  /* check last BUFFER byte */
490 mateuszvis 83
  if (BUFFER[sizeof(BUFFER) - 1] != 0xC7) {
84
    msg[22] = '2';
548 mateuszvis 85
    goto FAIL;
490 mateuszvis 86
  }
548 mateuszvis 87
 
500 mateuszvis 88
  /* check last cmdlinebuf byte */
89
  if (cmdlinebuf[CMDLINE_MAXLEN] != 0xC7) {
490 mateuszvis 90
    msg[22] = '3';
548 mateuszvis 91
    goto FAIL;
490 mateuszvis 92
  }
548 mateuszvis 93
 
94
  /* check rmod exec buf */
95
  if (rmod[RMOD_OFFSET_EXECPROG + 127] != 0) {
96
    msg[22] = '4';
97
    goto FAIL;
98
  }
99
 
100
  /* check rmod exec stdin buf */
101
  if (rmod[RMOD_OFFSET_STDINFILE + 127] != 0) {
102
    msg[22] = '5';
103
    goto FAIL;
104
  }
105
 
106
  /* check rmod exec stdout buf */
107
  if (rmod[RMOD_OFFSET_STDOUTFILE + 127] != 0) {
108
    msg[22] = '6';
109
    goto FAIL;
110
  }
111
 
112
  /* else all good */
490 mateuszvis 113
  return(0);
548 mateuszvis 114
 
115
  /* error handling */
116
  FAIL:
117
  outputnl(msg);
118
  return(1);
490 mateuszvis 119
}
120
 
121
 
443 mateuszvis 122
/* parses command line the hard way (directly from PSP) */
123
static void parse_argv(struct config *cfg) {
491 mateuszvis 124
  char *cmdline = (void *)0x81;
443 mateuszvis 125
 
349 mateuszvis 126
  memset(cfg, 0, sizeof(*cfg));
350 mateuszvis 127
 
1714 mateusz.vi 128
  while (*cmdline != 0x0d) {
443 mateuszvis 129
 
130
    /* skip over any leading spaces */
491 mateuszvis 131
    if (*cmdline == ' ') {
132
      cmdline++;
133
      continue;
349 mateuszvis 134
    }
443 mateuszvis 135
 
491 mateuszvis 136
    if (*cmdline != '/') {
989 mateusz.vi 137
      nls_output(0,6); /* "Invalid parameter" */
138
      output(": ");
491 mateuszvis 139
      outputnl(cmdline);
140
      goto SKIP_TO_NEXT_ARG;
141
    }
443 mateuszvis 142
 
491 mateuszvis 143
    /* got a slash */
144
    cmdline++;  /* skip the slash */
145
    switch (*cmdline) {
146
      case 'c': /* /C = execute command and quit */
147
      case 'C':
148
        cfg->flags |= FLAG_EXEC_AND_QUIT;
149
        /* FALLTHRU */
150
      case 'k': /* /K = execute command and keep running */
151
      case 'K':
1001 mateusz.vi 152
        cmdline++;
153
        cfg->execcmd = cmdline;
1714 mateusz.vi 154
        goto DONE; /* further arguments are for the executed program, not for me */
443 mateuszvis 155
 
1001 mateusz.vi 156
      case 'y': /* /Y = execute batch file step-by-step (with /P, /K or /C) */
157
      case 'Y':
158
        cfg->flags |= FLAG_STEPBYSTEP;
159
        break;
160
 
494 mateuszvis 161
      case 'd': /* /D = skip autoexec.bat processing */
162
      case 'D':
163
        cfg->flags |= FLAG_SKIP_AUTOEXEC;
164
        break;
165
 
491 mateuszvis 166
      case 'e': /* preset the initial size of the environment block */
167
      case 'E':
168
        cmdline++;
169
        if (*cmdline == ':') cmdline++; /* could be /E:size */
170
        atous(&(cfg->envsiz), cmdline);
171
        if (cfg->envsiz < 64) cfg->envsiz = 0;
172
        break;
449 mateuszvis 173
 
491 mateuszvis 174
      case 'p': /* permanent shell (can't exit + run autoexec.bat) */
175
      case 'P':
176
        cfg->flags |= FLAG_PERMANENT;
177
        break;
444 mateuszvis 178
 
491 mateuszvis 179
      case '?':
989 mateusz.vi 180
        nls_outputnl(1,0); /* "Starts the SvarCOM command interpreter" */
491 mateuszvis 181
        outputnl("");
989 mateusz.vi 182
        nls_outputnl(1,1); /* "COMMAND /E:nnn [/[C|K] [/P] [/D] command]" */
491 mateuszvis 183
        outputnl("");
989 mateusz.vi 184
        nls_outputnl(1,2); /* "/D      Skip AUTOEXEC.BAT processing (makes sense only with /P)" */
185
        nls_outputnl(1,3); /* "/E:nnn  Sets the environment size to nnn bytes" */
186
        nls_outputnl(1,4); /* "/P      Makes the new command interpreter permanent and run AUTOEXEC.BAT" */
187
        nls_outputnl(1,5); /* "/C      Executes the specified command and returns" */
188
        nls_outputnl(1,6); /* "/K      Executes the specified command and continues running" */
1001 mateusz.vi 189
        nls_outputnl(1,7); /* "/Y      Executes the batch program step by step" */
491 mateuszvis 190
        exit(1);
191
        break;
192
 
193
      default:
989 mateusz.vi 194
        nls_output(0,2); /* invalid switch */
195
        output(": /");
491 mateuszvis 196
        outputnl(cmdline);
197
        break;
350 mateuszvis 198
    }
443 mateuszvis 199
 
200
    /* move to next argument or quit processing if end of cmdline */
491 mateuszvis 201
    SKIP_TO_NEXT_ARG:
1714 mateusz.vi 202
    while ((*cmdline != 0x0d) && (*cmdline != ' ') && (*cmdline != '/')) cmdline++;
349 mateuszvis 203
  }
1714 mateusz.vi 204
 
205
  DONE:
206
 
207
  /* set a nul terminator on cmdline (expected later in the code) */
208
  while (*cmdline != 0x0d) cmdline++;
209
  *cmdline = 0;
349 mateuszvis 210
}
211
 
212
 
474 mateuszvis 213
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
214
static void build_and_display_prompt(char *buff, unsigned short envseg) {
215
  char *s = buff;
370 mateuszvis 216
  /* locate the prompt variable or use the default pattern */
438 mateuszvis 217
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
370 mateuszvis 218
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
219
  /* build the prompt string based on pattern */
354 mateuszvis 220
  for (; *fmt != 0; fmt++) {
221
    if (*fmt != '$') {
222
      *s = *fmt;
223
      s++;
224
      continue;
225
    }
226
    /* escape code ($P, etc) */
227
    fmt++;
228
    switch (*fmt) {
229
      case 'Q':  /* $Q = = (equal sign) */
230
      case 'q':
231
        *s = '=';
232
        s++;
233
        break;
234
      case '$':  /* $$ = $ (dollar sign) */
235
        *s = '$';
236
        s++;
237
        break;
238
      case 'T':  /* $t = current time */
239
      case 't':
240
        s += sprintf(s, "00:00"); /* TODO */
241
        break;
242
      case 'D':  /* $D = current date */
243
      case 'd':
244
        s += sprintf(s, "1985-07-29"); /* TODO */
245
        break;
246
      case 'P':  /* $P = current drive and path */
247
      case 'p':
248
        _asm {
249
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
250
          int 0x21
251
          mov bx, s
252
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
253
        }
254
        *s += 'A';
255
        s++;
256
        *s = ':';
257
        s++;
258
        *s = '\\';
259
        s++;
260
        _asm {
261
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
262
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
263
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
264
          int 0x21
526 mateuszvis 265
          jc DONE         /* leave path empty on error */
266
          /* move s ptr forward to end (0-termintor) of pathname */
267
          NEXTBYTE:
268
          mov si, s
269
          cmp byte ptr [si], 0
270
          je DONE
271
          inc s
272
          jmp NEXTBYTE
273
          DONE:
354 mateuszvis 274
        }
275
        break;
276
      case 'V':  /* $V = DOS version number */
277
      case 'v':
278
        s += sprintf(s, "VER"); /* TODO */
279
        break;
280
      case 'N':  /* $N = current drive */
281
      case 'n':
282
        _asm {
283
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
284
          int 0x21
285
          mov bx, s
286
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
287
        }
288
        *s += 'A';
289
        s++;
290
        break;
291
      case 'G':  /* $G = > (greater-than sign) */
292
      case 'g':
293
        *s = '>';
294
        s++;
295
        break;
296
      case 'L':  /* $L = < (less-than sign) */
297
      case 'l':
298
        *s = '<';
299
        s++;
300
        break;
301
      case 'B':  /* $B = | (pipe) */
302
      case 'b':
303
        *s = '|';
304
        s++;
305
        break;
306
      case 'H':  /* $H = backspace (erases previous character) */
307
      case 'h':
308
        *s = '\b';
309
        s++;
310
        break;
311
      case 'E':  /* $E = Escape code (ASCII 27) */
312
      case 'e':
313
        *s = 27;
314
        s++;
315
        break;
316
      case '_':  /* $_ = CR+LF */
317
        *s = '\r';
318
        s++;
319
        *s = '\n';
320
        s++;
321
        break;
322
    }
323
  }
474 mateuszvis 324
  *s = 0;
325
  output(buff);
354 mateuszvis 326
}
349 mateuszvis 327
 
328
 
1156 mateusz.vi 329
static void dos_fname2fcb(char far *fcb, const char *cmd) {
330
  unsigned short fcb_seg, fcb_off;
331
  fcb_seg = FP_SEG(fcb);
332
  fcb_off = FP_OFF(fcb);
333
  _asm {
334
    push ax
335
    push bx
336
    push cx
337
    push dx
338
    push es
339
    push si
340
 
341
    mov ax, 0x2900   /* DOS 1+ - parse filename into FCB (DS:SI=fname, ES:DI=FCB) */
342
    mov si, cmd
343
    mov es, fcb_seg
344
    mov di, fcb_off
345
    int 0x21
346
 
347
    pop si
348
    pop es
349
    pop dx
350
    pop cx
351
    pop bx
352
    pop ax
353
  }
354
}
355
 
356
 
357
/* parses cmdtail and fills fcb1 and fcb2 with first and second arguments,
358
 * respectively. an FCB is 12 bytes long:
359
 * drive (0=default, 1=A, 2=B, etc)
360
 * fname (8 chars, blank-padded)
361
 * fext (3 chars, blank-padded) */
362
static void cmdtail_to_fcb(char far *fcb1, char far *fcb2, const char *cmdtail) {
363
 
364
  /* skip any leading spaces */
365
  while (*cmdtail == ' ') cmdtail++;
366
 
367
  /* convert first arg */
368
  dos_fname2fcb(fcb1, cmdtail);
369
 
370
  /* skip to next arg */
371
  while ((*cmdtail != ' ') && (*cmdtail != 0)) cmdtail++;
372
  while (*cmdtail == ' ') cmdtail++;
373
 
374
  /* convert second arg */
375
  dos_fname2fcb(fcb2, cmdtail);
376
}
377
 
378
 
957 mateusz.vi 379
/* a few internal flags */
380
#define DELETE_STDIN_FILE 1
381
#define CALL_FLAG         2
382
 
383
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) {
472 mateuszvis 384
  char *cmdfile = buff + 512;
458 mateuszvis 385
  const char far *pathptr;
386
  int lookup;
387
  unsigned short i;
388
  const char *ext;
508 mateuszvis 389
  char *cmd = buff + 1024;
479 mateuszvis 390
  const char *cmdtail;
461 mateuszvis 391
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
392
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
393
  _Packed struct {
394
    unsigned short envseg;
395
    unsigned long cmdtail;
396
    unsigned long fcb1;
397
    unsigned long fcb2;
398
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
364 mateuszvis 399
 
472 mateuszvis 400
  /* find cmd and cmdtail */
401
  i = 0;
402
  cmdtail = cmdline;
403
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
404
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
405
    cmd[i++] = *cmdtail;
406
    cmdtail++;
407
  }
408
  cmd[i] = 0;
364 mateuszvis 409
 
458 mateuszvis 410
  /* is this a command in curdir? */
472 mateuszvis 411
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
458 mateuszvis 412
  if (lookup == 0) {
413
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
414
    goto RUNCMDFILE;
415
  } else if (lookup == -2) {
416
    /* puts("NOT FOUND"); */
417
    return;
418
  }
419
 
420
  /* try matching something in PATH */
421
  pathptr = env_lookup_val(envseg, "PATH");
422
 
423
  /* try each path in %PATH% */
571 mateuszvis 424
  while (pathptr) {
458 mateuszvis 425
    for (i = 0;; i++) {
426
      buff[i] = *pathptr;
427
      if ((buff[i] == 0) || (buff[i] == ';')) break;
428
      pathptr++;
429
    }
430
    buff[i] = 0;
472 mateuszvis 431
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
571 mateuszvis 432
    if (lookup == 0) goto RUNCMDFILE;
458 mateuszvis 433
    if (lookup == -2) return;
434
    if (*pathptr == ';') {
435
      pathptr++;
436
    } else {
571 mateuszvis 437
      break;
458 mateuszvis 438
    }
439
  }
440
 
571 mateuszvis 441
  /* last chance: is it an executable link? (trim extension from cmd first) */
442
  for (i = 0; (cmd[i] != 0) && (cmd[i] != '.') && (i < 9); i++) buff[128 + i] = cmd[i];
443
  buff[128 + i] = 0;
444
  if ((i < 9) && (link_computefname(buff, buff + 128, envseg) == 0)) {
445
    /* try opening the link file (if it exists) and read it into buff */
446
    i = 0;
447
    _asm {
448
      push ax
449
      push bx
450
      push cx
451
      push dx
452
 
453
      mov ax, 0x3d00  /* DOS 2+ - OPEN EXISTING FILE, READ-ONLY */
454
      mov dx, buff    /* file name */
455
      int 0x21
456
      jc ERR_FOPEN
457
      /* file handle in AX, read from file now */
458
      mov bx, ax      /* file handle */
459
      mov ah, 0x3f    /* Read from file via handle bx */
460
      mov cx, 128     /* up to 128 bytes */
461
      /* mov dx, buff */ /* dest buffer (already set) */
462
      int 0x21        /* read up to 256 bytes from file and write to buff */
463
      jc ERR_READ
464
      mov i, ax
465
      ERR_READ:
466
      mov ah, 0x3e    /* close file handle in BX */
467
      int 0x21
468
      ERR_FOPEN:
469
 
470
      pop dx
471
      pop cx
472
      pop bx
473
      pop ax
474
    }
475
 
476
    /* did I read anything? */
477
    if (i != 0) {
478
      buff[i] = 0;
479
      /* trim buff at first \n or \r, just in case someone fiddled with the
480
       * link file using a text editor */
481
      for (i = 0; (buff[i] != 0) && (buff[i] != '\r') && (buff[i] != '\n'); i++);
482
      buff[i] = 0;
483
      /* lookup check */
484
      if (buff[0] != 0) {
485
        lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
486
        if (lookup == 0) goto RUNCMDFILE;
487
      }
488
    }
489
  }
490
 
491
  /* all failed (ie. executable file not found) */
492
  return;
493
 
458 mateuszvis 494
  RUNCMDFILE:
495
 
469 mateuszvis 496
  /* special handling of batch files */
497
  if ((ext != NULL) && (imatch(ext, "bat"))) {
957 mateusz.vi 498
    struct batctx far *newbat;
499
 
500
    /* remember the echo flag (in case bat file disables echo, only when starting first bat) */
501
    if (rmod->bat == NULL) {
502
      rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
503
      if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
949 mateusz.vi 504
    }
957 mateusz.vi 505
 
506
    /* if bat is not called via a CALL, then free the bat-context linked list */
963 mateusz.vi 507
    if ((flags & CALL_FLAG) == 0) rmod_free_bat_llist(rmod);
508
 
957 mateusz.vi 509
    /* allocate a new bat context */
510
    newbat = rmod_fcalloc(sizeof(struct batctx), rmod->rmodseg, "SVBATCTX");
511
    if (newbat == NULL) {
512
      nls_outputnl_doserr(8); /* insufficient memory */
949 mateusz.vi 513
      return;
514
    }
515
 
957 mateusz.vi 516
    /* fill the newly allocated batctx structure */
517
    _fstrcpy(newbat->fname, cmdfile); /* truename of the BAT file */
1001 mateusz.vi 518
    newbat->flags = flags & FLAG_STEPBYSTEP;
508 mateuszvis 519
    /* explode args of the bat file and store them in rmod buff */
520
    cmd_explode(buff, cmdline, NULL);
957 mateusz.vi 521
    _fmemcpy(newbat->argv, buff, sizeof(newbat->argv));
508 mateuszvis 522
 
957 mateusz.vi 523
    /* push the new bat to the top of rmod's linked list */
524
    newbat->parent = rmod->bat;
525
    rmod->bat = newbat;
526
 
469 mateuszvis 527
    return;
528
  }
529
 
517 mateuszvis 530
  /* copy full filename to execute, along with redirected files (if any) */
548 mateuszvis 531
  _fstrcpy(rmod_execprog, cmdfile);
532
 
533
  /* copy stdin file if a redirection is needed */
517 mateuszvis 534
  if (redir->stdinfile) {
548 mateuszvis 535
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDINFILE);
576 mateuszvis 536
    char far *delstdin = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDIN_DEL);
548 mateuszvis 537
    _fstrcpy(farptr, redir->stdinfile);
957 mateusz.vi 538
    if (flags & DELETE_STDIN_FILE) {
576 mateuszvis 539
      *delstdin = redir->stdinfile[0];
540
    } else {
541
      *delstdin = 0;
542
    }
517 mateuszvis 543
  }
548 mateuszvis 544
 
545
  /* same for stdout file */
517 mateuszvis 546
  if (redir->stdoutfile) {
548 mateuszvis 547
    char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTFILE);
548
    unsigned short far *farptr16 = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTAPP);
549
    _fstrcpy(farptr, redir->stdoutfile);
517 mateuszvis 550
    /* openflag */
548 mateuszvis 551
    *farptr16 = redir->stdout_openflag;
517 mateuszvis 552
  }
461 mateuszvis 553
 
554
  /* copy cmdtail to rmod's PSP and compute its len */
555
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
556
  rmod_cmdtail[i] = '\r';
557
  rmod_cmdtail[-1] = i;
558
 
559
  /* set up rmod to execute the command */
560
 
464 mateuszvis 561
  ExecParam->envseg = envseg;
461 mateuszvis 562
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
1156 mateusz.vi 563
  /* far pointers to unopened FCB entries (stored in RMOD's own PSP) */
564
  {
565
    char far *farptr;
566
    /* prep the unopened FCBs */
567
    farptr = MK_FP(rmod->rmodseg, 0x5C);
568
    _fmemset(farptr, 0, 36); /* first FCB is 16 bytes long, second is 20 bytes long */
569
    cmdtail_to_fcb(farptr, farptr + 16, cmdtail);
570
    /* set (far) pointers in the ExecParam block */
571
    ExecParam->fcb1 = (unsigned long)MK_FP(rmod->rmodseg, 0x5C);
572
    ExecParam->fcb2 = (unsigned long)MK_FP(rmod->rmodseg, 0x6C);
573
  }
461 mateuszvis 574
  exit(0); /* let rmod do the job now */
364 mateuszvis 575
}
576
 
577
 
367 mateuszvis 578
static void set_comspec_to_self(unsigned short envseg) {
579
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
580
  char far *myenv = MK_FP(*psp_envseg, 0);
581
  unsigned short varcount;
582
  char buff[256] = "COMSPEC=";
583
  char *buffptr = buff + 8;
584
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
585
  while (*myenv != 0) {
586
    /* consume a NULL-terminated string */
587
    while (*myenv != 0) myenv++;
588
    /* move to next string */
589
    myenv++;
590
  }
591
  /* get next word, if 1 then EXEPATH follows */
592
  myenv++;
593
  varcount = *myenv;
594
  myenv++;
595
  varcount |= (*myenv << 8);
596
  myenv++;
597
  if (varcount != 1) return; /* NO EXEPATH FOUND */
598
  while (*myenv != 0) {
599
    *buffptr = *myenv;
600
    buffptr++;
601
    myenv++;
602
  }
603
  *buffptr = 0;
604
  /* printf("EXEPATH: '%s'\r\n", buff); */
605
  env_setvar(envseg, buff);
606
}
607
 
608
 
450 mateuszvis 609
/* wait for user input */
610
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
611
  _asm {
612
    push ax
613
    push bx
614
    push cx
615
    push dx
616
    push ds
617
 
618
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
619
    mov ax, 0x4800
620
    int 0x2f
621
    mov bl, al /* save doskey status in BL */
622
 
623
    /* set up buffered input to inpseg:inpoff */
624
    mov ax, inpseg
625
    push ax
626
    pop ds
627
    mov dx, inpoff
628
 
629
    /* execute either DOS input or DOSKEY */
630
    test bl, bl /* zf set if no DOSKEY present */
631
    jnz DOSKEY
632
 
633
    mov ah, 0x0a
634
    int 0x21
635
    jmp short DONE
636
 
637
    DOSKEY:
638
    mov ax, 0x4810
639
    int 0x2f
640
 
641
    DONE:
642
    /* terminate command with a CR/LF */
643
    mov ah, 0x02 /* display character in dl */
644
    mov dl, 0x0d
645
    int 0x21
646
    mov dl, 0x0a
647
    int 0x21
648
 
649
    pop ds
650
    pop dx
651
    pop cx
652
    pop bx
653
    pop ax
654
  }
655
}
656
 
657
 
479 mateuszvis 658
/* fetches a line from batch file and write it to buff (NULL-terminated),
659
 * increments rmod counter and returns 0 on success. */
484 mateuszvis 660
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
469 mateuszvis 661
  unsigned short i;
949 mateusz.vi 662
  unsigned short batname_seg = FP_SEG(rmod->bat->fname);
663
  unsigned short batname_off = FP_OFF(rmod->bat->fname);
664
  unsigned short filepos_cx = rmod->bat->nextline >> 16;
665
  unsigned short filepos_dx = rmod->bat->nextline & 0xffff;
474 mateuszvis 666
  unsigned char blen = 0;
505 mateuszvis 667
  unsigned short errv = 0;
474 mateuszvis 668
 
669
  /* open file, jump to offset filpos, and read data into buff.
670
   * result in blen (unchanged if EOF or failure). */
671
  _asm {
672
    push ax
673
    push bx
674
    push cx
675
    push dx
676
 
677
    /* open file (read-only) */
505 mateuszvis 678
    mov bx, 0xffff        /* preset BX to 0xffff to detect error conditions */
474 mateuszvis 679
    mov dx, batname_off
680
    mov ax, batname_seg
681
    push ds     /* save DS */
682
    mov ds, ax
683
    mov ax, 0x3d00
684
    int 0x21    /* handle in ax on success */
685
    pop ds      /* restore DS */
505 mateuszvis 686
    jc ERR
474 mateuszvis 687
    mov bx, ax  /* save handle to bx */
688
 
689
    /* jump to file offset CX:DX */
690
    mov ax, 0x4200
691
    mov cx, filepos_cx
692
    mov dx, filepos_dx
693
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
505 mateuszvis 694
    jc ERR
474 mateuszvis 695
 
696
    /* read the line into buff */
697
    mov ah, 0x3f
484 mateuszvis 698
    xor ch, ch
699
    mov cl, buffmaxlen
474 mateuszvis 700
    mov dx, buff
701
    int 0x21 /* CF clear on success, AX=number of bytes read */
505 mateuszvis 702
    jc ERR
474 mateuszvis 703
    mov blen, al
505 mateuszvis 704
    jmp CLOSEANDQUIT
474 mateuszvis 705
 
505 mateuszvis 706
    ERR:
707
    mov errv, ax
708
 
474 mateuszvis 709
    CLOSEANDQUIT:
505 mateuszvis 710
    /* close file (if bx contains a handle) */
711
    cmp bx, 0xffff
712
    je DONE
474 mateuszvis 713
    mov ah, 0x3e
714
    int 0x21
715
 
716
    DONE:
717
    pop dx
718
    pop cx
719
    pop bx
720
    pop ax
469 mateuszvis 721
  }
470 mateuszvis 722
 
474 mateuszvis 723
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
470 mateuszvis 724
 
538 mateuszvis 725
  if (errv != 0) nls_outputnl_doserr(errv);
505 mateuszvis 726
 
474 mateuszvis 727
  /* on EOF - abort processing the bat file */
728
  if (blen == 0) goto OOPS;
729
 
730
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
731
   * I support all CR/LF, CR- and LF-terminated batch files */
732
  for (i = 0; i < blen; i++) {
733
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
949 mateusz.vi 734
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->bat->nextline += 1;
474 mateuszvis 735
      break;
736
    }
737
  }
738
  buff[i] = 0;
949 mateusz.vi 739
  rmod->bat->nextline += i + 1;
474 mateuszvis 740
 
741
  return(0);
742
 
743
  OOPS:
949 mateusz.vi 744
  rmod->bat->fname[0] = 0;
745
  rmod->bat->nextline = 0;
474 mateuszvis 746
  return(-1);
469 mateuszvis 747
}
748
 
749
 
507 mateuszvis 750
/* replaces %-variables in a BAT line with resolved values:
751
 * %PATH%       -> replaced by the contend of the PATH env variable
752
 * %UNDEFINED%  -> undefined variables are replaced by nothing ("")
753
 * %NOTCLOSED   -> NOTCLOSED
754
 * %1           -> first argument of the batch file (or nothing if no arg) */
755
static void batpercrepl(char *res, unsigned short ressz, const char *line, const struct rmod_props far *rmod, unsigned short envseg) {
756
  unsigned short lastperc = 0xffff;
757
  unsigned short reslen = 0;
758
 
759
  if (ressz == 0) return;
760
  ressz--; /* reserve one byte for the NULL terminator */
761
 
762
  for (; (reslen < ressz) && (*line != 0); line++) {
763
    /* if not a percent, I don't care */
764
    if (*line != '%') {
765
      res[reslen++] = *line;
766
      continue;
767
    }
768
 
769
    /* *** perc char handling *** */
770
 
771
    /* closing perc? */
772
    if (lastperc != 0xffff) {
773
      /* %% is '%' */
774
      if (lastperc == reslen) {
775
        res[reslen++] = '%';
776
      } else {   /* otherwise variable name */
777
        const char far *ptr;
778
        res[reslen] = 0;
779
        reslen = lastperc;
1139 mateusz.vi 780
        nls_strtoup(res + reslen); /* turn varname uppercase before lookup */
507 mateuszvis 781
        ptr = env_lookup_val(envseg, res + reslen);
782
        if (ptr != NULL) {
783
          while ((*ptr != 0) && (reslen < ressz)) {
784
            res[reslen++] = *ptr;
785
            ptr++;
786
          }
787
        }
788
      }
789
      lastperc = 0xffff;
790
      continue;
791
    }
792
 
793
    /* digit? (bat arg) */
794
    if ((line[1] >= '0') && (line[1] <= '9')) {
508 mateuszvis 795
      unsigned short argid = line[1] - '0';
796
      unsigned short i;
949 mateusz.vi 797
      const char far *argv = "";
798
      if ((rmod != NULL) && (rmod->bat != NULL)) argv = rmod->bat->argv;
508 mateuszvis 799
 
800
      /* locate the proper arg */
801
      for (i = 0; i != argid; i++) {
802
        /* if string is 0, then end of list reached */
803
        if (*argv == 0) break;
804
        /* jump to next arg */
805
        while (*argv != 0) argv++;
806
        argv++;
807
      }
808
 
809
      /* copy the arg to result */
810
      for (i = 0; (argv[i] != 0) && (reslen < ressz); i++) {
811
        res[reslen++] = argv[i];
812
      }
507 mateuszvis 813
      line++;  /* skip the digit */
814
      continue;
815
    }
816
 
817
    /* opening perc */
818
    lastperc = reslen;
819
 
820
  }
821
 
822
  res[reslen] = 0;
823
}
824
 
825
 
1024 mateusz.vi 826
/* process the ongoing forloop, returns 0 on success, non-zero otherwise (no
827
   more things to process) */
828
static int forloop_process(char *res, struct forctx far *forloop) {
829
  unsigned short i, t;
830
  struct DTA *dta = (void *)0x80; /* default DTA at 80h in PSP */
1055 mateusz.vi 831
  char *fnameptr = dta->fname;
1070 mateusz.vi 832
  char *pathprefix = BUFFER + 256;
957 mateusz.vi 833
 
1070 mateusz.vi 834
  *pathprefix = 0;
835
 
1024 mateusz.vi 836
  TRYAGAIN:
837
 
838
  /* dta_inited: FindFirst() or FindNext()? */
839
  if (forloop->dta_inited == 0) {
840
 
1065 bttr 841
    /* copy next awaiting pattern to BUFFER (and skip all delimiters until
1054 mateusz.vi 842
     * next pattern or end of list) */
843
    t = 0;
1024 mateusz.vi 844
    for (i = 0;; i++) {
845
      BUFFER[i] = forloop->cmd[forloop->nextpat + i];
1070 mateusz.vi 846
      /* is this a delimiter? (all delimiters are already normalized to a space here) */
847
      if (BUFFER[i] == ' ') {
848
        BUFFER[i] = 0;
849
        t = 1;
850
      } else if (BUFFER[i] == 0) {
851
        /* end of patterns list */
852
        break;
853
      } else {
854
        /* quit if I got a pattern already */
855
        if (t == 1) break;
1024 mateusz.vi 856
      }
857
    }
858
 
859
    if (i == 0) return(-1);
860
 
861
    /* remember position of current pattern */
862
    forloop->curpat = forloop->nextpat;
863
 
864
    /* move nextpat forward to next pattern */
865
    i += forloop->nextpat;
866
    forloop->nextpat = i;
867
 
1055 mateusz.vi 868
    /* if this is a string and not a pattern, skip all the FindFirst business
869
     * a file pattern has a wildcard (* or ?), a message doesn't */
870
    for (i = 0; (BUFFER[i] != 0) && (BUFFER[i] != '?') && (BUFFER[i] != '*'); i++);
871
    if (BUFFER[i] == 0) {
872
      fnameptr = BUFFER;
873
      goto SKIP_DTA;
874
    }
875
 
1024 mateusz.vi 876
    /* FOR in MSDOS 6 includes hidden and system files, but not directories nor volumes */
877
    if (findfirst(dta, BUFFER, DOS_ATTR_RO | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_ARC) != 0) {
878
      goto TRYAGAIN;
879
    }
880
    forloop->dta_inited = 1;
881
  } else { /* dta in progress */
882
 
883
    /* copy forloop DTA to my local copy */
884
    _fmemcpy(dta, &(forloop->dta), sizeof(*dta));
885
 
886
    /* findnext() call */
887
    if (findnext(dta) != 0) {
888
      forloop->dta_inited = 0;
889
      goto TRYAGAIN;
890
    }
891
  }
892
 
893
  /* copy updated DTA to rmod */
894
  _fmemcpy(&(forloop->dta), dta, sizeof(*dta));
895
 
1070 mateusz.vi 896
  /* prefill pathprefix with the prefix (path) of the files */
897
  {
898
    short lastbk = -1;
899
    char far *c = forloop->cmd + forloop->curpat;
900
    for (i = 0;; i++) {
901
      pathprefix[i] = c[i];
902
      if (pathprefix[i] == '\\') lastbk = i;
903
      if ((pathprefix[i] == ' ') || (pathprefix[i] == 0)) break;
904
    }
905
    pathprefix[lastbk+1] = 0;
906
  }
907
 
1055 mateusz.vi 908
  SKIP_DTA:
909
 
1024 mateusz.vi 910
  /* fill res with command, replacing varname by actual filename */
911
  /* full filename is to be built with path of curpat and fname from dta */
912
  t = 0;
913
  i = 0;
914
  for (;;) {
915
    if ((forloop->cmd[forloop->exec + t] == '%') && (forloop->cmd[forloop->exec + t + 1] == forloop->varname)) {
1070 mateusz.vi 916
      strcpy(res + i, pathprefix);
917
      strcat(res + i, fnameptr);
918
      for (; res[i] != 0; i++);
1024 mateusz.vi 919
      t += 2;
920
    } else {
921
      res[i] = forloop->cmd[forloop->exec + t];
922
      t++;
923
      if (res[i++] == 0) break;
924
    }
925
  }
926
 
927
  return(0);
928
}
929
 
930
 
443 mateuszvis 931
int main(void) {
372 mateuszvis 932
  static struct config cfg;
933
  static unsigned short far *rmod_envseg;
934
  static unsigned short far *lastexitcode;
449 mateuszvis 935
  static struct rmod_props far *rmod;
500 mateuszvis 936
  static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
479 mateuszvis 937
  static char *cmdline;
517 mateuszvis 938
  static struct redir_data redirprops;
533 mateuszvis 939
  static enum cmd_result cmdres;
543 mateuszvis 940
  static unsigned short i; /* general-purpose variable for short-lived things */
957 mateusz.vi 941
  static unsigned char flags;
349 mateuszvis 942
 
479 mateuszvis 943
  rmod = rmod_find(BUFFER_len);
449 mateuszvis 944
  if (rmod == NULL) {
485 mateuszvis 945
    /* look at command line parameters (in case env size if set there) */
946
    parse_argv(&cfg);
479 mateuszvis 947
    rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
449 mateuszvis 948
    if (rmod == NULL) {
989 mateusz.vi 949
      nls_outputnl_err(2,1); /* "FATAL ERROR: rmod_install() failed" */
349 mateuszvis 950
      return(1);
951
    }
475 mateuszvis 952
    /* copy flags to rmod's storage (and enable ECHO) */
953
    rmod->flags = cfg.flags | FLAG_ECHOFLAG;
465 mateuszvis 954
    /* printf("rmod installed at %Fp\r\n", rmod); */
572 mateuszvis 955
    rmod->version = BYTE_VERSION;
349 mateuszvis 956
  } else {
465 mateuszvis 957
    /* printf("rmod found at %Fp\r\n", rmod); */
958
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
959
     * die asap, because the command has been executed already, so I no longer
960
     * have a purpose in life */
469 mateuszvis 961
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
572 mateuszvis 962
    /* */
963
    if (rmod->version != BYTE_VERSION) {
989 mateusz.vi 964
      nls_outputnl_err(2,0);
572 mateuszvis 965
      _asm {
966
        HALT:
967
        hlt
968
        jmp HALT
969
      }
970
    }
349 mateuszvis 971
  }
972
 
490 mateuszvis 973
  /* install a few guardvals in memory to detect some cases of overflows */
500 mateuszvis 974
  memguard_set(cmdlinebuf);
490 mateuszvis 975
 
465 mateuszvis 976
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
977
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
350 mateuszvis 978
 
1713 bttr 979
  /* make COMSPEC point to myself */
367 mateuszvis 980
  set_comspec_to_self(*rmod_envseg);
981
 
494 mateuszvis 982
  /* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
983
   * but skip this check if /D was also passed */
984
  if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
483 mateuszvis 985
    if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
986
  }
987
 
443 mateuszvis 988
  do {
1023 mateusz.vi 989
 
480 mateuszvis 990
    /* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
949 mateusz.vi 991
    if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->bat == NULL)) outputnl("");
474 mateuszvis 992
 
993
    SKIP_NEWLINE:
994
 
490 mateuszvis 995
    /* memory check */
500 mateuszvis 996
    memguard_check(rmod->rmodseg, cmdlinebuf);
474 mateuszvis 997
 
500 mateuszvis 998
    /* preset cmdline to point at the dedicated buffer */
999
    cmdline = cmdlinebuf;
490 mateuszvis 1000
 
437 mateuszvis 1001
    /* (re)load translation strings if needed */
1002
    nls_langreload(BUFFER, *rmod_envseg);
1003
 
1024 mateusz.vi 1004
    /* am I inside a FOR loop? */
1005
    if (rmod->forloop) {
1006
      if (forloop_process(cmdlinebuf, rmod->forloop) != 0) {
1007
        rmod_ffree(rmod->forloop);
1008
        rmod->forloop = NULL;
1009
      } else {
1010
        /* output prompt and command on screen if echo on and command is not
1011
         * inhibiting it with the @ prefix */
1012
        if (rmod->flags & FLAG_ECHOFLAG) {
1013
          build_and_display_prompt(BUFFER, *rmod_envseg);
1014
          outputnl(cmdline);
1015
        }
1016
        /* jump to command processing */
1017
        goto EXEC_CMDLINE;
1018
      }
1019
    }
1020
 
543 mateuszvis 1021
    /* load awaiting command, if any (used to run piped commands) */
1022
    if (rmod->awaitingcmd[0] != 0) {
1023
      _fstrcpy(cmdline, rmod->awaitingcmd);
1024
      rmod->awaitingcmd[0] = 0;
957 mateusz.vi 1025
      flags |= DELETE_STDIN_FILE;
543 mateuszvis 1026
      goto EXEC_CMDLINE;
576 mateuszvis 1027
    } else {
957 mateusz.vi 1028
      flags &= ~DELETE_STDIN_FILE;
543 mateuszvis 1029
    }
1030
 
1001 mateusz.vi 1031
    /* skip user input if I have a command to exec (/C or /K or /P) */
443 mateuszvis 1032
    if (cfg.execcmd != NULL) {
1033
      cmdline = cfg.execcmd;
1034
      cfg.execcmd = NULL;
1001 mateusz.vi 1035
      /* */
1036
      if (cfg.flags & FLAG_STEPBYSTEP) flags |= FLAG_STEPBYSTEP;
443 mateuszvis 1037
      goto EXEC_CMDLINE;
1038
    }
1039
 
469 mateuszvis 1040
    /* if batch file is being executed -> fetch next line */
949 mateusz.vi 1041
    if (rmod->bat != NULL) {
507 mateuszvis 1042
      if (getbatcmd(BUFFER, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
949 mateusz.vi 1043
        struct batctx far *victim = rmod->bat;
1044
        rmod->bat = rmod->bat->parent;
1045
        rmod_ffree(victim);
957 mateusz.vi 1046
        /* end of batch? then restore echo flag as it was before running the (first) bat file */
949 mateusz.vi 1047
        if (rmod->bat == NULL) {
1048
          rmod->flags &= ~FLAG_ECHOFLAG;
1049
          if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
1050
        }
474 mateuszvis 1051
        continue;
1052
      }
507 mateuszvis 1053
      /* %-decoding of variables (%PATH%, %1, %%...), result in cmdline */
1054
      batpercrepl(cmdline, CMDLINE_MAXLEN, BUFFER, rmod, *rmod_envseg);
480 mateuszvis 1055
      /* skip any leading spaces */
1056
      while (*cmdline == ' ') cmdline++;
960 mateusz.vi 1057
      /* skip batch labels */
1058
      if (*cmdline == ':') continue;
1001 mateusz.vi 1059
      /* step-by-step execution? */
1060
      if (rmod->bat->flags & FLAG_STEPBYSTEP) {
1061
        if (*cmdline == 0) continue; /* skip empty lines */
1062
        if (askchoice(cmdline, svarlang_str(0,10)) != 0) continue;
1063
      }
474 mateuszvis 1064
      /* output prompt and command on screen if echo on and command is not
1065
       * inhibiting it with the @ prefix */
479 mateuszvis 1066
      if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
474 mateuszvis 1067
        build_and_display_prompt(BUFFER, *rmod_envseg);
479 mateuszvis 1068
        outputnl(cmdline);
474 mateuszvis 1069
      }
479 mateuszvis 1070
      /* skip the @ prefix if present, it is no longer useful */
1071
      if (cmdline[0] == '@') cmdline++;
469 mateuszvis 1072
    } else {
983 mateusz.vi 1073
      unsigned char far *rmod_inputbuf = MK_FP(rmod->rmodseg, RMOD_OFFSET_INPUTBUF);
1074
      /* invalidate input history if it appears to be damaged (could occur
1075
       * because of a stack overflow, for example if some stack-hungry TSR is
1076
       * being used) */
987 mateusz.vi 1077
      if ((rmod_inputbuf[0] != 128) || (rmod_inputbuf[rmod_inputbuf[1] + 2] != '\r') || (rmod_inputbuf[rmod_inputbuf[1] + 3] != 0xCA) || (rmod_inputbuf[rmod_inputbuf[1] + 4] != 0xFE)) {
1078
        rmod_inputbuf[0] = 128;  /* max allowed input length */
1079
        rmod_inputbuf[1] = 0;    /* string len stored in buffer */
1080
        rmod_inputbuf[2] = '\r'; /* string terminator */
1081
        rmod_inputbuf[3] = 0xCA; /* trailing signature */
1082
        rmod_inputbuf[4] = 0xFE; /* trailing signature */
989 mateusz.vi 1083
        nls_outputnl_err(2,2); /* "stack overflow detected, command history flushed" */
983 mateusz.vi 1084
      }
474 mateuszvis 1085
      /* interactive mode: display prompt (if echo enabled) and wait for user
1086
       * command line */
475 mateuszvis 1087
      if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
474 mateuszvis 1088
      /* collect user input */
983 mateusz.vi 1089
      cmdline_getinput(rmod->rmodseg, RMOD_OFFSET_INPUTBUF);
987 mateusz.vi 1090
      /* append stack-overflow detection signature to the end of the input buffer */
1091
      rmod_inputbuf[rmod_inputbuf[1] + 3] = 0xCA; /* trailing signature */
1092
      rmod_inputbuf[rmod_inputbuf[1] + 4] = 0xFE; /* trailing signature */
479 mateuszvis 1093
      /* copy it to local cmdline */
983 mateusz.vi 1094
      if (rmod_inputbuf[1] != 0) _fmemcpy(cmdline, rmod_inputbuf + 2, rmod_inputbuf[1]);
1095
      cmdline[rmod_inputbuf[1]] = 0; /* zero-terminate local buff (original is '\r'-terminated) */
469 mateuszvis 1096
    }
349 mateuszvis 1097
 
405 mateuszvis 1098
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
479 mateuszvis 1099
    if (cmdline[0] == 0) goto SKIP_NEWLINE;
349 mateuszvis 1100
 
443 mateuszvis 1101
    /* I jump here when I need to exec an initial command (/C or /K) */
1102
    EXEC_CMDLINE:
1103
 
364 mateuszvis 1104
    /* move pointer forward to skip over any leading spaces */
1105
    while (*cmdline == ' ') cmdline++;
349 mateuszvis 1106
 
1138 mateusz.vi 1107
    /* sanitize separators into spaces */
1108
    for (i = 0; cmdline[i] != 0; i++) {
1109
      switch (cmdline[i]) {
1110
        case '\t':
1111
          cmdline[i] = ' ';
1112
      }
1113
    }
1114
 
1713 bttr 1115
    /* update rmod's ptr to COMSPEC so it is always up to date */
465 mateuszvis 1116
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
367 mateuszvis 1117
 
402 mateuszvis 1118
    /* handle redirections (if any) */
577 mateuszvis 1119
    i = redir_parsecmd(&redirprops, cmdline, rmod->awaitingcmd, *rmod_envseg);
543 mateuszvis 1120
    if (i != 0) {
1121
      nls_outputnl_doserr(i);
1122
      rmod->awaitingcmd[0] = 0;
1123
      continue;
1124
    }
402 mateuszvis 1125
 
364 mateuszvis 1126
    /* try matching (and executing) an internal command */
957 mateusz.vi 1127
    cmdres = cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER), &redirprops, flags & DELETE_STDIN_FILE);
533 mateuszvis 1128
    if ((cmdres == CMD_OK) || (cmdres == CMD_FAIL)) {
443 mateuszvis 1129
      /* internal command executed */
533 mateuszvis 1130
    } else if (cmdres == CMD_CHANGED) { /* cmdline changed, needs to be reprocessed */
1131
      goto EXEC_CMDLINE;
957 mateusz.vi 1132
    } else if (cmdres == CMD_CHANGED_BY_CALL) { /* cmdline changed *specifically* by CALL */
1133
      /* the distinction is important since it changes the way batch files are processed */
1134
      flags |= CALL_FLAG;
1135
      goto EXEC_CMDLINE;
533 mateuszvis 1136
    } else if (cmdres == CMD_NOTFOUND) {
1137
      /* this was not an internal command, try matching an external command */
957 mateusz.vi 1138
      run_as_external(BUFFER, cmdline, *rmod_envseg, rmod, &redirprops, flags);
1139
 
1140
      /* is it a newly launched BAT file? */
949 mateusz.vi 1141
      if ((rmod->bat != NULL) && (rmod->bat->nextline == 0)) goto SKIP_NEWLINE;
533 mateuszvis 1142
      /* run_as_external() does not return on success, if I am still alive then
1143
       * external command failed to execute */
989 mateusz.vi 1144
      nls_outputnl(0,5); /* "Bad command or file name" */
1001 mateusz.vi 1145
    } else {
1146
      /* I should never ever land here */
1147
      outputnl("INTERNAL ERR: INVALID CMDRES");
353 mateuszvis 1148
    }
352 mateuszvis 1149
 
1001 mateusz.vi 1150
    /* reset one-time only flags */
1151
    flags &= ~CALL_FLAG;
1152
    flags &= ~FLAG_STEPBYSTEP;
349 mateuszvis 1153
 
958 mateusz.vi 1154
    /* repeat unless /C was asked - but always finish running an ongoing batch
1155
     * file (otherwise only first BAT command would be executed with /C) */
1024 mateusz.vi 1156
  } while (((rmod->flags & FLAG_EXEC_AND_QUIT) == 0) || (rmod->bat != NULL) || (rmod->forloop != NULL));
349 mateuszvis 1157
 
449 mateuszvis 1158
  sayonara(rmod);
349 mateuszvis 1159
  return(0);
1160
}