Subversion Repositories SvarDOS

Rev

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

Rev 470 Rev 472
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,
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"
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) */
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) {
274
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
275
  unsigned short lastbslash = 0xffff;
275
  unsigned short lastbslash = 0xffff;
276
  unsigned short i, len;
276
  unsigned short i, len;
277
  unsigned char explicitpath = 0;
277
  unsigned char explicitpath = 0;
278
 
278
 
279
  /* does the original fname had an explicit path prefix or explicit ext? */
279
  /* does the original fname had an explicit path prefix or explicit ext? */
280
  *extptr = NULL;
280
  *extptr = NULL;
281
  for (i = 0; fname[i] != 0; i++) {
281
  for (i = 0; fname[i] != 0; i++) {
282
    switch (fname[i]) {
282
    switch (fname[i]) {
283
      case ':':
283
      case ':':
284
      case '\\':
284
      case '\\':
285
        explicitpath = 1;
285
        explicitpath = 1;
286
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
286
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
287
        break;
287
        break;
288
      case '.':
288
      case '.':
289
        *extptr = fname + i + 1;
289
        *extptr = fname + i + 1;
290
        break;
290
        break;
291
    }
291
    }
292
  }
292
  }
293
 
293
 
294
  /* normalize filename */
294
  /* normalize filename */
295
  if (file_truename(fname, res) != 0) return(-2);
295
  if (file_truename(fname, res) != 0) return(-2);
296
 
296
 
297
  /* printf("truename: %s\r\n", res); */
297
  /* printf("truename: %s\r\n", res); */
298
 
298
 
299
  /* figure out where the command starts and if it has an explicit extension */
299
  /* figure out where the command starts and if it has an explicit extension */
300
  for (len = 0; res[len] != 0; len++) {
300
  for (len = 0; res[len] != 0; len++) {
301
    switch (res[len]) {
301
    switch (res[len]) {
302
      case '?':   /* abort on any wildcard character */
302
      case '?':   /* abort on any wildcard character */
303
      case '*':
303
      case '*':
304
        return(-2);
304
        return(-2);
305
      case '\\':
305
      case '\\':
306
        lastbslash = len;
306
        lastbslash = len;
307
        break;
307
        break;
308
    }
308
    }
309
  }
309
  }
310
 
310
 
311
  /* printf("lastbslash=%u\r\n", lastbslash); */
311
  /* printf("lastbslash=%u\r\n", lastbslash); */
312
 
312
 
313
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
313
  /* if no path prefix in fname (':' or backslash), then assemble path+filename */
314
  if (!explicitpath) {
314
  if (!explicitpath) {
315
    if (path != NULL) {
315
    if (path != NULL) {
316
      i = strlen(path);
316
      i = strlen(path);
317
    } else {
317
    } else {
318
      i = 0;
318
      i = 0;
319
    }
319
    }
320
    if ((i != 0) && (path[i - 1] != '\\')) i++; /* add a byte for inserting a bkslash after path */
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);
321
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
322
    if (i != 0) {
322
    if (i != 0) {
323
      memmove(res, path, i);
323
      memmove(res, path, i);
324
      res[i - 1] = '\\';
324
      res[i - 1] = '\\';
325
    }
325
    }
326
  }
326
  }
327
 
327
 
328
  /* if no extension was initially provided, try matching COM, EXE, BAT */
328
  /* if no extension was initially provided, try matching COM, EXE, BAT */
329
  if (*extptr == NULL) {
329
  if (*extptr == NULL) {
330
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
330
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
331
    len = strlen(res);
331
    len = strlen(res);
332
    for (i = 0; ext[i] != NULL; i++) {
332
    for (i = 0; ext[i] != NULL; i++) {
333
      strcpy(res + len, ext[i]);
333
      strcpy(res + len, ext[i]);
334
      /* printf("? '%s'\r\n", res); */
334
      /* printf("? '%s'\r\n", res); */
335
      *extptr = ext[i] + 1;
335
      *extptr = ext[i] + 1;
336
      if (file_getattr(res) >= 0) return(0);
336
      if (file_getattr(res) >= 0) return(0);
337
    }
337
    }
338
  } else { /* try finding it as-is */
338
  } else { /* try finding it as-is */
339
    /* printf("? '%s'\r\n", res); */
339
    /* printf("? '%s'\r\n", res); */
340
    if (file_getattr(res) >= 0) return(0);
340
    if (file_getattr(res) >= 0) return(0);
341
  }
341
  }
