Subversion Repositories SvarDOS

Rev

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

Rev 438 Rev 443
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
#define FLAG_EXEC_AND_QUIT 1
-
 
82
 
81
struct config {
83
struct config {
82
  int locate;
84
  unsigned char flags;
83
  int install;
85
  char *execcmd;
84
  unsigned short envsiz;
86
  unsigned short envsiz;
85
} cfg;
87
};
-
 
88
 
86
 
89
 
-
 
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;
87
 
95
 
88
static void parse_argv(struct config *cfg, int argc, char **argv) {
-
 
89
  int i;
-
 
90
  memset(cfg, 0, sizeof(*cfg));
96
  memset(cfg, 0, sizeof(*cfg));
91
 
97
 
-
 
98
  /* set a NULL terminator on cmdline */
-
 
99
  cmdline[*cmdlinelen] = 0;
-
 
100
 
92
  for (i = 1; i < argc; i++) {
101
  for (i = 0;;) {
-
 
102
 
-
 
103
    /* skip over any leading spaces */
-
 
104
    for (;; i++) {
93
    if (strcmp(argv[i], "/locate") == 0) {
105
      if (cmdline[i] == 0) return;
94
      cfg->locate = 1;
106
      if (cmdline[i] != ' ') break;
95
    }
107
    }
-
 
108
 
96
    if (strstartswith(argv[i], "/e:") == 0) {
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);
97
      if ((atous(&(cfg->envsiz), argv[i] + 3) != 0) || (cfg->envsiz < 64)) {
130
          if (cfg->envsiz < 64) cfg->envsiz = 0;
-
 
131
          break;
-
 
132
 
-
 
133
        default:
-
 
134
          output("Invalid switch:");
98
        cfg->envsiz = 0;
135
          output(" ");
-
 
136
          outputnl(cmdline + i);
-
 
137
          exit(1);
-
 
138
          break;
99
      }
139
      }
100
    }
140
    }
-
 
141
 
-
 
142
    /* move to next argument or quit processing if end of cmdline */
-
 
143
    for (i++; (cmdline[i] != 0) && (cmdline[i] != ' ') && (cmdline[i] != '/'); i++);
-
 
144
 
101
  }
145
  }
102
}
146
}
103
 
147
 
104
 
148
 
