Subversion Repositories SvarDOS

Rev

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

Rev 349 Rev 350
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 <string.h>
52
#include <string.h>
52
 
53
 
53
#include <process.h>
54
#include <process.h>
54
 
55
 
55
#include "rmod.h"
56
#include "rmod.h"
56
 
57
 
57
struct config {
58
struct config {
58
  int locate;
59
  int locate;
59
  int install;
60
  int install;
-
 
61
  int envsiz;
60
} cfg;
62
} cfg;
61
 
63
 
62
 
64
 
-
 
65
#define RMOD_OFFSET_ENVSEG  0x08
-
 
66
#define RMOD_OFFSET_INPBUFF 0x0A
-
 
67
#define RMOD_OFFSET_ROUTINE 0x8C
-
 
68
 
63
/* returns segment where rmod is installed */
69
/* returns segment where rmod is installed */
64
unsigned short install_routine(void) {
70
unsigned short rmod_install(unsigned short envsize) {
65
  char far *ptr, far *mcb;
71
  char far *myptr, far *mcb;
66
  unsigned short far *owner;
72
  unsigned short far *owner;
67
  unsigned int memseg = 0xffff;
73
  unsigned int rmodseg = 0xffff;
-
 
74
  unsigned int envseg = 0;
-
 
75
 
-
 
76
  /* read my current env segment from PSP */
-
 
77
  _asm {
-
 
78
    push ax
-
 
79
    push bx
-
 
80
    mov bx, 0x2c
-
 
81
    mov ax, [bx]
-
 
82
    mov envseg, ax
-
 
83
    pop bx
-
 
84
    pop ax
-
 
85
  }
-
 
86
 
-
 
87
  printf("original (PSP) env buffer at %04X\r\n", envseg);
-
 
88
  /* if custom envsize requested, convert it to number of paragraphs */
-
 
89
  if (envsize != 0) {
-
 
90
    envsize += 15;
-
 
91
    envsize /= 16;
-
 
92
  }
-
 
93
 
68
 
94
 
69
  _asm {
95
  _asm {
70
    /* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */
96
    /* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */
71
    mov ax, 0x5802  /* GET UMB LINK STATE */
97
    mov ax, 0x5802  /* GET UMB LINK STATE */
72
    int 0x21
98
    int 0x21
73
    xor ah, ah
99
    xor ah, ah
74
    push ax         /* save link state on stack */
100
    push ax         /* save link state on stack */
75
    mov ax, 0x5803  /* SET UMB LINK STATE */
101
    mov ax, 0x5803  /* SET UMB LINK STATE */
76
    mov bx, 1
102
    mov bx, 1
77
    int 0x21
103
    int 0x21
78
    /* get current allocation strategy and save it in DX */
104
    /* get current allocation strategy and save it in DX */
79
    mov ax, 0x5800
105
    mov ax, 0x5800
80
    int 0x21
106
    int 0x21
81
    push ax
107
    push ax
82
    pop dx
108
    pop dx
83
    /* set strategy to 'last fit, try high then low memory' */
109
    /* set strategy to 'last fit, try high then low memory' */
84
    mov ax, 0x5801
110
    mov ax, 0x5801
85
    mov bx, 0x0082
111
    mov bx, 0x0082
86
    int 0x21
112
    int 0x21
87
    /* ask for a memory block and save the given segment to memseg */
113
    /* ask for a memory block and save the given segment to rmodseg */
88
    mov ah, 0x48
114
    mov ah, 0x48
89
    mov bx, (rmod_len + 15) / 16
115
    mov bx, (rmod_len + 15) / 16
90
    int 0x21
116
    int 0x21
91
    jc ALLOC_FAIL
117
    jc ALLOC_FAIL
92
    mov memseg, ax
118
    mov rmodseg, ax
-
 
119
    /* ask for a memory block for the environment and save it to envseg (only if custom size requested) */
-
 
120
    mov bx, envsize
-
 
121
    test bx, bx
-
 
122
    jz ALLOC_FAIL
-
 
123
    mov ah, 0x48
-
 
124
    int 0x21
-
 
125
    jc ALLOC_FAIL
-
 
126
    mov envseg, ax
-
 
127
 
93
    ALLOC_FAIL:
128
    ALLOC_FAIL:
94
    /* restore initial allocation strategy */
129
    /* restore initial allocation strategy */
95
    mov ax, 0x5801
130
    mov ax, 0x5801
96
    mov bx, dx
131
    mov bx, dx
97
    int 0x21
132
    int 0x21
98
    /* restore initial UMB memory link state */
133
    /* restore initial UMB memory link state */
99
    mov ax, 0x5803
134
    mov ax, 0x5803
100
    pop bx       /* pop initial UMB link state from stack */
135
    pop bx       /* pop initial UMB link state from stack */
101
    int 0x21
136
    int 0x21
102
  }
137
  }
103
 
138
 
104
  if (memseg == 0xffff) {
139
  if (rmodseg == 0xffff) {
105
    puts("malloc error");
140
    puts("malloc error");
106
    return(0xffff);
141
    return(0xffff);
107
  }
142
  }
-
 
143
 
-
 
144
  /* copy rmod to its destination */
108
  ptr = MK_FP(memseg, 0);
145
  myptr = MK_FP(rmodseg, 0);
-
 
146
  _fmemcpy(myptr, rmod, rmod_len);
-
 
147
 
-
 
148
  /* mark rmod memory as "self owned" */
109
  mcb = MK_FP(memseg - 1, 0);
149
  mcb = MK_FP(rmodseg - 1, 0);
110
  owner = (void far *)(mcb + 1);
150
  owner = (void far *)(mcb + 1);
-
 
151
  *owner = rmodseg;
111
  _fmemcpy(ptr, rmod, rmod_len);
152
  _fmemcpy(mcb + 8, "SVARCOM", 8);
112
 
153
 
-
 
154
  /* mark env memory as "self owned" (only if allocated by me) */
-
 
155
  if (envsize != 0) {
-
 
156
    printf("envseg allocated at %04X:0000 with %u paragraphs\r\n", envseg, envsize);
-
 
157
    mcb = MK_FP(envseg - 1, 0);
-
 
158
    owner = (void far *)(mcb + 1);
-
 
159
    *owner = rmodseg;
-
 
160
    _fmemcpy(mcb + 8, "SVARENV", 8);
-
 
161
  }
-
 
162
 
-
 
163
  /* write env segment to rmod buffer */
-
 
164
  owner = MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
-
 
165
  *owner = envseg;
-
 
166
 
-
 
167
/*
113
  printf("MCB sig: %c\r\nMCB owner: 0x%04X\r\n", mcb[0], *owner);
168
  printf("MCB sig: %c\r\nMCB owner: 0x%04X\r\n", mcb[0], *owner);
114
  {
169
  {
115
    int i;
170
    int i;
116
    for (i = 0; i < 17; i++) {
171
    for (i = 0; i < 17; i++) {
117
      printf("%02x ", mcb[i]);
172
      printf("%02x ", mcb[i]);
118
    }
173
    }
119
    printf("\r\n");
174
    printf("\r\n");
120
    for (i = 0; i < 17; i++) {
175
    for (i = 0; i < 17; i++) {
121
      if (mcb[i] > ' ') {
176
      if (mcb[i] > ' ') {
122
        printf(" %c ", mcb[i]);
177
        printf(" %c ", mcb[i]);
123
      } else {
178
      } else {
124
        printf(" . ", mcb[i]);
179
        printf(" . ", mcb[i]);
125
      }
180
      }
126
    }
181
    }
127
    printf("\r\n");
182
    printf("\r\n");
128
  }
183
  }*/
-
 
184
 
-
 
185
  return(rmodseg);
-
 
186
}
-
 
187
 
129
 
188
 
130
  /* mark memory as "self owned" */
189
/* returns zero if s1 starts with s2 */
-
 
190
static int strstartswith(const char *s1, const char *s2) {
131
  *owner = memseg;
191
  while (*s2 != 0) {
132
  _fmemcpy(mcb + 8, "COMMAND", 8);
192
    if (*s1 != *s2) return(-1);
-
 
193
    s1++;
-
 
194
    s2++;
-
 
195
  }
133
  return(memseg);
196
  return(0);
134
}
197
}
135
 
