Subversion Repositories SvarDOS

Rev

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

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