Subversion Repositories SvarDOS

Rev

Rev 472 | Rev 475 | 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
 *
4
 * Copyright (C) 2021 Mateusz Viste
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
/*
26
 * SvarCOM is a command-line interpreter.
27
 *
28
 * a little memory area is allocated as high as possible. it contains:
29
 *  - a signature (like XMS drivers do)
30
 *  - a routine for exec'ing programs
31
 *  - a "last command" buffer for input history
32
 *
33
 * when svarcom starts, it tries locating the routine in memory.
34
 *
35
 * if found:
36
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
37
 *
38
 * if not found:
39
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
40
 *   and quit.
41
 *
42
 * PSP structure
43
 * http://www.piclist.com/techref/dos/psps.htm
44
 *
45
 *
46
 *
47
 * === MCB ===
48
 *
49
 * each time that DOS allocates memory, it prefixes the allocated memory with
50
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
51
 * block has the following structure:
52
 *
53
 * Offset  Size     Description
54
 *   00h   byte     'M' =  non-last member of the MCB chain
55
 *                  'Z' = indicates last entry in MCB chain
56
 *                  other values cause "Memory Allocation Failure" on exit
57
 *   01h   word     PSP segment address of the owner (Process Id)
58
 *                  possible values:
59
 *                    0 = free
60
 *                    8 = Allocated by DOS before first user pgm loaded
61
 *                    other = Process Id/PSP segment address of owner
62
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
63
 *   05h  11 bytes  reserved
64
 *   10h  ...       start of actual allocated memory block
65
 */
66
 
67
#include <i86.h>
68
#include <dos.h>
69
#include <stdio.h>
350 mateuszvis 70
#include <stdlib.h>
349 mateuszvis 71
#include <string.h>
72
 
73
#include <process.h>
74
 
352 mateuszvis 75
#include "cmd.h"
366 mateuszvis 76
#include "env.h"
352 mateuszvis 77
#include "helpers.h"
402 mateuszvis 78
#include "redir.h"
351 mateuszvis 79
#include "rmodinit.h"
448 mateuszvis 80
#include "sayonara.h"
349 mateuszvis 81
 
443 mateuszvis 82
 
349 mateuszvis 83
struct config {
449 mateuszvis 84
  unsigned char flags; /* command.com flags, as defined in rmodinit.h */
443 mateuszvis 85
  char *execcmd;
410 mateuszvis 86
  unsigned short envsiz;
443 mateuszvis 87
};
349 mateuszvis 88
 
89
 
443 mateuszvis 90
/* parses command line the hard way (directly from PSP) */
91
static void parse_argv(struct config *cfg) {
92
  unsigned short i;
93
  const unsigned char *cmdlinelen = (unsigned char *)0x80;
94
  char *cmdline = (char *)0x81;
95
 
349 mateuszvis 96
  memset(cfg, 0, sizeof(*cfg));
350 mateuszvis 97
 
443 mateuszvis 98
  /* set a NULL terminator on cmdline */
99
  cmdline[*cmdlinelen] = 0;
100
 
101
  for (i = 0;;) {
102
 
103
    /* skip over any leading spaces */
104
    for (;; i++) {
105
      if (cmdline[i] == 0) return;
106
      if (cmdline[i] != ' ') break;
349 mateuszvis 107
    }
443 mateuszvis 108
 
109
    if (cmdline[i] != '/') {
110
      output("Invalid parameter: ");
111
      outputnl(cmdline + i);
112
      /* exit(1); */
113
    } else {
114
      i++;        /* skip the slash */
115
      switch (cmdline[i]) {
116
        case 'c': /* /C = execute command and quit */
117
        case 'C':
118
          cfg->flags |= FLAG_EXEC_AND_QUIT;
119
          /* FALLTHRU */
120
        case 'k': /* /K = execute command and keep running */
121
        case 'K':
122
          cfg->execcmd = cmdline + i + 1;
123
          return;
124
 
125
        case 'e': /* preset the initial size of the environment block */
126
        case 'E':
127
          i++;
128
          if (cmdline[i] == ':') i++; /* could be /E:size */
129
          atous(&(cfg->envsiz), cmdline + i);
130
          if (cfg->envsiz < 64) cfg->envsiz = 0;
131
          break;
132
 
449 mateuszvis 133
        case 'p': /* permanent shell (can't exit) */
134
        case 'P':
135
          cfg->flags |= FLAG_PERMANENT;
136
          break;
137
 
444 mateuszvis 138
        case '?':
139
          outputnl("Starts the SvarCOM command interpreter");
140
          outputnl("");
141
          outputnl("COMMAND /E:nnn [/[C|K] command]");
142
          outputnl("");
143
          outputnl("/E:nnn     Sets the environment size to nnn bytes");
449 mateuszvis 144
          outputnl("/P         Makes the new command interpreter permanent (can't exit)");
444 mateuszvis 145
          outputnl("/C         Executes the specified command and returns");
146
          outputnl("/K         Executes the specified command and continues running");
147
          exit(1);
148
          break;
149
 
443 mateuszvis 150
        default:
151
          output("Invalid switch:");
152
          output(" ");
153
          outputnl(cmdline + i);
154
          exit(1);
155
          break;
410 mateuszvis 156
      }
350 mateuszvis 157
    }
443 mateuszvis 158
 
159
    /* move to next argument or quit processing if end of cmdline */
160
    for (i++; (cmdline[i] != 0) && (cmdline[i] != ' ') && (cmdline[i] != '/'); i++);
161
 
349 mateuszvis 162
  }
163
}
164
 
