Subversion Repositories SvarDOS

Rev

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

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