Subversion Repositories SvarDOS

Rev

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

Rev 362 Rev 364
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 "helpers.h"
57
#include "helpers.h"
58
#include "rmodinit.h"
58
#include "rmodinit.h"
59
 
59
 
60
struct config {
60
struct config {
61
  int locate;
61
  int locate;
62
  int install;
62
  int install;
63
  int envsiz;
63
  int envsiz;
64
} cfg;
64
} cfg;
65
 
65
 
66
 
66
 
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
67
static void parse_argv(struct config *cfg, int argc, char **argv) {
68
  int i;
68
  int i;
69
  memset(cfg, 0, sizeof(*cfg));
69
  memset(cfg, 0, sizeof(*cfg));
70
 
70
 
71
  for (i = 1; i < argc; i++) {
71
  for (i = 1; i < argc; i++) {
72
    if (strcmp(argv[i], "/locate") == 0) {
72
    if (strcmp(argv[i], "/locate") == 0) {
73
      cfg->locate = 1;
73
      cfg->locate = 1;
74
    }
74
    }
75
    if (strstartswith(argv[i], "/e:") == 0) {
75
    if (strstartswith(argv[i], "/e:") == 0) {
76
      cfg->envsiz = atoi(argv[i]+3);
76
      cfg->envsiz = atoi(argv[i]+3);
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
77
      if (cfg->envsiz < 64) cfg->envsiz = 0;
78
    }
78
    }
79
  }
79
  }
80
}
80
}
81
 
81
 
82
 
82
 
83
static int explode_progparams(char *s, char const **argvlist) {
-
 
84
  int si = 0, argc = 0;
-
 
85
  for (;;) {
-
 
86
    /* skip to next non-space character */
-
 
87
    while (s[si] == ' ') si++;
-
 
88
    /* end of string? */
-
 
89
    if (s[si] == '\r') break;
-
 
90
    /* set argv ptr */
-
 
91
    argvlist[argc++] = s + si;
-
 
92
    /* find next space */
-
 
93
    while (s[si] != ' ' && s[si] != '\r') si++;
-
 
94
    /* is this end of string? */
-
 
95
    if (s[si] == '\r') {
-
 
96
      s[si] = 0;
-
 
97
      break;
-
 
98
    }
-
 
99
    /* not end: terminate arg and look for next one */
-
 
100
    s[si++] = 0;
-
 
101
  }
-
 
102
  /* terminate argvlist with a NULL value */
-
 
103
  argvlist[argc] = NULL;
-
 
104
  return(argc);
-
 
105
}
-
 
106
 
-
 
107
 
-
 