105
static void buildprompt(char *s, unsigned short envseg) {
149
static void buildprompt(char *s, unsigned short envseg) {
106
  /* locate the prompt variable or use the default pattern */
150
  /* locate the prompt variable or use the default pattern */
107
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
151
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
108
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
152
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
109
  /* build the prompt string based on pattern */
153
  /* build the prompt string based on pattern */
110
  for (; *fmt != 0; fmt++) {
154
  for (; *fmt != 0; fmt++) {
111
    if (*fmt != '$') {
155
    if (*fmt != '$') {
112
      *s = *fmt;
156
      *s = *fmt;
113
      s++;
157
      s++;
114
      continue;
158
      continue;
115
    }
159
    }
116
    /* escape code ($P, etc) */
160
    /* escape code ($P, etc) */
117
    fmt++;
161
    fmt++;
118
    switch (*fmt) {
162
    switch (*fmt) {
119
      case 'Q':  /* $Q = = (equal sign) */
163
      case 'Q':  /* $Q = = (equal sign) */
120
      case 'q':
164
      case 'q':
121
        *s = '=';
165
        *s = '=';
122
        s++;
166
        s++;
123
        break;
167
        break;
124
      case '$':  /* $$ = $ (dollar sign) */
168
      case '$':  /* $$ = $ (dollar sign) */
125
        *s = '$';
169
        *s = '$';
126
        s++;
170
        s++;
127
        break;
171
        break;
128
      case 'T':  /* $t = current time */
172
      case 'T':  /* $t = current time */
129
      case 't':
173
      case 't':
130
        s += sprintf(s, "00:00"); /* TODO */
174
        s += sprintf(s, "00:00"); /* TODO */
131
        break;
175
        break;
132
      case 'D':  /* $D = current date */
176
      case 'D':  /* $D = current date */
133
      case 'd':
177
      case 'd':
134
        s += sprintf(s, "1985-07-29"); /* TODO */
178
        s += sprintf(s, "1985-07-29"); /* TODO */
135
        break;
179
        break;
136
      case 'P':  /* $P = current drive and path */
180
      case 'P':  /* $P = current drive and path */
137
      case 'p':
181
      case 'p':
138
        _asm {
182
        _asm {
139
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
183
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
140
          int 0x21
184
          int 0x21
141
          mov bx, s
185
          mov bx, s
142
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
186
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
143
        }
187
        }
144
        *s += 'A';
188
        *s += 'A';
145
        s++;
189
        s++;
146
        *s = ':';
190
        *s = ':';
147
        s++;
191
        s++;
148
        *s = '\\';
192
        *s = '\\';
149
        s++;
193
        s++;
150
        _asm {
194
        _asm {
151
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
195
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
152
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
196
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
153
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
197
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
154
          int 0x21
198
          int 0x21
155
        }
199
        }
156
        while (*s != 0) s++; /* move ptr forward to end of pathname */
200
        while (*s != 0) s++; /* move ptr forward to end of pathname */
157
        break;
201
        break;
158
      case 'V':  /* $V = DOS version number */
202
      case 'V':  /* $V = DOS version number */
159
      case 'v':
203
      case 'v':
160
        s += sprintf(s, "VER"); /* TODO */
204
        s += sprintf(s, "VER"); /* TODO */
161
        break;
205
        break;
162
      case 'N':  /* $N = current drive */
206
      case 'N':  /* $N = current drive */
163
      case 'n':
207
      case 'n':
164
        _asm {
208
        _asm {
165
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
209
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
166
          int 0x21
210
          int 0x21
167
          mov bx, s
211
          mov bx, s
168
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
212
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
169
        }
213
        }
170
        *s += 'A';
214
        *s += 'A';
171
        s++;
215
        s++;
172
        break;
216
        break;
173
      case 'G':  /* $G = > (greater-than sign) */
217
      case 'G':  /* $G = > (greater-than sign) */
174
      case 'g':
218
      case 'g':
175
        *s = '>';
219
        *s = '>';
176
        s++;
220
        s++;
177
        break;
221
        break;
178
      case 'L':  /* $L = < (less-than sign) */
222
      case 'L':  /* $L = < (less-than sign) */
179
      case 'l':
223
      case 'l':
180
        *s = '<';
224
        *s = '<';
181
        s++;
225
        s++;
182
        break;
226
        break;
183
      case 'B':  /* $B = | (pipe) */
227
      case 'B':  /* $B = | (pipe) */
184
      case 'b':
228
      case 'b':
185
        *s = '|';
229
        *s = '|';
186
        s++;
230
        s++;
187
        break;
231
        break;
188
      case 'H':  /* $H = backspace (erases previous character) */
232
      case 'H':  /* $H = backspace (erases previous character) */
189
      case 'h':
233
      case 'h':
190
        *s = '\b';
234
        *s = '\b';
191
        s++;
235
        s++;
192
        break;
236
        break;
193
      case 'E':  /* $E = Escape code (ASCII 27) */
237
      case 'E':  /* $E = Escape code (ASCII 27) */
194
      case 'e':
238
      case 'e':
195
        *s = 27;
239
        *s = 27;
196
        s++;
240
        s++;
197
        break;
241
        break;
198
      case '_':  /* $_ = CR+LF */
242
      case '_':  /* $_ = CR+LF */
199
        *s = '\r';
243
        *s = '\r';
200
        s++;
244
        s++;
201
        *s = '\n';
245
        *s = '\n';
202
        s++;
246
        s++;
203
        break;
247
        break;
204
    }
248
    }
205
  }
249
  }
206
  *s = '$';
250
  *s = '$';
207
}
251
}
208
 
252
 
209
 
253
 
