Subversion Repositories SvarDOS

Rev

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

Rev 410 Rev 421
-
 
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
 
1
/*
25
/*
2
 * SvarCOM is a command-line interpreter.
26
 * SvarCOM is a command-line interpreter.
3
 *
27
 *
4
 * a little memory area is allocated as high as possible. it contains:
28
 * a little memory area is allocated as high as possible. it contains:
5
 *  - a signature (like XMS drivers do)
29
 *  - a signature (like XMS drivers do)
6
 *  - a routine for exec'ing programs
30
 *  - a routine for exec'ing programs
7
 *  - a "last command" buffer for input history
31
 *  - a "last command" buffer for input history
8
 *
32
 *
9
 * when svarcom starts, it tries locating the routine in memory.
33
 * when svarcom starts, it tries locating the routine in memory.
10
 *
34
 *
11
 * if found:
35
 * if found:
12
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
36
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
13
 *
37
 *
14
 * if not found:
38
 * if not found:
15
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
39
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
16
 *   and quit.
40
 *   and quit.
17
 *
41
 *
18
 * PSP structure
42
 * PSP structure
19
 * http://www.piclist.com/techref/dos/psps.htm
43
 * http://www.piclist.com/techref/dos/psps.htm
20
 *
44
 *
21
 *
45
 *
22
 *
46
 *
23
 * === MCB ===
47
 * === MCB ===
24
 *
48
 *
25
 * each time that DOS allocates memory, it prefixes the allocated memory with
49
 * each time that DOS allocates memory, it prefixes the allocated memory with
26
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
50
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
27
 * block has the following structure:
51
 * block has the following structure:
28
 *
52
 *
29
 * Offset  Size     Description
53
 * Offset  Size     Description
30
 *   00h   byte     'M' =  non-last member of the MCB chain
54
 *   00h   byte     'M' =  non-last member of the MCB chain
31
 *                  'Z' = indicates last entry in MCB chain
55
 *                  'Z' = indicates last entry in MCB chain
32
 *                  other values cause "Memory Allocation Failure" on exit
56
 *                  other values cause "Memory Allocation Failure" on exit
33
 *   01h   word     PSP segment address of the owner (Process Id)
57
 *   01h   word     PSP segment address of the owner (Process Id)
34
 *                  possible values:
58
 *                  possible values:
35
 *                    0 = free
59
 *                    0 = free
36
 *                    8 = Allocated by DOS before first user pgm loaded
60
 *                    8 = Allocated by DOS before first user pgm loaded
37
 *                    other = Process Id/PSP segment address of owner
61
 *                    other = Process Id/PSP segment address of owner
38
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
62
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
39
 *   05h  11 bytes  reserved
63
 *   05h  11 bytes  reserved
40
 *   10h  ...       start of actual allocated memory block
64
 *   10h  ...       start of actual allocated memory block
41
 */
65
 */
42
 
66
 
43
#include <i86.h>
67
#include <i86.h>
44
#include <dos.h>
68
#include <dos.h>
45
#include <stdio.h>
69
#include <stdio.h>
46
#include <stdlib.h>
70
#include <stdlib.h>
47
#include <string.h>
71
#include <string.h>
48
 
72
 
49
#include <process.h>
73
#include <process.h>
50
 
74
 
51
#include "cmd.h"
75
#include "cmd.h"
52
#include "env.h"
76
#include "env.h"
53
#include "helpers.h"
77
#include "helpers.h"
54
#include "redir.h"
78
#include "redir.h"
55
#include "rmodinit.h"
79
#include "rmodinit.h"
56
 
80
 
57
struct config {
81
struct config {
58
  int locate;
82
  int locate;
59
  int install;
83
  int install;
60
  unsigned short envsiz;
84
  unsigned short envsiz;
61
} cfg;
85
} cfg;
62
 
86
 
63
 
87
 
64
static void parse_argv(struct config *cfg, int argc, char **argv) {
88
static void parse_argv(struct config *cfg, int argc, char **argv) {
65
  int i;
89
  int i;
66
  memset(cfg, 0, sizeof(*cfg));
90
  memset(cfg, 0, sizeof(*cfg));
67
 
91
 
68
  for (i = 1; i < argc; i++) {
92
  for (i = 1; i < argc; i++) {
69
    if (strcmp(argv[i], "/locate") == 0) {
93
    if (strcmp(argv[i], "/locate") == 0) {
70
      cfg->locate = 1;
94
      cfg->locate = 1;
71
    }
95
    }
72
    if (strstartswith(argv[i], "/e:") == 0) {
96
    if (strstartswith(argv[i], "/e:") == 0) {
73
      if ((atouns(&(cfg->envsiz), argv[i] + 3) != 0) || (cfg->envsiz < 64)) {
97
      if ((atouns(&(cfg->envsiz), argv[i] + 3) != 0) || (cfg->envsiz < 64)) {
74
        cfg->envsiz = 0;
98
        cfg->envsiz = 0;
75
      }
99
      }
76
    }
100
    }
77
  }
101
  }
78
}
102
}
79
 