342
 
342
 
343
  /* not found */
343
  /* not found */
344
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
344
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
345
  return(-1);
345
  return(-1);
346
}
346
}
347
 
347
 
348
 
348
 
349
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
349
static void run_as_external(char *buff, const char far *cmdline, unsigned short envseg, struct rmod_props far *rmod) {
350
  char const **argvlist = (void *)(buff + 512);
-
 
351
  char *cmdfile = buff + 1024;
350
  char *cmdfile = buff + 512;
352
  const char far *pathptr;
351
  const char far *pathptr;
353
  int lookup;
352
  int lookup;
354
  unsigned short i;
353
  unsigned short i;
355
  const char *ext;
354
  const char *ext;
-
 
355
  char *cmd = buff + 256;
356
  const char far *cmdtail;
356
  const char far *cmdtail;
357
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
357
  char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
358
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
358
  char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
359
  _Packed struct {
359
  _Packed struct {
360
    unsigned short envseg;
360
    unsigned short envseg;
361
    unsigned long cmdtail;
361
    unsigned long cmdtail;
362
    unsigned long fcb1;
362
    unsigned long fcb1;
363
    unsigned long fcb2;
363
    unsigned long fcb2;
364
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
364
  } far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
365
 
365
 
366
  cmd_explode(buff + 2048, cmdline, argvlist);
366
  /* find cmd and cmdtail */
367
 
367
  i = 0;
-
 
368
  cmdtail = cmdline;
-
 
369
  while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
368
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
370
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
-
 
371
    cmd[i++] = *cmdtail;
-
 
372
    cmdtail++;
-
 
373
  }
-
 
374
  cmd[i] = 0;
369
 
375
 
370
  /* is this a command in curdir? */
376
  /* is this a command in curdir? */
371
  lookup = lookup_cmd(cmdfile, argvlist[0], NULL, &ext);
377
  lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
372
  if (lookup == 0) {
378
  if (lookup == 0) {
373
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
379
    /* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
374
    goto RUNCMDFILE;
380
    goto RUNCMDFILE;
375
  } else if (lookup == -2) {
381
  } else if (lookup == -2) {
376
    /* puts("NOT FOUND"); */
382
    /* puts("NOT FOUND"); */
377
    return;
383
    return;
378
  }
384
  }
379
 
385
 
380
  /* try matching something in PATH */
386
  /* try matching something in PATH */
381
  pathptr = env_lookup_val(envseg, "PATH");
387
  pathptr = env_lookup_val(envseg, "PATH");
382
  if (pathptr == NULL) return;
388
  if (pathptr == NULL) return;
383
 
389
 
384
  /* try each path in %PATH% */
390
  /* try each path in %PATH% */
385
  for (;;) {
391
  for (;;) {
386
    for (i = 0;; i++) {
392
    for (i = 0;; i++) {
387
      buff[i] = *pathptr;
393
      buff[i] = *pathptr;
388
      if ((buff[i] == 0) || (buff[i] == ';')) break;
394
      if ((buff[i] == 0) || (buff[i] == ';')) break;
389
      pathptr++;
395
      pathptr++;
390
    }
396
    }
391
    buff[i] = 0;
397
    buff[i] = 0;
392
    lookup = lookup_cmd(cmdfile, argvlist[0], buff, &ext);
398
    lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
393
    if (lookup == 0) break;
399
    if (lookup == 0) break;
394
    if (lookup == -2) return;
400
    if (lookup == -2) return;
395
    if (*pathptr == ';') {
401
    if (*pathptr == ';') {
396
      pathptr++;
402
      pathptr++;
397
    } else {
403
    } else {
398
      return;
404
      return;
399
    }
405
    }
400
  }
406
  }
401
 
407
 
402
  RUNCMDFILE:
408
  RUNCMDFILE:
403
 
409
 
404
  /* find cmdtail */
-
 
405
  cmdtail = cmdline;
-
 
406
  while (*cmdtail == ' ') cmdtail++;
-
 
407
  while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
-
 
408
    cmdtail++;
-
 
409
  }
-
 
410
 
-
 
411
  /* special handling of batch files */
410
  /* special handling of batch files */