198
 
136
 
199
 
137
static void parse_argv(struct config *cfg, int argc, char **argv) {
200
static void parse_argv(struct config *cfg, int argc, char **argv) {
138
  int i;
201
  int i;
139
  memset(cfg, 0, sizeof(*cfg));
202
  memset(cfg, 0, sizeof(*cfg));
-
 
203
 
140
  for (i = 1; i < argc; i++) {
204
  for (i = 1; i < argc; i++) {
141
    if (strcmp(argv[i], "/locate") == 0) {
205
    if (strcmp(argv[i], "/locate") == 0) {
142
      cfg->locate = 1;
206
      cfg->locate = 1;
143
    }
207
    }
-
 
208
    if (strstartswith(argv[i], "/e:") == 0) {
-
 
209
      cfg->envsiz = atoi(argv[i]+3);
-
 
210
      if (cfg->envsiz < 64) cfg->envsiz = 0;
-
 
211
    }
144
  }
212
  }
145
}
213
}
146
 
214
 
147
 
215
 
148
/* scan memory for my shared buffer, return segment of buffer */
216
/* scan memory for my shared buffer, return segment of buffer */
149
static unsigned short find_shm(void) {
217
static unsigned short rmod_find(void) {
150
  unsigned short i;
218
  unsigned short i;
151
  unsigned short far *pattern;
219
  unsigned short far *pattern;
152
 
220
 
153
  /* iterate over all paragraphs, looking for my signature */
221
  /* iterate over all paragraphs, looking for my signature */
154
  for (i = 0; i != 65535; i++) {
222
  for (i = 0; i != 65535; i++) {
155
    pattern = MK_FP(i, 0);
223
    pattern = MK_FP(i, 0);
156
    if (pattern[1] != 0x1983) continue;
224
    if (pattern[0] != 0x1983) continue;
157
    if (pattern[2] != 0x1985) continue;
225
    if (pattern[1] != 0x1985) continue;
158
    if (pattern[3] != 0x2017) continue;
226
    if (pattern[2] != 0x2017) continue;
159
    if (pattern[4] != 0x2019) continue;
227
    if (pattern[3] != 0x2019) continue;
160
    return(i);
228
    return(i);
161
  }
229
  }
162
  return(0xffff);
230
  return(0xffff);
163
}
231
}
164
 
