Subversion Repositories SvarDOS

Rev

Rev 369 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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