103
 
80
 
104
 
81
static void buildprompt(char *s, unsigned short envseg) {
105
static void buildprompt(char *s, unsigned short envseg) {
82
  /* locate the prompt variable or use the default pattern */
106
  /* locate the prompt variable or use the default pattern */
83
  const char far *fmt = env_lookup(envseg, "PROMPT");
107
  const char far *fmt = env_lookup(envseg, "PROMPT");
84
  while ((fmt != NULL) && (*fmt != 0)) {
108
  while ((fmt != NULL) && (*fmt != 0)) {
85
    fmt++;
109
    fmt++;
86
    if (fmt[-1] == '=') break;
110
    if (fmt[-1] == '=') break;
87
  }
111
  }
88
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
112
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
89
  /* build the prompt string based on pattern */
113
  /* build the prompt string based on pattern */
90
  for (; *fmt != 0; fmt++) {
114
  for (; *fmt != 0; fmt++) {
91
    if (*fmt != '$') {
115
    if (*fmt != '$') {
92
      *s = *fmt;
116
      *s = *fmt;
93
      s++;
117
      s++;
94
      continue;
118
      continue;
95
    }
119
    }
96
    /* escape code ($P, etc) */
120
    /* escape code ($P, etc) */
97
    fmt++;
121
    fmt++;
98
    switch (*fmt) {
122
    switch (*fmt) {
99
      case 'Q':  /* $Q = = (equal sign) */
123
      case 'Q':  /* $Q = = (equal sign) */
100
      case 'q':
124
      case 'q':
101
        *s = '=';
125
        *s = '=';
102
        s++;
126
        s++;
103
        break;
127
        break;
104
      case '$':  /* $$ = $ (dollar sign) */
128
      case '$':  /* $$ = $ (dollar sign) */
105
        *s = '$';
129
        *s = '$';
106
        s++;
130
        s++;
107
        break;
131
        break;
108
      case 'T':  /* $t = current time */
132
      case 'T':  /* $t = current time */
109
      case 't':
133
      case 't':
110
        s += sprintf(s, "00:00"); /* TODO */
134
        s += sprintf(s, "00:00"); /* TODO */
111
        break;
135
        break;
112
      case 'D':  /* $D = current date */
136
      case 'D':  /* $D = current date */
113
      case 'd':
137
      case 'd':
114
        s += sprintf(s, "1985-07-29"); /* TODO */
138
        s += sprintf(s, "1985-07-29"); /* TODO */
115
        break;
139
        break;
116
      case 'P':  /* $P = current drive and path */
140
      case 'P':  /* $P = current drive and path */
117
      case 'p':
141
      case 'p':
118
        _asm {
142
        _asm {
119
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
143
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
120
          int 0x21
144
          int 0x21
121
          mov bx, s
145
          mov bx, s
122
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
146
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
123
        }
147
        }
124
        *s += 'A';
148
        *s += 'A';
125
        s++;
149
        s++;
126
        *s = ':';
150
        *s = ':';
127
        s++;
151
        s++;
128
        *s = '\\';
152
        *s = '\\';
129
        s++;
153
        s++;
130
        _asm {
154
        _asm {
131
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
155
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
132
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
156
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
133
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
157
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
134
          int 0x21
158
          int 0x21
135
        }
159
        }
136
        while (*s != 0) s++; /* move ptr forward to end of pathname */
160
        while (*s != 0) s++; /* move ptr forward to end of pathname */
137
        break;
161
        break;
138
      case 'V':  /* $V = DOS version number */
162
      case 'V':  /* $V = DOS version number */
139
      case 'v':
163
      case 'v':
140
        s += sprintf(s, "VER"); /* TODO */
164
        s += sprintf(s, "VER"); /* TODO */
141
        break;
165
        break;
142
      case 'N':  /* $N = current drive */
166
      case 'N':  /* $N = current drive */
143
      case 'n':
167
      case 'n':
144
        _asm {
168
        _asm {
145
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
169
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
146
          int 0x21
170
          int 0x21
147
          mov bx, s
171
          mov bx, s
148
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
172
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
149
        }
173
        }
150
        *s += 'A';
174
        *s += 'A';
151
        s++;
175
        s++;
152
        break;
176
        break;
153
      case 'G':  /* $G = > (greater-than sign) */
177
      case 'G':  /* $G = > (greater-than sign) */
154
      case 'g':
178
      case 'g':
155
        *s = '>';
179
        *s = '>';
156
        s++;
180
        s++;
157
        break;
181
        break;
158
      case 'L':  /* $L = < (less-than sign) */
182
      case 'L':  /* $L = < (less-than sign) */
159
      case 'l':
183
      case 'l':
160
        *s = '<';
184
        *s = '<';
161
        s++;
185
        s++;
162
        break;
186
        break;
163
      case 'B':  /* $B = | (pipe) */
187
      case 'B':  /* $B = | (pipe) */
164
      case 'b':
188
      case 'b':
165
        *s = '|';
189
        *s = '|';
166
        s++;
190
        s++;
167
        break;
191
        break;
168
      case 'H':  /* $H = backspace (erases previous character) */
192
      case 'H':  /* $H = backspace (erases previous character) */
169
      case 'h':
193
      case 'h':
170
        *s = '\b';
194
        *s = '\b';
171
        s++;
195
        s++;
172
        break;
196
        break;
173
      case 'E':  /* $E = Escape code (ASCII 27) */
197
      case 'E':  /* $E = Escape code (ASCII 27) */
174
      case 'e':
198
      case 'e':
175
        *s = 27;
199
        *s = 27;
176
        s++;
200
        s++;
177
        break;
201
        break;
178
      case '_':  /* $_ = CR+LF */
202
      case '_':  /* $_ = CR+LF */
179
        *s = '\r';
203
        *s = '\r';
180
        s++;
204
        s++;
181
        *s = '\n';
205
        *s = '\n';
182
        s++;
206
        s++;
183
        break;
207
        break;
184
    }
208
    }
185
  }
209
  }
186
  *s = '$';
210
  *s = '$';
187
}
211
}
188
 