210
static void run_as_external(const char far *cmdline) {
254
static void run_as_external(const char far *cmdline) {
211
  char buff[256];
255
  char buff[256];
212
  char const *argvlist[256];
256
  char const *argvlist[256];
213
  int i, n;
257
  int i, n;
214
  /* copy buffer to a near var (incl. trailing CR), insert a space before
258
  /* copy buffer to a near var (incl. trailing CR), insert a space before
215
     every slash to make sure arguments are well separated */
259
     every slash to make sure arguments are well separated */
216
  n = 0;
260
  n = 0;
217
  i = 0;
261
  i = 0;
218
  for (;;) {
262
  for (;;) {
219
    if (cmdline[i] == '/') buff[n++] = ' ';
263
    if (cmdline[i] == '/') buff[n++] = ' ';
220
    buff[n++] = cmdline[i++];
264
    buff[n++] = cmdline[i++];
221
    if (buff[n] == 0) break;
265
    if (buff[n] == 0) break;
222
  }
266
  }
223
 
267
 
224
  cmd_explode(buff, cmdline, argvlist);
268
  cmd_explode(buff, cmdline, argvlist);
225
 
269
 
226
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
270
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
227
 
271
 
228
  /* must be an external command then. this call should never return, unless
272
  /* must be an external command then. this call should never return, unless
229
   * the other program failed to be executed. */
273
   * the other program failed to be executed. */
230
  execvp(argvlist[0], argvlist);
274
  execvp(argvlist[0], argvlist);
231
}
275
}
232
 
276
 
233
 
277
 
234
static void set_comspec_to_self(unsigned short envseg) {
278
static void set_comspec_to_self(unsigned short envseg) {
235
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
279
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
236
  char far *myenv = MK_FP(*psp_envseg, 0);
280
  char far *myenv = MK_FP(*psp_envseg, 0);
237
  unsigned short varcount;
281
  unsigned short varcount;
238
  char buff[256] = "COMSPEC=";
282
  char buff[256] = "COMSPEC=";
239
  char *buffptr = buff + 8;
283
  char *buffptr = buff + 8;
240
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
284
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
241
  while (*myenv != 0) {
285
  while (*myenv != 0) {
242
    /* consume a NULL-terminated string */
286
    /* consume a NULL-terminated string */
243
    while (*myenv != 0) myenv++;
287
    while (*myenv != 0) myenv++;
244
    /* move to next string */
288
    /* move to next string */
245
    myenv++;
289
    myenv++;
246
  }
290
  }
247
  /* get next word, if 1 then EXEPATH follows */
291
  /* get next word, if 1 then EXEPATH follows */
248
  myenv++;
292
  myenv++;
249
  varcount = *myenv;
293
  varcount = *myenv;
250
  myenv++;
294
  myenv++;
251
  varcount |= (*myenv << 8);
295
  varcount |= (*myenv << 8);
252
  myenv++;
296
  myenv++;
253
  if (varcount != 1) return; /* NO EXEPATH FOUND */
297
  if (varcount != 1) return; /* NO EXEPATH FOUND */
254
  while (*myenv != 0) {
298
  while (*myenv != 0) {
255
    *buffptr = *myenv;
299
    *buffptr = *myenv;
256
    buffptr++;
300
    buffptr++;
257
    myenv++;
301
    myenv++;
258
  }
302
  }
259
  *buffptr = 0;
303
  *buffptr = 0;
260
  /* printf("EXEPATH: '%s'\r\n", buff); */
304
  /* printf("EXEPATH: '%s'\r\n", buff); */
261
  env_setvar(envseg, buff);
305
  env_setvar(envseg, buff);
262
}
306
}
263
 
307
 
264
 
308
 
