Subversion Repositories SvarDOS

Rev

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

Rev 472 Rev 474
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
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
166
static void buildprompt(char *s, unsigned short envseg) {
167
static void build_and_display_prompt(char *buff, unsigned short envseg) {
-
 
168
  char *s = buff;
167
  /* locate the prompt variable or use the default pattern */
169
  /* locate the prompt variable or use the default pattern */
168
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
170
  const char far *fmt = env_lookup_val(envseg, "PROMPT");
169
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
171
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
170
  /* build the prompt string based on pattern */
172
  /* build the prompt string based on pattern */
171
  for (; *fmt != 0; fmt++) {
173
  for (; *fmt != 0; fmt++) {
172
    if (*fmt != '$') {
174
    if (*fmt != '$') {
173
      *s = *fmt;
175
      *s = *fmt;
174
      s++;
176
      s++;
175
      continue;
177
      continue;
176
    }
178
    }
177
    /* escape code ($P, etc) */
179
    /* escape code ($P, etc) */
178
    fmt++;
180
    fmt++;
179
    switch (*fmt) {
181
    switch (*fmt) {
180
      case 'Q':  /* $Q = = (equal sign) */
182
      case 'Q':  /* $Q = = (equal sign) */
181
      case 'q':
183
      case 'q':
182
        *s = '=';
184
        *s = '=';
183
        s++;
185
        s++;
184
        break;
186
        break;
185
      case '$':  /* $$ = $ (dollar sign) */
187
      case '$':  /* $$ = $ (dollar sign) */
186
        *s = '$';
188
        *s = '$';
187
        s++;
189
        s++;
188
        break;
190
        break;
189
      case 'T':  /* $t = current time */
191
      case 'T':  /* $t = current time */
190
      case 't':
192
      case 't':
191
        s += sprintf(s, "00:00"); /* TODO */
193
        s += sprintf(s, "00:00"); /* TODO */
192
        break;
194
        break;
193
      case 'D':  /* $D = current date */
195
      case 'D':  /* $D = current date */
194
      case 'd':
196
      case 'd':
195
        s += sprintf(s, "1985-07-29"); /* TODO */
197
        s += sprintf(s, "1985-07-29"); /* TODO */
196
        break;
198
        break;
197
      case 'P':  /* $P = current drive and path */
199
      case 'P':  /* $P = current drive and path */
198
      case 'p':
200
      case 'p':
199
        _asm {
201
        _asm {
200
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
202
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
201
          int 0x21
203
          int 0x21
202
          mov bx, s
204
          mov bx, s
203
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
205
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
204
        }
206
        }
205
        *s += 'A';
207
        *s += 'A';
206
        s++;
208
        s++;
207
        *s = ':';
209
        *s = ':';
208
        s++;
210
        s++;
209
        *s = '\\';
211
        *s = '\\';
210
        s++;
212
        s++;
211
        _asm {
213
        _asm {
212
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
214
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
213
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
215
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
214
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
216
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
215
          int 0x21
217
          int 0x21
216
        }
218
        }
217
        while (*s != 0) s++; /* move ptr forward to end of pathname */
219
        while (*s != 0) s++; /* move ptr forward to end of pathname */
218
        break;
220
        break;
219
      case 'V':  /* $V = DOS version number */
221
      case 'V':  /* $V = DOS version number */
220
      case 'v':
222
      case 'v':
221
        s += sprintf(s, "VER"); /* TODO */
223
        s += sprintf(s, "VER"); /* TODO */
222
        break;
224
        break;
223
      case 'N':  /* $N = current drive */
225
      case 'N':  /* $N = current drive */
224
      case 'n':
226
      case 'n':
225
        _asm {
227
        _asm {
226
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
228
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
227
          int 0x21
229
          int 0x21
228
          mov bx, s
230
          mov bx, s
229
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
231
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
230
        }
232
        }
231
        *s += 'A';
233
        *s += 'A';
232
        s++;
234
        s++;
233
        break;
235
        break;
234
      case 'G':  /* $G = > (greater-than sign) */
236
      case 'G':  /* $G = > (greater-than sign) */
235
      case 'g':
237
      case 'g':
236
        *s = '>';
238
        *s = '>';
237
        s++;
239
        s++;
238
        break;
240
        break;
239
      case 'L':  /* $L = < (less-than sign) */
241
      case 'L':  /* $L = < (less-than sign) */
240
      case 'l':
242
      case 'l':
241
        *s = '<';
243
        *s = '<';
242
        s++;
244
        s++;
243
        break;
245
        break;
244
      case 'B':  /* $B = | (pipe) */
246
      case 'B':  /* $B = | (pipe) */
245
      case 'b':
247
      case 'b':
246
        *s = '|';
248
        *s = '|';
247
        s++;
249
        s++;
248
        break;
250
        break;
249
      case 'H':  /* $H = backspace (erases previous character) */
251
      case 'H':  /* $H = backspace (erases previous character) */
250
      case 'h':
252
      case 'h':
251
        *s = '\b';
253
        *s = '\b';
252
        s++;
254
        s++;
253
        break;
255
        break;
254
      case 'E':  /* $E = Escape code (ASCII 27) */
256
      case 'E':  /* $E = Escape code (ASCII 27) */
255
      case 'e':
257
      case 'e':
256
        *s = 27;
258
        *s = 27;
257
        s++;
259
        s++;
258
        break;
260
        break;
259
      case '_':  /* $_ = CR+LF */
261
      case '_':  /* $_ = CR+LF */
260
        *s = '\r';
262
        *s = '\r';
261
        s++;
263
        s++;
262
        *s = '\n';
264
        *s = '\n';
263
        s++;
265
        s++;
264
        break;
266
        break;
265
    }
267
    }
266
  }
268
  }
267
  *s = '$';
269
  *s = 0;
-
 
270
  output(buff);
268
}
271
}
269
 
