Subversion Repositories SvarDOS

Rev

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

Rev 405 Rev 410
1
/*
1
/*
2
 * SvarCOM is a command-line interpreter.
2
 * SvarCOM is a command-line interpreter.
3
 *
3
 *
4
 * a little memory area is allocated as high as possible. it contains:
4
 * a little memory area is allocated as high as possible. it contains:
5
 *  - a signature (like XMS drivers do)
5
 *  - a signature (like XMS drivers do)
6
 *  - a routine for exec'ing programs
6
 *  - a routine for exec'ing programs
7
 *  - a "last command" buffer for input history
7
 *  - a "last command" buffer for input history
8
 *
8
 *
9
 * when svarcom starts, it tries locating the routine in memory.
9
 * when svarcom starts, it tries locating the routine in memory.
10
 *
10
 *
11
 * if found:
11
 * if found:
12
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
12
 *   waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit.
13
 *
13
 *
14
 * if not found:
14
 * if not found:
15
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
15
 *   installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine
16
 *   and quit.
16
 *   and quit.
17
 *
17
 *
18
 * PSP structure
18
 * PSP structure
19
 * http://www.piclist.com/techref/dos/psps.htm
19
 * http://www.piclist.com/techref/dos/psps.htm
20
 *
20
 *
21
 *
21
 *
22
 *
22
 *
23
 * === MCB ===
23
 * === MCB ===
24
 *
24
 *
25
 * each time that DOS allocates memory, it prefixes the allocated memory with
25
 * each time that DOS allocates memory, it prefixes the allocated memory with
26
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
26
 * a 16-bytes structure called a "Memory Control Block" (MCB). This control
27
 * block has the following structure:
27
 * block has the following structure:
28
 *
28
 *
29
 * Offset  Size     Description
29
 * Offset  Size     Description
30
 *   00h   byte     'M' =  non-last member of the MCB chain
30
 *   00h   byte     'M' =  non-last member of the MCB chain
31
 *                  'Z' = indicates last entry in MCB chain
31
 *                  'Z' = indicates last entry in MCB chain
32
 *                  other values cause "Memory Allocation Failure" on exit
32
 *                  other values cause "Memory Allocation Failure" on exit
33
 *   01h   word     PSP segment address of the owner (Process Id)
33
 *   01h   word     PSP segment address of the owner (Process Id)
34
 *                  possible values:
34
 *                  possible values:
35
 *                    0 = free
35
 *                    0 = free
36
 *                    8 = Allocated by DOS before first user pgm loaded
36
 *                    8 = Allocated by DOS before first user pgm loaded
37
 *                    other = Process Id/PSP segment address of owner
37
 *                    other = Process Id/PSP segment address of owner
38
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
38
 *   03h  word      number of paragraphs related to this MCB (excluding MCB)
39
 *   05h  11 bytes  reserved
39
 *   05h  11 bytes  reserved
40
 *   10h  ...       start of actual allocated memory block
40
 *   10h  ...       start of actual allocated memory block
41
 */
41
 */
42
 
42
 
43
#include <i86.h>
43
#include <i86.h>
44
#include <dos.h>
44
#include <dos.h>
45
#include <stdio.h>
45
#include <stdio.h>
46
#include <stdlib.h>
46
#include <stdlib.h>
47
#include <string.h>
47
#include <string.h>
48
 
48
 
49
#include <process.h>
49
#include <process.h>
50
 
50
 
51
#include "cmd.h"
51
#include "cmd.h"
52
#include "env.h"
52
#include "env.h"
53
#include "helpers.h"
53
#include "helpers.h"
54
#include "redir.h"
54
#include "redir.h"
55
#include "rmodinit.h"
55
#include "rmodinit.h"
56
 
56
 
57
struct config {
57
struct config {
58
  int locate;
58
  int locate;
59
  int install;
59
  int install;
60
  int envsiz;
60
  unsigned short envsiz;
61
} cfg;
61
} cfg;
62
 
62
 
63
 
63
 
