Subversion Repositories SvarDOS

Compare Revisions

Ignore whitespace Rev 396 → Rev 397

/svarcom/env.c
File deleted
/svarcom/helpers.h
File deleted
/svarcom/file2c.c
File deleted
/svarcom/doserr.h
File deleted
/svarcom/doserr.c
File deleted
/svarcom/env.h
File deleted
/svarcom/command.c
File deleted
/svarcom/freecom.txt
File deleted
/svarcom/cmd/_notimpl.c
File deleted
/svarcom/cmd/type.c
File deleted
/svarcom/cmd/prompt.c
File deleted
/svarcom/cmd/verify.c
File deleted
/svarcom/cmd/del.c
File deleted
/svarcom/cmd/mkdir.c
File deleted
/svarcom/cmd/cd.c
File deleted
/svarcom/cmd/exit.c
File deleted
/svarcom/cmd/set.c
File deleted
/svarcom/cmd/ver.c
File deleted
/svarcom/cmd/path.c
File deleted
/svarcom/cmd/rmdir.c
File deleted
/svarcom/cmd/dir.c
File deleted
/svarcom/rmodinit.c
File deleted
/svarcom/cmd.c
File deleted
/svarcom/rmodinit.h
File deleted
/svarcom/svarcom.txt
File deleted
/svarcom/cmd.h
File deleted
/svarcom/makefile
File deleted
/svarcom/rmod.h
File deleted
/svarcom/helpers.c
File deleted
/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] ===