Subversion Repositories SvarDOS

Rev

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

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