212
 
189
 
213
 
190
static void run_as_external(const char far *cmdline) {
214
static void run_as_external(const char far *cmdline) {
191
  char buff[256];
215
  char buff[256];
192
  char const *argvlist[256];
216
  char const *argvlist[256];
193
  int i, n;
217
  int i, n;
194
  /* copy buffer to a near var (incl. trailing CR), insert a space before
218
  /* copy buffer to a near var (incl. trailing CR), insert a space before
195
     every slash to make sure arguments are well separated */
219
     every slash to make sure arguments are well separated */
196
  n = 0;
220
  n = 0;
197
  i = 0;
221
  i = 0;
198
  for (;;) {
222
  for (;;) {
199
    if (cmdline[i] == '/') buff[n++] = ' ';
223
    if (cmdline[i] == '/') buff[n++] = ' ';
200
    buff[n++] = cmdline[i++];
224
    buff[n++] = cmdline[i++];
201
    if (buff[n] == 0) break;
225
    if (buff[n] == 0) break;
202
  }
226
  }
203
 
227
 
204
  cmd_explode(buff, cmdline, argvlist);
228
  cmd_explode(buff, cmdline, argvlist);
205
 
229
 
206
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
230
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
207
 
231
 
208
  /* must be an external command then. this call should never return, unless
232
  /* must be an external command then. this call should never return, unless
209
   * the other program failed to be executed. */
233
   * the other program failed to be executed. */
210
  execvp(argvlist[0], argvlist);
234
  execvp(argvlist[0], argvlist);
211
}
235
}
212
 
236
 
213
 
237
 
214
static void set_comspec_to_self(unsigned short envseg) {
238
static void set_comspec_to_self(unsigned short envseg) {
215
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
239
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
216
  char far *myenv = MK_FP(*psp_envseg, 0);
240
  char far *myenv = MK_FP(*psp_envseg, 0);
217
  unsigned short varcount;
241
  unsigned short varcount;
218
  char buff[256] = "COMSPEC=";
242
  char buff[256] = "COMSPEC=";
219
  char *buffptr = buff + 8;
243
  char *buffptr = buff + 8;
220
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
244
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
221
  while (*myenv != 0) {
245
  while (*myenv != 0) {
222
    /* consume a NULL-terminated string */
246
    /* consume a NULL-terminated string */
223
    while (*myenv != 0) myenv++;
247
    while (*myenv != 0) myenv++;
224
    /* move to next string */
248
    /* move to next string */
225
    myenv++;
249
    myenv++;
226
  }
250
  }
227
  /* get next word, if 1 then EXEPATH follows */
251
  /* get next word, if 1 then EXEPATH follows */
228
  myenv++;
252
  myenv++;
229
  varcount = *myenv;
253
  varcount = *myenv;
230
  myenv++;
254
  myenv++;
231
  varcount |= (*myenv << 8);
255
  varcount |= (*myenv << 8);
232
  myenv++;
256
  myenv++;
233
  if (varcount != 1) return; /* NO EXEPATH FOUND */
257
  if (varcount != 1) return; /* NO EXEPATH FOUND */
234
  while (*myenv != 0) {
258
  while (*myenv != 0) {
235
    *buffptr = *myenv;
259
    *buffptr = *myenv;
236
    buffptr++;
260
    buffptr++;
237
    myenv++;
261
    myenv++;
238
  }
262
  }
239
  *buffptr = 0;
263
  *buffptr = 0;
240
  /* printf("EXEPATH: '%s'\r\n", buff); */
264
  /* printf("EXEPATH: '%s'\r\n", buff); */
241
  env_setvar(envseg, buff);
265
  env_setvar(envseg, buff);
242
}
266
}
243
 