108
static void buildprompt(char *s, const char *fmt) {
83
static void buildprompt(char *s, const char *fmt) {
109
  for (; *fmt != 0; fmt++) {
84
  for (; *fmt != 0; fmt++) {
110
    if (*fmt != '$') {
85
    if (*fmt != '$') {
111
      *s = *fmt;
86
      *s = *fmt;
112
      s++;
87
      s++;
113
      continue;
88
      continue;
114
    }
89
    }
115
    /* escape code ($P, etc) */
90
    /* escape code ($P, etc) */
116
    fmt++;
91
    fmt++;
117
    switch (*fmt) {
92
    switch (*fmt) {
118
      case 'Q':  /* $Q = = (equal sign) */
93
      case 'Q':  /* $Q = = (equal sign) */
119
      case 'q':
94
      case 'q':
120
        *s = '=';
95
        *s = '=';
121
        s++;
96
        s++;
122
        break;
97
        break;
123
      case '$':  /* $$ = $ (dollar sign) */
98
      case '$':  /* $$ = $ (dollar sign) */
124
        *s = '$';
99
        *s = '$';
125
        s++;
100
        s++;
126
        break;
101
        break;
127
      case 'T':  /* $t = current time */
102
      case 'T':  /* $t = current time */
128
      case 't':
103
      case 't':
129
        s += sprintf(s, "00:00"); /* TODO */
104
        s += sprintf(s, "00:00"); /* TODO */
130
        break;
105
        break;
131
      case 'D':  /* $D = current date */
106
      case 'D':  /* $D = current date */
132
      case 'd':
107
      case 'd':
133
        s += sprintf(s, "1985-07-29"); /* TODO */
108
        s += sprintf(s, "1985-07-29"); /* TODO */
134
        break;
109
        break;
135
      case 'P':  /* $P = current drive and path */
110
      case 'P':  /* $P = current drive and path */
136
      case 'p':
111
      case 'p':
137
        _asm {
112
        _asm {
138
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
113
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
139
          int 0x21
114
          int 0x21
140
          mov bx, s
115
          mov bx, s
141
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
116
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
142
        }
117
        }
143
        *s += 'A';
118
        *s += 'A';
144
        s++;
119
        s++;
145
        *s = ':';
120
        *s = ':';
146
        s++;
121
        s++;
147
        *s = '\\';
122
        *s = '\\';
148
        s++;
123
        s++;
149
        _asm {
124
        _asm {
150
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
125
          mov ah, 0x47    /* DOS 2+ - CWD - GET CURRENT DIRECTORY */
151
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
126
          xor dl,dl       /* DL = drive number (00h = default, 01h = A:, etc) */
152
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
127
          mov si, s       /* DS:SI -> 64-byte buffer for ASCIZ pathname */
153
          int 0x21
128
          int 0x21
154
        }
129
        }
155
        while (*s != 0) s++; /* move ptr forward to end of pathname */
130
        while (*s != 0) s++; /* move ptr forward to end of pathname */
156
        break;
131
        break;
157
      case 'V':  /* $V = DOS version number */
132
      case 'V':  /* $V = DOS version number */
158
      case 'v':
133
      case 'v':
159
        s += sprintf(s, "VER"); /* TODO */
134
        s += sprintf(s, "VER"); /* TODO */
160
        break;
135
        break;
161
      case 'N':  /* $N = current drive */
136
      case 'N':  /* $N = current drive */
162
      case 'n':
137
      case 'n':
163
        _asm {
138
        _asm {
164
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
139
          mov ah, 0x19    /* DOS 1+ - GET CURRENT DRIVE */
165
          int 0x21
140
          int 0x21
166
          mov bx, s
141
          mov bx, s
167
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
142
          mov [bx], al  /* AL = drive (00 = A:, 01 = B:, etc */
168
        }
143
        }
169
        *s += 'A';
144
        *s += 'A';
170
        s++;
145
        s++;
171
        break;
146
        break;
172
      case 'G':  /* $G = > (greater-than sign) */
147
      case 'G':  /* $G = > (greater-than sign) */
173
      case 'g':
148
      case 'g':
174
        *s = '>';
149
        *s = '>';
175
        s++;
150
        s++;
176
        break;
151
        break;
177
      case 'L':  /* $L = < (less-than sign) */
152
      case 'L':  /* $L = < (less-than sign) */
178
      case 'l':
153
      case 'l':
179
        *s = '<';
154
        *s = '<';
180
        s++;
155
        s++;
181
        break;
156
        break;
182
      case 'B':  /* $B = | (pipe) */
157
      case 'B':  /* $B = | (pipe) */
183
      case 'b':
158
      case 'b':
184
        *s = '|';
159
        *s = '|';
185
        s++;
160
        s++;
186
        break;
161
        break;
187
      case 'H':  /* $H = backspace (erases previous character) */
162
      case 'H':  /* $H = backspace (erases previous character) */
188
      case 'h':
163
      case 'h':
189
        *s = '\b';
164
        *s = '\b';
190
        s++;
165
        s++;
191
        break;
166
        break;
192
      case 'E':  /* $E = Escape code (ASCII 27) */
167
      case 'E':  /* $E = Escape code (ASCII 27) */
193
      case 'e':
168
      case 'e':
194
        *s = 27;
169
        *s = 27;
195
        s++;
170
        s++;
196
        break;
171
        break;
197
      case '_':  /* $_ = CR+LF */
172
      case '_':  /* $_ = CR+LF */
198
        *s = '\r';
173
        *s = '\r';
199
        s++;
174
        s++;
200
        *s = '\n';
175
        *s = '\n';
201
        s++;
176
        s++;
202
        break;
177
        break;
203
    }
178
    }
204
  }
179
  }
205
  *s = '$';
180
  *s = '$';
206
}
181
}
207
 
182
 
208
 
183
 
-
 