64
static void parse_argv(struct config *cfg, int argc, char **argv) {
64
static void parse_argv(struct config *cfg, int argc, char **argv) {
65
  int i;
65
  int i;
66
  memset(cfg, 0, sizeof(*cfg));
66
  memset(cfg, 0, sizeof(*cfg));
67
 
67
 
68
  for (i = 1; i < argc; i++) {
68
  for (i = 1; i < argc; i++) {
69
    if (strcmp(argv[i], "/locate") == 0) {
69
    if (strcmp(argv[i], "/locate") == 0) {
70
      cfg->locate = 1;
70
      cfg->locate = 1;
71
    }
71
    }
72
    if (strstartswith(argv[i], "/e:") == 0) {
72
    if (strstartswith(argv[i], "/e:") == 0) {
73
      cfg->envsiz = atoi(argv[i]+3);
73
      if ((atouns(&(cfg->envsiz), argv[i] + 3) != 0) || (cfg->envsiz < 64)) {
74
      if (cfg->envsiz < 64) cfg->envsiz = 0;
74
        cfg->envsiz = 0;
-
 
75
      }
75
    }
76
    }
76
  }
77
  }
77
}
78
}
78
 
79
 
79
 
80
 
80
static void buildprompt(char *s, unsigned short envseg) {
81
static void buildprompt(char *s, unsigned short envseg) {
81
  /* locate the prompt variable or use the default pattern */
82
  /* locate the prompt variable or use the default pattern */
82
  const char far *fmt = env_lookup(envseg, "PROMPT");
83
  const char far *fmt = env_lookup(envseg, "PROMPT");
83
  while ((fmt != NULL) && (*fmt != 0)) {
84
  while ((fmt != NULL) && (*fmt != 0)) {
84
    fmt++;
85
    fmt++;
85
    if (fmt[-1] == '=') break;
86
    if (fmt[-1] == '=') break;
86
  }
87
  }
87
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
88
  if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */
88
  /* build the prompt string based on pattern */
89
  /* build the prompt string based on pattern */
89
  for (; *fmt != 0; fmt++) {
90
  for (; *fmt != 0; fmt++) {
90
    if (*fmt != '$') {
91
    if (*fmt != '$') {
91
      *s = *fmt;
92
      *s = *fmt;
92
      s++;
93
      s++;
93
      continue;
94
      continue;
94
    }
95
    }
95
    /* escape code ($P, etc) */
96
    /* escape code ($P, etc) */
96
    fmt++;
97
    fmt++;
97
    switch (*fmt) {
98
    switch (*fmt) {
98
      case 'Q':  /* $Q = = (equal sign) */
99
      case 'Q':  /* $Q = = (equal sign) */
99
      case 'q':
100
      case 'q':
100
        *s = '=';
101
        *s = '=';
101
        s++;
102
        s++;
102
        break;
103
        break;
103
      case '$':  /* $$ = $ (dollar sign) */
104
      case '$':  /* $$ = $ (dollar sign) */
104
        *s = '$';
105
        *s = '$';
105
        s++;
106
        s++;
106
        break;
107
        break;
107
      case 'T':  /* $t = current time */
108
      case 'T':  /* $t = current time */
108
      case 't':
109
      case 't':
109
        s += sprintf(s, "00:00"); /* TODO */
110
        s += sprintf(s, "00:00"); /* TODO */
110
        break;
111
        break;
111
      case 'D':  /* $D = current date */
112
      case 'D':  /* $D = current date */
112
      case 'd':
113
      case 'd':
113
        s += sprintf(s, "1985-07-29"); /* TODO */
114
        s += sprintf(s, "1985-07-29"); /* TODO */
114
        break;
115
        break;
115
      case 'P':  /* $P = current drive and path */
116
      case 'P':  /* $P = current drive and path */
116
      case 'p':
117
      case 'p':
117
        _asm {
118
        _asm {
118
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
119
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
119
          int 0x21
120
          int 0x21
120
          mov bx, s
121
          mov bx, s
121
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
122
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
122
        }
123
        }
123
        *s += 'A';
124
        *s += 'A';
124
        s++;
125
        s++;
125
        *s = ':';
126
        *s = ':';
126
        s++;
127
        s++;
127
        *s = '\\';
128
        *s = '\\';
128
        s++;
129
        s++;
129
        _asm {
130
        _asm {
130
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
131
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
131
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
132
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
132
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
133
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
133
          int 0x21
134
          int 0x21
134
        }
135
        }
135
        while (*s != 0) s++; /* move ptr forward to end of pathname */
136
        while (*s != 0) s++; /* move ptr forward to end of pathname */
136
        break;
137
        break;
137
      case 'V':  /* $V = DOS version number */
138
      case 'V':  /* $V = DOS version number */
138
      case 'v':
139
      case 'v':
139
        s += sprintf(s, "VER"); /* TODO */
140
        s += sprintf(s, "VER"); /* TODO */
140
        break;
141
        break;
141
      case 'N':  /* $N = current drive */
142
      case 'N':  /* $N = current drive */
142
      case 'n':
143
      case 'n':
143
        _asm {
144
        _asm {
144
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
145
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
145
          int 0x21
146
          int 0x21
146
          mov bx, s
147
          mov bx, s
147
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
148
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
148
        }
149
        }
149
        *s += 'A';
150
        *s += 'A';
150
        s++;
151
        s++;
151
        break;
152
        break;
152
      case 'G':  /* $G = > (greater-than sign) */
153
      case 'G':  /* $G = > (greater-than sign) */
153
      case 'g':
154
      case 'g':
154
        *s = '>';
155
        *s = '>';
155
        s++;
156
        s++;
156
        break;
157
        break;
157
      case 'L':  /* $L = < (less-than sign) */
158
      case 'L':  /* $L = < (less-than sign) */
158
      case 'l':
159
      case 'l':
159
        *s = '<';
160
        *s = '<';
160
        s++;
161
        s++;
161
        break;
162
        break;
162
      case 'B':  /* $B = | (pipe) */
163
      case 'B':  /* $B = | (pipe) */
163
      case 'b':
164
      case 'b':
164
        *s = '|';
165
        *s = '|';
165
        s++;
166
        s++;
166
        break;
167
        break;
167
      case 'H':  /* $H = backspace (erases previous character) */
168
      case 'H':  /* $H = backspace (erases previous character) */
168
      case 'h':
169
      case 'h':
169
        *s = '\b';
170
        *s = '\b';
170
        s++;
171
        s++;
171
        break;
172
        break;
172
      case 'E':  /* $E = Escape code (ASCII 27) */
173
      case 'E':  /* $E = Escape code (ASCII 27) */
173
      case 'e':
174
      case 'e':
174
        *s = 27;
175
        *s = 27;
175
        s++;
176
        s++;
176
        break;
177
        break;
177
      case '_':  /* $_ = CR+LF */
178
      case '_':  /* $_ = CR+LF */
178
        *s = '\r';
179
        *s = '\r';
179
        s++;
180
        s++;
180
        *s = '\n';
181
        *s = '\n';
181
        s++;
182
        s++;
182
        break;
183
        break;
183
    }
184
    }
184
  }
185
  }
185
  *s = '$';
186
  *s = '$';
186
}
187
}
187
 