165
 
474 mateuszvis 166
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
167
static void build_and_display_prompt(char *buff, unsigned short envseg) {
168
  char *s = buff;
370 mateuszvis 169
  /* locate the prompt variable or use the default pattern */
438 mateuszvis 170
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
370 mateuszvis 171
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
172
  /* build the prompt string based on pattern */
354 mateuszvis 173
  for (; *fmt != 0; fmt++) {
174
    if (*fmt != '$') {
175
      *s = *fmt;
176
      s++;
177
      continue;
178
    }
179
    /* escape code ($P, etc) */
180
    fmt++;
181
    switch (*fmt) {
182
      case 'Q':  /* $Q = = (equal sign) */
183
      case 'q':
184
        *s = '=';
185
        s++;
186
        break;
187
      case '$':  /* $$ = $ (dollar sign) */
188
        *s = '$';
189
        s++;
190
        break;
191
      case 'T':  /* $t = current time */
192
      case 't':
193
        s += sprintf(s, "00:00"); /* TODO */
194
        break;
195
      case 'D':  /* $D = current date */
196
      case 'd':
197
        s += sprintf(s, "1985-07-29"); /* TODO */
198
        break;
199
      case 'P':  /* $P = current drive and path */
200
      case 'p':
201
        _asm {
202
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
203
          int 0x21
204
          mov bx, s
205
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
206
        }
207
        *s += 'A';
208
        s++;
209
        *s = ':';
210
        s++;
211
        *s = '\\';
212
        s++;
213
        _asm {
214
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
215
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
216
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
217
          int 0x21
218
        }
219
        while (*s != 0) s++; /* move ptr forward to end of pathname */
220
        break;
221
      case 'V':  /* $V = DOS version number */
222
      case 'v':
223
        s += sprintf(s, "VER"); /* TODO */
224
        break;
225
      case 'N':  /* $N = current drive */
226
      case 'n':
227
        _asm {
228
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
229
          int 0x21
230
          mov bx, s
231
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
232
        }
233
        *s += 'A';
234
        s++;
235
        break;
236
      case 'G':  /* $G = > (greater-than sign) */
237
      case 'g':
238
        *s = '>';
239
        s++;
240
        break;
241
      case 'L':  /* $L = < (less-than sign) */
242
      case 'l':
243
        *s = '<';
244
        s++;
245
        break;
246
      case 'B':  /* $B = | (pipe) */
247
      case 'b':
248
        *s = '|';
249
        s++;
250
        break;
251
      case 'H':  /* $H = backspace (erases previous character) */
252
      case 'h':
253
        *s = '\b';
254
        s++;
255
        break;
256
      case 'E':  /* $E = Escape code (ASCII 27) */
257
      case 'e':
258
        *s = 27;
259
        s++;
260
        break;
261
      case '_':  /* $_ = CR+LF */
262
        *s = '\r';
263
        s++;
264
        *s = '\n';
265
        s++;
266
        break;
267
    }
268
  }
474 mateuszvis 269
  *s = 0;
270
  output(buff);
354 mateuszvis 271
}
349 mateuszvis 272
 