412
  if ((ext != NULL) && (imatch(ext, "bat"))) {
411
  if ((ext != NULL) && (imatch(ext, "bat"))) {
413
    /* copy truename of the bat file to rmod buff */
412
    /* copy truename of the bat file to rmod buff */
414
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
413
    for (i = 0; cmdfile[i] != 0; i++) rmod->batfile[i] = cmdfile[i];
415
    rmod->batfile[i] = 0;
414
    rmod->batfile[i] = 0;
416
    /* copy args of the bat file to rmod buff */
415
    /* copy args of the bat file to rmod buff */
417
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
416
    for (i = 0; cmdtail[i] != 0; i++) rmod->batargs[i] = cmdtail[i];
418
    /* reset the 'next line to execute' counter */
417
    /* reset the 'next line to execute' counter */
419
    rmod->batnextline = 0;
418
    rmod->batnextline = 0;
420
    return;
419
    return;
421
  }
420
  }
422
 
421
 
423
  /* copy full filename to execute */
422
  /* copy full filename to execute */
424
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
423
  for (i = 0; cmdfile[i] != 0; i++) rmod_execprog[i] = cmdfile[i];
425
  rmod_execprog[i] = 0;
424
  rmod_execprog[i] = 0;
426
 
425
 
427
  /* copy cmdtail to rmod's PSP and compute its len */
426
  /* copy cmdtail to rmod's PSP and compute its len */
428
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
427
  for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
429
  rmod_cmdtail[i] = '\r';
428
  rmod_cmdtail[i] = '\r';
430
  rmod_cmdtail[-1] = i;
429
  rmod_cmdtail[-1] = i;
431
 
430
 
432
  /* set up rmod to execute the command */
431
  /* set up rmod to execute the command */
433
 
432
 
434
  ExecParam->envseg = envseg;
433
  ExecParam->envseg = envseg;
435
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
434
  ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
436
  ExecParam->fcb1 = 0; /* TODO farptr */
435
  ExecParam->fcb1 = 0; /* TODO farptr */
437
  ExecParam->fcb2 = 0; /* TODO farptr */
436
  ExecParam->fcb2 = 0; /* TODO farptr */
438
  exit(0); /* let rmod do the job now */
437
  exit(0); /* let rmod do the job now */
439
}
438
}
440
 
439
 
441
 
440
 
442
static void set_comspec_to_self(unsigned short envseg) {
441
static void set_comspec_to_self(unsigned short envseg) {
443
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
442
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
444
  char far *myenv = MK_FP(*psp_envseg, 0);
443
  char far *myenv = MK_FP(*psp_envseg, 0);
445
  unsigned short varcount;
444
  unsigned short varcount;
446
  char buff[256] = "COMSPEC=";
445
  char buff[256] = "COMSPEC=";
447
  char *buffptr = buff + 8;
446
  char *buffptr = buff + 8;
448
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
447
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
449
  while (*myenv != 0) {
448
  while (*myenv != 0) {
450
    /* consume a NULL-terminated string */
449
    /* consume a NULL-terminated string */
451
    while (*myenv != 0) myenv++;
450
    while (*myenv != 0) myenv++;
452
    /* move to next string */
451
    /* move to next string */
453
    myenv++;
452
    myenv++;
454
  }
453
  }
455
  /* get next word, if 1 then EXEPATH follows */
454
  /* get next word, if 1 then EXEPATH follows */
456
  myenv++;
455
  myenv++;
457
  varcount = *myenv;
456
  varcount = *myenv;
458
  myenv++;
457
  myenv++;
459
  varcount |= (*myenv << 8);
458
  varcount |= (*myenv << 8);
460
  myenv++;
459
  myenv++;
461
  if (varcount != 1) return; /* NO EXEPATH FOUND */
460
  if (varcount != 1) return; /* NO EXEPATH FOUND */
462
  while (*myenv != 0) {
461
  while (*myenv != 0) {
463
    *buffptr = *myenv;
462
    *buffptr = *myenv;
464
    buffptr++;
463
    buffptr++;
465
    myenv++;
464
    myenv++;
466
  }
465
  }
467
  *buffptr = 0;
466
  *buffptr = 0;
468
  /* printf("EXEPATH: '%s'\r\n", buff); */
467
  /* printf("EXEPATH: '%s'\r\n", buff); */
469
  env_setvar(envseg, buff);
468
  env_setvar(envseg, buff);
470
}
469
}
471
 
470
 
472
 
471
 
