Subversion Repositories SvarDOS

Rev

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

Rev 457 Rev 458
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
/* tries locating executable fname in path and fille res with result. returns 0 on success,
-
 
272
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
-
 
273
 * format is filled the offset where extension starts in fname (-1 if not found) */
-
 
274
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
-
 
275
  unsigned short lastbslash = 0xffff;
-
 
276
  unsigned short i, len;
-
 
277
  unsigned char explicitpath = 0;
-
 
278
 
-
 
279
  /* does the original fname had an explicit path prefix or explicit ext? */
-
 
280
  *extptr = NULL;
-
 
281
  for (i = 0; fname[i] != 0; i++) {
-
 
282
    switch (fname[i]) {
-
 
283
      case ':':
-
 
284
      case '\\':
-
 
285
        explicitpath = 1;
-
 
286
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
-
 
287
        break;
-
 
288
      case '.':
-
 
289
        *extptr = fname + i + 1;
-
 
290
        break;
-
 
291
    }
-
 
292
  }
-
 
293
 
-
 
294
  /* normalize filename */
-
 
295
  if (file_truename(fname, res) != 0) return(-2);
-
 
296
 
-
 
297
  /* printf("truename: %s\r\n", res); */
-
 
298
 
-
 
299
  /* figure out where the command starts and if it has an explicit extension */
-
 
300
  for (len = 0; res[len] != 0; len++) {
-
 
301
    switch (res[len]) {
-
 
302
      case '?':   /* abort on any wildcard character */
-
 
303
      case '*':
-
 
304
        return(-2);
-
 
305
      case '\\':
-
 
306
        lastbslash = len;
-
 
307
        break;
-
 
308
    }
-
 
309
  }
-
 
310
 
-
 
311
  /* printf("lastbslash=%u\r\n", lastbslash); */
-
 
312
 
-
 
313
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
-
 
314
  if (!explicitpath) {
-
 
315
    if (path != NULL) {
-
 
316
      i = strlen(path);
-
 
317
    } else {
-
 
318
      i = 0;
-
 
319
    }
-
 
320
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
-
 
321
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
-
 
322
    if (i != 0) {
-
 
323
      memmove(res, path, i);
-
 
324
      res[i - 1] = '\\';
-
 
325
    }
-
 
326
  }
-
 
327
 
-
 
328
  /* if no extension was initially provided, try matching COM, EXE, BAT */
-
 
329
  if (*extptr == NULL) {
-
 
330
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
-
 
331
    len = strlen(res);
-
 
332
    for (i = 0; ext[i] != NULL; i++) {
-
 
333
      strcpy(res + len, ext[i]);
-
 
334
      /* printf("? '%s'\r\n", res); */
-
 
335
      *extptr = ext[i] + 1;
-
 
336
      if (file_getattr(res) >= 0) return(0);
-
 
337
    }
-
 
338
  } else { /* try finding it as-is */
-
 
339
    /* printf("? '%s'\r\n", res); */
-
 
340
    if (file_getattr(res) >= 0) return(0);
-
 
341
  }
-
 
342
 
-
 
343
  /* not found */
-
 
344
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
-
 
345
  return(-1);
-
 
346
}
-
 
347
 
-
 
348
 