272
 
270
 
273
 
271
/* tries locating executable fname in path and fille res with result. returns 0 on success,
274
/* 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"
275
 * -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) */
276
 * 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) {
277
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
275
  unsigned short lastbslash = 0xffff;
278
  unsigned short lastbslash = 0xffff;
276
  unsigned short i, len;
279
  unsigned short i, len;
277
  unsigned char explicitpath = 0;
280
  unsigned char explicitpath = 0;
278
 
281
 
279
  /* does the original fname had an explicit path prefix or explicit ext? */
282
  /* does the original fname had an explicit path prefix or explicit ext? */
280
  *extptr = NULL;
283
  *extptr = NULL;
281
  for (i = 0; fname[i] != 0; i++) {
284
  for (i = 0; fname[i] != 0; i++) {
282
    switch (fname[i]) {
285
    switch (fname[i]) {
283
      case ':':
286
      case ':':
284
      case '\\':
287
      case '\\':
285
        explicitpath = 1;
288
        explicitpath = 1;
286
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
289
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
287
        break;
290
        break;
288
      case '.':
291
      case '.':
289
        *extptr = fname + i + 1;
292
        *extptr = fname + i + 1;
290
        break;
293
        break;
291
    }
294
    }
292
  }
295
  }
293
 
296
 
294
  /* normalize filename */
297
  /* normalize filename */
295
  if (file_truename(fname, res) != 0) return(-2);
298
  if (file_truename(fname, res) != 0) return(-2);
296
 
299
 
297
  /* printf("truename: %s\r\n", res); */
300
  /* printf("truename: %s\r\n", res); */
298
 
301
 
299
  /* figure out where the command starts and if it has an explicit extension */
302
  /* figure out where the command starts and if it has an explicit extension */
300
  for (len = 0; res[len] != 0; len++) {
303
  for (len = 0; res[len] != 0; len++) {
301
    switch (res[len]) {
304
    switch (res[len]) {
302
      case '?':   /* abort on any wildcard character */
305
      case '?':   /* abort on any wildcard character */
303
      case '*':
306
      case '*':
304
        return(-2);
307
        return(-2);
305
      case '\\':
308
      case '\\':
306
        lastbslash = len;
309
        lastbslash = len;
307
        break;
310
        break;
308
    }
311
    }
309
  }
312
  }
310
 
313
 
311
  /* printf("lastbslash=%u\r\n", lastbslash); */
314
  /* printf("lastbslash=%u\r\n", lastbslash); */
312
 
315
 
313
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
316
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
314
  if (!explicitpath) {
317
  if (!explicitpath) {
315
    if (path != NULL) {
318
    if (path != NULL) {
316
      i = strlen(path);
319
      i = strlen(path);
317
    } else {
320
    } else {
318
      i = 0;
321
      i = 0;
319
    }
322
    }
320
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
323
    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);
324
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
322
    if (i != 0) {
325
    if (i != 0) {
323
      memmove(res, path, i);
326
      memmove(res, path, i);
324
      res[i - 1] = '\\';
327
      res[i - 1] = '\\';
325
    }
328
    }
326
  }
329
  }