473
/* wait for user input */
472
/* wait for user input */
474
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
473
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
475
  _asm {
474
  _asm {
476
    push ax
475
    push ax
477
    push bx
476
    push bx
478
    push cx
477
    push cx
479
    push dx
478
    push dx
480
    push ds
479
    push ds
481
 
480
 
482
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
481
    /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
483
    mov ax, 0x4800
482
    mov ax, 0x4800
484
    int 0x2f
483
    int 0x2f
485
    mov bl, al /* save doskey status in BL */
484
    mov bl, al /* save doskey status in BL */
486
 
485
 
487
    /* set up buffered input to inpseg:inpoff */
486
    /* set up buffered input to inpseg:inpoff */
488
    mov ax, inpseg
487
    mov ax, inpseg
489
    push ax
488
    push ax
490
    pop ds
489
    pop ds
491
    mov dx, inpoff
490
    mov dx, inpoff
492
 
491
 
493
    /* execute either DOS input or DOSKEY */
492
    /* execute either DOS input or DOSKEY */
494
    test bl, bl /* zf set if no DOSKEY present */
493
    test bl, bl /* zf set if no DOSKEY present */
495
    jnz DOSKEY
494
    jnz DOSKEY
496
 
495
 
497
    mov ah, 0x0a
496
    mov ah, 0x0a
498
    int 0x21
497
    int 0x21
499
    jmp short DONE
498
    jmp short DONE
500
 
499
 
501
    DOSKEY:
500
    DOSKEY:
502
    mov ax, 0x4810
501
    mov ax, 0x4810
503
    int 0x2f
502
    int 0x2f
504
 
503
 
505
    DONE:
504
    DONE:
506
    /* terminate command with a CR/LF */
505
    /* terminate command with a CR/LF */
507
    mov ah, 0x02 /* display character in dl */
506
    mov ah, 0x02 /* display character in dl */
508
    mov dl, 0x0d
507
    mov dl, 0x0d
509
    int 0x21
508
    int 0x21
510
    mov dl, 0x0a
509
    mov dl, 0x0a
511
    int 0x21
510
    int 0x21
512
 
511
 
513
    pop ds
512
    pop ds
514
    pop dx
513
    pop dx
515
    pop cx
514
    pop cx
516
    pop bx
515
    pop bx
517
    pop ax
516
    pop ax
518
  }
517
  }
519
}
518
}
520
 
519
 
521
 
520
 
522
/* fetches a line from batch file and write it to buff, increments
521
/* fetches a line from batch file and write it to buff, increments
523
 * rmod counter on success. returns NULL on failure.
522
 * rmod counter on success. returns NULL on failure.
524
 * buff must start with a length byte but the returned pointer must
523
 * buff must start with a length byte but the returned pointer must
525
 * skip it. */
524
 * skip it. */
526
static char far *getbatcmd(char *buff, struct rmod_props far *rmod) {
525
static char far *getbatcmd(char *buff, struct rmod_props far *rmod) {
527
  unsigned short i;
526
  unsigned short i;
528
  buff++; /* make room for the len byte */
527
  buff++; /* make room for the len byte */
529
  /* TODO temporary hack to display a dummy message */
528
  /* TODO temporary hack to display a dummy message */
530
  if (rmod->batnextline == 0) {
529
  if (rmod->batnextline == 0) {
531
    char *msg = "ECHO batch files not supported yet";
530
    char *msg = "ECHO batch files not supported yet";
532
    for (i = 0; msg[i] != 0; i++) buff[i] = msg[i];
531
    for (i = 0; msg[i] != 0; i++) buff[i] = msg[i];
533
    buff[i] = 0;
532
    buff[i] = 0;
534
    buff[-1] = i;
533
    buff[-1] = i;
535
  } else {
534
  } else {
536
    rmod->batfile[0] = 0;
535
    rmod->batfile[0] = 0;
537
    return(NULL);
536
    return(NULL);
538
  }
537
  }
539
  /* open file */
538
  /* open file */
540
  /* read until awaiting line */
539
  /* read until awaiting line */
541
  /* copy line to buff */
540
  /* copy line to buff */
542
  /* close file */
541
  /* close file */
543
  /* */
542
  /* */
544
  rmod->batnextline++;
543
  rmod->batnextline++;
545
  if (rmod->batnextline == 0) rmod->batfile[0] = 0; /* max line count reached */
544
  if (rmod->batnextline == 0) rmod->batfile[0] = 0; /* max line count reached */
546
 
545
 
547
  /* output command on screen if echo on */
546
  /* output command on screen if echo on */
548
  if (rmod->echoflag != 0) outputnl(buff);
547
  if (rmod->echoflag != 0) outputnl(buff);
549
 
548
 
550
  return(buff);
549
  return(buff);
551
}
550
}
552
 