273
 
458 mateuszvis 274
/* tries locating executable fname in path and fille res with result. returns 0 on success,
275
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
276
 * format is filled the offset where extension starts in fname (-1 if not found) */
277
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
278
  unsigned short lastbslash = 0xffff;
279
  unsigned short i, len;
280
  unsigned char explicitpath = 0;
281
 
282
  /* does the original fname had an explicit path prefix or explicit ext? */
283
  *extptr = NULL;
284
  for (i = 0; fname[i] != 0; i++) {
285
    switch (fname[i]) {
286
      case ':':
287
      case '\\':
288
        explicitpath = 1;
289
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
290
        break;
291
      case '.':
292
        *extptr = fname + i + 1;
293
        break;
294
    }
295
  }
296
 
297
  /* normalize filename */
298
  if (file_truename(fname, res) != 0) return(-2);
299
 
300
  /* printf("truename: %s\r\n", res); */
301
 
302
  /* figure out where the command starts and if it has an explicit extension */
303
  for (len = 0; res[len] != 0; len++) {
304
    switch (res[len]) {
305
      case '?':   /* abort on any wildcard character */
306
      case '*':
307
        return(-2);
308
      case '\\':
309
        lastbslash = len;
310
        break;
311
    }
312
  }
313
 
314
  /* printf("lastbslash=%u\r\n", lastbslash); */
315
 
316
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
317
  if (!explicitpath) {
318
    if (path != NULL) {
319
      i = strlen(path);
320
    } else {
321
      i = 0;
322
    }
323
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
324
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
325
    if (i != 0) {
326
      memmove(res, path, i);
327
      res[i - 1] = '\\';
328
    }
329
  }
330
 
331
  /* if no extension was initially provided, try matching COM, EXE, BAT */
332
  if (*extptr == NULL) {
333
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
334
    len = strlen(res);
335
    for (i = 0; ext[i] != NULL; i++) {
336
      strcpy(res + len, ext[i]);
337
      /* printf("? '%s'\r\n", res); */
338
      *extptr = ext[i] + 1;
339
      if (file_getattr(res) >= 0) return(0);
340
    }
341
  } else { /* try finding it as-is */
342
    /* printf("? '%s'\r\n", res); */
343
    if (file_getattr(res) >= 0) return(0);
344
  }
345
 
346
  /* not found */
347
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
348
  return(-1);
349
}
350
 
351
 
461 mateuszvis 352
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
472 mateuszvis 353
  char *cmdfile = buff + 512;
458 mateuszvis 354
  const char far *pathptr;
355
  int lookup;
356
  unsigned short i;
357
  const char *ext;
472 mateuszvis 358
  char *cmd = buff + 256;
461 mateuszvis 359
  const char far *cmdtail;
360
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
361
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
362
  _Packed struct {
363
    unsigned short envseg;
364
    unsigned long cmdtail;
365
    unsigned long fcb1;
366
    unsigned long fcb2;
367
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
364 mateuszvis 368
 
472 mateuszvis 369
  /* find cmd and cmdtail */
370
  i = 0;
371
  cmdtail = cmdline;
372
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
373
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
374
    cmd[i++] = *cmdtail;
375
    cmdtail++;
376
  }
377
  cmd[i] = 0;
364 mateuszvis 378
 
458 mateuszvis 379
  /* is this a command in curdir? */
472 mateuszvis 380
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
458 mateuszvis 381
  if (lookup == 0) {
382
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
383
    goto RUNCMDFILE;
384
  } else if (lookup == -2) {
385
    /* puts("NOT FOUND"); */
386
    return;
387
  }
388
 
389
  /* try matching something in PATH */
390
  pathptr = env_lookup_val(envseg, "PATH");
391
  if (pathptr == NULL) return;
392
 
393
  /* try each path in %PATH% */
394
  for (;;) {
395
    for (i = 0;; i++) {
396
      buff[i] = *pathptr;
397
      if ((buff[i] == 0) || (buff[i] == ';')) break;
398
      pathptr++;
399
    }
400
    buff[i] = 0;
472 mateuszvis 401
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
458 mateuszvis 402
    if (lookup == 0) break;
403
    if (lookup == -2) return;
404
    if (*pathptr == ';') {
405
      pathptr++;
406
    } else {
407
      return;
408
    }
409
  }