184
static void run_as_external(const char far *cmdline) {
-
 
185
  char buff[256];
-
 
186
  char const *argvlist[256];
-
 
187
  int i, n;
-
 
188
  /* copy buffer to a near var (incl. trailing CR), insert a space before
-
 
189
     every slash to make sure arguments are well separated */
-
 
190
  n = 0;
-
 
191
  i = 0;
-
 
192
  for (;;) {
-
 
193
    if (cmdline[i] == '/') buff[n++] = ' ';
-
 
194
    buff[n++] = cmdline[i++];
-
 
195
    if (buff[n] == 0) break;
-
 
196
  }
-
 
197
 
-
 
198
  cmd_explode(buff, cmdline, argvlist);
-
 
199
 
-
 
200
  /* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
-
 
201
 
-
 
202
  /* must be an external command then. this call should never return, unless
-
 
203
   * the other program failed to be executed. */
-
 
204
  execvp(argvlist[0], argvlist);
-
 
205
}
-
 
206
 
-
 
207
 
209
int main(int argc, char **argv) {
208
int main(int argc, char **argv) {
210
  struct config cfg;
209
  struct config cfg;
211
  unsigned short rmod_seg;
210
  unsigned short rmod_seg;
212
  unsigned short far *rmod_envseg;
211
  unsigned short far *rmod_envseg;
213
  unsigned short far *lastexitcode;
212
  unsigned short far *lastexitcode;
214
 
213
 
215
  parse_argv(&cfg, argc, argv);
214
  parse_argv(&cfg, argc, argv);
216
 
215
 
217
  rmod_seg = rmod_find();
216
  rmod_seg = rmod_find();
218
  if (rmod_seg == 0xffff) {
217
  if (rmod_seg == 0xffff) {
219
    rmod_seg = rmod_install(cfg.envsiz);
218
    rmod_seg = rmod_install(cfg.envsiz);
220
    if (rmod_seg == 0xffff) {
219
    if (rmod_seg == 0xffff) {
221
      puts("ERROR: rmod_install() failed");
220
      puts("ERROR: rmod_install() failed");
222
      return(1);
221
      return(1);
223
    } else {
222
    } else {
224
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
223
      printf("rmod installed at seg 0x%04X\r\n", rmod_seg);
225
    }
224
    }
226
  } else {
225
  } else {
227
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
226
    printf("rmod found at seg 0x%04x\r\n", rmod_seg);
228
  }
227
  }
229
 
228
 
230
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
229
  rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG);
231
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
230
  lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE);
232
 
231
 
233
  {
232
  {
234
    unsigned short envsiz;
233
    unsigned short envsiz;
235
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
234
    unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3);
236
    envsiz = *sizptr;
235
    envsiz = *sizptr;
237
    envsiz *= 16;
236
    envsiz *= 16;
238
    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);
237
    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);
239
  }
238
  }
240
 
239
 