188
 
188
 
189
 
189
static void run_as_external(const char far *cmdline) {
190
static void run_as_external(const char far *cmdline) {
190
  char buff[256];
191
  char buff[256];
191
  char const *argvlist[256];
192
  char const *argvlist[256];
192
  int i, n;
193
  int i, n;
193
  /* copy buffer to a near var (incl. trailing CR), insert a space before
194
  /* copy buffer to a near var (incl. trailing CR), insert a space before
194
     every slash to make sure arguments are well separated */
195
     every slash to make sure arguments are well separated */
195
  n = 0;
196
  n = 0;
196
  i = 0;
197
  i = 0;
197
  for (;;) {
198
  for (;;) {
198
    if (cmdline[i] == '/') buff[n++] = ' ';
199
    if (cmdline[i] == '/') buff[n++] = ' ';
199
    buff[n++] = cmdline[i++];
200
    buff[n++] = cmdline[i++];
200
    if (buff[n] == 0) break;
201
    if (buff[n] == 0) break;
201
  }
202
  }
202
 
203
 
203
  cmd_explode(buff, cmdline, argvlist);
204
  cmd_explode(buff, cmdline, argvlist);
204
 
205
 
205
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
206
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
206
 
207
 
207
  /* must be an external command then. this call should never return, unless
208
  /* must be an external command then. this call should never return, unless
208
   * the other program failed to be executed. */
209
   * the other program failed to be executed. */
209
  execvp(argvlist[0], argvlist);
210
  execvp(argvlist[0], argvlist);
210
}
211
}
211
 