410
 
411
  RUNCMDFILE:
412
 
469 mateuszvis 413
  /* special handling of batch files */
414
  if ((ext != NULL) && (imatch(ext, "bat"))) {
415
    /* copy truename of the bat file to rmod buff */
416
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
417
    rmod->batfile[i] = 0;
418
    /* copy args of the bat file to rmod buff */
419
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
420
    /* reset the 'next line to execute' counter */
421
    rmod->batnextline = 0;
474 mateuszvis 422
    /* remember the echo flag (in case bat file disables echo) */
423
    rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
424
    if (rmod->echoflag) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
469 mateuszvis 425
    return;
426
  }
427
 
461 mateuszvis 428
  /* copy full filename to execute */
429
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
430
  rmod_execprog[i] = 0;
431
 
432
  /* copy cmdtail to rmod's PSP and compute its len */
433
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
434
  rmod_cmdtail[i] = '\r';
435
  rmod_cmdtail[-1] = i;
436
 
437
  /* set up rmod to execute the command */
438
 
464 mateuszvis 439
  ExecParam->envseg = envseg;
461 mateuszvis 440
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
441
  ExecParam->fcb1 = 0; /* TODO farptr */
442
  ExecParam->fcb2 = 0; /* TODO farptr */
443
  exit(0); /* let rmod do the job now */
364 mateuszvis 444
}
445
 
446
 
367 mateuszvis 447
static void set_comspec_to_self(unsigned short envseg) {
448
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
449
  char far *myenv = MK_FP(*psp_envseg, 0);
450
  unsigned short varcount;
451
  char buff[256] = "COMSPEC=";
452
  char *buffptr = buff + 8;
453
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
454
  while (*myenv != 0) {
455
    /* consume a NULL-terminated string */
456
    while (*myenv != 0) myenv++;
457
    /* move to next string */
458
    myenv++;
459
  }
460
  /* get next word, if 1 then EXEPATH follows */
461
  myenv++;
462
  varcount = *myenv;
463
  myenv++;
464
  varcount |= (*myenv << 8);
465
  myenv++;
466
  if (varcount != 1) return; /* NO EXEPATH FOUND */
467
  while (*myenv != 0) {
468
    *buffptr = *myenv;
469
    buffptr++;
470
    myenv++;
471
  }
472
  *buffptr = 0;
473
  /* printf("EXEPATH: '%s'\r\n", buff); */
474
  env_setvar(envseg, buff);
475
}
476
 
477
 
450 mateuszvis 478
/* wait for user input */
479
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
480
  _asm {
481
    push ax
482
    push bx
483
    push cx
484
    push dx
485
    push ds
486
 
487
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
488
    mov ax, 0x4800
489
    int 0x2f
490
    mov bl, al /* save doskey status in BL */
491
 
492
    /* set up buffered input to inpseg:inpoff */
493
    mov ax, inpseg
494
    push ax
495
    pop ds
496
    mov dx, inpoff
497
 
498
    /* execute either DOS input or DOSKEY */
499
    test bl, bl /* zf set if no DOSKEY present */
500
    jnz DOSKEY
501
 
502
    mov ah, 0x0a
503
    int 0x21
504
    jmp short DONE
505
 
506
    DOSKEY:
507
    mov ax, 0x4810
508
    int 0x2f
509
 
510
    DONE:
511
    /* terminate command with a CR/LF */
512
    mov ah, 0x02 /* display character in dl */
513
    mov dl, 0x0d
514
    int 0x21
515
    mov dl, 0x0a
516
    int 0x21
517
 
518
    pop ds
519
    pop dx
520
    pop cx
521
    pop bx
522
    pop ax
523
  }
524
}
525
 
526
 
469 mateuszvis 527
/* fetches a line from batch file and write it to buff, increments
474 mateuszvis 528
 * rmod counter on success. returns 0 on success.
529
 * buff starts with a length byte and is NULL-terminated */