265
int main(int argc, char **argv) {
309
int main(void) {
266
  static struct config cfg;
310
  static struct config cfg;
267
  static unsigned short rmod_seg;
311
  static unsigned short rmod_seg;
268
  static unsigned short far *rmod_envseg;
312
  static unsigned short far *rmod_envseg;
269
  static unsigned short far *lastexitcode;
313
  static unsigned short far *lastexitcode;
270
  static unsigned char BUFFER[4096];
314
  static unsigned char BUFFER[4096];
271
 
315
 
272
  parse_argv(&cfg, argc, argv);
316
  parse_argv(&cfg);
273
 
317
 
274
  rmod_seg = rmod_find();
318
  rmod_seg = rmod_find();
275
  if (rmod_seg == 0xffff) {
319
  if (rmod_seg == 0xffff) {
276
    rmod_seg = rmod_install(cfg.envsiz);
320
    rmod_seg = rmod_install(cfg.envsiz);
277
    if (rmod_seg == 0xffff) {
321
    if (rmod_seg == 0xffff) {
278
      outputnl("ERROR: rmod_install() failed");
322
      outputnl("ERROR: rmod_install() failed");
279
      return(1);
323
      return(1);
280
    }
324
    }
281
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
325
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
282
  } else {
326
  } else {
283
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
327
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
284
  }
328
  }
285
 
329
 
286
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
330
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
287
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
331
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
288
 
332
 
289
  /* make COMPSEC point to myself */
333
  /* make COMPSEC point to myself */
290
  set_comspec_to_self(*rmod_envseg);
334
  set_comspec_to_self(*rmod_envseg);
291
 
335
 
292
/*  {
336
/*  {
293
    unsigned short envsiz;
337
    unsigned short envsiz;
294
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
338
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
295
    envsiz = *sizptr;
339
    envsiz = *sizptr;
296
    envsiz *= 16;
340
    envsiz *= 16;
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);
341
    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);
298
  }*/
342
  }*/
299
 
343
 
300
  for (;;) {
344
  do {
301
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
345
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
302
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
346
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
303
 
347
 
304
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
-
 
305
 
-
 
306
    /* (re)load translation strings if needed */
348
    /* (re)load translation strings if needed */
307
    nls_langreload(BUFFER, *rmod_envseg);
349
    nls_langreload(BUFFER, *rmod_envseg);
308
 
350
 
-
 
351
    /* skip user input if I have a command to exec (/C or /K) */
-
 
352
    if (cfg.execcmd != NULL) {
-
 
353
      cmdline = cfg.execcmd;
-
 
354
      cfg.execcmd = NULL;
-
 
355
      goto EXEC_CMDLINE;
-
 
356
    }
-
 
357
 
-
 
358
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
-
 
359
 
309
    SKIP_NEWLINE:
360
    SKIP_NEWLINE:
310
 
361
 
311
    /* print shell prompt (only if ECHO is enabled) */
362
    /* print shell prompt (only if ECHO is enabled) */
312
    if (*echostatus != 0) {
363
    if (*echostatus != 0) {
313
      char *promptptr = BUFFER;
364
      char *promptptr = BUFFER;
314
      buildprompt(promptptr, *rmod_envseg);
365
      buildprompt(promptptr, *rmod_envseg);
315
      _asm {
366
      _asm {
316
        push ax
367
        push ax
317
        push dx
368
        push dx
318
        mov ah, 0x09
369
        mov ah, 0x09
319
        mov dx, promptptr
370
        mov dx, promptptr
320
        int 0x21
371
        int 0x21
321
        pop dx
372
        pop dx
322
        pop ax
373
        pop ax
323
      }
374
      }
324
    }
375
    }
325
 
376
 
326
    /* revert input history terminator to \r */
377
    /* revert input history terminator to \r */
327
    if (cmdline[-1] != 0) {
378
    if (cmdline[-1] != 0) {
328
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
379
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
329
    }
380
    }
330
 
381
 
331
    /* wait for user input */
382
    /* wait for user input */
332
    _asm {
383
    _asm {
333
      push ax
384
      push ax
334
      push bx
385
      push bx
335
      push cx
386
      push cx
336
      push dx
387
      push dx
337
      push ds
388
      push ds
338
 
389
 
339
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
390
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
340
      mov ax, 0x4800
391
      mov ax, 0x4800
341
      int 0x2f
392
      int 0x2f
342
      mov bl, al /* save doskey status in BL */
393
      mov bl, al /* save doskey status in BL */
343
 
394
 
344
      /* set up buffered input */
395
      /* set up buffered input */
345
      mov ax, rmod_seg
396
      mov ax, rmod_seg
346
      push ax
397
      push ax
347
      pop ds
398
      pop ds
348
      mov dx, RMOD_OFFSET_INPBUFF
399
      mov dx, RMOD_OFFSET_INPBUFF
349
 
400
 
350
      /* execute either DOS input or DOSKEY */
401
      /* execute either DOS input or DOSKEY */
351
      test bl, bl /* zf set if no DOSKEY present */
402
      test bl, bl /* zf set if no DOSKEY present */
352
      jnz DOSKEY
403
      jnz DOSKEY
353
 
404
 
354
      mov ah, 0x0a
405
      mov ah, 0x0a
355
      int 0x21
406
      int 0x21
356
      jmp short DONE
407
      jmp short DONE
357
 
408
 
358
      DOSKEY:
409
      DOSKEY:
359
      mov ax, 0x4810
410
      mov ax, 0x4810
360
      int 0x2f
411
      int 0x2f
361
 
412
 
362
      DONE:
413
      DONE:
363
      /* terminate command with a CR/LF */
414
      /* terminate command with a CR/LF */
364
      mov ah, 0x02 /* display character in dl */
415
      mov ah, 0x02 /* display character in dl */
365
      mov dl, 0x0d
416
      mov dl, 0x0d
366
      int 0x21
417
      int 0x21
367
      mov dl, 0x0a
418
      mov dl, 0x0a
368
      int 0x21
419
      int 0x21
369
 
420
 
370
      pop ds
421
      pop ds
371
      pop dx
422
      pop dx
372
      pop cx
423
      pop cx
373
      pop bx
424
      pop bx
374
      pop ax
425
      pop ax
375
    }
426
    }
376
 
427
 
377
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
428
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
378
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
429
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
379
 
430
 
380
    /* replace \r by a zero terminator */
431
    /* replace \r by a zero terminator */
381
    cmdline[(unsigned char)(cmdline[-1])] = 0;
432
    cmdline[(unsigned char)(cmdline[-1])] = 0;
382
 
433
 
-
 
434
    /* I jump here when I need to exec an initial command (/C or /K) */
-
 
435
    EXEC_CMDLINE:
-
 
436
 
383
    /* move pointer forward to skip over any leading spaces */
437
    /* move pointer forward to skip over any leading spaces */
384
    while (*cmdline == ' ') cmdline++;
438
    while (*cmdline == ' ') cmdline++;
385
 
439
 
386
    /* update rmod's ptr to COMPSPEC so it is always up to date */
440
    /* update rmod's ptr to COMPSPEC so it is always up to date */
387
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
441
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
388
 
442
 
389
    /* handle redirections (if any) */
443
    /* handle redirections (if any) */
390
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
444
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
391
      outputnl("");
445
      outputnl("");
392
      continue;
446
      continue;
393
    }
447
    }
394
 
448
 
395
    /* try matching (and executing) an internal command */
449
    /* try matching (and executing) an internal command */
396
    {
-
 
397
      int ecode = cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER);
450
    if (cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER) >= -1) {
398
      if (ecode >= 0) *lastexitcode = ecode;
-
 
399
      if (ecode >= -1) { /* internal command executed */
451
      /* internal command executed */
400
        redir_revert(); /* revert stdout (in case it was redirected) */
452
      redir_revert(); /* revert stdout (in case it was redirected) */
401
        continue;
453
      continue;
402
      }
-
 
403
    }
454
    }
404
 
455
 
405
    /* if here, then this was not an internal command */
456
    /* if here, then this was not an internal command */
406
    run_as_external(cmdline);
457
    run_as_external(cmdline);
407
 
458
 
408
    /* revert stdout (in case it was redirected) */
459
    /* revert stdout (in case it was redirected) */
409
    redir_revert();
460
    redir_revert();
410
 
461
 
411
    /* execvp() replaces the current process by the new one
462
    /* execvp() replaces the current process by the new one
412
    if I am still alive then external command failed to execute */
463
    if I am still alive then external command failed to execute */
413
    outputnl("Bad command or file name");
464
    outputnl("Bad command or file name");
414
 
465
 
415
  }
-
 
-
 
466
  } while ((cfg.flags & FLAG_EXEC_AND_QUIT) == 0);
416
 
467
 
417
  return(0);
468
  return(0);
418
}
469
}
419
 
470