Subversion Repositories SvarDOS

Rev

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