530
static int getbatcmd(char *buff, struct rmod_props far *rmod) {
469 mateuszvis 531
  unsigned short i;
474 mateuszvis 532
  unsigned short batname_seg = FP_SEG(rmod->batfile);
533
  unsigned short batname_off = FP_OFF(rmod->batfile);
534
  unsigned short filepos_cx = rmod->batnextline >> 16;
535
  unsigned short filepos_dx = rmod->batnextline & 0xffff;
536
  unsigned char blen = 0;
537
 
538
  buff++; /* make room for the len byte prefix */
539
 
540
  /* open file, jump to offset filpos, and read data into buff.
541
   * result in blen (unchanged if EOF or failure). */
542
  _asm {
543
    push ax
544
    push bx
545
    push cx
546
    push dx
547
 
548
    /* open file (read-only) */
549
    mov dx, batname_off
550
    mov ax, batname_seg
551
    push ds     /* save DS */
552
    mov ds, ax
553
    mov ax, 0x3d00
554
    int 0x21    /* handle in ax on success */
555
    pop ds      /* restore DS */
556
    jc DONE
557
    mov bx, ax  /* save handle to bx */
558
 
559
    /* jump to file offset CX:DX */
560
    mov ax, 0x4200
561
    mov cx, filepos_cx
562
    mov dx, filepos_dx
563
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
564
    jc CLOSEANDQUIT
565
 
566
    /* read the line into buff */
567
    mov ah, 0x3f
568
    mov cx, 255
569
    mov dx, buff
570
    int 0x21 /* CF clear on success, AX=number of bytes read */
571
    jc CLOSEANDQUIT
572
    mov blen, al
573
 
574
    CLOSEANDQUIT:
575
    /* close file (handle in bx) */
576
    mov ah, 0x3e
577
    int 0x21
578
 
579
    DONE:
580
    pop dx
581
    pop cx
582
    pop bx
583
    pop ax
469 mateuszvis 584
  }
470 mateuszvis 585
 
474 mateuszvis 586
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
470 mateuszvis 587
 
474 mateuszvis 588
  /* on EOF - abort processing the bat file */
589
  if (blen == 0) goto OOPS;
590
 
591
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
592
   * I support all CR/LF, CR- and LF-terminated batch files */
593
  for (i = 0; i < blen; i++) {
594
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
595
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->batnextline += 1;
596
      break;
597
    }
598
  }
599
  buff[i] = 0;
600
  buff[-1] = i;
601
  rmod->batnextline += i + 1;
602
 
603
  return(0);
604
 
605
  OOPS:
606
  rmod->batfile[0] = 0;
607
  rmod->batnextline = 0;
608
  return(-1);
469 mateuszvis 609
}
610
 
611
 