271
static void run_as_external(char *buff, const char far *cmdline) {
349
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg) {
272
  char const **argvlist = (void *)(buff + 512);
350
  char const **argvlist = (void *)(buff + 512);
-
 
351
  char *cmdfile = buff + 1024;
-
 
352
  const char far *pathptr;
-
 
353
  int lookup;
-
 
354
  unsigned short i;
-
 
355
  const char *ext;
273
 
356
 
274
  cmd_explode(buff, cmdline, argvlist);
357
  cmd_explode(buff + 2048, cmdline, argvlist);
275
 
358
 
276
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
359
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
277
 
360
 
-
 
361
  /* is this a command in curdir? */
-
 
362
  lookup = lookup_cmd(cmdfile, argvlist[0], NULL, &ext);
-
 
363
  if (lookup == 0) {
-
 
364
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
-
 
365
    goto RUNCMDFILE;
-
 
366
  } else if (lookup == -2) {
-
 
367
    /* puts("NOT FOUND"); */
-
 
368
    return;
-
 
369
  }
-
 
370
 
-
 
371
  /* try matching something in PATH */
-
 
372
  pathptr = env_lookup_val(envseg, "PATH");
-
 
373
  if (pathptr == NULL) return;
-
 
374
 
-
 
375
  /* try each path in %PATH% */
-
 
376
  for (;;) {
-
 
377
    for (i = 0;; i++) {
-
 
378
      buff[i] = *pathptr;
-
 
379
      if ((buff[i] == 0) || (buff[i] == ';')) break;
-
 
380
      pathptr++;
-
 
381
    }
-
 
382
    buff[i] = 0;
-
 
383
    lookup = lookup_cmd(cmdfile, argvlist[0], buff, &ext);
-
 
384
    if (lookup == 0) break;
-
 
385
    if (lookup == -2) return;
-
 
386
    if (*pathptr == ';') {
-
 
387
      pathptr++;
-
 
388
    } else {
-
 
389
      return;
-
 
390
    }
-
 
391
  }
-
 
392
 
-
 
393
  RUNCMDFILE:
-
 
394
  /* TODO run command through INT 0x21 */
-
 
395
  /* TODO copy environment and append full exec path */
-
 
396
  /* TODO special handling of batch files */
-
 
397
  if ((ext != NULL) && (imatch(ext, "bat"))) {
-
 
398
    outputnl("batch processing not supported yet");
-
 
399
    return;
-
 
400
  }
-
 
401
 
-
 
402
  /* printf("Exec: '%s'\r\n", cmdfile); */
-
 
403
 
278
  /* this call should never return, unless the program failed to be executed */
404
  /* this call should never return, unless the program failed to be executed */
279
  execvp(argvlist[0], argvlist);
405
  execvp(cmdfile, argvlist);
280
}
406
}
281
 
407
 
282
 
408
 
283
static void set_comspec_to_self(unsigned short envseg) {
409
static void set_comspec_to_self(unsigned short envseg) {
284
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
410
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
285
  char far *myenv = MK_FP(*psp_envseg, 0);
411
  char far *myenv = MK_FP(*psp_envseg, 0);
286
  unsigned short varcount;
412
  unsigned short varcount;
287
  char buff[256] = "COMSPEC=";
413
  char buff[256] = "COMSPEC=";
288
  char *buffptr = buff + 8;
414
  char *buffptr = buff + 8;
289
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
415
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
290
  while (*myenv != 0) {
416
  while (*myenv != 0) {
291
    /* consume a NULL-terminated string */
417
    /* consume a NULL-terminated string */
292
    while (*myenv != 0) myenv++;
418
    while (*myenv != 0) myenv++;
293
    /* move to next string */
419
    /* move to next string */
294
    myenv++;
420
    myenv++;
295
  }
421
  }
296
  /* get next word, if 1 then EXEPATH follows */
422
  /* get next word, if 1 then EXEPATH follows */
297
  myenv++;
423
  myenv++;
298
  varcount = *myenv;
424
  varcount = *myenv;
299
  myenv++;
425
  myenv++;
300
  varcount |= (*myenv << 8);
426
  varcount |= (*myenv << 8);
301
  myenv++;
427
  myenv++;
302
  if (varcount != 1) return; /* NO EXEPATH FOUND */
428
  if (varcount != 1) return; /* NO EXEPATH FOUND */
303
  while (*myenv != 0) {
429
  while (*myenv != 0) {
304
    *buffptr = *myenv;
430
    *buffptr = *myenv;
305
    buffptr++;
431
    buffptr++;
306
    myenv++;
432
    myenv++;
307
  }
433
  }
308
  *buffptr = 0;
434
  *buffptr = 0;
309
  /* printf("EXEPATH: '%s'\r\n", buff); */
435
  /* printf("EXEPATH: '%s'\r\n", buff); */
310
  env_setvar(envseg, buff);
436
  env_setvar(envseg, buff);
311
}
437
}
312
 
438
 
313
 
439
 