212
 
212
 
213
 
213
static void set_comspec_to_self(unsigned short envseg) {
214
static void set_comspec_to_self(unsigned short envseg) {
214
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
215
  unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */
215
  char far *myenv = MK_FP(*psp_envseg, 0);
216
  char far *myenv = MK_FP(*psp_envseg, 0);
216
  unsigned short varcount;
217
  unsigned short varcount;
217
  char buff[256] = "COMSPEC=";
218
  char buff[256] = "COMSPEC=";
218
  char *buffptr = buff + 8;
219
  char *buffptr = buff + 8;
219
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
220
  /* who am i? look into my own environment, at the end of it should be my EXEPATH string */
220
  while (*myenv != 0) {
221
  while (*myenv != 0) {
221
    /* consume a NULL-terminated string */
222
    /* consume a NULL-terminated string */
222
    while (*myenv != 0) myenv++;
223
    while (*myenv != 0) myenv++;
223
    /* move to next string */
224
    /* move to next string */
224
    myenv++;
225
    myenv++;
225
  }
226
  }
226
  /* get next word, if 1 then EXEPATH follows */
227
  /* get next word, if 1 then EXEPATH follows */
227
  myenv++;
228
  myenv++;
228
  varcount = *myenv;
229
  varcount = *myenv;
229
  myenv++;
230
  myenv++;
230
  varcount |= (*myenv << 8);
231
  varcount |= (*myenv << 8);
231
  myenv++;
232
  myenv++;
232
  if (varcount != 1) return; /* NO EXEPATH FOUND */
233
  if (varcount != 1) return; /* NO EXEPATH FOUND */
233
  while (*myenv != 0) {
234
  while (*myenv != 0) {
234
    *buffptr = *myenv;
235
    *buffptr = *myenv;
235
    buffptr++;
236
    buffptr++;
236
    myenv++;
237
    myenv++;
237
  }
238
  }
238
  *buffptr = 0;
239
  *buffptr = 0;
239
  /* printf("EXEPATH: '%s'\r\n", buff); */
240
  /* printf("EXEPATH: '%s'\r\n", buff); */
240
  env_setvar(envseg, buff);
241
  env_setvar(envseg, buff);
241
}
242
}
242
 
243
 
243
 
244
 