551
 
553
 
552
 
554
int main(void) {
553
int main(void) {
555
  static struct config cfg;
554
  static struct config cfg;
556
  static unsigned short far *rmod_envseg;
555
  static unsigned short far *rmod_envseg;
557
  static unsigned short far *lastexitcode;
556
  static unsigned short far *lastexitcode;
558
  static unsigned char BUFFER[4096];
557
  static unsigned char BUFFER[4096];
559
  static struct rmod_props far *rmod;
558
  static struct rmod_props far *rmod;
560
 
559
 
561
  rmod = rmod_find();
560
  rmod = rmod_find();
562
  if (rmod == NULL) {
561
  if (rmod == NULL) {
563
    rmod = rmod_install(cfg.envsiz);
562
    rmod = rmod_install(cfg.envsiz);
564
    if (rmod == NULL) {
563
    if (rmod == NULL) {
565
      outputnl("ERROR: rmod_install() failed");
564
      outputnl("ERROR: rmod_install() failed");
566
      return(1);
565
      return(1);
567
    }
566
    }
568
    /* look at command line parameters */
567
    /* look at command line parameters */
569
    parse_argv(&cfg);
568
    parse_argv(&cfg);
570
    /* copy flags to rmod's storage */
569
    /* copy flags to rmod's storage */
571
    rmod->flags = cfg.flags;
570
    rmod->flags = cfg.flags;
572
    /* printf("rmod installed at %Fp\r\n", rmod); */
571
    /* printf("rmod installed at %Fp\r\n", rmod); */
573
  } else {
572
  } else {
574
    /* printf("rmod found at %Fp\r\n", rmod); */
573
    /* printf("rmod found at %Fp\r\n", rmod); */
575
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
574
    /* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
576
     * die asap, because the command has been executed already, so I no longer
575
     * die asap, because the command has been executed already, so I no longer
577
     * have a purpose in life */
576
     * have a purpose in life */
578
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
577
    if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
579
  }
578
  }
580
 
579
 
581
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
580
  rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
582
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
581
  lastexitcode = MK_FP(rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
583
 
582
 
584
  /* make COMPSEC point to myself */
583
  /* make COMPSEC point to myself */
585
  set_comspec_to_self(*rmod_envseg);
584
  set_comspec_to_self(*rmod_envseg);
586
 
585
 
587
/*  {
586
/*  {
588
    unsigned short envsiz;
587
    unsigned short envsiz;
589
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
588
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
590
    envsiz = *sizptr;
589
    envsiz = *sizptr;
591
    envsiz *= 16;
590
    envsiz *= 16;
592
    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);
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);
593
  }*/
592
  }*/
594
 
593
 
595
  do {
594
  do {
596
    char far *cmdline = rmod->inputbuf + 2;
595
    char far *cmdline = rmod->inputbuf + 2;
597
 
596
 
598
    /* (re)load translation strings if needed */
597
    /* (re)load translation strings if needed */
599
    nls_langreload(BUFFER, *rmod_envseg);
598
    nls_langreload(BUFFER, *rmod_envseg);
600
 
599
 
601
    /* skip user input if I have a command to exec (/C or /K) */
600
    /* skip user input if I have a command to exec (/C or /K) */
602
    if (cfg.execcmd != NULL) {
601
    if (cfg.execcmd != NULL) {
603
      cmdline = cfg.execcmd;
602
      cmdline = cfg.execcmd;
604
      cfg.execcmd = NULL;
603
      cfg.execcmd = NULL;
605
      goto EXEC_CMDLINE;
604
      goto EXEC_CMDLINE;
606
    }
605
    }
607
 
606
 
608
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
607
    if (rmod->echoflag != 0) outputnl(""); /* terminate the previous command with a CR/LF */
609
 
608
 
610
    SKIP_NEWLINE:
609
    SKIP_NEWLINE:
611
 
610
 
612
    /* print shell prompt (only if ECHO is enabled) */
611
    /* print shell prompt (only if ECHO is enabled) */
613
    if (rmod->echoflag != 0) {
612
    if (rmod->echoflag != 0) {
614
      char *promptptr = BUFFER;
613
      char *promptptr = BUFFER;
615
      buildprompt(promptptr, *rmod_envseg);
614
      buildprompt(promptptr, *rmod_envseg);
616
      _asm {
615
      _asm {
617
        push ax
616
        push ax
618
        push dx
617
        push dx
619
        mov ah, 0x09
618
        mov ah, 0x09
620
        mov dx, promptptr
619
        mov dx, promptptr
621
        int 0x21
620
        int 0x21
622
        pop dx
621
        pop dx
623
        pop ax
622
        pop ax
624
      }
623
      }
625
    }
624
    }
626
 
625
 
627
    /* revert input history terminator to \r */
626
    /* revert input history terminator to \r */
628
    if (cmdline[-1] != 0) {
627
    if (cmdline[-1] != 0) {
629
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
628
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
630
    }
629
    }
631
 
630
 
632
    /* if batch file is being executed -> fetch next line */
631
    /* if batch file is being executed -> fetch next line */
633
    if (rmod->batfile[0] != 0) {
632
    if (rmod->batfile[0] != 0) {
634
      cmdline = getbatcmd(BUFFER + sizeof(BUFFER) - 130, rmod);
633
      cmdline = getbatcmd(BUFFER + sizeof(BUFFER) - 130, rmod);
635
      if (cmdline == NULL) continue;
634
      if (cmdline == NULL) continue;
636
    } else {
635
    } else {
637
      /* interactive mode: wait for user command line */
636
      /* interactive mode: wait for user command line */
638
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
637
      cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
639
    }
638
    }
640
 
639
 
641
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
640
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
642
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
641
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
643
 
642
 
644
    /* replace \r by a zero terminator */
643
    /* replace \r by a zero terminator */
645
    cmdline[(unsigned char)(cmdline[-1])] = 0;
644
    cmdline[(unsigned char)(cmdline[-1])] = 0;
646
 
645
 
647
    /* I jump here when I need to exec an initial command (/C or /K) */
646
    /* I jump here when I need to exec an initial command (/C or /K) */
648
    EXEC_CMDLINE:
647
    EXEC_CMDLINE:
649
 
648
 
650
    /* move pointer forward to skip over any leading spaces */
649
    /* move pointer forward to skip over any leading spaces */
651
    while (*cmdline == ' ') cmdline++;
650
    while (*cmdline == ' ') cmdline++;
652
 
651
 
653
    /* update rmod's ptr to COMPSPEC so it is always up to date */
652
    /* update rmod's ptr to COMPSPEC so it is always up to date */
654
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
653
    rmod_updatecomspecptr(rmod->rmodseg, *rmod_envseg);
655
 
654
 
656
    /* handle redirections (if any) */
655
    /* handle redirections (if any) */
657
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
656
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
658
      outputnl("");
657
      outputnl("");
659
      continue;
658
      continue;
660
    }
659
    }
661
 
660
 
662
    /* try matching (and executing) an internal command */
661
    /* try matching (and executing) an internal command */
663
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
662
    if (cmd_process(rmod, *rmod_envseg, cmdline, BUFFER) >= -1) {
664
      /* internal command executed */
663
      /* internal command executed */
665
      redir_revert(); /* revert stdout (in case it was redirected) */
664
      redir_revert(); /* revert stdout (in case it was redirected) */
666
      continue;
665
      continue;
667
    }
666
    }
668
 
667
 
669
    /* if here, then this was not an internal command */
668
    /* if here, then this was not an internal command */
670
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
669
    run_as_external(BUFFER, cmdline, *rmod_envseg, rmod);
671
    /* perhaps this is a newly launched BAT file */
670
    /* perhaps this is a newly launched BAT file */
672
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
671
    if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
673
 
672
 
674
    /* revert stdout (in case it was redirected) */
673
    /* revert stdout (in case it was redirected) */
675
    redir_revert();
674
    redir_revert();
676
 
675
 
677
    /* run_as_external() does not return on success, if I am still alive then
676
    /* run_as_external() does not return on success, if I am still alive then
678
     * external command failed to execute */
677
     * external command failed to execute */
679
    outputnl("Bad command or file name");
678
    outputnl("Bad command or file name");
680
 
679
 
681
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
680
  } while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
682
 
681
 
683
  sayonara(rmod);
682
  sayonara(rmod);
684
  return(0);
683
  return(0);
685
}
684
}
686
 
685