443 mateuszvis 612
int main(void) {
372 mateuszvis 613
  static struct config cfg;
614
  static unsigned short far *rmod_envseg;
615
  static unsigned short far *lastexitcode;
616
  static unsigned char BUFFER[4096];
449 mateuszvis 617
  static struct rmod_props far *rmod;
349 mateuszvis 618
 
449 mateuszvis 619
  rmod = rmod_find();
620
  if (rmod == NULL) {
621
    rmod = rmod_install(cfg.envsiz);
622
    if (rmod == NULL) {
369 mateuszvis 623
      outputnl("ERROR: rmod_install() failed");
349 mateuszvis 624
      return(1);
625
    }
465 mateuszvis 626
    /* look at command line parameters */
627
    parse_argv(&cfg);
449 mateuszvis 628
    /* copy flags to rmod's storage */
629
    rmod->flags = cfg.flags;
465 mateuszvis 630
    /* printf("rmod installed at %Fp\r\n", rmod); */
349 mateuszvis 631
  } else {
465 mateuszvis 632
    /* printf("rmod found at %Fp\r\n", rmod); */
633
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
634
     * die asap, because the command has been executed already, so I no longer
635
     * have a purpose in life */
469 mateuszvis 636
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
349 mateuszvis 637
  }
638
 
465 mateuszvis 639
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
640
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
350 mateuszvis 641
 
367 mateuszvis 642
  /* make COMPSEC point to myself */
643
  set_comspec_to_self(*rmod_envseg);
644
 
369 mateuszvis 645
/*  {
350 mateuszvis 646
    unsigned short envsiz;
647
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
648
    envsiz = *sizptr;
649
    envsiz *= 16;
465 mateuszvis 650
    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);
369 mateuszvis 651
  }*/
349 mateuszvis 652
 
443 mateuszvis 653
  do {
474 mateuszvis 654
    char far *cmdline;
349 mateuszvis 655
 
474 mateuszvis 656
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
657
 
658
    SKIP_NEWLINE:
659
 
660
    /* cancel any redirections that may have been set up before */
661
    redir_revert();
662
 
663
    cmdline = rmod->inputbuf + 2;
664
 
437 mateuszvis 665
    /* (re)load translation strings if needed */
666
    nls_langreload(BUFFER, *rmod_envseg);
667
 
443 mateuszvis 668
    /* skip user input if I have a command to exec (/C or /K) */
669
    if (cfg.execcmd != NULL) {
670
      cmdline = cfg.execcmd;
671
      cfg.execcmd = NULL;
672
      goto EXEC_CMDLINE;
673
    }
674
 
469 mateuszvis 675
    /* if batch file is being executed -> fetch next line */
676
    if (rmod->batfile[0] != 0) {
474 mateuszvis 677
      char *tmpbuff = BUFFER + sizeof(BUFFER) - 256;
678
      if (getbatcmd(tmpbuff, rmod) != 0) { /* end of batch */
679
        redir_revert(); /* cancel redirections (if there were any) */
680
        /* restore echo flag as it was before running the bat file */
681
        rmod->echoflag = 0;
682
        if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->echoflag = 1;
683
        continue;
684
      }
685
      /* output prompt and command on screen if echo on and command is not
686
       * inhibiting it with the @ prefix */
687
      if ((rmod->echoflag != 0) && (tmpbuff[1] != '@')) {
688
        build_and_display_prompt(BUFFER, *rmod_envseg);
689
        outputnl(tmpbuff + 1);
690
      }
691
      /* strip the @ prefix if present, it is no longer useful */
692
      if (tmpbuff[1] == '@') {
693
        memmove(tmpbuff + 1, tmpbuff + 2, tmpbuff[0] + 1);
694
        tmpbuff[0] -= 1; /* update the length byte */
695
      }
696
      cmdline = tmpbuff + 1;
469 mateuszvis 697
    } else {
474 mateuszvis 698
      /* interactive mode: display prompt (if echo enabled) and wait for user
699
       * command line */
700
      if (rmod->echoflag != 0) build_and_display_prompt(BUFFER, *rmod_envseg);
701
      /* revert input history terminator to \r so DOS or DOSKEY are not confused */
702
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
703
      /* collect user input */
469 mateuszvis 704
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
474 mateuszvis 705
      /* replace \r by a zero terminator */
706
      cmdline[(unsigned char)(cmdline[-1])] = 0;
469 mateuszvis 707
    }
349 mateuszvis 708
 
405 mateuszvis 709
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
710
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
349 mateuszvis 711
 
443 mateuszvis 712
    /* I jump here when I need to exec an initial command (/C or /K) */
713
    EXEC_CMDLINE:
714
 
364 mateuszvis 715
    /* move pointer forward to skip over any leading spaces */
716
    while (*cmdline == ' ') cmdline++;
349 mateuszvis 717
 
367 mateuszvis 718
    /* update rmod's ptr to COMPSPEC so it is always up to date */
465 mateuszvis 719
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
367 mateuszvis 720
 
402 mateuszvis 721
    /* handle redirections (if any) */
722
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
723
      outputnl("");
724
      continue;
725
    }
726
 
364 mateuszvis 727
    /* try matching (and executing) an internal command */
449 mateuszvis 728
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
443 mateuszvis 729
      /* internal command executed */
730
      redir_revert(); /* revert stdout (in case it was redirected) */
731
      continue;
353 mateuszvis 732
    }
352 mateuszvis 733
 
364 mateuszvis 734
    /* if here, then this was not an internal command */
461 mateuszvis 735
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
469 mateuszvis 736
    /* perhaps this is a newly launched BAT file */
737
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
349 mateuszvis 738
 
474 mateuszvis 739
    /* revert stdout (so the err msg is not redirected) */
402 mateuszvis 740
    redir_revert();
741
 
465 mateuszvis 742
    /* run_as_external() does not return on success, if I am still alive then
743
     * external command failed to execute */
369 mateuszvis 744
    outputnl("Bad command or file name");
349 mateuszvis 745
 
449 mateuszvis 746
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
349 mateuszvis 747
 
449 mateuszvis 748
  sayonara(rmod);
349 mateuszvis 749
  return(0);
750
}