Subversion Repositories SvarDOS

Rev

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

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