327
 
330
 
328
  /* if no extension was initially provided, try matching COM, EXE, BAT */
331
  /* if no extension was initially provided, try matching COM, EXE, BAT */
329
  if (*extptr == NULL) {
332
  if (*extptr == NULL) {
330
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
333
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
331
    len = strlen(res);
334
    len = strlen(res);
332
    for (i = 0; ext[i] != NULL; i++) {
335
    for (i = 0; ext[i] != NULL; i++) {
333
      strcpy(res + len, ext[i]);
336
      strcpy(res + len, ext[i]);
334
      /* printf("? '%s'\r\n", res); */
337
      /* printf("? '%s'\r\n", res); */
335
      *extptr = ext[i] + 1;
338
      *extptr = ext[i] + 1;
336
      if (file_getattr(res) >= 0) return(0);
339
      if (file_getattr(res) >= 0) return(0);
337
    }
340
    }
338
  } else { /* try finding it as-is */
341
  } else { /* try finding it as-is */
339
    /* printf("? '%s'\r\n", res); */
342
    /* printf("? '%s'\r\n", res); */
340
    if (file_getattr(res) >= 0) return(0);
343
    if (file_getattr(res) >= 0) return(0);
341
  }
344
  }
342
 
345
 
343
  /* not found */
346
  /* not found */
344
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
347
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
345
  return(-1);
348
  return(-1);
346
}
349
}
347
 
350
 
348
 
351
 
349
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
352
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
350
  char *cmdfile = buff + 512;
353
  char *cmdfile = buff + 512;
351
  const char far *pathptr;
354
  const char far *pathptr;
352
  int lookup;
355
  int lookup;
353
  unsigned short i;
356
  unsigned short i;
354
  const char *ext;
357
  const char *ext;
355
  char *cmd = buff + 256;
358
  char *cmd = buff + 256;
356
  const char far *cmdtail;
359
  const char far *cmdtail;
357
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
360
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
358
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
361
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
359
  _Packed struct {
362
  _Packed struct {
360
    unsigned short envseg;
363
    unsigned short envseg;
361
    unsigned long cmdtail;
364
    unsigned long cmdtail;
362
    unsigned long fcb1;
365
    unsigned long fcb1;
363
    unsigned long fcb2;
366
    unsigned long fcb2;
364
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
367
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
365
 
368
 
366
  /* find cmd and cmdtail */
369
  /* find cmd and cmdtail */
367
  i = 0;
370
  i = 0;
368
  cmdtail = cmdline;
371
  cmdtail = cmdline;
369
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
372
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
370
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
373
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
371
    cmd[i++] = *cmdtail;
374
    cmd[i++] = *cmdtail;
372
    cmdtail++;
375
    cmdtail++;
373
  }
376
  }
374
  cmd[i] = 0;
377
  cmd[i] = 0;
375
 
378
 
376
  /* is this a command in curdir? */
379
  /* is this a command in curdir? */
377
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
380
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
378
  if (lookup == 0) {
381
  if (lookup == 0) {
379
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
382
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
380
    goto RUNCMDFILE;
383
    goto RUNCMDFILE;
381
  } else if (lookup == -2) {
384
  } else if (lookup == -2) {
382
    /* puts("NOT FOUND"); */
385
    /* puts("NOT FOUND"); */
383
    return;
386
    return;
384
  }
387
  }
385
 
388
 
386
  /* try matching something in PATH */
389
  /* try matching something in PATH */
387
  pathptr = env_lookup_val(envseg, "PATH");
390
  pathptr = env_lookup_val(envseg, "PATH");
388
  if (pathptr == NULL) return;
391
  if (pathptr == NULL) return;
389
 
392
 
390
  /* try each path in %PATH% */
393
  /* try each path in %PATH% */