244
int main(int argc, char **argv) {
245
int main(int argc, char **argv) {
245
  static struct config cfg;
246
  static struct config cfg;
246
  static unsigned short rmod_seg;
247
  static unsigned short rmod_seg;
247
  static unsigned short far *rmod_envseg;
248
  static unsigned short far *rmod_envseg;
248
  static unsigned short far *lastexitcode;
249
  static unsigned short far *lastexitcode;
249
  static unsigned char BUFFER[4096];
250
  static unsigned char BUFFER[4096];
250
 
251
 
251
  parse_argv(&cfg, argc, argv);
252
  parse_argv(&cfg, argc, argv);
252
 
253
 
253
  rmod_seg = rmod_find();
254
  rmod_seg = rmod_find();
254
  if (rmod_seg == 0xffff) {
255
  if (rmod_seg == 0xffff) {
255
    rmod_seg = rmod_install(cfg.envsiz);
256
    rmod_seg = rmod_install(cfg.envsiz);
256
    if (rmod_seg == 0xffff) {
257
    if (rmod_seg == 0xffff) {
257
      outputnl("ERROR: rmod_install() failed");
258
      outputnl("ERROR: rmod_install() failed");
258
      return(1);
259
      return(1);
259
    }
260
    }
260
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
261
/*    printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */
261
  } else {
262
  } else {
262
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
263
/*    printf("rmod found at seg 0x%04x\r\n", rmod_seg); */
263
  }
264
  }
264
 
265
 
265
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
266
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
266
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
267
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
267
 
268
 
268
  /* make COMPSEC point to myself */
269
  /* make COMPSEC point to myself */
269
  set_comspec_to_self(*rmod_envseg);
270
  set_comspec_to_self(*rmod_envseg);
270
 
271
 
271
/*  {
272
/*  {
272
    unsigned short envsiz;
273
    unsigned short envsiz;
273
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
274
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
274
    envsiz = *sizptr;
275
    envsiz = *sizptr;
275
    envsiz *= 16;
276
    envsiz *= 16;
276
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
277
    printf("rmod_inpbuff at %04X:%04X, env_seg at %04X:0000 (env_size = %u bytes)\r\n", rmod_seg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
277
  }*/
278
  }*/
278
 
279
 
279
  for (;;) {
280
  for (;;) {
280
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
281
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
281
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
282
    unsigned char far *echostatus = MK_FP(rmod_seg, RMOD_OFFSET_ECHOFLAG);
282
 
283
 
283
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
284
    if (*echostatus != 0) outputnl(""); /* terminate the previous command with a CR/LF */
284
 
285
 
285
    SKIP_NEWLINE:
286
    SKIP_NEWLINE:
286
 
287
 
287
    /* print shell prompt (only if ECHO is enabled) */
288
    /* print shell prompt (only if ECHO is enabled) */
288
    if (*echostatus != 0) {
289
    if (*echostatus != 0) {
289
      char *promptptr = BUFFER;
290
      char *promptptr = BUFFER;
290
      buildprompt(promptptr, *rmod_envseg);
291
      buildprompt(promptptr, *rmod_envseg);
291
      _asm {
292
      _asm {
292
        push ax
293
        push ax
293
        push dx
294
        push dx
294
        mov ah, 0x09
295
        mov ah, 0x09
295
        mov dx, promptptr
296
        mov dx, promptptr
296
        int 0x21
297
        int 0x21
297
        pop dx
298
        pop dx
298
        pop ax
299
        pop ax
299
      }
300
      }
300
    }
301
    }
301
 
302
 
302
    /* revert input history terminator to \r */
303
    /* revert input history terminator to \r */
303
    if (cmdline[-1] != 0) {
304
    if (cmdline[-1] != 0) {
304
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
305
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
305
    }
306
    }
306
 
307
 
307
    /* wait for user input */
308
    /* wait for user input */
308
    _asm {
309
    _asm {
309
      push ax
310
      push ax
310
      push bx
311
      push bx
311
      push cx
312
      push cx
312
      push dx
313
      push dx
313
      push ds
314
      push ds
314
 
315
 
315
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
316
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
316
      mov ax, 0x4800
317
      mov ax, 0x4800
317
      int 0x2f
318
      int 0x2f
318
      mov bl, al /* save doskey status in BL */
319
      mov bl, al /* save doskey status in BL */
319
 
320
 
320
      /* set up buffered input */
321
      /* set up buffered input */
321
      mov ax, rmod_seg
322
      mov ax, rmod_seg
322
      push ax
323
      push ax
323
      pop ds
324
      pop ds
324
      mov dx, RMOD_OFFSET_INPBUFF
325
      mov dx, RMOD_OFFSET_INPBUFF
325
 
326
 
326
      /* execute either DOS input or DOSKEY */
327
      /* execute either DOS input or DOSKEY */
327
      test bl, bl /* zf set if no DOSKEY present */
328
      test bl, bl /* zf set if no DOSKEY present */
328
      jnz DOSKEY
329
      jnz DOSKEY
329
 
330
 
330
      mov ah, 0x0a
331
      mov ah, 0x0a
331
      int 0x21
332
      int 0x21
332
      jmp short DONE
333
      jmp short DONE
333
 
334
 
334
      DOSKEY:
335
      DOSKEY:
335
      mov ax, 0x4810
336
      mov ax, 0x4810
336
      int 0x2f
337
      int 0x2f
337
 
338
 
338
      DONE:
339
      DONE:
339
      /* terminate command with a CR/LF */
340
      /* terminate command with a CR/LF */
340
      mov ah, 0x02 /* display character in dl */
341
      mov ah, 0x02 /* display character in dl */
341
      mov dl, 0x0d
342
      mov dl, 0x0d
342
      int 0x21
343
      int 0x21
343
      mov dl, 0x0a
344
      mov dl, 0x0a
344
      int 0x21
345
      int 0x21
345
 
346
 
346
      pop ds
347
      pop ds
347
      pop dx
348
      pop dx
348
      pop cx
349
      pop cx
349
      pop bx
350
      pop bx
350
      pop ax
351
      pop ax
351
    }
352
    }
352
 
353
 
353
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
354
    /* if nothing entered, loop again (but without appending an extra CR/LF) */
354
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
355
    if (cmdline[-1] == 0) goto SKIP_NEWLINE;
355
 
356
 
356
    /* replace \r by a zero terminator */
357
    /* replace \r by a zero terminator */
357
    cmdline[(unsigned char)(cmdline[-1])] = 0;
358
    cmdline[(unsigned char)(cmdline[-1])] = 0;
358
 
359
 
359
    /* move pointer forward to skip over any leading spaces */
360
    /* move pointer forward to skip over any leading spaces */
360
    while (*cmdline == ' ') cmdline++;
361
    while (*cmdline == ' ') cmdline++;
361
 
362
 
362
    /* update rmod's ptr to COMPSPEC so it is always up to date */
363
    /* update rmod's ptr to COMPSPEC so it is always up to date */
363
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
364
    rmod_updatecomspecptr(rmod_seg, *rmod_envseg);
364
 
365
 
365
    /* handle redirections (if any) */
366
    /* handle redirections (if any) */
366
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
367
    if (redir_parsecmd(cmdline, BUFFER) != 0) {
367
      outputnl("");
368
      outputnl("");
368
      continue;
369
      continue;
369
    }
370
    }
370
 
371
 
371
    /* try matching (and executing) an internal command */
372
    /* try matching (and executing) an internal command */
372
    {
373
    {
373
      int ecode = cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER);
374
      int ecode = cmd_process(rmod_seg, *rmod_envseg, cmdline, BUFFER);
374
      if (ecode >= 0) *lastexitcode = ecode;
375
      if (ecode >= 0) *lastexitcode = ecode;
375
      if (ecode >= -1) { /* internal command executed */
376
      if (ecode >= -1) { /* internal command executed */
376
        redir_revert(); /* revert stdout (in case it was redirected) */
377
        redir_revert(); /* revert stdout (in case it was redirected) */
377
        continue;
378
        continue;
378
      }
379
      }
379
    }
380
    }
380
 
381
 
381
    /* if here, then this was not an internal command */
382
    /* if here, then this was not an internal command */
382
    run_as_external(cmdline);
383
    run_as_external(cmdline);
383
 
384
 
384
    /* revert stdout (in case it was redirected) */
385
    /* revert stdout (in case it was redirected) */
385
    redir_revert();
386
    redir_revert();
386
 
387
 
387
    /* execvp() replaces the current process by the new one
388
    /* execvp() replaces the current process by the new one
388
    if I am still alive then external command failed to execute */
389
    if I am still alive then external command failed to execute */
389
    outputnl("Bad command or file name");
390
    outputnl("Bad command or file name");
390
 
391
 
391
  }
392
  }
392
 
393
 
393
  return(0);
394
  return(0);
394
}
395
}
395
 
396