232
 
165
 
233
 
166
static int explode_progparams(char *s, char const **argvlist) {
234
static int explode_progparams(char *s, char const **argvlist) {
167
  int si = 0, argc = 0;
235
  int si = 0, argc = 0;
168
  for (;;) {
236
  for (;;) {
169
    /* skip to next non-space character */
237
    /* skip to next non-space character */
170
    while (s[si] == ' ') si++;
238
    while (s[si] == ' ') si++;
171
    /* end of string? */
239
    /* end of string? */
172
    if (s[si] == '\r') break;
240
    if (s[si] == '\r') break;
173
    /* set argv ptr */
241
    /* set argv ptr */
174
    argvlist[argc++] = s + si;
242
    argvlist[argc++] = s + si;
175
    /* find next space */
243
    /* find next space */
176
    while (s[si] != ' ' && s[si] != '\r') si++;
244
    while (s[si] != ' ' && s[si] != '\r') si++;
177
    /* is this end of string? */
245
    /* is this end of string? */
178
    if (s[si] == '\r') {
246
    if (s[si] == '\r') {
179
      s[si] = 0;
247
      s[si] = 0;
180
      break;
248
      break;
181
    }
249
    }
182
    /* not end: terminate arg and look for next one */
250
    /* not end: terminate arg and look for next one */
183
    s[si++] = 0;
251
    s[si++] = 0;
184
  }
252
  }
185
  /* terminate argvlist with a NULL value */
253
  /* terminate argvlist with a NULL value */
186
  argvlist[argc] = NULL;
254
  argvlist[argc] = NULL;
187
  return(argc);
255
  return(argc);
188
}
256
}
189
 
