/svarcom/cmd/cd.c |
---|
10,14 → 10,14 |
* Type CD without parameters to display the current drive and directory. |
*/ |
static int cmd_cd(int argc, char const **argv) { |
static int cmd_cd(const struct cmd_funcparam *p) { |
/* two arguments max */ |
if (argc > 2) { |
if (p->argc > 1) { |
puts("Too many parameters"); |
} |
/* no argument? display current drive and dir ("CWD") */ |
if (argc == 1) { |
if (p->argc == 0) { |
char buff[64]; |
char *buffptr = buff; |
_asm { |
40,21 → 40,22 |
xor dl, dl /* select drive (0 = current drive) */ |
mov si, buffptr /* 64-byte buffer for ASCIZ pathname */ |
int 0x21 |
pop si |
pop dx |
pop ax |
pop dx |
pop si |
} |
puts(buff); |
} |
/* argument can be either a drive (D:) or a path */ |
if (argc == 2) { |
if (p->argc == 1) { |
const char *arg = p->argv[0]; |
unsigned short err = 0; |
/* drive (CD B:) */ |
if ((argv[1][0] != '\\') && (argv[1][1] == ':') && (argv[1][2] == 0)) { |
if ((arg[0] != '\\') && (arg[1] == ':') && (arg[2] == 0)) { |
char buff[64]; |
char *buffptr = buff; |
unsigned char drive = argv[1][0]; |
unsigned short err = 0; |
unsigned char drive = arg[0]; |
if (drive >= 'a') { |
drive -= 'a'; |
} else { |
76,19 → 77,13 |
pop ax |
pop si |
} |
if (err != 0) { |
if (err != 0) puts(doserr(err)); |
} else { |
printf("%c:\\%s\r\n", drive + 'A' - 1, buff); |
} |
if (err == 0) printf("%c:\\%s\r\n", drive + 'A' - 1, buff); |
} else { /* path */ |
char const *dir = argv[1]; |
unsigned short err = 0; |
_asm { |
push dx |
push ax |
mov ah, 0x3B /* CHDIR (set current directory) */ |
mov dx, dir |
mov dx, arg |
int 0x21 |
jnc DONE |
mov [err], ax |
96,8 → 91,8 |
pop ax |
pop dx |
} |
if (err != 0) puts(doserr(err)); |
} |
if (err != 0) puts(doserr(err)); |
} |
return(-1); |
/svarcom/cmd/exit.c |
---|
0,0 → 1,18 |
/* |
* exit |
* |
* Quits the COMMAND.COM program (command interpreter) |
* |
*/ |
static int cmd_exit(const struct cmd_funcparam *p) { |
if ((p->argc == 1) && (imatch(p->argv[0], "/?"))) { |
puts("EXIT"); |
puts(""); |
puts("Quits the COMMAND.COM program (command interpreter)"); |
puts(""); |
} else { |
exit(0); |
} |
return(-1); |
} |
/svarcom/cmd/set.c |
---|
7,13 → 7,12 |
#include "env.h" |
static int cmd_set(int argc, char const **argv, unsigned short env_seg, const char far *cmdline) { |
char far *env = MK_FP(env_seg, 0); |
static int cmd_set(const struct cmd_funcparam *p) { |
char far *env = MK_FP(p->env_seg, 0); |
char buff[256]; |
int i; |
/* no arguments - display content */ |
if (argc == 1) { |
if (p->argc == 0) { |
while (*env != 0) { |
/* copy string to local buff for display */ |
for (i = 0;; i++) { |
23,7 → 22,7 |
} |
puts(buff); |
} |
} else if ((argc == 2) && (imatch(argv[1], "/?"))) { |
} else if ((p->argc == 1) && (imatch(p->argv[0], "/?"))) { |
puts("TODO: help screen"); /* TODO */ |
} else { /* set variable (do not rely on argv, SET has its own rules...) */ |
const char far *ptr; |
30,7 → 29,7 |
char buff[256]; |
unsigned short i; |
/* locate the first space */ |
for (ptr = cmdline; *ptr != ' '; ptr++); |
for (ptr = p->cmdline; *ptr != ' '; ptr++); |
/* now locate the first non-space: that's where the variable name begins */ |
for (; *ptr == ' '; ptr++); |
/* copy variable to buff and switch it upercase */ |
52,7 → 51,7 |
buff[i] = 0; |
/* commit variable to environment */ |
i = env_setvar(env_seg, buff); |
i = env_setvar(p->env_seg, buff); |
if (i == ENV_INVSYNT) goto syntax_err; |
if (i == ENV_NOTENOM) puts("Not enough available space within the environment block"); |
} |
/svarcom/cmd.c |
---|
5,19 → 5,114 |
#include <i86.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include "doserr.h" |
#include "helpers.h" |
struct cmd_funcparam { |
int argc; |
const char *argv[256]; |
unsigned short env_seg; |
const char far *cmdline; |
}; |
#include "cmd/cd.c" |
#include "cmd/exit.c" |
#include "cmd/set.c" |
#include "cmd.h" |
int cmd_process(int argc, const char **argv, unsigned short env_seg, const char far *cmdline) { |
if ((imatch(argv[0], "cd")) || (imatch(argv[0], "chdir"))) return(cmd_cd(argc, argv)); |
if (imatch(argv[0], "set")) return(cmd_set(argc, argv, env_seg, cmdline)); |
struct CMD_ID { |
const char *cmd; |
int (*func_ptr)(const struct cmd_funcparam *); /* pointer to handling function */ |
}; |
return(-2); /* command is not recognized as internal */ |
const struct CMD_ID INTERNAL_CMDS[] = { |
{"CD", cmd_cd}, |
{"CHDIR", cmd_cd}, |
{"EXIT", cmd_exit}, |
{"SET", cmd_set}, |
{NULL, NULL} |
}; |
/* NULL if cmdline is not matching an internal command, otherwise returns a |
* pointer to a CMD_ID struct */ |
static const struct CMD_ID *cmd_match(const char far *cmdline, unsigned short *argoffset) { |
unsigned short i; |
char buff[10]; |
/* copy command to buffer, until space, NULL, tab, return, dot, slash or backslash */ |
for (i = 0; i < 9; i++) { |
if (cmdline[i] == ' ') break; |
if (cmdline[i] == 0) break; |
if (cmdline[i] == '\t') break; |
if (cmdline[i] == '\r') break; |
if (cmdline[i] == '.') break; |
if (cmdline[i] == '/') break; |
if (cmdline[i] == '\\') break; |
buff[i] = cmdline[i]; |
} |
buff[i] = 0; |
/* advance to nearest non-space to find where arguments start */ |
while (cmdline[i] == ' ') i++; |
*argoffset = i; |
/* try matching an internal command */ |
for (i = 0; INTERNAL_CMDS[i].cmd != NULL; i++) { |
/*printf("imatch(%s,%s)\r\n", buff, INTERNAL_CMDS[i].cmd); */ |
if (imatch(buff, INTERNAL_CMDS[i].cmd)) { |
/*printf("match cmd i=%u (buff=%s)\r\n", i, buff);*/ |
return(&(INTERNAL_CMDS[i])); |
} |
} |
return(NULL); /* command is not recognized as internal */ |
} |
/* explodes a command into an array of arguments where last arg is NULL |
* returns number of args */ |
unsigned short cmd_explode(char *buff, const char far *s, char const **argvlist) { |
int si = 0, argc = 0, i = 0; |
for (;;) { |
/* skip to next non-space character */ |
while (s[si] == ' ') si++; |
/* end of string? */ |
if (s[si] == 0) break; |
/* set argv ptr */ |
argvlist[argc++] = buff + i; |
/* find next space while copying arg to local buffer */ |
do { |
buff[i++] = s[si++]; |
} while (s[si] != ' ' && s[si] != 0 && s[si] != '/'); |
buff[i++] = 0; |
/* is this end of string? */ |
if (s[si] == 0) break; |
} |
argvlist[argc] = NULL; |
return(argc); |
} |
int cmd_process(unsigned short env_seg, const char far *cmdline) { |
const struct CMD_ID *cmdptr; |
struct cmd_funcparam p; |
unsigned short argoffset; |
char cmdbuff[256]; |
cmdptr = cmd_match(cmdline, &argoffset); |
if (cmdptr == NULL) return(-2); /* command is not recognized as internal */ |
/* printf("recognized internal command: '%s', tail of command at offset %u\r\n", cmdptr->cmd, argoffset); */ |
/* prepare function parameters and feed it to the cmd handling function */ |
p.argc = cmd_explode(cmdbuff, cmdline + argoffset, p.argv); |
p.env_seg = env_seg; |
p.cmdline = cmdline; |
return((cmdptr->func_ptr)(&p)); |
} |
/svarcom/cmd.h |
---|
1,6 → 1,11 |
#ifndef CMD_H |
#define CMD_H |
int cmd_process(int argc, const char **argv, unsigned short env_seg, const char far *cmdline); |
/* process internal commands */ |
int cmd_process(unsigned short env_seg, const char far *cmdline); |
/* explodes a command into an array of arguments where last arg is NULL |
* returns number of args */ |
unsigned short cmd_explode(char *buff, const char far *s, char const **argvlist); |
#endif |
/svarcom/command.c |
---|
80,31 → 80,6 |
} |
static int explode_progparams(char *s, char const **argvlist) { |
int si = 0, argc = 0; |
for (;;) { |
/* skip to next non-space character */ |
while (s[si] == ' ') si++; |
/* end of string? */ |
if (s[si] == '\r') break; |
/* set argv ptr */ |
argvlist[argc++] = s + si; |
/* find next space */ |
while (s[si] != ' ' && s[si] != '\r') si++; |
/* is this end of string? */ |
if (s[si] == '\r') { |
s[si] = 0; |
break; |
} |
/* not end: terminate arg and look for next one */ |
s[si++] = 0; |
} |
/* terminate argvlist with a NULL value */ |
argvlist[argc] = NULL; |
return(argc); |
} |
static void buildprompt(char *s, const char *fmt) { |
for (; *fmt != 0; fmt++) { |
if (*fmt != '$') { |
206,6 → 181,30 |
} |
static void run_as_external(const char far *cmdline) { |
char buff[256]; |
char const *argvlist[256]; |
int i, n; |
/* copy buffer to a near var (incl. trailing CR), insert a space before |
every slash to make sure arguments are well separated */ |
n = 0; |
i = 0; |
for (;;) { |
if (cmdline[i] == '/') buff[n++] = ' '; |
buff[n++] = cmdline[i++]; |
if (buff[n] == 0) break; |
} |
cmd_explode(buff, cmdline, argvlist); |
/* for (i = 0; argvlist[i] != NULL; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */ |
/* must be an external command then. this call should never return, unless |
* the other program failed to be executed. */ |
execvp(argvlist[0], argvlist); |
} |
int main(int argc, char **argv) { |
struct config cfg; |
unsigned short rmod_seg; |
239,26 → 238,35 |
} |
for (;;) { |
int i, n, argcount; |
char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2); |
char buff[256]; |
char const *argvlist[256]; |
/* revert input history terminator to \r */ |
if (cmdline[-1] != 0) { |
cmdline[(unsigned short)(cmdline[-1])] = '\r'; |
} |
{ |
/* print shell prompt */ |
char buff[256]; |
char *promptptr = buff; |
buildprompt(promptptr, "$p$g"); /* TODO: prompt should be configurable via environment */ |
_asm { |
push ax |
push dx |
mov ah, 0x09 |
mov dx, promptptr |
int 0x21 |
pop dx |
pop ax |
} |
} |
/* wait for user input */ |
_asm { |
push ax |
push bx |
push cx |
push dx |
push ds |
/* is DOSKEY support present? (INT 2Fh, AX=4800h, returns non-zero in AL if present) */ |
286,6 → 294,10 |
DONE: |
pop ds |
pop dx |
pop cx |
pop bx |
pop ax |
} |
printf("\r\n"); |
292,34 → 304,21 |
/* if nothing entered, loop again */ |
if (cmdline[-1] == 0) continue; |
/* copy buffer to a near var (incl. trailing CR), insert a space before |
every slash to make sure arguments are well separated */ |
n = 0; |
for (i = 0; i < cmdline[-1] + 1; i++) { |
if (cmdline[i] == '/') buff[n++] = ' '; |
buff[n++] = cmdline[i]; |
} |
buff[n] = 0; |
/* replace \r by a zero terminator */ |
cmdline[(unsigned char)(cmdline[-1])] = 0; |
argcount = explode_progparams(buff, argvlist); |
/* move pointer forward to skip over any leading spaces */ |
while (*cmdline == ' ') cmdline++; |
/* if no args found (eg. all-spaces), loop again */ |
if (argcount == 0) continue; |
/* for (i = 0; i < argcount; i++) printf("arg #%d = '%s'\r\n", i, argvlist[i]); */ |
/* is it about quitting? */ |
if (imatch(argvlist[0], "exit")) break; |
/* try running it as an internal command */ |
/* try matching (and executing) an internal command */ |
{ |
int ecode = cmd_process(argcount, argvlist, *rmod_envseg, cmdline); |
int ecode = cmd_process(*rmod_envseg, cmdline); |
if (ecode >= 0) *lastexitcode = ecode; |
if (ecode >= -1) continue; /* command is internal but did not set an exit code */ |
if (ecode >= -1) continue; /* internal command executed */ |
} |
/* must be an external command then */ |
execvp(argvlist[0], argvlist); |
/* if here, then this was not an internal command */ |
run_as_external(cmdline); |
/* execvp() replaces the current process by the new one |
if I am still alive then external command failed to execute */ |
/svarcom/doserr.c |
---|
0,0 → 1,56 |
/* |
* translates a DOS extended error into a human string |
* (as defined by INT 21/AH=59h/BX=0000h) |
*/ |
#include <stdio.h> |
#include "doserr.h" |
const char *doserr(unsigned short err) { |
static char buf[24]; |
switch (err) { |
case 0x00: return("Success"); |
case 0x01: return("Function number invalid"); |
case 0x02: return("File not found"); |
case 0x03: return("Path not found"); |
case 0x04: return("Too many open files (no handles available)"); |
case 0x05: return("Access denied"); |
case 0x06: return("Invalid handle"); |
case 0x07: return("Memory control block destroyed"); |
case 0x08: return("Insufficient memory"); |
case 0x09: return("Memory block address invalid"); |
case 0x0A: return("Environment invalid"); |
case 0x0B: return("Format invalid"); |
case 0x0C: return("Access code invalid"); |
case 0x0D: return("Data invalid"); |
case 0x0F: return("Invalid drive"); |
case 0x10: return("Attemted to remove current directory"); |
case 0x11: return("Not same device"); |
case 0x12: return("No more files"); |
case 0x13: return("Disk write-protected"); |
case 0x14: return("Unknown unit"); |
case 0x15: return("Drive not ready"); |
case 0x16: return("Unknown command"); |
case 0x17: return("Data error (CRC)"); |
case 0x18: return("Bad request structure length"); |
case 0x19: return("Seek error"); |
case 0x1A: return("Unknown media type (non-DOS disk)"); |
case 0x1B: return("Sector not found"); |
case 0x1C: return("Printer out of paper"); |
case 0x1D: return("Write fault"); |
case 0x1E: return("Read fault"); |
case 0x1F: return("General failure"); |
case 0x20: return("Sharing violation"); |
case 0x21: return("Lock violation"); |
case 0x22: return("Disk change invalid"); |
case 0x23: return("FCB unavailable"); |
case 0x24: return("Sharing buffer overflow"); |
case 0x25: return("Code page mismatch"); |
case 0x26: return("Cannot complete file operations (EOF / out of input)"); |
case 0x27: return("Insufficient disk space"); |
default: |
snprintf(buf, sizeof(buf), "DOS ERROR 0x%02X", err); |
return(buf); |
} |
} |
/svarcom/doserr.h |
---|
0,0 → 1,11 |
/* |
* translates a DOS extended error into a human string |
* (as defined by INT 21/AH=59h/BX=0000h) |
*/ |
#ifndef DOSERR_H |
#define DOSERR_H |
const char *doserr(unsigned short err); |
#endif |