391
  for (;;) {
394
  for (;;) {
392
    for (i = 0;; i++) {
395
    for (i = 0;; i++) {
393
      buff[i] = *pathptr;
396
      buff[i] = *pathptr;
394
      if ((buff[i] == 0) || (buff[i] == ';')) break;
397
      if ((buff[i] == 0) || (buff[i] == ';')) break;
395
      pathptr++;
398
      pathptr++;
396
    }
399
    }
397
    buff[i] = 0;
400
    buff[i] = 0;
398
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
401
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
399
    if (lookup == 0) break;
402
    if (lookup == 0) break;
400
    if (lookup == -2) return;
403
    if (lookup == -2) return;
401
    if (*pathptr == ';') {
404
    if (*pathptr == ';') {
402
      pathptr++;
405
      pathptr++;
403
    } else {
406
    } else {
404
      return;
407
      return;
405
    }
408
    }
406
  }
409
  }
407
 
410
 
408
  RUNCMDFILE:
411
  RUNCMDFILE:
409
 
412
 
410
  /* special handling of batch files */
413
  /* special handling of batch files */
411
  if ((ext != NULL) && (imatch(ext, "bat"))) {
414
  if ((ext != NULL) && (imatch(ext, "bat"))) {
412
    /* copy truename of the bat file to rmod buff */
415
    /* copy truename of the bat file to rmod buff */
413
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
416
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
414
    rmod->batfile[i] = 0;
417
    rmod->batfile[i] = 0;
415
    /* copy args of the bat file to rmod buff */
418
    /* copy args of the bat file to rmod buff */
416
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
419
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
417
    /* reset the 'next line to execute' counter */
420
    /* reset the 'next line to execute' counter */
418
    rmod->batnextline = 0;
421
    rmod->batnextline = 0;
-
 
422
    /* remember the echo flag (in case bat file disables echo) */
-
 
423
    rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
-
 
424
    if (rmod->echoflag) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
419
    return;
425
    return;
420
  }
426
  }
421
 
427
 
422
  /* copy full filename to execute */
428
  /* copy full filename to execute */
423
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
429
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
424
  rmod_execprog[i] = 0;
430
  rmod_execprog[i] = 0;
425
 
431
 
426
  /* copy cmdtail to rmod's PSP and compute its len */
432
  /* copy cmdtail to rmod's PSP and compute its len */
427
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
433
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
428
  rmod_cmdtail[i] = '\r';
434
  rmod_cmdtail[i] = '\r';
429
  rmod_cmdtail[-1] = i;
435
  rmod_cmdtail[-1] = i;
430
 
436
 
431
  /* set up rmod to execute the command */
437
  /* set up rmod to execute the command */
432
 
438
 
433
  ExecParam->envseg = envseg;
439
  ExecParam->envseg = envseg;
434
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
440
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
435
  ExecParam->fcb1 = 0; /* TODO farptr */
441
  ExecParam->fcb1 = 0; /* TODO farptr */
436
  ExecParam->fcb2 = 0; /* TODO farptr */
442
  ExecParam->fcb2 = 0; /* TODO farptr */
437
  exit(0); /* let rmod do the job now */
443
  exit(0); /* let rmod do the job now */
438
}
444
}
439
 
445
 
440
 
446
 
441
static void set_comspec_to_self(unsigned short envseg) {
447
static void set_comspec_to_self(unsigned short envseg) {
442
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
448
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
443
  char far *myenv = MK_FP(*psp_envseg, 0);
449
  char far *myenv = MK_FP(*psp_envseg, 0);
444
  unsigned short varcount;
450
  unsigned short varcount;
445
  char buff[256] = "COMSPEC=";
451
  char buff[256] = "COMSPEC=";
446
  char *buffptr = buff + 8;
452
  char *buffptr = buff + 8;
447
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
453
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
448
  while (*myenv != 0) {
454
  while (*myenv != 0) {
449
    /* consume a NULL-terminated string */
455
    /* consume a NULL-terminated string */
450
    while (*myenv != 0) myenv++;
456
    while (*myenv != 0) myenv++;
451
    /* move to next string */
457
    /* move to next string */
452
    myenv++;
458
    myenv++;
453
  }
459
  }
454
  /* get next word, if 1 then EXEPATH follows */
460
  /* get next word, if 1 then EXEPATH follows */
455
  myenv++;
461
  myenv++;
456
  varcount = *myenv;
462
  varcount = *myenv;
457
  myenv++;
463
  myenv++;
458
  varcount |= (*myenv << 8);
464
  varcount |= (*myenv << 8);
459
  myenv++;
465
  myenv++;
460
  if (varcount != 1) return; /* NO EXEPATH FOUND */
466
  if (varcount != 1) return; /* NO EXEPATH FOUND */
461
  while (*myenv != 0) {
467
  while (*myenv != 0) {
462
    *buffptr = *myenv;
468
    *buffptr = *myenv;
463
    buffptr++;
469
    buffptr++;
464
    myenv++;
470
    myenv++;
465
  }
471
  }
466
  *buffptr = 0;
472
  *buffptr = 0;
467
  /* printf("EXEPATH: '%s'\r\n", buff); */
473
  /* printf("EXEPATH: '%s'\r\n", buff); */
468
  env_setvar(envseg, buff);
474
  env_setvar(envseg, buff);
469
}
475
}
470
 
