Subversion Repositories SvarDOS

Rev

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

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