/svarcom/trunk/cmd/_notimpl.c |
---|
0,0 → 1,8 |
/* |
* handler for all "not implemented yet" commands |
*/ |
static int cmd_notimpl(struct cmd_funcparam *p) { |
outputnl("This command is not implemented yet. Sorry!"); |
return(-1); |
} |
/svarcom/trunk/cmd/cd.c |
---|
0,0 → 1,124 |
/* |
* chdir |
* |
* displays the name of or changes the current directory. |
* |
* CHDIR [drive:][path] |
* CD.. |
* |
* Type CD drive: to display the current directory in the specified drive. |
* Type CD without parameters to display the current drive and directory. |
*/ |
/* display current path drive d (A=1, B=2, etc) |
* returns 0 on success, doserr otherwise */ |
static unsigned short cmd_cd_curpathfordrv(char *buff, unsigned char d) { |
unsigned short r = 0; |
buff[0] = d + 'A' - 1; |
buff[1] = ':'; |
buff[2] = '\\'; |
_asm { |
push si |
push ax |
push dx |
mov ah, 0x47 /* get current directory */ |
mov dl, [d] /* A: = 1, B: = 2, etc */ |
mov si, buff /* append cur dir to buffer */ |
add si, 3 /* skip the present drive:\ prefix */ |
int 0x21 |
jnc DONE |
mov [r], ax /* copy result from ax */ |
DONE: |
pop dx |
pop ax |
pop si |
} |
return(r); |
} |
static int cmd_cd(struct cmd_funcparam *p) { |
char *buffptr = p->BUFFER; |
/* CD /? */ |
if (cmd_ishlp(p)) { |
outputnl("Displays the name of or changes the current directory."); |
outputnl(""); |
outputnl("CHDIR [drive:][path]"); |
outputnl("CHDIR[..]"); |
outputnl("CD [drive:][path]"); |
outputnl("CD[..]"); |
outputnl(""); |
outputnl(".. Specifies that you want to change to the parent directory."); |
outputnl(""); |
outputnl("Type CD drive: to display the current directory in the specified drive."); |
outputnl("Type CD without parameters to display the current drive and directory."); |
return(-1); |
} |
/* one argument max */ |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
/* no argument? display current drive and dir ("CWD") */ |
if (p->argc == 0) { |
unsigned char drv = 0; |
_asm { |
push ax |
push dx |
push si |
mov ah, 0x19 /* get current default drive */ |
int 0x21 /* al = drive (00h = A:, 01h = B:, etc) */ |
inc al /* convert to 1=A, 2=B, etc */ |
mov [drv], al |
} |
cmd_cd_curpathfordrv(buffptr, drv); |
outputnl(buffptr); |
return(-1); |
} |
/* argument can be either a drive (D:) or a path */ |
if (p->argc == 1) { |
const char *arg = p->argv[0]; |
unsigned short err = 0; |
/* drive (CD B:) */ |
if ((arg[0] != '\\') && (arg[1] == ':') && (arg[2] == 0)) { |
unsigned char drive = arg[0]; |
if (drive >= 'a') { |
drive -= ('a' - 1); |
} else { |
drive -= ('A' - 1); |
} |
err = cmd_cd_curpathfordrv(buffptr, drive); |
if (err == 0) outputnl(buffptr); |
} else { /* path */ |
_asm { |
push dx |
push ax |
mov ah, 0x3B /* CHDIR (set current directory) */ |
mov dx, arg |
int 0x21 |
jnc DONE |
mov [err], ax |
DONE: |
pop ax |
pop dx |
} |
} |
if (err != 0) { |
outputnl(doserr(err)); |
} |
} |
return(-1); |
} |
/svarcom/trunk/cmd/del.c |
---|
0,0 → 1,107 |
/* |
* del/erase |
*/ |
static int cmd_del(struct cmd_funcparam *p) { |
const char *delspec = NULL; |
unsigned short err = 0; |
unsigned short confirmflag = 0; |
unsigned short i; |
unsigned short pathlimit = 0; |
char *buff = p->BUFFER; |
struct DTA *dta = (void *)0x80; /* use the default DTA at location 80h in PSP */ |
char *fname = dta->fname; |
if (cmd_ishlp(p)) { |
outputnl("Deletes one or more files."); |
outputnl(""); |
outputnl("DEL [drive:][path]filename [/P]"); |
outputnl("ERASE [drive:][path]filename [/P]"); |
outputnl(""); |
outputnl("[drive:][path]filename Specifies the file(s) to delete."); |
outputnl("/P Prompts for confirmation before deleting each file."); |
return(-1); |
} |
if (p->argc == 0) { |
outputnl("Required parameter missing"); |
return(-1); |
} |
/* scan argv for delspec and possible /p or /v */ |
for (i = 0; i < p->argc; i++) { |
/* delspec? */ |
if (p->argv[i][0] == '/') { |
if (imatch(p->argv[i], "/p")) { |
confirmflag = 1; |
} else { |
output("Invalid switch:"); |
output(" "); |
outputnl(p->argv[i]); |
return(-1); |
} |
} else if (delspec != NULL) { /* otherwise its a delspec */ |
outputnl("Too many parameters"); |
return(-1); |
} else { |
delspec = p->argv[i]; |
} |
} |
/* convert path to canonical form */ |
file_truename(delspec, buff); |
/* is delspec pointing at a directory? if so, add a \*.* */ |
{ int attr = file_getattr(delspec); |
if ((attr > 0) && (attr & DOS_ATTR_DIR)) strcat(buff, "\\????????.???"); |
} |
/* parse delspec in buff and remember where last backslash or slash is */ |
for (i = 0; buff[i] != 0; i++) if (buff[i] == '\\') pathlimit = i + 1; |
/* is this about deleting all content inside a directory? if no per-file |
* confirmation set, ask for a global confirmation */ |
if ((confirmflag == 0) && (imatch(buff + pathlimit, "????????.???"))) { |
outputnl("All files in directory will be deleted!"); |
if (askchoice("Are you sure (Y/N)?", "YN") != 0) return(-1); |
} |
for (i = 0;; i = 1) { |
/* exec FindFirst or FindNext */ |
if (i == 0) { |
err = findfirst(dta, buff, DOS_ATTR_RO | DOS_ATTR_SYS | DOS_ATTR_HID); |
} else { |
err = findnext(dta); |
} |
if (err != 0) break; |
/* ask if confirmation required: PLIK.TXT Delete (Y/N)? */ |
if (confirmflag) { |
strcpy(buff + pathlimit, fname); /* note: buff contained the search pattern but it no longer needed so I can reuse it now */ |
output(buff); |
output(" \t"); |
if (askchoice("Delete (Y/N)?", "YN") != 0) continue; |
} |
/* del found file */ |
_asm { |
mov ah, 0x41 /* delete a file, DS:DX points to an ASCIIZ filespec (no wildcards allowed) */ |
mov dx, fname |
int 0x21 |
jnc DONE |
mov [err], ax |
DONE: |
} |
if (err != 0) { |
output(fname); |
output(": "); |
outputnl(doserr(err)); |
break; |
} |
} |
return(-1); |
} |
/svarcom/trunk/cmd/dir.c |
---|
0,0 → 1,133 |
/* |
* dir |
* |
* Displays a list of files and subdirectories in a directory. |
* |
* DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L] |
* |
* /P Pauses after each screenful of information. |
* /W Uses wide list format. |
* |
* /A Displays file with specified attributes: |
* D Directories R Read-only files H Hidden files |
* A Ready for archiving S System files - prefix meaning "not" |
* |
* /O List files in sorted order: |
* N by name S by size E by extension |
* D by date G group dirs first - prefix to reverse order |
* |
* /S Displays files in specified directory and all subdirectories. |
* /B Uses bare format (no heading information or summary) |
* /L Uses lowercases |
*/ |
/* NOTE: /A attributes are matched in an exclusive way, ie. only files with |
* the specified attributes are matched. This is different from how DOS |
* itself matches attributes hence DIR cannot rely on the attributes |
* filter within FindFirst. |
* |
* NOTE: Multiple /A are not supported - only the last one is significant. |
*/ |
static int cmd_dir(struct cmd_funcparam *p) { |
const char *filespecptr = NULL; |
struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */ |
int i; |
unsigned short availrows; /* counter of available rows on display (used for /P) */ |
#define DIR_FLAG_PAUSE 1 |
#define DIR_FLAG_WIDE 2 |
#define DIR_FLAG_RECUR 4 |
#define DIR_FLAG_BARE 8 |
#define DIR_FLAG_LCASE 16 |
unsigned char flags = 0; |
// unsigned char attribs_show = 0; /* show only files with ALL these attribs */ |
// unsigned char attribs_hide = 0; /* hide files with ANY of these attribs */ |
if (cmd_ishlp(p)) { |
outputnl("Displays a list of files and subdirectories in a directory"); |
outputnl(""); |
outputnl("DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]"); |
outputnl(""); |
outputnl("/P Pauses after each screenful of information"); |
outputnl("/W Uses wide list format"); |
outputnl(""); |
outputnl("/A Displays files with specified attributes:"); |
outputnl(" D Directories R Read-only files H Hidden files"); |
outputnl(" A Ready for archiving S System files - prefix meaning \"not\""); |
outputnl(""); |
outputnl("/O List files in sorted order:"); |
outputnl(" N by name S by size E by extension"); |
outputnl(" D by date G group dirs first - prefix to reverse order"); |
outputnl(""); |
outputnl("/S Displays files in specified directory and all subdirectories"); |
outputnl("/B Uses bare format (no heading information or summary)"); |
outputnl("/L Uses lowercases"); |
/* TODO FIXME REMOVE THIS ONCE ALL IMPLEMENTED */ |
outputnl("\r\n*** THIS COMMAND IS NOT FULLY IMPLEMENTED YET ***"); |
return(-1); |
} |
/* parse command line */ |
for (i = 0; i < p->argc; i++) { |
if (p->argv[i][0] == '/') { |
char arg; |
char neg = 0; |
/* detect negations and get actual argument */ |
if (p->argv[i][1] == '-') neg = 1; |
arg = p->argv[i][1 + neg]; |
/* */ |
switch (arg) { |
case 'a': |
case 'A': |
/* TODO */ |
outputnl("/A NOT IMPLEMENTED YET"); |
return(-1); |
break; |
case 'p': |
case 'P': |
flags |= DIR_FLAG_PAUSE; |
if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE); |
break; |
default: |
outputnl("Invalid switch"); |
return(-1); |
} |
} else { /* filespec */ |
if (filespecptr != NULL) { |
outputnl("Too many parameters"); |
return(-1); |
} |
filespecptr = p->argv[i]; |
} |
} |
if (filespecptr == NULL) filespecptr = "."; |
file_truename(filespecptr, p->BUFFER); |
/* if dir then append \????????.??? */ |
i = file_getattr(p->BUFFER); |
if ((i > 0) && (i & DOS_ATTR_DIR)) strcat(p->BUFFER, "\\????????.???"); |
if (findfirst(dta, p->BUFFER, DOS_ATTR_RO | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_DIR | DOS_ATTR_ARC) != 0) return(-1); |
availrows = screen_getheight(); |
outputnl(dta->fname); |
availrows--; |
while (findnext(dta) == 0) { |
outputnl(dta->fname); |
if (flags & DIR_FLAG_PAUSE) { |
availrows--; |
if (availrows < 2) { |
press_any_key(); |
availrows = screen_getheight(); |
} |
} |
} |
return(-1); |
} |
/svarcom/trunk/cmd/exit.c |
---|
0,0 → 1,16 |
/* |
* exit |
* |
* Quits the COMMAND.COM program (command interpreter) |
* |
*/ |
static int cmd_exit(struct cmd_funcparam *p) { |
if (cmd_ishlp(p)) { |
outputnl("EXIT\r\n"); |
outputnl("Quits the COMMAND.COM program (command interpreter)"); |
} else { |
exit(0); |
} |
return(-1); |
} |
/svarcom/trunk/cmd/mkdir.c |
---|
0,0 → 1,50 |
/* |
* mkdir |
*/ |
static int cmd_mkdir(struct cmd_funcparam *p) { |
const char *dname = p->argv[0]; |
unsigned short err = 0; |
if (cmd_ishlp(p)) { |
outputnl("Creates a directory"); |
outputnl(""); |
outputnl("MKDIR [drive:]path"); |
outputnl("MD [drive:]path"); |
return(-1); |
} |
if (p->argc == 0) { |
outputnl("Required parameter missing"); |
return(-1); |
} |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
if (p->argv[0][0] == '/') { |
outputnl("Invalid parameter"); |
return(-1); |
} |
_asm { |
push ax |
push dx |
mov ah, 0x39 /* create new directory, DS:DX points to ASCIIZ dir name */ |
mov dx, [dname] |
int 0x21 |
jnc DONE |
mov [err], ax |
DONE: |
pop dx |
pop ax |
} |
if (err != 0) outputnl(doserr(err)); |
return(-1); |
} |
/svarcom/trunk/cmd/path.c |
---|
0,0 → 1,67 |
/* |
* path |
* |
* Displays or sets a search path for executable files. |
*/ |
static int cmd_path(struct cmd_funcparam *p) { |
char *buff = p->BUFFER; |
/* help screen (/?) */ |
if (cmd_ishlp(p)) { |
output("Displays or sets a search path for executable files.\r\n" |
"\r\n" |
"PATH [[drive:]path[;...]]\r\n" |
"PATH ;\r\n" |
"\r\n" |
"Type PATH ; to clear all search-path settings and direct DOS to search\r\n" |
"only in the current directory.\r\n" |
"\r\n" |
"Type PATH without parameters to display the current path.\r\n"); |
return(-1); |
} |
/* no parameter - display current path */ |
if (p->argc == 0) { |
char far *curpath = env_lookup(p->env_seg, "PATH"); |
if (curpath == NULL) { |
outputnl("No Path"); |
} else { |
unsigned short i; |
for (i = 0;; i++) { |
buff[i] = curpath[i]; |
if (buff[i] == 0) break; |
} |
outputnl(buff); |
} |
return(-1); |
} |
/* more than 1 parameter */ |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
/* IF HERE: THERE IS EXACTLY 1 ARGUMENT (argc == 1) */ |
/* reset the PATH string (PATH ;) */ |
if (imatch(p->argv[0], ";")) { |
env_dropvar(p->env_seg, "PATH"); |
return(-1); |
} |
/* otherwise set PATH to whatever is passed on command-line */ |
{ |
unsigned short i; |
strcpy(buff, "PATH="); |
for (i = 0;; i++) { |
buff[i + 5] = p->argv[0][i]; |
if (buff[i + 5] == '\r') break; |
} |
buff[i + 5] = 0; |
env_setvar(p->env_seg, buff); |
} |
return(-1); |
} |
/svarcom/trunk/cmd/prompt.c |
---|
0,0 → 1,37 |
/* |
* prompt |
* |
* Changes the DOS command prompt. |
* |
*/ |
static int cmd_prompt(struct cmd_funcparam *p) { |
if (cmd_ishlp(p)) { |
outputnl("Changes the DOS command prompt."); |
outputnl(""); |
outputnl("PROMPT [new command prompt specification]"); |
return(-1); |
} |
/* no parameter - restore default prompt path */ |
if (p->argc == 0) { |
env_dropvar(p->env_seg, "PROMPT"); |
return(-1); |
} |
/* otherwise set PROMPT to whatever is passed on command-line */ |
{ |
unsigned short i; |
char *buff = p->BUFFER; |
strcpy(buff, "PROMPT="); |
for (i = 0;; i++) { |
buff[i + 7] = p->cmdline[p->argoffset + i]; |
if (buff[i + 7] == '\r') break; |
} |
buff[i + 7] = 0; |
env_setvar(p->env_seg, buff); |
} |
return(-1); |
} |
/svarcom/trunk/cmd/rmdir.c |
---|
0,0 → 1,50 |
/* |
* rmdir |
*/ |
static int cmd_rmdir(struct cmd_funcparam *p) { |
const char *dname = p->argv[0]; |
unsigned short err = 0; |
if (cmd_ishlp(p)) { |
outputnl("Removes (deletes) a directory"); |
outputnl(""); |
outputnl("RMDIR [drive:]path"); |
outputnl("RD [drive:]path"); |
return(-1); |
} |
if (p->argc == 0) { |
outputnl("Required parameter missing"); |
return(-1); |
} |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
if (p->argv[0][0] == '/') { |
outputnl("Invalid parameter"); |
return(-1); |
} |
_asm { |
push ax |
push dx |
mov ah, 0x3a /* delete a directory, DS:DX points to ASCIIZ dir name */ |
mov dx, [dname] |
int 0x21 |
jnc DONE |
mov [err], ax |
DONE: |
pop dx |
pop ax |
} |
if (err != 0) outputnl(doserr(err)); |
return(-1); |
} |
/svarcom/trunk/cmd/set.c |
---|
0,0 → 1,72 |
/* |
* set [varname[=value]] |
* |
* value cannot contain any '=' character, but it can contain spaces |
* varname can also contain spaces |
*/ |
static int cmd_set(struct cmd_funcparam *p) { |
char far *env = MK_FP(p->env_seg, 0); |
char *buff = p->BUFFER; |
if (cmd_ishlp(p)) { |
outputnl("Displays, sets, or removes DOS environment variables"); |
outputnl(""); |
outputnl("SET [variable=[string]]"); |
outputnl(""); |
outputnl("variable Specifies the environment-variable name"); |
outputnl("string Specifies a series of characters to assign to the variable"); |
outputnl(""); |
outputnl("Type SET without parameters to display the current environment variables."); |
} |
/* no arguments - display content */ |
if (p->argc == 0) { |
while (*env != 0) { |
unsigned short i; |
/* copy string to local buff for display */ |
for (i = 0;; i++) { |
buff[i] = *env; |
env++; |
if (buff[i] == 0) break; |
} |
outputnl(buff); |
} |
} else { /* set variable (do not rely on argv, SET has its own rules...) */ |
const char far *ptr; |
unsigned short i; |
/* locate the first space */ |
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 */ |
i = 0; |
for (; *ptr != '='; ptr++) { |
if (*ptr == '\r') goto syntax_err; |
buff[i] = *ptr; |
if ((buff[i] >= 'a') && (buff[i] <= 'z')) buff[i] -= ('a' - 'A'); |
i++; |
} |
/* copy value now */ |
while (*ptr != '\r') { |
buff[i++] = *ptr; |
ptr++; |
} |
/* terminate buff */ |
buff[i] = 0; |
/* commit variable to environment */ |
i = env_setvar(p->env_seg, buff); |
if (i == ENV_INVSYNT) goto syntax_err; |
if (i == ENV_NOTENOM) outputnl("Not enough available space within the environment block"); |
} |
return(-1); |
syntax_err: |
outputnl("Syntax error"); |
return(-1); |
} |
/svarcom/trunk/cmd/type.c |
---|
0,0 → 1,86 |
/* |
* type |
*/ |
static int cmd_type(struct cmd_funcparam *p) { |
char *buff = p->BUFFER; |
const char *fname = p->argv[0]; |
unsigned short err = 0; |
if (cmd_ishlp(p)) { |
outputnl("Displays the contents of a text file."); |
outputnl(""); |
outputnl("TYPE [drive:][path]filename"); |
return(-1); |
} |
if (p->argc == 0) { |
outputnl("Required parameter missing"); |
return(-1); |
} |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
/* if here then display the file */ |
_asm { |
push ax |
push bx |
push cx |
push dx |
push si |
mov ax, 0x3d00 /* open file via handle, access mode in AL (0 = read) */ |
mov dx, fname |
int 0x21 /* file handle in ax on success (CF clear) */ |
jnc FILE_OPEN_OK |
mov [err], ax /* on error AX contains the DOS err code */ |
jmp FOPENFAIL |
FILE_OPEN_OK: |
/* copy obtained file handle to BX */ |
mov bx, ax |
READNEXTBLOCK: |
/* read file block by block */ |
mov cx, 1024 /* read 1K at a time */ |
mov dx, buff |
mov ah, 0x3f /* read CX bytes from file handle in BX and write to DS:DX */ |
int 0x21 /* CF set on error, AX=errno or AX=number of bytes read */ |
jc GOTERROR /* abort on error */ |
test ax, ax /* EOF? */ |
jz ENDFILE |
/* display read block (AX=len) */ |
mov si, dx /* preset DS:SI to DS:DX (DL will be reused soon) */ |
mov cx, ax /* set loop count to CX */ |
mov ah, 0x02 /* write character in DL to stdout */ |
NEXTCHAR: |
mov dl, [si] |
inc si |
int 0x21 |
loopnz NEXTCHAR /* CX-- ; jnz NEXTCHAR (display CX characters) */ |
/* read (and display) next block */ |
jmp READNEXTBLOCK |
GOTERROR: |
mov [err], ax |
ENDFILE: |
/* close file */ |
mov ah, 0x3e /* close file handle (file handle already in BX) */ |
int 0x21 |
FOPENFAIL: |
pop si |
pop dx |
pop cx |
pop bx |
pop ax |
} |
if (err != 0) outputnl(doserr(err)); |
return(-1); |
} |
/svarcom/trunk/cmd/ver.c |
---|
0,0 → 1,40 |
/* |
* ver |
*/ |
#define PVER "20211027" |
static int cmd_ver(struct cmd_funcparam *p) { |
char *buff = p->BUFFER; |
unsigned char maj = 0, min = 0; |
/* help screen */ |
if (cmd_ishlp(p)) { |
outputnl("Displays the DOS version."); |
return(-1); |
} |
if (p->argc != 0) { |
outputnl("Invalid parameter"); |
return(-1); |
} |
_asm { |
push ax |
push bx |
push cx |
mov ah, 0x30 /* Get DOS version number */ |
int 0x21 /* AL=maj_ver_num AH=min_ver_num BX,CX=OEM */ |
mov [maj], al |
mov [min], ah |
pop cx |
pop bx |
pop ax |
} |
sprintf(buff, "DOS kernel version %u.%u", maj, min); |
outputnl(buff); |
outputnl("SvarCOM shell ver " PVER); |
return(-1); |
} |
/svarcom/trunk/cmd/verify.c |
---|
0,0 → 1,62 |
/* |
* verify |
*/ |
static int cmd_verify(struct cmd_funcparam *p) { |
if (cmd_ishlp(p)) { |
outputnl("Tells DOS whether to verify that files are written correctly to disk."); |
outputnl("\r\nVERIFY [ON | OFF]\r\n"); |
outputnl("Type VERIFY without a parameter to display its current setting."); |
return(-1); |
} |
if (p->argc > 1) { |
outputnl("Too many parameters"); |
return(-1); |
} |
if (p->argc == 0) { |
unsigned char verstate = 0; |
_asm { |
push ax |
mov ah, 0x54 /* Get VERIFY status */ |
int 0x21 /* AL == 0 (off) or AL == 1 (on) */ |
mov [verstate], al |
pop ax |
} |
if (verstate == 0) { |
outputnl("VERIFY is off"); |
} else { |
outputnl("VERIFY is on"); |
} |
return(-1); |
} |
/* argc == 1*/ |
if (imatch(p->argv[0], "on")) { |
_asm { |
push ax |
push dx |
mov ax, 0x2e01 /* set verify ON */ |
xor dl, dl /* apparently required by MS-DOS 2.x */ |
int 0x21 |
pop dx |
pop ax |
} |
} else if (imatch(p->argv[0], "off")) { |
_asm { |
push ax |
push dx |
mov ax, 0x2e00 /* set verify OFF */ |
xor dl, dl /* apparently required by MS-DOS 2.x */ |
int 0x21 |
pop dx |
pop ax |
} |
} else { |
outputnl("Must specify ON or OFF"); |
} |
return(-1); |
} |
/svarcom/trunk/cmd.c |
---|
0,0 → 1,195 |
/* entry point for internal commands |
* matches internal commands and executes them |
* returns -1 or exit code if processed |
* returns -2 if command unrecognized */ |
#include <i86.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include "doserr.h" |
#include "env.h" |
#include "helpers.h" |
struct cmd_funcparam { |
int argc; /* number of arguments */ |
const char *argv[256]; /* pointers to each argument */ |
unsigned short env_seg; /* segment of environment block */ |
unsigned short argoffset; /* offset of cmdline where first argument starts */ |
const char far *cmdline; /* original cmdline (terminated by \r) */ |
char BUFFER[1024]; /* a buffer for whatever is needed */ |
}; |
/* scans argv for the presence of a "/?" parameter. returns 1 if found, 0 otherwise */ |
static int cmd_ishlp(const struct cmd_funcparam *p) { |
int i; |
for (i = 0; i < p->argc; i++) { |
if ((p->argv[i][0] == '/') && (p->argv[i][1] == '?')) return(1); |
} |
return(0); |
} |
#include "cmd/_notimpl.c" |
#include "cmd/cd.c" |
#include "cmd/del.c" |
#include "cmd/dir.c" |
#include "cmd/exit.c" |
#include "cmd/mkdir.c" |
#include "cmd/path.c" |
#include "cmd/prompt.c" |
#include "cmd/rmdir.c" |
#include "cmd/set.c" |
#include "cmd/ver.c" |
#include "cmd/verify.c" |
#include "cmd/type.c" |
#include "cmd.h" |
struct CMD_ID { |
const char *cmd; |
int (*func_ptr)(struct cmd_funcparam *); /* pointer to handling function */ |
}; |
const struct CMD_ID INTERNAL_CMDS[] = { |
{"BREAK", cmd_notimpl}, |
{"CD", cmd_cd}, |
{"CHCP", cmd_notimpl}, |
{"CHDIR", cmd_cd}, |
{"CLS", cmd_notimpl}, |
{"COPY", cmd_notimpl}, |
{"CTTY", cmd_notimpl}, |
{"DATE", cmd_notimpl}, |
{"DEL", cmd_del}, |
{"DIR", cmd_dir}, |
{"ECHO", cmd_notimpl}, |
{"ERASE", cmd_del}, |
{"EXIT", cmd_exit}, |
{"LH", cmd_notimpl}, |
{"LOADHIGH",cmd_notimpl}, |
{"MD", cmd_mkdir}, |
{"MKDIR", cmd_mkdir}, |
{"PAUSE", cmd_notimpl}, |
{"PATH", cmd_path}, |
{"PROMPT", cmd_prompt}, |
{"RD", cmd_rmdir}, |
{"REN", cmd_notimpl}, |
{"RENAME", cmd_notimpl}, |
{"RMDIR", cmd_rmdir}, |
{"SET", cmd_set}, |
{"TIME", cmd_notimpl}, |
{"TYPE", cmd_type}, |
{"VER", cmd_ver}, |
{"VERIFY", cmd_verify}, |
{"VOL", cmd_notimpl}, |
{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, char *BUFFER) { |
const struct CMD_ID *cmdptr; |
unsigned short argoffset; |
struct cmd_funcparam *p = (void *)BUFFER; |
/* special case: is this a drive change? (like "E:") */ |
if ((cmdline[0] != 0) && (cmdline[1] == ':') && ((cmdline[2] == ' ') || (cmdline[2] == 0))) { |
if (((cmdline[0] >= 'a') && (cmdline[0] <= 'z')) || ((cmdline[0] >= 'A') && (cmdline[0] <= 'Z'))) { |
unsigned char drive = cmdline[0]; |
unsigned char curdrive = 0; |
if (drive >= 'a') { |
drive -= 'a'; |
} else { |
drive -= 'A'; |
} |
_asm { |
push ax |
push dx |
mov ah, 0x0e /* DOS 1+ - SELECT DEFAULT DRIVE */ |
mov dl, drive /* DL = new default drive (00h = A:, 01h = B:, etc) */ |
int 0x21 |
mov ah, 0x19 /* DOS 1+ - GET CURRENT DRIVE */ |
int 0x21 |
mov curdrive, al /* cur drive (0=A, 1=B, etc) */ |
pop dx |
pop ax |
} |
if (curdrive != drive) puts(doserr(0x0f)); |
return(-1); |
} |
} |
/* try matching an internal command */ |
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(BUFFER + sizeof(*p), cmdline + argoffset, p->argv); |
p->env_seg = env_seg; |
p->argoffset = argoffset; |
p->cmdline = cmdline; |
return((cmdptr->func_ptr)(p)); |
} |
/svarcom/trunk/cmd.h |
---|
0,0 → 1,11 |
#ifndef CMD_H |
#define CMD_H |
/* process internal commands */ |
int cmd_process(unsigned short env_seg, const char far *cmdline, char *BUFFER); |
/* 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/trunk/command.c |
---|
0,0 → 1,378 |
/* |
* SvarCOM is a command-line interpreter. |
* |
* a little memory area is allocated as high as possible. it contains: |
* - a signature (like XMS drivers do) |
* - a routine for exec'ing programs |
* - a "last command" buffer for input history |
* |
* when svarcom starts, it tries locating the routine in memory. |
* |
* if found: |
* waits for user input and processes it. if execing something is required, set the "next exec" field in routine's memory and quit. |
* |
* if not found: |
* installs it by creating a new PSP, set int 22 vector to the routine, set my "parent PSP" to the routine |
* and quit. |
* |
* |
* |
* good lecture about PSP and allocating memory |
* https://retrocomputing.stackexchange.com/questions/20001/how-much-of-the-program-segment-prefix-area-can-be-reused-by-programs-with-impun/20006#20006 |
* |
* PSP structure |
* http://www.piclist.com/techref/dos/psps.htm |
* |
* |
* |
* === MCB === |
* |
* each time that DOS allocates memory, it prefixes the allocated memory with |
* a 16-bytes structure called a "Memory Control Block" (MCB). This control |
* block has the following structure: |
* |
* Offset Size Description |
* 00h byte 'M' = non-last member of the MCB chain |
* 'Z' = indicates last entry in MCB chain |
* other values cause "Memory Allocation Failure" on exit |
* 01h word PSP segment address of the owner (Process Id) |
* possible values: |
* 0 = free |
* 8 = Allocated by DOS before first user pgm loaded |
* other = Process Id/PSP segment address of owner |
* 03h word number of paragraphs related to this MCB (excluding MCB) |
* 05h 11 bytes reserved |
* 10h ... start of actual allocated memory block |
*/ |
#include <i86.h> |
#include <dos.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <process.h> |
#include "cmd.h" |
#include "env.h" |
#include "helpers.h" |
#include "rmodinit.h" |
struct config { |
int locate; |
int install; |
int envsiz; |
} cfg; |
static void parse_argv(struct config *cfg, int argc, char **argv) { |
int i; |
memset(cfg, 0, sizeof(*cfg)); |
for (i = 1; i < argc; i++) { |
if (strcmp(argv[i], "/locate") == 0) { |
cfg->locate = 1; |
} |
if (strstartswith(argv[i], "/e:") == 0) { |
cfg->envsiz = atoi(argv[i]+3); |
if (cfg->envsiz < 64) cfg->envsiz = 0; |
} |
} |
} |
static void buildprompt(char *s, unsigned short envseg) { |
/* locate the prompt variable or use the default pattern */ |
const char far *fmt = env_lookup(envseg, "PROMPT"); |
while ((fmt != NULL) && (*fmt != 0)) { |
fmt++; |
if (fmt[-1] == '=') break; |
} |
if ((fmt == NULL) || (*fmt == 0)) fmt = "$p$g"; /* fallback to default if empty */ |
/* build the prompt string based on pattern */ |
for (; *fmt != 0; fmt++) { |
if (*fmt != '$') { |
*s = *fmt; |
s++; |
continue; |
} |
/* escape code ($P, etc) */ |
fmt++; |
switch (*fmt) { |
case 'Q': /* $Q = = (equal sign) */ |
case 'q': |
*s = '='; |
s++; |
break; |
case '$': /* $$ = $ (dollar sign) */ |
*s = '$'; |
s++; |
break; |
case 'T': /* $t = current time */ |
case 't': |
s += sprintf(s, "00:00"); /* TODO */ |
break; |
case 'D': /* $D = current date */ |
case 'd': |
s += sprintf(s, "1985-07-29"); /* TODO */ |
break; |
case 'P': /* $P = current drive and path */ |
case 'p': |
_asm { |
mov ah, 0x19 /* DOS 1+ - GET CURRENT DRIVE */ |
int 0x21 |
mov bx, s |
mov [bx], al /* AL = drive (00 = A:, 01 = B:, etc */ |
} |
*s += 'A'; |
s++; |
*s = ':'; |
s++; |
*s = '\\'; |
s++; |
_asm { |
mov ah, 0x47 /* DOS 2+ - CWD - GET CURRENT DIRECTORY */ |
xor dl,dl /* DL = drive number (00h = default, 01h = A:, etc) */ |
mov si, s /* DS:SI -> 64-byte buffer for ASCIZ pathname */ |
int 0x21 |
} |
while (*s != 0) s++; /* move ptr forward to end of pathname */ |
break; |
case 'V': /* $V = DOS version number */ |
case 'v': |
s += sprintf(s, "VER"); /* TODO */ |
break; |
case 'N': /* $N = current drive */ |
case 'n': |
_asm { |
mov ah, 0x19 /* DOS 1+ - GET CURRENT DRIVE */ |
int 0x21 |
mov bx, s |
mov [bx], al /* AL = drive (00 = A:, 01 = B:, etc */ |
} |
*s += 'A'; |
s++; |
break; |
case 'G': /* $G = > (greater-than sign) */ |
case 'g': |
*s = '>'; |
s++; |
break; |
case 'L': /* $L = < (less-than sign) */ |
case 'l': |
*s = '<'; |
s++; |
break; |
case 'B': /* $B = | (pipe) */ |
case 'b': |
*s = '|'; |
s++; |
break; |
case 'H': /* $H = backspace (erases previous character) */ |
case 'h': |
*s = '\b'; |
s++; |
break; |
case 'E': /* $E = Escape code (ASCII 27) */ |
case 'e': |
*s = 27; |
s++; |
break; |
case '_': /* $_ = CR+LF */ |
*s = '\r'; |
s++; |
*s = '\n'; |
s++; |
break; |
} |
} |
*s = '$'; |
} |
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); |
} |
static void set_comspec_to_self(unsigned short envseg) { |
unsigned short *psp_envseg = (void *)(0x2c); /* pointer to my env segment field in the PSP */ |
char far *myenv = MK_FP(*psp_envseg, 0); |
unsigned short varcount; |
char buff[256] = "COMSPEC="; |
char *buffptr = buff + 8; |
/* who am i? look into my own environment, at the end of it should be my EXEPATH string */ |
while (*myenv != 0) { |
/* consume a NULL-terminated string */ |
while (*myenv != 0) myenv++; |
/* move to next string */ |
myenv++; |
} |
/* get next word, if 1 then EXEPATH follows */ |
myenv++; |
varcount = *myenv; |
myenv++; |
varcount |= (*myenv << 8); |
myenv++; |
if (varcount != 1) return; /* NO EXEPATH FOUND */ |
while (*myenv != 0) { |
*buffptr = *myenv; |
buffptr++; |
myenv++; |
} |
*buffptr = 0; |
/* printf("EXEPATH: '%s'\r\n", buff); */ |
env_setvar(envseg, buff); |
} |
int main(int argc, char **argv) { |
static struct config cfg; |
static unsigned short rmod_seg; |
static unsigned short far *rmod_envseg; |
static unsigned short far *lastexitcode; |
static unsigned char BUFFER[4096]; |
parse_argv(&cfg, argc, argv); |
rmod_seg = rmod_find(); |
if (rmod_seg == 0xffff) { |
rmod_seg = rmod_install(cfg.envsiz); |
if (rmod_seg == 0xffff) { |
outputnl("ERROR: rmod_install() failed"); |
return(1); |
} |
/* printf("rmod installed at seg 0x%04X\r\n", rmod_seg); */ |
} else { |
/* printf("rmod found at seg 0x%04x\r\n", rmod_seg); */ |
} |
rmod_envseg = MK_FP(rmod_seg, RMOD_OFFSET_ENVSEG); |
lastexitcode = MK_FP(rmod_seg, RMOD_OFFSET_LEXITCODE); |
/* make COMPSEC point to myself */ |
set_comspec_to_self(*rmod_envseg); |
/* { |
unsigned short envsiz; |
unsigned short far *sizptr = MK_FP(*rmod_envseg - 1, 3); |
envsiz = *sizptr; |
envsiz *= 16; |
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); |
}*/ |
for (;;) { |
char far *cmdline = MK_FP(rmod_seg, RMOD_OFFSET_INPBUFF + 2); |
/* revert input history terminator to \r */ |
if (cmdline[-1] != 0) { |
cmdline[(unsigned short)(cmdline[-1])] = '\r'; |
} |
{ |
/* print shell prompt */ |
char *promptptr = BUFFER; |
buildprompt(promptptr, *rmod_envseg); |
_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) */ |
mov ax, 0x4800 |
int 0x2f |
mov bl, al /* save doskey status in BL */ |
/* set up buffered input */ |
mov ax, rmod_seg |
push ax |
pop ds |
mov dx, RMOD_OFFSET_INPBUFF |
/* execute either DOS input or DOSKEY */ |
test bl, bl /* zf set if no DOSKEY present */ |
jnz DOSKEY |
mov ah, 0x0a |
int 0x21 |
jmp short DONE |
DOSKEY: |
mov ax, 0x4810 |
int 0x2f |
DONE: |
pop ds |
pop dx |
pop cx |
pop bx |
pop ax |
} |
outputnl(""); |
/* if nothing entered, loop again */ |
if (cmdline[-1] == 0) continue; |
/* replace \r by a zero terminator */ |
cmdline[(unsigned char)(cmdline[-1])] = 0; |
/* move pointer forward to skip over any leading spaces */ |
while (*cmdline == ' ') cmdline++; |
/* update rmod's ptr to COMPSPEC so it is always up to date */ |
rmod_updatecomspecptr(rmod_seg, *rmod_envseg); |
/* try matching (and executing) an internal command */ |
{ |
int ecode = cmd_process(*rmod_envseg, cmdline, BUFFER); |
if (ecode >= 0) *lastexitcode = ecode; |
if (ecode >= -1) { /* internal command executed */ |
outputnl(""); |
continue; |
} |
} |
/* 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 */ |
outputnl("Bad command or file name"); |
} |
return(0); |
} |
/svarcom/trunk/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/trunk/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 |
/svarcom/trunk/env.c |
---|
0,0 → 1,113 |
/* |
* routines used to manipulate the environment block |
*/ |
#include <i86.h> |
#include <string.h> |
#include "env.h" |
/* looks for varname in environment block and returns a far ptr to it if |
* found, NULL otherwise. varname MUST be in upper-case and MUST be terminated |
* by either a = sign or a NULL terminator */ |
char far *env_lookup(unsigned short env_seg, const char *varname) { |
char far *env = MK_FP(env_seg, 0); |
int i; |
for (;;) { |
/* is this it? */ |
for (i = 0;; i++) { |
if ((varname[i] == '=') || (varname[i] == 0)) { |
if (env[i] == '=') return(env); /* FOUND! */ |
break; /* else look for next string */ |
} |
if (varname[i] != env[i]) break; |
} |
/* move env to end of current string */ |
while (*env != 0) env++; |
/* if there's another trailing zero, then that is the end of environment */ |
env++; |
if (*env == 0) return(NULL); |
} |
} |
/* returns the size, in bytes, of the allocated environment block */ |
unsigned short env_allocsz(unsigned short env_seg) { |
unsigned short far *mcbsz = MK_FP(env_seg - 1, 3); /* block size is a word at offset +3 in the MCB */ |
return(*mcbsz * 16); /* return size in bytes, not paragraphs */ |
} |
/* remove a variable from environment, if present. returns 0 on success, non-zero if variable not found */ |
int env_dropvar(unsigned short env_seg, const char *varname) { |
unsigned short blocksz, traillen; |
unsigned short len; |
char far *varptr = env_lookup(env_seg, varname); |
/* if variable not found in environment, quit now */ |
if (varptr == NULL) return(-1); |
for (len = 0; varptr[len] != 0; len++); /* compute length of variable (without trailing null) */ |
blocksz = env_allocsz(env_seg); /* total environment size */ |
traillen = blocksz - (FP_OFF(varptr) + len + 1); /* how much bytes are present after the variable */ |
_fmemset(varptr, 0, len); /* zero out the variable */ |
if (traillen != 0) { |
_fmemmove(varptr, varptr + len + 1, traillen); /* move rest of memory */ |
} |
return(0); |
} |
/* Writes a variable to environment block. The variable must in the form |
* "VARNAME=value". If variable is already present in the environment block, |
* then the value will be updated. If the new value is empty, then the |
* existing variable (if any) is removed. |
* VARNAME *MUST* be all-uppercase. |
* |
* This function returns: |
* ENV_SUCCESS = success |
* ENV_NOTENOM = not enough available space in memory block |
* ENV_INVSYNT = invalid syntax |
*/ |
int env_setvar(unsigned short env_seg, const char *v) { |
unsigned short envlen; |
unsigned short envfree; |
unsigned short vlen, veqpos; |
char far *env = MK_FP(env_seg, 0); |
/* remove variable from environment, if already set */ |
env_dropvar(env_seg, v); |
/* compute v length and find the position of the eq sign */ |
veqpos = 0xffff; |
for (vlen = 0; v[vlen] != 0; vlen++) { |
if (v[vlen] == '=') { |
if (veqpos != 0xffff) return(ENV_INVSYNT); /* equal sign is forbidden in value */ |
veqpos = vlen; |
} |
} |
/* if variable empty, stop here */ |
if (veqpos == vlen - 1) return(ENV_SUCCESS); |
/* compute current size of the environment */ |
for (envlen = 0; env[envlen] != 0; envlen++) { |
while (env[envlen] != 0) envlen++; /* consume a string */ |
} |
/* compute free space available in environment */ |
envfree = env_allocsz(env_seg); |
envfree -= envlen; |
envfree -= 1; /* 1 byte for the environment's NULL terminator */ |
/* do I have enough env space for the new var? */ |
if (envfree < vlen + 1) return(ENV_NOTENOM); |
/* write the new variable (with its NULL terminator) to environment tail */ |
_fmemcpy(env + envlen, v, vlen + 1); |
/* add the environment's NULL terminator */ |
env[envlen + vlen + 1] = 0; |
return(ENV_SUCCESS); |
} |
/svarcom/trunk/env.h |
---|
0,0 → 1,36 |
/* |
* routines used to manipulate the environment block |
* Copyright (C) 2021, Mateusz Viste |
*/ |
#ifndef ENV_H |
#define ENV_H |
/* looks for varname in environment block and returns a far ptr to it if |
* found, NULL otherwise. varname MUST be in upper-case and MUST be terminated |
* by either a = sign or a NULL terminator */ |
char far *env_lookup(unsigned short env_seg, const char *varname); |
/* returns the size, in bytes, of the allocated environment block */ |
unsigned short env_allocsz(unsigned short env_seg); |
/* remove a variable from environment, if present. returns 0 on success, non-zero if variable not found */ |
int env_dropvar(unsigned short env_seg, const char *varname); |
#define ENV_SUCCESS 0 |
#define ENV_NOTENOM -1 |
#define ENV_INVSYNT -2 |
/* Writes a variable to environment block. The variable must in the form |
* "varname=value". If variable is already present in the environment block, |
* then the value will be updated. If the new value is empty, then the |
* existing variable (if any) is removed. |
* |
* This function returns: |
* ENV_SUCCESS = success |
* ENV_NOTENOM = not enough available space in memory block |
* ENV_INVSYNT = invalid syntax |
*/ |
int env_setvar(unsigned short env_seg, const char *v); |
#endif |
/svarcom/trunk/file2c.c |
---|
0,0 → 1,47 |
/* |
* translates a binary file to a C include. |
* used by the SvarCOM build process to embedd rcom inside COMMAND.COM |
* |
* Copyright (C) 2021 Mateusz Viste |
*/ |
#include <stdio.h> |
int main(int argc, char **argv) { |
FILE *fdin, *fdout; |
unsigned long len; |
int c; |
if (argc != 4) { |
puts("usage: file2c infile.dat outfile.c varname"); |
return(1); |
} |
fdin = fopen(argv[1], "rb"); |
if (fdin == NULL) { |
puts("ERROR: failed to open input file"); |
return(1); |
} |
fdout = fopen(argv[2], "wb"); |
if (fdout == NULL) { |
fclose(fdin); |
puts("ERROR: failed to open output file"); |
return(1); |
} |
fprintf(fdout, "const char %s[] = {", argv[3]); |
for (len = 0;; len++) { |
c = getc(fdin); |
if (c == EOF) break; |
if (len > 0) fprintf(fdout, ","); |
if ((len & 15) == 0) fprintf(fdout, "\r\n"); |
fprintf(fdout, "%3u", c); |
} |
fprintf(fdout, "};\r\n"); |
fprintf(fdout, "#define %s_len %lu\r\n", argv[3], len); |
fclose(fdin); |
fclose(fdout); |
return(0); |
} |
/svarcom/trunk/freecom.txt |
---|
0,0 → 1,56 |
*** SVARCOM VS FREECOM *** |
The FreeCOM project is an impressive piece of software, but there are a few |
things that I do not like about it. SvarCOM is my attempt at addressing these |
things. |
*** MEMORY FOOTPRINT ********************************************************* |
FreeCOM is not suitable for low-memory machines. It takes about 55K of |
conventional memory when XMS is unavailable. XMS being a 386+ thing, FreeCOM |
is a poor fit for pre-386 machines. There is the KSSF hack, but it is a kludge |
with many limitations. As pointed out by one of the FreeCOM authors, FreeCOM |
is designed with 21'st century machines in mind and not IBM PC compatibles. |
SvarDOS does not rely on XMS and performs runtime swapping that works on any |
IBM PC compatible machine. |
*** NLS RESSOURCES *********************************************************** |
FreeCOM requires custom NLS files. While the vast majority of FreeDOS programs |
use a single "standard" (CATS/Kitten), FreeCOM uses a different approach with |
pre-compiled NLS strings, which makes it necessary to distribute as many |
binary blobs as there are supported languages. It also makes the translation |
process much more difficult. |
SvarDOS will use Kitten-style translations, like other applications. |
*** CODE COMPLEXITY ********************************************************** |
FreeCOM is a complex beast: it aims for compatibility with multiple compilers |
and supports many embedded features. This makes the code uneasy to follow and |
changes require careful testing on all supported compilers and all possible |
build variants. |
SvarDOS, on the other hand, is meant to be simple and universal. It is meant |
to be compiled with OpenWatcom only, which makes a ton of IFDEF's go away. It |
also won't integrate features that can be reasonably implemented as external |
tools (typically: DOSKEY). It strives to reimplement the functionality of |
MS-DOS 5/6. |
*** NON-FREE LICENSE ********************************************************* |
FreeCOM code is released under the terms of a license that restrains the |
freedom of its users due to its virality (GPL). |
SvarCOM is released under the terms of a liberal and permissive (MIT) license |
that does not impose limitations on how users may or may not use it. |
********************************************************************** EOF *** |
/svarcom/trunk/helpers.c |
---|
0,0 → 1,210 |
/* |
* a variety of helper functions |
* Copyright (C) 2021 Mateusz Viste |
*/ |
#include <i86.h> /* MK_FP() */ |
#include "helpers.h" |
/* case-insensitive comparison of strings, returns non-zero on equality */ |
int imatch(const char *s1, const char *s2) { |
for (;;) { |
char c1, c2; |
c1 = *s1; |
c2 = *s2; |
if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A'); |
if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A'); |
/* */ |
if (c1 != c2) return(0); |
if (c1 == 0) return(1); |
s1++; |
s2++; |
} |
} |
/* returns zero if s1 starts with s2 */ |
int strstartswith(const char *s1, const char *s2) { |
while (*s2 != 0) { |
if (*s1 != *s2) return(-1); |
s1++; |
s2++; |
} |
return(0); |
} |
/* outputs a NULL-terminated string to stdout */ |
void output_internal(const char *s, unsigned short nl) { |
_asm { |
mov ah, 0x02 /* AH=9 - write character in DL to stdout */ |
mov si, s |
cld /* clear DF so lodsb increments SI */ |
NEXTBYTE: |
lodsb /* load byte from DS:SI into AL, SI++ */ |
mov dl, al |
or al, 0 /* is al == 0? */ |
jz DONE |
int 0x21 |
jmp NEXTBYTE |
DONE: |
or nl, 0 |
jz FINITO |
/* print out a CR/LF trailer if nl set */ |
mov dl, 0x0D /* CR */ |
int 0x21 |
mov dl, 0x0A /* LF */ |
int 0x21 |
FINITO: |
} |
} |
/* find first matching files using a FindFirst DOS call |
* returns 0 on success or a DOS err code on failure */ |
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) { |
unsigned short res = 0; |
_asm { |
/* set DTA location */ |
mov ah, 0x1a |
mov dx, dta |
int 0x21 |
/* */ |
mov ah, 0x4e /* FindFirst */ |
mov dx, pattern |
mov cx, attr |
int 0x21 /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */ |
jnc DONE |
mov [res], ax |
DONE: |
} |
return(res); |
} |
/* find next matching, ie. continues an action intiated by findfirst() */ |
unsigned short findnext(struct DTA *dta) { |
unsigned short res = 0; |
_asm { |
mov ah, 0x4f /* FindNext */ |
mov dx, dta |
int 0x21 /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */ |
jnc DONE |
mov [res], ax |
DONE: |
} |
return(res); |
} |
/* print s string and wait for a single key press from stdin. accepts only |
* key presses defined in the c ASCIIZ string. returns offset of pressed key |
* in string. keys in c MUST BE UPPERCASE! */ |
unsigned short askchoice(const char *s, const char *c) { |
unsigned short res; |
char key = 0; |
AGAIN: |
output(s); |
output(" "); |
_asm { |
push ax |
push dx |
mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */ |
int 0x21 |
/* if AL == 0 then this is an extended character */ |
test al, al |
jnz GOTCHAR |
mov ah, 0x08 /* read again to flush extended char from input buffer */ |
int 0x21 |
xor al, al /* all extended chars are ignored */ |
GOTCHAR: /* received key is in AL now */ |
mov [key], al /* save key */ |
/* print a cr/lf */ |
mov ah, 0x02 |
mov dl, 0x0D |
int 0x21 |
mov dl, 0x0A |
int 0x21 |
pop dx |
pop ax |
} |
/* ucase() result */ |
if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A'); |
/* is there a match? */ |
for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res); |
goto AGAIN; |
} |
/* converts a path to its canonic representation */ |
void file_truename(const char *src, char *dst) { |
_asm { |
mov ah, 0x60 /* query truename, DS:SI=src, ES:DI=dst */ |
push ds |
pop es |
mov si, src |
mov di, dst |
int 0x21 |
} |
} |
/* returns DOS attributes of file, or -1 on error */ |
int file_getattr(const char *fname) { |
int res = -1; |
_asm { |
mov ax, 0x4300 /* query file attributes, fname at DS:DX */ |
mov dx, fname |
int 0x21 /* CX=attributes if CF=0, otherwise AX=errno */ |
jc DONE |
mov [res], cx |
DONE: |
} |
return(res); |
} |
/* returns screen's width (in columns) */ |
unsigned short screen_getwidth(void) { |
/* BIOS 0040:004A = word containing screen width in text columns */ |
unsigned short far *scrw = MK_FP(0x40, 0x4a); |
return(*scrw); |
} |
/* returns screen's height (in rows) */ |
unsigned short screen_getheight(void) { |
/* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */ |
unsigned char far *scrh = MK_FP(0x40, 0x84); |
if (*scrh == 0) return(25); /* pre-EGA adapter */ |
return(*scrh + 1); |
} |
/* displays the "Press any key to continue" msg and waits for a keypress */ |
void press_any_key(void) { |
output("Press any key to continue..."); |
_asm { |
mov ah, 0x08 /* no echo console input */ |
int 0x21 /* pressed key in AL now (0 for extended keys) */ |
test al, al |
jnz DONE |
int 0x21 /* executed ah=8 again to read the rest of extended key */ |
DONE: |
/* output CR/LF */ |
mov ah, 0x02 |
mov dl, 0x0D |
int 0x21 |
mov dl, 0x0A |
int 0x21 |
} |
} |
/svarcom/trunk/helpers.h |
---|
0,0 → 1,69 |
#ifndef HELPERS_H |
#define HELPERS_H |
/* case-insensitive comparison of strings, returns non-zero on equality */ |
int imatch(const char *s1, const char *s2); |
/* returns zero if s1 starts with s2 */ |
int strstartswith(const char *s1, const char *s2); |
/* outputs a NULL-terminated string to stdout */ |
void output_internal(const char *s, unsigned short nl); |
#define output(x) output_internal(x, 0) |
#define outputnl(x) output_internal(x, 1) |
/* |
* FileInfoRec (DTA) format: |
* offset size desc |
* +0 21 reserved |
* +15h 1 file attr (1=RO 2=Hidden 4=System 8=VOL 16=DIR 32=Archive |
* +16h 2 time: bits 0-4=bi-seconds (0-30), bits 5-10=minutes (0-59), bits 11-15=hour (0-23) |
* +18h 2 date: bits 0-4=day(0-31), bits 5-8=month (1-12), bits 9-15=years since 1980 |
* +1ah 4 DWORD file size, in bytes |
* +1eh 13 13-bytes max ASCIIZ filename |
*/ |
_Packed struct DTA { |
char reserved[21]; |
unsigned char attr; |
unsigned short time; |
unsigned short date; |
unsigned long size; |
char fname[13]; |
}; |
#define DOS_ATTR_RO 1 |
#define DOS_ATTR_HID 2 |
#define DOS_ATTR_SYS 4 |
#define DOS_ATTR_VOL 8 |
#define DOS_ATTR_DIR 16 |
#define DOS_ATTR_ARC 32 |
/* find first matching files using a FindFirst DOS call |
* returns 0 on success or a DOS err code on failure */ |
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr); |
/* find next matching, ie. continues an action intiated by findfirst() */ |
unsigned short findnext(struct DTA *dta); |
/* print s string and wait for a single key press from stdin. accepts only |
* key presses defined in the c ASCIIZ string. returns offset of pressed key |
* in string. keys in c MUST BE UPPERCASE! */ |
unsigned short askchoice(const char *s, const char *c); |
/* converts a path to its canonic representation */ |
void file_truename(const char *src, char *dst); |
/* returns DOS attributes of file, or -1 on error */ |
int file_getattr(const char *fname); |
/* returns screen's width (in columns) */ |
unsigned short screen_getwidth(void); |
/* returns screen's height (in rows) */ |
unsigned short screen_getheight(void); |
/* displays the "Press any key to continue" msg and waits for a keypress */ |
void press_any_key(void); |
#endif |
/svarcom/trunk/makefile |
---|
0,0 → 1,42 |
# |
# This is a makefile to build the SVARCOM command interpreter (COMMAND.COM) |
# |
# You can use following targets: |
# |
# wmake - compiles the program |
# wmake clean - cleans up all non-source files |
# |
CFLAGS = -0 -y -cc -wx -mt -lr -we -d0 -ox -fm=command.map |
# -0 generate 8086 compatible code |
# -y ignore %WCL% if present |
# -cc C code |
# -wx maximum warnings level |
# -mt TINY memory model |
# -lr real-mode target |
# -we any warning is considered an error |
# -d0 no debug data |
# -ox maximum optimization level |
all: command.com |
command.com: rmod.h command.c cmd.c doserr.c env.c rmodinit.c helpers.c cmd\*.c |
wcl $(CFLAGS) command.c cmd.c doserr.c env.c rmodinit.c helpers.c |
rmod.h: file2c.com rmod.com |
file2c rmod.com rmod.h rmod |
file2c.com: file2c.c |
wcl $(CFLAGS) file2c.c |
rmod.com: rmod.asm |
nasm -f bin -l rmod.lst -o rmod.com rmod.asm |
clean: .SYMBOLIC |
del *.com |
del *.obj |
pkg: svarcom.zip |
svarcom.zip: command.com |
zip -9 svarcom.zip command.com *.txt |
/svarcom/trunk/rmod.h |
---|
0,0 → 1,25 |
const char rmod[] = { |
131, 25,133, 25, 23, 32, 25, 32, 0, 0, 0, 0,128, 0, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, |
50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 0, 0, |
64, 58, 92, 67, 79, 77, 77, 65, 78, 68, 46, 67, 79, 77, 0,140, |
200,142,216,142,192,142,208,188,105, 1,180, 77,205, 33, 48,228, |
163, 10, 0,186,144, 0,131, 14,142, 0, 0,116, 8,142, 6, 8, |
0,139, 22,142, 0,161, 8, 0,163,247, 0,137, 22,249, 0,140, |
6,251, 0,184, 0, 75, 6, 31, 14, 7,187,247, 0,205, 33,115, |
190,140,203,142,219, 4, 48,162, 9, 1,180, 9,186, 5, 1,205, |
33,180, 8,205, 33,235,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
0, 0, 0, 0, 0, 69, 82, 82, 32,120, 44, 32, 70, 65, 73, 76, |
69, 68, 32, 84, 79, 32, 76, 79, 65, 68, 32, 67, 79, 77, 77, 65, |
78, 68, 46, 67, 79, 77, 13, 10, 36, 48, 49, 50, 51, 52, 53, 54, |
55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, 50, 51, 52, 53, 54, |
55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, 50, 51, 52, 53, 54, |
55, 56, 57, 65, 66, 67, 68, 69, 70, 48, 49, 50, 51, 52, 53, 54, |
55, 56, 57, 65, 66, 67, 68, 69, 70,120,120}; |
#define rmod_len 363 |
/svarcom/trunk/rmodinit.c |
---|
0,0 → 1,181 |
#include <i86.h> |
#include <string.h> |
#include "env.h" |
#include "helpers.h" |
#include "rmod.h" |
#include "rmodinit.h" |
/* returns segment where rmod is installed */ |
unsigned short rmod_install(unsigned short envsize) { |
char far *myptr, far *mcb; |
unsigned short far *owner; |
unsigned int rmodseg = 0xffff; |
unsigned int envseg = 0; |
/* read my current env segment from PSP */ |
_asm { |
push ax |
push bx |
mov bx, 0x2c |
mov ax, [bx] |
mov envseg, ax |
pop bx |
pop ax |
} |
/* printf("original (PSP) env buffer at %04X\r\n", envseg); */ |
/* if custom envsize requested, convert it to number of paragraphs */ |
if (envsize != 0) { |
envsize += 15; |
envsize /= 16; |
} |
_asm { |
/* link in the UMB memory chain for enabling high-memory allocation (and save initial status on stack) */ |
mov ax, 0x5802 /* GET UMB LINK STATE */ |
int 0x21 |
xor ah, ah |
push ax /* save link state on stack */ |
mov ax, 0x5803 /* SET UMB LINK STATE */ |
mov bx, 1 |
int 0x21 |
/* get current allocation strategy and save it in DX */ |
mov ax, 0x5800 |
int 0x21 |
push ax |
pop dx |
/* set strategy to 'last fit, try high then low memory' */ |
mov ax, 0x5801 |
mov bx, 0x0082 |
int 0x21 |
/* ask for a memory block and save the given segment to rmodseg */ |
mov ah, 0x48 |
mov bx, (rmod_len + 15) / 16 |
int 0x21 |
jc ALLOC_FAIL |
mov rmodseg, ax |
/* ask for a memory block for the environment and save it to envseg (only if custom size requested) */ |
mov bx, envsize |
test bx, bx |
jz ALLOC_FAIL |
mov ah, 0x48 |
int 0x21 |
jc ALLOC_FAIL |
mov envseg, ax |
ALLOC_FAIL: |
/* restore initial allocation strategy */ |
mov ax, 0x5801 |
mov bx, dx |
int 0x21 |
/* restore initial UMB memory link state */ |
mov ax, 0x5803 |
pop bx /* pop initial UMB link state from stack */ |
int 0x21 |
} |
if (rmodseg == 0xffff) { |
outputnl("malloc error"); |
return(0xffff); |
} |
/* copy rmod to its destination */ |
myptr = MK_FP(rmodseg, 0); |
_fmemcpy(myptr, rmod, rmod_len); |
/* mark rmod memory as "self owned" */ |
mcb = MK_FP(rmodseg - 1, 0); |
owner = (void far *)(mcb + 1); |
*owner = rmodseg; |
_fmemcpy(mcb + 8, "SVARCOM", 8); |
/* mark env memory as "self owned" */ |
mcb = MK_FP(envseg - 1, 0); |
owner = (void far *)(mcb + 1); |
*owner = rmodseg; |
_fmemcpy(mcb + 8, "SVARENV", 8); |
/* write env segment to rmod buffer */ |
owner = MK_FP(rmodseg, RMOD_OFFSET_ENVSEG); |
*owner = envseg; |
/* write boot drive to rmod bootdrive field */ |
_asm { |
push ax |
push bx |
push dx |
push ds |
mov ax, 0x3305 /* DOS 4.0+ - GET BOOT DRIVE */ |
int 0x21 /* boot drive is in DL now (1=A:, 2=B:, etc) */ |
add dl, 'A'-1 /* convert to a proper ASCII letter */ |
/* set DS to rmodseg */ |
mov ax, rmodseg |
mov ds, ax |
/* write boot drive to rmod bootdrive field */ |
mov bx, RMOD_OFFSET_BOOTDRIVE |
mov [bx], dl |
pop ds |
pop dx |
pop bx |
pop ax |
} |
/* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I terminate */ |
_asm { |
push ax |
push bx |
mov bx, 0x0a /* int22 handler is at 0x0A of the PSP */ |
mov ax, RMOD_OFFSET_ROUTINE |
mov [bx], ax /* int handler offset */ |
mov ax, rmodseg |
mov [bx+2], ax /* int handler segment */ |
pop bx |
pop ax |
} |
return(rmodseg); |
} |
/* scan memory for rmod, returns its segment if found, 0xffff otherwise */ |
unsigned short rmod_find(void) { |
unsigned short i; |
unsigned short far *ptrword; |
unsigned char far *ptrbyte; |
/* iterate over all paragraphs, looking for my signature */ |
for (i = 1; i != 65535; i++) { |
ptrword = MK_FP(i, 0); |
if (ptrword[0] != 0x1983) continue; |
if (ptrword[1] != 0x1985) continue; |
if (ptrword[2] != 0x2017) continue; |
if (ptrword[3] != 0x2019) continue; |
/* extra check: make sure the paragraph before is an MCB block and that it |
* belongs to itself. otherwise I could find the rmod code embedded inside |
* the command.com binary... */ |
ptrbyte = MK_FP(i - 1, 0); |
if ((*ptrbyte != 'M') && (*ptrbyte != 'Z')) continue; /* not an MCB */ |
ptrword = MK_FP(i - 1, 1); |
if (*ptrword != i) continue; /* not belonging to self */ |
return(i); |
} |
return(0xffff); |
} |
/* update rmod's pointer to comspec */ |
void rmod_updatecomspecptr(unsigned short rmod_seg, unsigned short env_seg) { |
unsigned short far *comspecptr = MK_FP(rmod_seg, RMOD_OFFSET_COMSPECPTR); |
char far *comspecfp = env_lookup(env_seg, "COMSPEC"); |
if (comspecfp != NULL) { |
*comspecptr = FP_OFF(comspecfp) + 8; /* +8 to skip the "COMSPEC=" prefix */ |
} else { |
*comspecptr = 0; |
} |
} |
/svarcom/trunk/rmodinit.h |
---|
0,0 → 1,15 |
#ifndef RMODINIT_H |
#define RMODINIT_H |
#define RMOD_OFFSET_ENVSEG 0x08 |
#define RMOD_OFFSET_LEXITCODE 0x0A |
#define RMOD_OFFSET_INPBUFF 0x0C |
#define RMOD_OFFSET_COMSPECPTR 0x8E |
#define RMOD_OFFSET_BOOTDRIVE 0x90 |
#define RMOD_OFFSET_ROUTINE 0x9F |
unsigned short rmod_install(unsigned short envsize); |
unsigned short rmod_find(void); |
void rmod_updatecomspecptr(unsigned short rmod_seg, unsigned short env_seg); |
#endif |
/svarcom/trunk/svarcom.txt |
---|
0,0 → 1,70 |
=== SVARCOM === |
SvarCOM is the SvarDOS command line interpreter, known usually under the name |
"COMMAND.COM". It is designed and maintained by Mateusz Viste, and distributed |
under the terms of the MIT license. |
For the time being, it is an incomplete, "work in progress" project. The goal |
is to make SvarCOM the default SvarDOS shell, replacing FreeCOM. |
Why replacing FreeCOM, you ask? See FREECOM.TXT for details. |
Since SvarCOM is a work-in-progress effort, it still lacks a few things: |
- no support for redirections or pipes (eg. file.exe | more > out.txt) |
- no batch file support |
- few internal commands missing: BREAK, CHCP, CLS, CTTY, LH, REN, COPY, DATE, |
TIME, VOL, PAUSE, ECHO, IF, REM, SHIFT |
Latest version available here: http://svardos.osdn.io/svarcom |
=== INTERNAL COMMANDS ======================================================== |
SvarCOM implements the following internal commands. For help on each command, |
run it with a "/?" argument. |
CD/CHDIR - displays the name of or changes the current directory |
DEL/ERASE - deletes one or more files |
DIR - displays a list of files and subdirectories in a directory |
EXIT - quits the command.com program (command interpreter) |
MD/MKDIR - creates a directory |
PATH - displays or sets a search path for executable files |
PROMPT - changes the DOS command prompt |
RMDIR - removes (deletes) a directory |
SET - displays, sets or removes DOS environment variables |
TYPE - displays the contents of a text file |
VER - displays the DOS version |
VERIFY - tells DOS whether to verify that files are written correctly |
=== LICENSE ================================================================== |
SvarCOM is published under the terms of the MIT license. |
Copyright (C) 2021 Mateusz Viste |
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is |
furnished to do so, subject to the following conditions: |
The above copyright notice and this permission notice shall be included in all |
copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
SOFTWARE. |
==================================================================== [EOF] === |