476
 
471
 
477
 
472
/* wait for user input */
478
/* wait for user input */
473
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
479
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
474
  _asm {
480
  _asm {
475
    push ax
481
    push ax
476
    push bx
482
    push bx
477
    push cx
483
    push cx
478
    push dx
484
    push dx
479
    push ds
485
    push ds
480
 
486
 
481
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
487
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
482
    mov ax, 0x4800
488
    mov ax, 0x4800
483
    int 0x2f
489
    int 0x2f
484
    mov bl, al /* save doskey status in BL */
490
    mov bl, al /* save doskey status in BL */
485
 
491
 
486
    /* set up buffered input to inpseg:inpoff */
492
    /* set up buffered input to inpseg:inpoff */
487
    mov ax, inpseg
493
    mov ax, inpseg
488
    push ax
494
    push ax
489
    pop ds
495
    pop ds
490
    mov dx, inpoff
496
    mov dx, inpoff
491
 
497
 
492
    /* execute either DOS input or DOSKEY */
498
    /* execute either DOS input or DOSKEY */
493
    test bl, bl /* zf set if no DOSKEY present */
499
    test bl, bl /* zf set if no DOSKEY present */
494
    jnz DOSKEY
500
    jnz DOSKEY
495
 
501
 
496
    mov ah, 0x0a
502
    mov ah, 0x0a
497
    int 0x21
503
    int 0x21
498
    jmp short DONE
504
    jmp short DONE
499
 
505
 
500
    DOSKEY:
506
    DOSKEY:
501
    mov ax, 0x4810
507
    mov ax, 0x4810
502
    int 0x2f
508
    int 0x2f
503
 
509
 
504
    DONE:
510
    DONE:
505
    /* terminate command with a CR/LF */
511
    /* terminate command with a CR/LF */
506
    mov ah, 0x02 /* display character in dl */
512
    mov ah, 0x02 /* display character in dl */
507
    mov dl, 0x0d
513
    mov dl, 0x0d
508
    int 0x21
514
    int 0x21
509
    mov dl, 0x0a
515
    mov dl, 0x0a
510
    int 0x21
516
    int 0x21
511
 
517
 
512
    pop ds
518
    pop ds
513
    pop dx
519
    pop dx
514
    pop cx
520
    pop cx
515
    pop bx
521
    pop bx
516
    pop ax
522
    pop ax
517
  }
523
  }
518
}
524
}
519
 
525
 
520
 
526
 
521
/* fetches a line from batch file and write it to buff, increments
527
/* fetches a line from batch file and write it to buff, increments
522
 * rmod counter on success. returns NULL on failure.
528
 * rmod counter on success. returns 0 on success.
523
 * buff must start with a length byte but the returned pointer must
529
 * buff starts with a length byte and is NULL-terminated */
524
 * skip it. */
-
 