314
/* wait for user input */
440
/* wait for user input */
315
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
441
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
316
  _asm {
442
  _asm {
317
    push ax
443
    push ax
318
    push bx
444
    push bx
319
    push cx
445
    push cx
320
    push dx
446
    push dx
321
    push ds
447
    push ds
322
 
448
 
323
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
449
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
324
    mov ax, 0x4800
450
    mov ax, 0x4800
325
    int 0x2f
451
    int 0x2f
326
    mov bl, al /* save doskey status in BL */
452
    mov bl, al /* save doskey status in BL */
327
 
453
 
328
    /* set up buffered input to inpseg:inpoff */
454
    /* set up buffered input to inpseg:inpoff */
329
    mov ax, inpseg
455
    mov ax, inpseg
330
    push ax
456
    push ax
331
    pop ds
457
    pop ds
332
    mov dx, inpoff
458
    mov dx, inpoff
333
 
459
 
334
    /* execute either DOS input or DOSKEY */
460
    /* execute either DOS input or DOSKEY */
335
    test bl, bl /* zf set if no DOSKEY present */
461
    test bl, bl /* zf set if no DOSKEY present */
336
    jnz DOSKEY
462
    jnz DOSKEY
337
 
463
 
338
    mov ah, 0x0a
464
    mov ah, 0x0a
339
    int 0x21
465
    int 0x21
340
    jmp short DONE
466
    jmp short DONE
341
 
467
 
342
    DOSKEY:
468
    DOSKEY:
343
    mov ax, 0x4810
469
    mov ax, 0x4810
344
    int 0x2f
470
    int 0x2f
345
 
471
 
346
    DONE:
472
    DONE:
347
    /* terminate command with a CR/LF */
473
    /* terminate command with a CR/LF */
348
    mov ah, 0x02 /* display character in dl */
474
    mov ah, 0x02 /* display character in dl */
349
    mov dl, 0x0d
475
    mov dl, 0x0d
350
    int 0x21
476
    int 0x21
351
    mov dl, 0x0a
477
    mov dl, 0x0a
352
    int 0x21
478
    int 0x21
353
 
479
 
354
    pop ds
480
    pop ds
355
    pop dx
481
    pop dx
356
    pop cx
482
    pop cx
357
    pop bx
483
    pop bx
358
    pop ax
484
    pop ax
359
  }
485
  }
360
}
486
}
361
 
487
 
362
 
488
 