257
 
190
 
258
 
191
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
259
static void cmd_set(int argc, char const **argv, unsigned short env_seg) {
192
  char far *env = MK_FP(env_seg, 0);
260
  char far *env = MK_FP(env_seg, 0);
193
  char buff[256];
261
  char buff[256];
194
  int i;
262
  int i;
195
  while (*env != 0) {
263
  while (*env != 0) {
196
    /* copy string to local buff for display */
264
    /* copy string to local buff for display */
197
    for (i = 0;; i++) {
265
    for (i = 0;; i++) {
198
      buff[i] = *env;
266
      buff[i] = *env;
199
      env++;
267
      env++;
200
      if (buff[i] == 0) break;
268
      if (buff[i] == 0) break;
201
    }
269
    }
202
    puts(buff);
270
    puts(buff);
203
  }
271
  }
204
}
272
}
205
 
273
 
206
 
274
 
207
int main(int argc, char **argv) {
275
int main(int argc, char **argv) {
208
  struct config cfg;
276
  struct config cfg;
209
  unsigned short env_seg = 0, rmod_seg, rmod_buff = 0;
277
  unsigned short rmod_seg;
210
  void far *rmod_func;
278
  unsigned short far *rmod_envseg;
211
 
279
 
212
  parse_argv(&cfg, argc, argv);
280
  parse_argv(&cfg, argc, argv);
213
 
281
 
214
  rmod_seg = find_shm();
282
  rmod_seg = rmod_find();
215
  if (rmod_seg == 0xffff) {
283
  if (rmod_seg == 0xffff) {
216
    rmod_seg = install_routine();
284
    rmod_seg = rmod_install(cfg.envsiz);
217
    if (rmod_seg == 0xffff) {
285
    if (rmod_seg == 0xffff) {
218
      puts("ERROR: install_rmod() failed");
286
      puts("ERROR: rmod_install() failed");
219
      return(1);
287
      return(1);
220
    } else {
288
    } else {
221
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
289
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
222
    }
290
    }
223
  } else {
291
  } else {
224
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
292
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
225
  }
293
  }
226
 
294
 
227
  rmod_func = MK_FP(rmod_seg, 0x0A);
295
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
228
  /* fetch offset of buffer (result in AX) */
-
 
229
  _asm {
-
 
230
    call dword ptr [rmod_func]
-
 
231
    mov rmod_buff, ax
-
 
232
  }
-
 
233
 
296
 
-
 
297
  {
-
 
298
    unsigned short envsiz;
234
  printf("rmod_buff at %04X:%04X\r\n", rmod_seg, rmod_buff);
299
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
-
 
300
    envsiz = *sizptr;
-
 
301
    envsiz *= 16;
-
 
302
    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);
-
 
303
  }
235
 
304
 
236
  _asm {
305
  _asm {
237
    /* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I terminate */
306
    /* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I terminate */
238
    mov bx, 0x0a
307
    mov bx, 0x0a
239
    xor ax, ax
308
    xor ax, ax
240
    mov [bx], ax
309
    mov [bx], ax
241
    mov ax, rmod_seg
310
    mov ax, rmod_seg
242
    mov [bx+2], ax
311
    mov [bx+2], ax
243
    /* get the segment of my environment */
-
 
244
    mov bx, 0x2c
-
 
245
    mov ax, [bx]
-
 
246
    mov env_seg, ax
-
 
247
  }
312
  }
248
 
313
 
249
  printf("env_seg at %04X\r\n", env_seg);
-
 
250
 
-
 
251
  for (;;) {
314
  for (;;) {
252
    int i, argcount;
315
    int i, argcount;
253
    char far *cmdline = MK_FP(rmod_seg, rmod_buff + 2);
316
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
254
    char path[256] = "C:\\>$";
317
    char path[256] = "C:\\>$";
255
    char const *argvlist[256];
318
    char const *argvlist[256];
256
    union REGS r;
319
    union REGS r;
257
 
320
 
258
    /* print shell prompt */
321
    /* print shell prompt */
259
    r.h.ah = 0x09;
322
    r.h.ah = 0x09;
260
    r.x.dx = FP_OFF(path);
323
    r.x.dx = FP_OFF(path);
261
    intdos(&r, &r);
324
    intdos(&r, &r);
262
 
325
 
263
    /* wait for user input */
326
    /* wait for user input */
264
    _asm {
327
    _asm {
265
      push ds
328
      push ds
266
 
329
 
267
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
330
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
268
      mov ax, 0x4800
331
      mov ax, 0x4800
269
      int 0x2f
332
      int 0x2f
270
      mov bl, al /* save doskey status in BL */
333
      mov bl, al /* save doskey status in BL */
271
 
334
 
272
      /* set up buffered input */
335
      /* set up buffered input */
273
      mov ax, rmod_seg
336
      mov ax, rmod_seg
274
      push ax
337
      push ax
275
      pop ds
338
      pop ds
276
      mov dx, rmod_buff
339
      mov dx, RMOD_OFFSET_INPBUFF
277
 
340
 
278
      /* execute either DOS input or DOSKEY */
341
      /* execute either DOS input or DOSKEY */
279
      test bl, bl /* zf set if no DOSKEY present */
342
      test bl, bl /* zf set if no DOSKEY present */
280
      jnz DOSKEY
343
      jnz DOSKEY
281
 
344
 
282
      mov ah, 0x0a
345
      mov ah, 0x0a
283
      int 0x21
346
      int 0x21
284
      jmp short DONE
347
      jmp short DONE
285
 
348
 
286
      DOSKEY:
349
      DOSKEY:
287
      mov ax, 0x4810
350
      mov ax, 0x4810
288
      int 0x2f
351
      int 0x2f
289
 
352
 
290
      DONE:
353
      DONE:
291
      pop ds
354
      pop ds
292
    }
355
    }
293
    printf("\r\n");
356
    printf("\r\n");
294
 
357
 
295
    /* if nothing entered, loop again */
358
    /* if nothing entered, loop again */
296
    if (cmdline[-1] == 0) continue;
359
    if (cmdline[-1] == 0) continue;
297
 
360
 
298
    /* copy buffer to a near var (incl. trailing CR) */
361
    /* copy buffer to a near var (incl. trailing CR) */
299
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
362
    _fmemcpy(path, cmdline, cmdline[-1] + 1);
300
 
363
 
301
    argcount = explode_progparams(path, argvlist);
364
    argcount = explode_progparams(path, argvlist);
302
 
365
 
303
    /* if nothing args found (eg. all-spaces), loop again */
366
    /* if nothing args found (eg. all-spaces), loop again */
304
    if (argcount == 0) continue;
367
    if (argcount == 0) continue;
305
 
368
 
306
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
369
    printf("got %u bytes of cmdline (%d args)\r\n", cmdline[-1], argcount);
307
    for (i = 0; i < argcount; i++) {
370
    for (i = 0; i < argcount; i++) {
308
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
371
      printf("arg #%d = '%s'\r\n", i, argvlist[i]);
309
    }
372
    }
310
 
373
 
311
    /* TODO is it an internal command? */
374
    /* TODO is it an internal command? */
312
    if (strcmp(argvlist[0], "set") == 0) {
375
    if (strcmp(argvlist[0], "set") == 0) {
313
      cmd_set(argcount, argvlist, env_seg);
376
      cmd_set(argcount, argvlist, *rmod_envseg);
314
      continue;
377
      continue;
315
    }
378
    }
316
 
379
 
317
    execvp(argvlist[0], argvlist);
380
    execvp(argvlist[0], argvlist);
318
 
381
 
319
    /* execvp() replaces the current process by the new one
382
    /* execvp() replaces the current process by the new one
320
    if I am still alive then external command failed to execute */
383
    if I am still alive then external command failed to execute */
321
    puts("Bad command or file name");
384
    puts("Bad command or file name");
322
 
385
 
323
  }
386
  }
324
 
387
 
325
  return(0);
388
  return(0);
326
}
389
}
327
 
390