241
  for (;;) {
240
  for (;;) {
242
    int i, n, argcount;
-
 
243
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
241
    char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2);
-
 
242
 
-
 
243
    /* revert input history terminator to \r */
244
    char buff[256];
244
    if (cmdline[-1] != 0) {
245
    char const *argvlist[256];
245
      cmdline[(unsigned short)(cmdline[-1])] = '\r';
-
 
246
    }
246
 
247
 
247
    {
248
    {
248
      /* print shell prompt */
249
      /* print shell prompt */
-
 
250
      char buff[256];
249
      char *promptptr = buff;
251
      char *promptptr = buff;
250
      buildprompt(promptptr, "$p$g"); /* TODO: prompt should be configurable via environment */
252
      buildprompt(promptptr, "$p$g"); /* TODO: prompt should be configurable via environment */
251
      _asm {
253
      _asm {
-
 
254
        push ax
252
        push dx
255
        push dx
253
        mov ah, 0x09
256
        mov ah, 0x09
254
        mov dx, promptptr
257
        mov dx, promptptr
255
        int 0x21
258
        int 0x21
256
        pop dx
259
        pop dx
-
 
260
        pop ax
257
      }
261
      }
258
    }
262
    }
259
 
263
 
260
    /* wait for user input */
264
    /* wait for user input */
261
    _asm {
265
    _asm {
-
 
266
      push ax
-
 
267
      push bx
-
 
268
      push cx
-
 
269
      push dx
262
      push ds
270
      push ds
263
 
271
 
264
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
272
      /* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */
265
      mov ax, 0x4800
273
      mov ax, 0x4800
266
      int 0x2f
274
      int 0x2f
267
      mov bl, al /* save doskey status in BL */
275
      mov bl, al /* save doskey status in BL */
268
 
276
 
269
      /* set up buffered input */
277
      /* set up buffered input */
270
      mov ax, rmod_seg
278
      mov ax, rmod_seg
271
      push ax
279
      push ax
272
      pop ds
280
      pop ds
273
      mov dx, RMOD_OFFSET_INPBUFF
281
      mov dx, RMOD_OFFSET_INPBUFF
274
 
282
 
275
      /* execute either DOS input or DOSKEY */
283
      /* execute either DOS input or DOSKEY */
276
      test bl, bl /* zf set if no DOSKEY present */
284
      test bl, bl /* zf set if no DOSKEY present */
277
      jnz DOSKEY
285
      jnz DOSKEY
278
 
286
 
279
      mov ah, 0x0a
287
      mov ah, 0x0a
280
      int 0x21
288
      int 0x21
281
      jmp short DONE
289
      jmp short DONE
282
 
290
 
283
      DOSKEY:
291
      DOSKEY:
284
      mov ax, 0x4810
292
      mov ax, 0x4810
285
      int 0x2f
293
      int 0x2f
286
 
294
 
287
      DONE:
295
      DONE:
288
      pop ds
296
      pop ds
-
 
297
      pop dx
-
 
298
      pop cx
-
 
299
      pop bx
-
 
300
      pop ax
289
    }
301
    }
290
    printf("\r\n");
302
    printf("\r\n");
291
 
303
 
292
    /* if nothing entered, loop again */
304
    /* if nothing entered, loop again */
293
    if (cmdline[-1] == 0) continue;
305
    if (cmdline[-1] == 0) continue;
294
 
306
 
295
    /* copy buffer to a near var (incl. trailing CR), insert a space before
-
 
296
       every slash to make sure arguments are well separated */
307
    /* replace \r by a zero terminator */
297
    n = 0;
-
 
298
    for (i = 0; i < cmdline[-1] + 1; i++) {
-
 
299
      if (cmdline[i] == '/') buff[n++] = ' ';
308
    cmdline[(unsigned char)(cmdline[-1])] = 0;
300
      buff[n++] = cmdline[i];
-
 
301
    }
-
 
302
    buff[n] = 0;
-
 
303
 
-
 
304
    argcount = explode_progparams(buff, argvlist);
-
 
305
 
-
 
306
    /* if no args found (eg. all-spaces), loop again */
-
 
307
    if (argcount == 0) continue;
-
 
308
 
-
 
309
    /* for (i = 0; i < argcount; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */
-
 
310
 
309
 
311
    /* is it about quitting? */
310
    /* move pointer forward to skip over any leading spaces */
312
    if (imatch(argvlist[0], "exit")) break;
311
    while (*cmdline == ' ') cmdline++;
313
 
312
 
314
    /* try running it as an internal command */
313
    /* try matching (and executing) an internal command */
315
    {
314
    {
316
      int ecode = cmd_process(argcount, argvlist, *rmod_envseg, cmdline);
315
      int ecode = cmd_process(*rmod_envseg, cmdline);
317
      if (ecode >= 0) *lastexitcode = ecode;
316
      if (ecode >= 0) *lastexitcode = ecode;
318
      if (ecode >= -1) continue; /* command is internal but did not set an exit code */
317
      if (ecode >= -1) continue; /* internal command executed */
319
    }
318
    }
320
 
319
 
321
    /* must be an external command then */
320
    /* if here, then this was not an internal command */
322
    execvp(argvlist[0], argvlist);
321
    run_as_external(cmdline);
323
 
322
 
324
    /* execvp() replaces the current process by the new one
323
    /* execvp() replaces the current process by the new one
325
    if I am still alive then external command failed to execute */
324
    if I am still alive then external command failed to execute */
326
    puts("Bad command or file name");
325
    puts("Bad command or file name");
327
 
326
 
328
  }
327
  }
329
 
328
 
330
  return(0);
329
  return(0);
331
}
330
}
332
 
331