525
static char far *getbatcmd(char *buff, struct rmod_props far *rmod) {
530
static int getbatcmd(char *buff, struct rmod_props far *rmod) {
526
  unsigned short i;
531
  unsigned short i;
-
 
532
  unsigned short batname_seg = FP_SEG(rmod->batfile);
-
 
533
  unsigned short batname_off = FP_OFF(rmod->batfile);
-
 
534
  unsigned short filepos_cx = rmod->batnextline >> 16;
-
 
535
  unsigned short filepos_dx = rmod->batnextline & 0xffff;
-
 
536
  unsigned char blen = 0;
-
 
537
 
527
  buff++; /* make room for the len byte */
538
  buff++; /* make room for the len byte prefix */
-
 
539
 
-
 
540
  /* open file, jump to offset filpos, and read data into buff.
528
  /* TODO temporary hack to display a dummy message */
541
   * result in blen (unchanged if EOF or failure). */
-
 
542
  _asm {
-
 
543
    push ax
-
 
544
    push bx
-
 
545
    push cx
-
 
546
    push dx
-
 
547
 
529
  if (rmod->batnextline == 0) {
548
    /* open file (read-only) */
-
 
549
    mov dx, batname_off
-
 
550
    mov ax, batname_seg
-
 
551
    push ds     /* save DS */
-
 
552
    mov ds, ax
-
 
553
    mov ax, 0x3d00
530
    char *msg = "ECHO batch files not supported yet";
554
    int 0x21    /* handle in ax on success */
-
 
555
    pop ds      /* restore DS */
-
 
556
    jc DONE
531
    for (i = 0; msg[i] != 0; i++) buff[i] = msg[i];
557
    mov bx, ax  /* save handle to bx */
-
 
558
 
-
 
559
    /* jump to file offset CX:DX */
532
    buff[i] = 0;
560
    mov ax, 0x4200
-
 
561
    mov cx, filepos_cx
-
 
562
    mov dx, filepos_dx
-
 
563
    int 0x21  /* CF clear on success, DX:AX set to cur pos */
-
 
564
    jc CLOSEANDQUIT
-
 
565
 
-
 
566
    /* read the line into buff */
533
    buff[-1] = i;
567
    mov ah, 0x3f
534
  } else {
568
    mov cx, 255
-
 
569
    mov dx, buff
-
 
570
    int 0x21 /* CF clear on success, AX=number of bytes read */
-
 
571
    jc CLOSEANDQUIT
535
    rmod->batfile[0] = 0;
572
    mov blen, al
-
 
573
 
536
    return(NULL);
574
    CLOSEANDQUIT:
-
 
575
    /* close file (handle in bx) */
-
 
576
    mov ah, 0x3e
-
 
577
    int 0x21
-
 
578
 
-
 
579
    DONE:
-
 
580
    pop dx
-
 
581
    pop cx
-
 
582
    pop bx
-
 
583
    pop ax
537
  }
584
  }
538
  /* open file */
-
 
539
  /* read until awaiting line */
-
 
540
  /* copy line to buff */
-
 
541
  /* close file */
-
 
542
  /* */
-
 
543
  rmod->batnextline++;
-
 
544
  if (rmod->batnextline == 0) rmod->batfile[0] = 0; /* max line count reached */
-
 
545
 
585
 
-
 
586
  /* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
-
 
587
 
546
  /* output command on screen if echo on */
588
  /* on EOF - abort processing the bat file */
547
  if (rmod->echoflag != 0) outputnl(buff);
589
  if (blen == 0) goto OOPS;
548
 
590
 
-
 
591
  /* find nearest \n to inc batch offset and replace \r by NULL terminator
-
 
592
   * I support all CR/LF, CR- and LF-terminated batch files */
-
 
593
  for (i = 0; i < blen; i++) {
-
 
594
    if ((buff[i] == '\r') || (buff[i] == '\n')) {
-
 
595
      if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->batnextline += 1;
-
 
596
      break;
-
 
597
    }
-
 
598
  }
-
 
599
  buff[i] = 0;
-
 
600
  buff[-1] = i;
-
 
601
  rmod->batnextline += i + 1;
-
 
602
 
-
 
603
  return(0);
-
 
604
 
-
 
605
  OOPS:
-
 
606
  rmod->batfile[0] = 0;
-
 
607
  rmod->batnextline = 0;
549
  return(buff);
608
  return(-1);
550
}
609
}
551
 
610
 
552
 
611
 