363
int main(void) {
489
int main(void) {
364
  static struct config cfg;
490
  static struct config cfg;
365
  static unsigned short rmod_seg;
491
  static unsigned short rmod_seg;
366
  static unsigned short far *rmod_envseg;
492
  static unsigned short far *rmod_envseg;
367
  static unsigned short far *lastexitcode;
493
  static unsigned short far *lastexitcode;
368
  static unsigned char BUFFER[4096];
494
  static unsigned char BUFFER[4096];
369
  static struct rmod_props far *rmod;
495
  static struct rmod_props far *rmod;
370
 
496
 
371
  parse_argv(&cfg);
497
  parse_argv(&cfg);
372
 
498
 
373
  rmod = rmod_find();
499
  rmod = rmod_find();
374
  if (rmod == NULL) {
500
  if (rmod == NULL) {
375
    rmod = rmod_install(cfg.envsiz);
501
    rmod = rmod_install(cfg.envsiz);
376
    if (rmod == NULL) {
502
    if (rmod == NULL) {
377
      outputnl("ERROR: rmod_install() failed");
503
      outputnl("ERROR: rmod_install() failed");
378
      return(1);
504
      return(1);
379
    }
505
    }
380
    /* copy flags to rmod's storage */
506
    /* copy flags to rmod's storage */
381
    rmod->flags = cfg.flags;
507
    rmod->flags = cfg.flags;
382
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
508
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
383
  } else {
509
  } else {
384
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
510
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
385
  }
511
  }
386
 
512
 
387
  rmod_seg = rmod->rmodseg;
513
  rmod_seg = rmod->rmodseg;
388
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
514
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
389
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
515
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
390
 
516
 
391
  /* make COMPSEC point to myself */
517
  /* make COMPSEC point to myself */
392
  set_comspec_to_self(*rmod_envseg);
518
  set_comspec_to_self(*rmod_envseg);
393
 
519
 
394
/*  {
520
/*  {
395
    unsigned short envsiz;
521
    unsigned short envsiz;
396
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
522
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
397
    envsiz = *sizptr;
523
    envsiz = *sizptr;
398
    envsiz *= 16;
524
    envsiz *= 16;
399
    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);
525
    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);
400
  }*/
526
  }*/
401
 
527
 
402
  do {
528
  do {
403
    char far *cmdline = rmod->inputbuf + 2;
529
    char far *cmdline = rmod->inputbuf + 2;
404
 
530
 
405
    /* (re)load translation strings if needed */
531
    /* (re)load translation strings if needed */
406
    nls_langreload(BUFFER, *rmod_envseg);
532
    nls_langreload(BUFFER, *rmod_envseg);
407
 
533
 
408
    /* skip user input if I have a command to exec (/C or /K) */
534
    /* skip user input if I have a command to exec (/C or /K) */
409
    if (cfg.execcmd != NULL) {
535
    if (cfg.execcmd != NULL) {
410
      cmdline = cfg.execcmd;
536
      cmdline = cfg.execcmd;
411
      cfg.execcmd = NULL;
537
      cfg.execcmd = NULL;
412
      goto EXEC_CMDLINE;
538
      goto EXEC_CMDLINE;
413
    }
539
    }
414
 
540
 
415
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
541
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
416
 
542
 
417
    SKIP_NEWLINE:
543
    SKIP_NEWLINE:
418
 
544
 
419
    /* print shell prompt (only if ECHO is enabled) */
545
    /* print shell prompt (only if ECHO is enabled) */
420
    if (rmod->echoflag != 0) {
546
    if (rmod->echoflag != 0) {
421
      char *promptptr = BUFFER;
547
      char *promptptr = BUFFER;
422
      buildprompt(promptptr, *rmod_envseg);
548
      buildprompt(promptptr, *rmod_envseg);
423
      _asm {
549
      _asm {
424
        push ax
550
        push ax
425
        push dx
551
        push dx
426
        mov ah, 0x09
552
        mov ah, 0x09
427
        mov dx, promptptr
553
        mov dx, promptptr
428
        int 0x21
554
        int 0x21
429
        pop dx
555
        pop dx
430
        pop ax
556
        pop ax
431
      }
557
      }
432
    }
558
    }
433
 
559
 
434
    /* revert input history terminator to \r */
560
    /* revert input history terminator to \r */
435
    if (cmdline[-1] != 0) {
561
    if (cmdline[-1] != 0) {
436
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
562
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
437
    }
563
    }
438
 
564
 
439
    /* wait for user command line */
565
    /* wait for user command line */
440
    cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
566
    cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
441
 
567
 
442
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
568
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
443
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
569
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
444
 
570
 
445
    /* replace \r by a zero terminator */
571
    /* replace \r by a zero terminator */
446
    cmdline[(unsigned char)(cmdline[-1])] = 0;
572
    cmdline[(unsigned char)(cmdline[-1])] = 0;
447
 
573
 
448
    /* I jump here when I need to exec an initial command (/C or /K) */
574
    /* I jump here when I need to exec an initial command (/C or /K) */
449
    EXEC_CMDLINE:
575
    EXEC_CMDLINE:
450
 
576
 
451
    /* move pointer forward to skip over any leading spaces */
577
    /* move pointer forward to skip over any leading spaces */
452
    while (*cmdline == ' ') cmdline++;
578
    while (*cmdline == ' ') cmdline++;
453
 
579
 
454
    /* update rmod's ptr to COMPSPEC so it is always up to date */
580
    /* update rmod's ptr to COMPSPEC so it is always up to date */
455
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
581
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
456
 
582
 
457
    /* handle redirections (if any) */
583
    /* handle redirections (if any) */
458
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
584
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
459
      outputnl("");
585
      outputnl("");
460
      continue;
586
      continue;
461
    }
587
    }
462
 
588
 
463
    /* try matching (and executing) an internal command */
589
    /* try matching (and executing) an internal command */
464
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
590
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
465
      /* internal command executed */
591
      /* internal command executed */
466
      redir_revert(); /* revert stdout (in case it was redirected) */
592
      redir_revert(); /* revert stdout (in case it was redirected) */
467
      continue;
593
      continue;
468
    }
594
    }
469
 
595
 
470
    /* if here, then this was not an internal command */
596
    /* if here, then this was not an internal command */
471
    run_as_external(BUFFER, cmdline);
597
    run_as_external(BUFFER, cmdline, *rmod_envseg);
472
 
598
 
473
    /* revert stdout (in case it was redirected) */
599
    /* revert stdout (in case it was redirected) */
474
    redir_revert();
600
    redir_revert();
475
 
601
 
476
    /* execvp() replaces the current process by the new one
602
    /* execvp() replaces the current process by the new one
477
    if I am still alive then external command failed to execute */
603
    if I am still alive then external command failed to execute */
478
    outputnl("Bad command or file name");
604
    outputnl("Bad command or file name");
479
 
605
 
480
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
606
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
481
 
607
 
482
  sayonara(rmod);
608
  sayonara(rmod);
483
  return(0);
609
  return(0);
484
}
610
}
485
 
611