Subversion Repositories SvarDOS

Rev

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

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