267
 
244
 
268
 
245
int main(int argc, char **argv) {
269
int main(int argc, char **argv) {
246
  static struct config cfg;
270
  static struct config cfg;
247
  static unsigned short rmod_seg;
271
  static unsigned short rmod_seg;
248
  static unsigned short far *rmod_envseg;
272
  static unsigned short far *rmod_envseg;
249
  static unsigned short far *lastexitcode;
273
  static unsigned short far *lastexitcode;
250
  static unsigned char BUFFER[4096];
274
  static unsigned char BUFFER[4096];
251
 
275
 
252
  parse_argv(&cfg, argc, argv);
276
  parse_argv(&cfg, argc, argv);
253
 
277
 
254
  rmod_seg = rmod_find();
278
  rmod_seg = rmod_find();
255
  if (rmod_seg == 0xffff) {
279
  if (rmod_seg == 0xffff) {
256
    rmod_seg = rmod_install(cfg.envsiz);
280
    rmod_seg = rmod_install(cfg.envsiz);
257
    if (rmod_seg == 0xffff) {
281
    if (rmod_seg == 0xffff) {
258
      outputnl("ERROR: rmod_install() failed");
282
      outputnl("ERROR: rmod_install() failed");
259
      return(1);
283
      return(1);
260
    }
284
    }
261
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
285
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
262
  } else {
286
  } else {
263
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
287
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
264
  }
288
  }
265
 
289
 
266
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
290
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
267
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
291
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
268
 
292
 
269
  /* make COMPSEC point to myself */
293
  /* make COMPSEC point to myself */
270
  set_comspec_to_self(*rmod_envseg);
294
  set_comspec_to_self(*rmod_envseg);
271
 
295
 
272
/*  {
296
/*  {
273
    unsigned short envsiz;
297
    unsigned short envsiz;
274
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
298
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
275
    envsiz = *sizptr;
299
    envsiz = *sizptr;
276
    envsiz *= 16;
300
    envsiz *= 16;
277
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
301
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
278
  }*/
302
  }*/
279
 
303
 
280
  for (;;) {
304
  for (;;) {
281
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
305
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
282
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
306
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
283
 
307
 
284
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
308
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
285
 
309
 
286
    SKIP_NEWLINE:
310
    SKIP_NEWLINE:
287
 
311
 
288
    /* print shell prompt (only if ECHO is enabled) */
312
    /* print shell prompt (only if ECHO is enabled) */
289
    if (*echostatus != 0) {
313
    if (*echostatus != 0) {
290
      char *promptptr = BUFFER;
314
      char *promptptr = BUFFER;
291
      buildprompt(promptptr, *rmod_envseg);
315
      buildprompt(promptptr, *rmod_envseg);
292
      _asm {
316
      _asm {
293
        push ax
317
        push ax
294
        push dx
318
        push dx
295
        mov ah, 0x09
319
        mov ah, 0x09
296
        mov dx, promptptr
320
        mov dx, promptptr
297
        int 0x21
321
        int 0x21
298
        pop dx
322
        pop dx
299
        pop ax
323
        pop ax
300
      }
324
      }
301
    }
325
    }
302
 
326
 
303
    /* revert input history terminator to \r */
327
    /* revert input history terminator to \r */
304
    if (cmdline[-1] != 0) {
328
    if (cmdline[-1] != 0) {
305
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
329
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
306
    }
330
    }
307
 
331
 
308
    /* wait for user input */
332
    /* wait for user input */
309
    _asm {
333
    _asm {
310
      push ax
334
      push ax
311
      push bx
335
      push bx
312
      push cx
336
      push cx
313
      push dx
337
      push dx
314
      push ds
338
      push ds
315
 
339
 
316
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
340
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
317
      mov ax, 0x4800
341
      mov ax, 0x4800
318
      int 0x2f
342
      int 0x2f
319
      mov bl, al /* save doskey status in BL */
343
      mov bl, al /* save doskey status in BL */
320
 
344
 
321
      /* set up buffered input */
345
      /* set up buffered input */
322
      mov ax, rmod_seg
346
      mov ax, rmod_seg
323
      push ax
347
      push ax
324
      pop ds
348
      pop ds
325
      mov dx, RMOD_OFFSET_INPBUFF
349
      mov dx, RMOD_OFFSET_INPBUFF
326
 
350
 
327
      /* execute either DOS input or DOSKEY */
351
      /* execute either DOS input or DOSKEY */
328
      test bl, bl /* zf set if no DOSKEY present */
352
      test bl, bl /* zf set if no DOSKEY present */
329
      jnz DOSKEY
353
      jnz DOSKEY
330
 
354
 
331
      mov ah, 0x0a
355
      mov ah, 0x0a
332
      int 0x21
356
      int 0x21
333
      jmp short DONE
357
      jmp short DONE
334
 
358
 
335
      DOSKEY:
359
      DOSKEY:
336
      mov ax, 0x4810
360
      mov ax, 0x4810
337
      int 0x2f
361
      int 0x2f
338
 
362
 
339
      DONE:
363
      DONE:
340
      /* terminate command with a CR/LF */
364
      /* terminate command with a CR/LF */
341
      mov ah, 0x02 /* display character in dl */
365
      mov ah, 0x02 /* display character in dl */
342
      mov dl, 0x0d
366
      mov dl, 0x0d
343
      int 0x21
367
      int 0x21
344
      mov dl, 0x0a
368
      mov dl, 0x0a
345
      int 0x21
369
      int 0x21
346
 
370
 
347
      pop ds
371
      pop ds
348
      pop dx
372
      pop dx
349
      pop cx
373
      pop cx
350
      pop bx
374
      pop bx
351
      pop ax
375
      pop ax
352
    }
376
    }
353
 
377
 
354
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
378
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
355
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
379
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
356
 
380
 
357
    /* replace \r by a zero terminator */
381
    /* replace \r by a zero terminator */
358
    cmdline[(unsigned char)(cmdline[-1])] = 0;
382
    cmdline[(unsigned char)(cmdline[-1])] = 0;
359
 
383
 
360
    /* move pointer forward to skip over any leading spaces */
384
    /* move pointer forward to skip over any leading spaces */
361
    while (*cmdline == ' ') cmdline++;
385
    while (*cmdline == ' ') cmdline++;
362
 
386
 
363
    /* update rmod's ptr to COMPSPEC so it is always up to date */
387
    /* update rmod's ptr to COMPSPEC so it is always up to date */
364
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
388
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
365
 
389
 
366
    /* handle redirections (if any) */
390
    /* handle redirections (if any) */
367
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
391
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
368
      outputnl("");
392
      outputnl("");
369
      continue;
393
      continue;
370
    }
394
    }
371
 
395
 
372
    /* try matching (and executing) an internal command */
396
    /* try matching (and executing) an internal command */
373
    {
397
    {
374
      int ecode = cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER);
398
      int ecode = cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER);
375
      if (ecode >= 0) *lastexitcode = ecode;
399
      if (ecode >= 0) *lastexitcode = ecode;
376
      if (ecode >= -1) { /* internal command executed */
400
      if (ecode >= -1) { /* internal command executed */
377
        redir_revert(); /* revert stdout (in case it was redirected) */
401
        redir_revert(); /* revert stdout (in case it was redirected) */
378
        continue;
402
        continue;
379
      }
403
      }
380
    }
404
    }
381
 
405
 
382
    /* if here, then this was not an internal command */
406
    /* if here, then this was not an internal command */
383
    run_as_external(cmdline);
407
    run_as_external(cmdline);
384
 
408
 
385
    /* revert stdout (in case it was redirected) */
409
    /* revert stdout (in case it was redirected) */
386
    redir_revert();
410
    redir_revert();
387
 
411
 
388
    /* execvp() replaces the current process by the new one
412
    /* execvp() replaces the current process by the new one
389
    if I am still alive then external command failed to execute */
413
    if I am still alive then external command failed to execute */
390
    outputnl("Bad command or file name");
414
    outputnl("Bad command or file name");
391
 
415
 
392
  }
416
  }
393
 
417
 
394
  return(0);
418
  return(0);
395
}
419
}
396
 
420