Subversion Repositories SvarDOS

Rev

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

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