553
int main(void) {
612
int main(void) {
554
  static struct config cfg;
613
  static struct config cfg;
555
  static unsigned short far *rmod_envseg;
614
  static unsigned short far *rmod_envseg;
556
  static unsigned short far *lastexitcode;
615
  static unsigned short far *lastexitcode;
557
  static unsigned char BUFFER[4096];
616
  static unsigned char BUFFER[4096];
558
  static struct rmod_props far *rmod;
617
  static struct rmod_props far *rmod;
559
 
618
 
560
  rmod = rmod_find();
619
  rmod = rmod_find();
561
  if (rmod == NULL) {
620
  if (rmod == NULL) {
562
    rmod = rmod_install(cfg.envsiz);
621
    rmod = rmod_install(cfg.envsiz);
563
    if (rmod == NULL) {
622
    if (rmod == NULL) {
564
      outputnl("ERROR: rmod_install() failed");
623
      outputnl("ERROR: rmod_install() failed");
565
      return(1);
624
      return(1);
566
    }
625
    }
567
    /* look at command line parameters */
626
    /* look at command line parameters */
568
    parse_argv(&cfg);
627
    parse_argv(&cfg);
569
    /* copy flags to rmod's storage */
628
    /* copy flags to rmod's storage */
570
    rmod->flags = cfg.flags;
629
    rmod->flags = cfg.flags;
571
    /* printf("rmod installed at %Fp\r\n", rmod); */
630
    /* printf("rmod installed at %Fp\r\n", rmod); */
572
  } else {
631
  } else {
573
    /* printf("rmod found at %Fp\r\n", rmod); */
632
    /* printf("rmod found at %Fp\r\n", rmod); */
574
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
633
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
575
     * die asap, because the command has been executed already, so I no longer
634
     * die asap, because the command has been executed already, so I no longer
576
     * have a purpose in life */
635
     * have a purpose in life */
577
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
636
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
578
  }
637
  }
579
 
638
 
580
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
639
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
581
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
640
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
582
 
641
 
583
  /* make COMPSEC point to myself */
642
  /* make COMPSEC point to myself */
584
  set_comspec_to_self(*rmod_envseg);
643
  set_comspec_to_self(*rmod_envseg);
585
 
644
 
586
/*  {
645
/*  {
587
    unsigned short envsiz;
646
    unsigned short envsiz;
588
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
647
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
589
    envsiz = *sizptr;
648
    envsiz = *sizptr;
590
    envsiz *= 16;
649
    envsiz *= 16;
591
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod->rmodseg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
650
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod->rmodseg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
592
  }*/
651
  }*/
593
 
652
 
594
  do {
653
  do {
-
 
654
    char far *cmdline;
-
 
655
 
-
 
656
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
-
 
657
 
-
 
658
    SKIP_NEWLINE:
-
 
659
 
-
 
660
    /* cancel any redirections that may have been set up before */
-
 
661
    redir_revert();
-
 
662
 
595
    char far *cmdline = rmod->inputbuf + 2;
663
    cmdline = rmod->inputbuf + 2;
596
 
664
 
597
    /* (re)load translation strings if needed */
665
    /* (re)load translation strings if needed */
598
    nls_langreload(BUFFER, *rmod_envseg);
666
    nls_langreload(BUFFER, *rmod_envseg);
599
 
667
 
600
    /* skip user input if I have a command to exec (/C or /K) */
668
    /* skip user input if I have a command to exec (/C or /K) */
601
    if (cfg.execcmd != NULL) {
669
    if (cfg.execcmd != NULL) {
602
      cmdline = cfg.execcmd;
670
      cmdline = cfg.execcmd;
603
      cfg.execcmd = NULL;
671
      cfg.execcmd = NULL;
604
      goto EXEC_CMDLINE;
672
      goto EXEC_CMDLINE;
605
    }
673
    }
606
 
674
 
607
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
-
 
608
 
-
 
609
    SKIP_NEWLINE:
-
 
610
 
-
 
611
    /* print shell prompt (only if ECHO is enabled) */
-
 
612
    if (rmod->echoflag != 0) {
-
 
613
      char *promptptr = BUFFER;
-
 
614
      buildprompt(promptptr, *rmod_envseg);
-
 
615
      _asm {
-
 
616
        push ax
-
 
617
        push dx
-
 
618
        mov ah, 0x09
-
 
619
        mov dx, promptptr
-
 
620
        int 0x21
-
 
621
        pop dx
-
 
622
        pop ax
-
 
623
      }
-
 
624
    }
-
 
625
 
-
 
626
    /* revert input history terminator to \r */
-
 
627
    if (cmdline[-1] != 0) {
-
 
628
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
-
 
629
    }
-
 
630
 
-
 
631
    /* if batch file is being executed -> fetch next line */
675
    /* if batch file is being executed -> fetch next line */
632
    if (rmod->batfile[0] != 0) {
676
    if (rmod->batfile[0] != 0) {
633
      cmdline = getbatcmd(BUFFER + sizeof(BUFFER) - 130, rmod);
677
      char *tmpbuff = BUFFER + sizeof(BUFFER) - 256;
-
 
678
      if (getbatcmd(tmpbuff, rmod) != 0) { /* end of batch */
-
 
679
        redir_revert(); /* cancel redirections (if there were any) */
-
 
680
        /* restore echo flag as it was before running the bat file */
-
 
681
        rmod->echoflag = 0;
-
 
682
        if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->echoflag = 1;
-
 
683
        continue;
-
 
684
      }
-
 
685
      /* output prompt and command on screen if echo on and command is not
-
 
686
       * inhibiting it with the @ prefix */
-
 
687
      if ((rmod->echoflag != 0) && (tmpbuff[1] != '@')) {
-
 
688
        build_and_display_prompt(BUFFER, *rmod_envseg);
-
 
689
        outputnl(tmpbuff + 1);
-
 
690
      }
-
 
691
      /* strip the @ prefix if present, it is no longer useful */
-
 
692
      if (tmpbuff[1] == '@') {
-
 
693
        memmove(tmpbuff + 1, tmpbuff + 2, tmpbuff[0] + 1);
-
 
694
        tmpbuff[0] -= 1; /* update the length byte */
-
 
695
      }
634
      if (cmdline == NULL) continue;
696
      cmdline = tmpbuff + 1;
635
    } else {
697
    } else {
636
      /* interactive mode: wait for user command line */
698
      /* interactive mode: display prompt (if echo enabled) and wait for user
-
 
699
       * command line */
-
 
700
      if (rmod->echoflag != 0) build_and_display_prompt(BUFFER, *rmod_envseg);
-
 
701
      /* revert input history terminator to \r so DOS or DOSKEY are not confused */
-
 
702
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
-
 
703
      /* collect user input */
637
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
704
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
-
 
705
      /* replace \r by a zero terminator */
-
 
706
      cmdline[(unsigned char)(cmdline[-1])] = 0;
638
    }
707
    }
639
 
708
 
640
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
709
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
641
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
710
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
642
 
711
 
643
    /* replace \r by a zero terminator */
-
 
644
    cmdline[(unsigned char)(cmdline[-1])] = 0;
-
 
645
 
-
 
646
    /* I jump here when I need to exec an initial command (/C or /K) */
712
    /* I jump here when I need to exec an initial command (/C or /K) */
647
    EXEC_CMDLINE:
713
    EXEC_CMDLINE:
648
 
714
 
649
    /* move pointer forward to skip over any leading spaces */
715
    /* move pointer forward to skip over any leading spaces */
650
    while (*cmdline == ' ') cmdline++;
716
    while (*cmdline == ' ') cmdline++;
651
 
717
 
652
    /* update rmod's ptr to COMPSPEC so it is always up to date */
718
    /* update rmod's ptr to COMPSPEC so it is always up to date */
653
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
719
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
654
 
720
 
655
    /* handle redirections (if any) */
721
    /* handle redirections (if any) */
656
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
722
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
657
      outputnl("");
723
      outputnl("");
658
      continue;
724
      continue;
659
    }
725
    }
660
 
726
 
661
    /* try matching (and executing) an internal command */
727
    /* try matching (and executing) an internal command */
662
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
728
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
663
      /* internal command executed */
729
      /* internal command executed */
664
      redir_revert(); /* revert stdout (in case it was redirected) */
730
      redir_revert(); /* revert stdout (in case it was redirected) */
665
      continue;
731
      continue;
666
    }
732
    }
667
 
733
 
668
    /* if here, then this was not an internal command */
734
    /* if here, then this was not an internal command */
669
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
735
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
670
    /* perhaps this is a newly launched BAT file */
736
    /* perhaps this is a newly launched BAT file */
671
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
737
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
672
 
738
 
673
    /* revert stdout (in case it was redirected) */
739
    /* revert stdout (so the err msg is not redirected) */
674
    redir_revert();
740
    redir_revert();
675
 
741
 
676
    /* run_as_external() does not return on success, if I am still alive then
742
    /* run_as_external() does not return on success, if I am still alive then
677
     * external command failed to execute */
743
     * external command failed to execute */
678
    outputnl("Bad command or file name");
744
    outputnl("Bad command or file name");
679
 
745
 
680
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
746
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
681
 
747
 
682
  sayonara(rmod);
748
  sayonara(rmod);
683
  return(0);
749
  return(0);
684
}
750
}
685
 
751