Subversion Repositories SvarDOS

Compare Revisions

Ignore whitespace Rev 580 → Rev 581

/svarcom/tags/svarcom-2022.0/cmd/_notimpl.c
0,0 → 1,32
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* handler for all "not implemented yet" commands
*/
 
static enum cmd_result cmd_notimpl(struct cmd_funcparam *p) {
outputnl("This command is not implemented yet. Sorry!");
return(CMD_FAIL);
}
/svarcom/tags/svarcom-2022.0/cmd/break.c
0,0 → 1,90
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* break
*/
 
static enum cmd_result cmd_break(struct cmd_funcparam *p) {
unsigned char brkflag = 0;
 
if (cmd_ishlp(p)) {
outputnl("Sets or clears extended CTRL+C checking");
outputnl("");
outputnl("BREAK [ON | OFF]");
outputnl("");
outputnl("Type BREAK without a parameter to display the current BREAK setting.");
return(CMD_OK);
}
 
/* no params: display current break state */
if (p->argc == 0) {
_asm {
push ax
push dx
 
mov ax, 0x3300 /* query break-check flag */
int 0x21 /* status (0=OFF, 1=ON) in DL */
mov [brkflag], dl
 
pop dx
pop ax
}
if (brkflag == 0) {
outputnl("BREAK is off");
} else {
outputnl("BREAK is on");
}
return(CMD_OK);
}
 
/* too many params? */
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
/* exactly 1 parameter - "on" or "off" */
if (imatch(p->argv[0], "on")) {
brkflag = 1;
} else if (!imatch(p->argv[0], "off")) {
outputnl("Invalid parameter");
return(CMD_FAIL);
}
 
/* set break accordingly to brkflag */
_asm {
push ax
push dx
 
mov ax, 0x3301 /* set break-check level */
mov dl, [brkflag] /* 0=OFF 1=ON */
int 0x21
 
pop dx
pop ax
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/cd.c
0,0 → 1,106
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* 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.
*/
 
 
static enum cmd_result 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(CMD_OK);
}
 
/* one argument max */
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
/* no argument? display current drive and dir ("CWD") */
if (p->argc == 0) {
curpathfordrv(buffptr, 0);
outputnl(buffptr);
return(CMD_OK);
}
 
/* 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 = 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) {
nls_outputnl_doserr(err);
return(CMD_FAIL);
}
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/chcp.c
0,0 → 1,115
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* chcp
*/
 
static enum cmd_result cmd_chcp(struct cmd_funcparam *p) {
unsigned short nnn = 0;
unsigned short errcode = 0;
 
if (cmd_ishlp(p)) {
outputnl("Displays or sets the active code page number");
outputnl("");
outputnl("CHCP [nnn]");
outputnl("");
outputnl("nnn Specifies a code page number");
outputnl("");
outputnl("Type CHCP without a parameter to display the active code page number.");
return(CMD_OK);
}
 
/* too many parameters */
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
/* one param? must be numeric in range 1+ */
if (p->argc == 1) {
unsigned char nlsfuncflag = 0;
if (atous(&nnn, p->argv[0]) != 0) {
outputnl("Invalid code page number");
return(CMD_FAIL);
}
_asm {
/* verify that NLSFUNC is installed */
push ax
push bx
 
mov ax, 0x1400 /* DOS 3+ -- is NLSFUNC.EXE installed? */
int 0x2f /* AL = 0xff -> installed */
cmp al, 0xff
jne DONE
mov [nlsfuncflag], 1
 
/* set code page to nnn */
 
mov ax, 0x6602 /* DOS 3.3+ -- Activate Code Page */
mov bx, [nnn]
int 0x21 /* CF set on error and err code in AX */
jnc DONE
mov [errcode], ax /* store err code in nnn on failure */
DONE:
 
pop bx
pop ax
}
if (nlsfuncflag == 0) {
outputnl("NLSFUNC not installed");
} else if (errcode != 0) {
outputnl("Failed to change code page");
return(CMD_FAIL);
}
 
} else { /* no parameter given: display active code page */
 
_asm {
push ax
push bx
push dx
 
mov ax, 0x6601 /* DOS 3.3+ -- Query Active Code Page */
int 0x21 /* CF set on error, current CP in BX */
mov [nnn], bx
jnc DONE
mov [errcode], ax
DONE:
 
pop dx
pop bx
pop ax
}
if (errcode == 0) {
sprintf(p->BUFFER, "Active code page: %d", nnn);
outputnl(p->BUFFER);
} else {
nls_outputnl_doserr(errcode);
return(CMD_FAIL);
}
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/cls.c
0,0 → 1,87
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* cls
*/
 
static enum cmd_result cmd_cls(struct cmd_funcparam *p) {
unsigned char screenw, screenh;
const char *ansiesc = "\x1B[2J$";
 
if (cmd_ishlp(p)) {
outputnl("Clears the screen");
outputnl("");
outputnl("CLS");
return(CMD_OK);
}
 
screenw = screen_getwidth();
screenh = screen_getheight();
 
_asm {
/* output an ANSI ESC code for "clear screen" in case the console is
* some kind of terminal */
mov ah, 0x09 /* write $-terminated string to stdout */
mov dx, ansiesc
int 0x21
 
/* check what stdout is set to */
mov ax, 0x4400 /* IOCTL query device/flags flags */
mov bx, 1 /* file handle (1 = stdout) */
int 0x21 /* CF set on error, otherwise DX set with flags */
jc DONE /* abort on error */
/* DX = 10000010
| |||
| ||+--- indicates standard output
| |+---- set if NUL device
| +----- set if CLOCK device
+--------- set if handle is a device (ie. not a file)
in other words, DL & 10001110 (8Eh) should result in 10000010 (82h)
*/
and dl, 0x8e
cmp dl, 0x82
jne DONE /* abort on error */
 
/* scroll vram out of screen */
mov ax, 0x0600 /* scroll up entire rectangle */
mov bh, 0x07 /* fill screen with white-on-black */
xor cx, cx /* upper left location in CH,CL (0,0) */
/* DX is bottom right location of rectangle (DH=row, DL=column) */
mov dh, [screenh]
dec dh
mov dl, [screenw]
dec dl
int 0x10
 
/* set cursor to top left corner (0,0) of the screen */
mov ah, 0x02 /* set cursor position */
xor bh, bh /* page number */
xor dx, dx /* location in DH,DL */
int 0x10
DONE:
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/copy.c
0,0 → 1,363
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* copy
*/
 
/* /A - Used to copy ASCII files. Applies to the filename preceding it and to
* all following filenames. Files will be copied until an end-of-file mark is
* encountered in the file being copied. If an end-of-file mark is encountered
* in the file, the rest of the file is not copied. DOS will append an EOF
* mark at the end of the copied file.
*
* /B - Used to copy binary files. Applies to the filename preceding it and to
* all following filenames. Copied files will be read by size (according to
* the number of bytes indicated in the file`s directory listing). An EOF mark
* is not placed at the end of the copied file.
*
* /V - Checks after the copy to assure that a file was copied correctly. If
* the copy cannot be verified, the program will display an error message.
* Using this option will result in a slower copying process.
*
* special case: "COPY A+B+C+D" means "append B, C and D files to the A file"
* if A does not exist, then "append C and D to B", etc.
*/
 
struct copy_setup {
const char *src[64];
unsigned short src_count; /* how many sources are declared */
char cursrc[256]; /* buffer for currently processed src */
char dst[256];
unsigned short dstlen;
char src_asciimode[64];
char dst_asciimode;
char last_asciimode; /* /A or /B impacts the file preceding it and becomes the new default for all files that follow */
char verifyflag;
char lastitemwasplus;
unsigned short databufsz;
char databuf[1];
};
 
 
/* copies src to dst, overwriting or appending to the destination.
* - copy is performed in ASCII mode if asciiflag set (stop at first EOF in src
* and append an EOF in dst).
* - returns zero on success, DOS error code on error */
static unsigned short cmd_copy_internal(const char *dst, char dstascii, const char *src, char srcascii, unsigned char appendflag, void *buff, unsigned short buffsz) {
unsigned short errcode = 0;
unsigned short srch = 0xffff, dsth = 0xffff;
_asm {
 
/* open src */
OPENSRC:
mov ax, 0x3d00 /* DOS 2+ -- open an existing file, read access mode */
mov dx, src /* ASCIIZ fname */
int 0x21 /* CF clear on success, handle in AX */
jc FAIL
mov [srch], ax /* store src handle in memory */
 
/* check appendflag so I know if I have to try opening dst for append */
xor al, al
or al, [appendflag]
jz CREATEDST
 
/* try opening dst first if appendflag set */
mov ax, 0x3d01 /* DOS 2+ -- open an existing file, write access mode */
mov dx, dst /* ASCIIZ fname */
int 0x21 /* CF clear on success, handle in AX */
jc CREATEDST /* failed to open file (file does not exist) */
mov [dsth], ax /* store dst handle in memory */
 
/* got file open, LSEEK to end of it now so future data is appended */
mov bx, ax /* file handle in BX (was still in AX) */
mov ax, 0x4202 /* DOS 2+ -- set file pointer to end of file + CX:DX */
xor cx, cx /* offset zero */
xor dx, dx /* offset zero */
int 0x21 /* CF set on error */
jc FAIL
jmp COPY
 
/* create dst */
CREATEDST:
mov ah, 0x3c /* DOS 2+ -- create a file */
mov dx, dst
xor cx, cx /* zero out attributes */
int 0x21 /* handle in AX on success, CF set on error */
jc FAIL
mov [dsth], ax /* store dst handle in memory */
 
/* perform actual copy */
COPY:
/* read a block from src */
mov ah, 0x3f /* DOS 2+ -- read from file */
mov bx, [srch]
mov cx, [buffsz]
mov dx, [buff] /* DX points to buffer */
int 0x21 /* CF set on error, bytes read in AX (0=EOF) */
jc FAIL /* abort on error */
/* EOF? (ax == 0) */
test ax, ax
jz ENDOFFILE
/* write block of AX bytes to dst */
mov cx, ax /* block length */
mov ah, 0x40 /* DOS 2+ -- write to file (CX bytes from DS:DX) */
mov bx, [dsth] /* file handle */
/* mov dx, [buff] */ /* DX points to buffer already */
int 0x21 /* CF clear and AX=CX on success */
jc FAIL
cmp ax, cx /* sould be equal, otherwise failed */
mov ax, 0x08 /* preset to DOS error "Insufficient memory" */
jne FAIL
jmp COPY
 
ENDOFFILE:
/* if dst ascii mode -> add an EOF (ASCII mode not supported for the time being) */
 
jmp CLOSESRC
 
FAIL:
mov [errcode], ax
 
CLOSESRC:
/* close src and dst */
mov bx, [srch]
cmp bx, 0xffff
je CLOSEDST
mov ah, 0x3e /* DOS 2+ -- close a file handle */
int 0x21
 
CLOSEDST:
mov bx, [dsth]
cmp bx, 0xffff
je DONE
mov ah, 0x3e /* DOS 2+ -- close a file handle */
int 0x21
 
DONE:
}
return(errcode);
}
 
 
static enum cmd_result cmd_copy(struct cmd_funcparam *p) {
struct copy_setup *setup = (void *)(p->BUFFER);
unsigned short i;
unsigned short copiedcount_in = 0, copiedcount_out = 0; /* number of input/output copied files */
struct DTA *dta = (void *)0x80; /* use DTA at default location in PSP */
 
if (cmd_ishlp(p)) {
outputnl("Copies one or more files to another location.");
outputnl("");
outputnl("COPY [/A|/B] source [/A|/B] [+source [/A|/B] [+...]] [destination [/A|/B]] [/V]");
outputnl("");
outputnl("source Specifies the file or files to be copied");
outputnl("/A Indicates an ASCII text file");
outputnl("/B Indicates a binary file");
outputnl("destination Specifies the directory and/or filename for the new file(s)");
outputnl("/V Verifies that new files are written correctly");
outputnl("");
outputnl("To append files, specify a single file for destination, but multiple files");
outputnl("for source (using wildcards or file1+file2+file3 format).");
outputnl("");
outputnl("NOTE: /A and /B are no-ops (ignored), provided only for compatibility reasons.");
return(CMD_OK);
}
 
/* parse cmdline and fill the setup struct accordingly */
 
memset(setup, 0, sizeof(*setup));
setup->databufsz = p->BUFFERSZ - sizeof(*setup);
 
for (i = 0; i < p->argc; i++) {
 
/* switch? */
if (p->argv[i][0] == '/') {
if ((imatch(p->argv[i], "/a")) || (imatch(p->argv[i], "/b"))) {
setup->last_asciimode = 'b';
if (imatch(p->argv[i], "/a")) setup->last_asciimode = 'a';
/* */
if (setup->dst[0] != 0) {
setup->dst_asciimode = setup->last_asciimode;
} else if (setup->src_count != 0) {
setup->src_asciimode[setup->src_count - 1] = setup->last_asciimode;
}
} else if (imatch(p->argv[i], "/v")) {
setup->verifyflag = 1;
} else {
outputnl("Invalid switch");
return(CMD_FAIL);
}
continue;
}
 
/* not a switch - must be either a source, a destination or a + */
if (p->argv[i][0] == '+') {
/* a plus cannot appear after destination or before first source */
if ((setup->dst[0] != 0) || (setup->src_count == 0)) {
outputnl("Invalid syntax");
return(CMD_FAIL);
}
setup->lastitemwasplus = 1;
/* a plus may be immediately followed by a filename - if so, emulate
* a new argument */
if (p->argv[i][1] != 0) {
p->argv[i] += 1;
i--;
}
continue;
}
 
/* src? (first non-switch or something that follows a +) */
if ((setup->lastitemwasplus) || (setup->src_count == 0)) {
setup->src[setup->src_count] = p->argv[i];
setup->src_asciimode[setup->src_count] = setup->last_asciimode;
setup->src_count++;
setup->lastitemwasplus = 0;
continue;
}
 
/* must be a dst then */
if (setup->dst[0] != 0) {
outputnl("Invalid syntax");
return(CMD_FAIL);
}
if (file_truename(p->argv[i], setup->dst) != 0) {
outputnl("Invalid destination");
return(CMD_FAIL);
}
setup->dst_asciimode = setup->last_asciimode;
/* if dst is a directory then append a backslash */
setup->dstlen = path_appendbkslash_if_dir(setup->dst);
}
 
/* DEBUG: output setup content ("if 1" to enable) */
#if 0
printf("src: ");
for (i = 0; i < setup->src_count; i++) {
if (i != 0) printf(", ");
printf("%s [%c]", setup->src[i], setup->src_asciimode[i]);
}
printf("\r\n");
printf("dst: %s [%c]\r\n", setup->dst, setup->dst_asciimode);
printf("verify: %s\r\n", (setup->verifyflag)?"ON":"OFF");
#endif
 
/* must have at least one source */
if (setup->src_count == 0) {
outputnl("Required parameter missing");
return(CMD_FAIL);
}
 
/* perform the operation based on setup directives:
* iterate over every source and copy it to dest */
 
for (i = 0; i < setup->src_count; i++) {
unsigned short t;
unsigned short cursrclen;
unsigned short pathendoffset;
 
/* resolve truename of src and write it to buffer */
t = file_truename(setup->src[i], setup->cursrc);
if (t != 0) {
output(setup->src[i]);
output(" - ");
nls_outputnl_doserr(t);
continue;
}
cursrclen = strlen(setup->cursrc); /* remember cursrc length */
 
/* if length zero, skip (not sure why this would be possible, though) */
if (cursrclen == 0) continue;
 
/* if src does not end with a backslash AND it is a directory then append a backslash */
cursrclen = path_appendbkslash_if_dir(setup->cursrc);
 
/* if src ends with a '\' then append *.* */
if (setup->cursrc[cursrclen - 1] == '\\') {
strcat(setup->cursrc, "*.*");
}
 
/* remember where the path in cursrc ends */
for (t = 0; setup->cursrc[t] != 0; t++) {
if (setup->cursrc[t] == '\\') pathendoffset = t + 1;
}
 
/* */
if (findfirst(dta, setup->cursrc, 0) != 0) {
continue;
}
 
do {
char appendflag;
if (dta->attr & DOS_ATTR_DIR) continue; /* skip directories */
 
/* compute full path/name of the file */
strcpy(setup->cursrc + pathendoffset, dta->fname);
 
/* if there was no destination, then YOU are the destination now!
* this handles situations like COPY a.txt+b.txt+c.txt */
if (setup->dst[0] == NULL) {
strcpy(setup->dst, setup->cursrc);
setup->dstlen = strlen(setup->dst);
copiedcount_in++;
copiedcount_out++;
continue;
}
 
/* is dst ending with a backslash? then append fname to it */
if (setup->dst[setup->dstlen - 1] == '\\') strcpy(setup->dst + setup->dstlen, dta->fname);
 
/* now cursrc contains the full source and dst contains the full dest... COPY TIME! */
 
/* if dst file exists already -> overwrite it or append?
- if dst is a dir (dstlen-1 points at a \\) -> overwrite
- otherwise: if copiedcount_in==0 overwrite, else append */
output(setup->cursrc);
if ((setup->dst[setup->dstlen - 1] == '\\') || (copiedcount_in == 0)) {
appendflag = 0;
output(" > ");
copiedcount_out++;
} else {
appendflag = 1;
output(" >> ");
}
outputnl(setup->dst);
 
t = cmd_copy_internal(setup->dst, 0, setup->cursrc, 0, appendflag, setup->databuf, setup->databufsz);
if (t != 0) {
nls_outputnl_doserr(t);
return(CMD_FAIL);
}
 
copiedcount_in++;
} while (findnext(dta) == 0);
 
}
 
sprintf(setup->databuf, "%u file(s) copied", copiedcount_out);
outputnl(setup->databuf);
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/date.c
0,0 → 1,226
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* date [date]
*/
 
 
/* parse a NULL-terminated string int hour, minutes and seconds, returns 0 on success
* valid inputs: 0, 7, 5:5, 23:23, 17:54:45, 9p, 9:05, ...
*/
static int cmd_date_parse(const char *s, unsigned short *year, unsigned char *mo, unsigned char *dy, struct nls_patterns *nls) {
unsigned short i;
const char *ptrs[2] = {NULL, NULL};
 
*year = 0;
*mo = 0;
*dy = 0;
 
/* validate input - must contain only chars 0-9 and time separator */
for (i = 0; s[i] != 0; i++) {
switch (s[i]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
if ((s[i] != nls->datesep[0]) || (i == 0)) return(-1);
if (ptrs[0] == NULL) {
ptrs[0] = s + i + 1;
} else if (ptrs[1] == NULL) {
ptrs[1] = s + i + 1;
} else { /* too many separators */
return(-1);
}
break;
}
}
 
/* did I get all separators? */
if ((ptrs[0] == NULL) || (ptrs[1] == NULL)) goto FAIL;
 
/* d/m/y order depends on NLS settings */
switch (nls->dateformat) {
case 0: /* m/d/y */
atous(&i, s);
*mo = i;
atous(&i, ptrs[0]);
*dy = i;
atous(year, ptrs[1]);
break;
case 1: /* d/m/y */
atous(&i, s);
*dy = i;
atous(&i, ptrs[0]);
*mo = i;
atous(year, ptrs[1]);
break;
default: /* y/m/d */
atous(year, s);
atous(&i, ptrs[0]);
*mo = i;
atous(&i, ptrs[1]);
*dy = i;
break;
}
 
return(0);
 
FAIL:
*year = 0;
return(-1);
}
 
 
/* set system date, return 0 on success */
static int cmd_date_set(unsigned short year, unsigned char mo, unsigned char dy) {
_asm {
push ax
push bx
push cx
push dx
 
mov ax, 0x2b00 /* DOS 1+ -- Set DOS Date */
mov cx, [year] /* year (1980-2099) */
mov dh, [mo] /* month (1-12) */
mov dl, [dy] /* day (1-31) */
int 0x21 /* AL = 0 on success */
cmp al, 0
je DONE
mov [year], 0
DONE:
 
pop dx
pop cx
pop bx
pop ax
}
 
if (year == 0) return(-1);
return(0);
}
 
 
static enum cmd_result cmd_date(struct cmd_funcparam *p) {
struct nls_patterns *nls = (void *)(p->BUFFER);
char *buff = p->BUFFER + sizeof(*nls);
unsigned short i;
unsigned short year = 0;
unsigned char mo, dy;
 
if (cmd_ishlp(p)) {
outputnl("Displays or sets the system date.");
outputnl("");
outputnl("DATE [date]");
outputnl("");
outputnl("Type DATE with no parameters to display the current date and a prompt for a");
outputnl("new one. Press ENTER to keep the same date.");
return(CMD_OK);
}
 
i = nls_getpatterns(nls);
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
/* display current date if no args */
if (p->argc == 0) {
/* get cur date */
_asm {
push ax
push cx
push dx
 
mov ah, 0x2a /* DOS 1+ -- Query DOS Date */
int 0x21 /* CX=year DH=month DL=day */
mov [year], cx
mov [mo], dh
mov [dy], dl
 
pop dx
pop cx
pop ax
}
buff[0] = ' ';
nls_format_date(buff + 1, year, mo, dy, nls);
output("Current date is");
outputnl(buff);
year = 0;
} else { /* parse date if provided */
if ((cmd_date_parse(p->argv[0], &year, &mo, &dy, nls) != 0) || (cmd_date_set(year, mo, dy) != 0)) {
outputnl("Invalid date");
year = 0;
}
}
 
/* ask for date if not provided or if input was malformed */
while (year == 0) {
output("Enter new date:");
output(" ");
/* collect user input into buff */
_asm {
push ax
push bx
push dx
 
mov ah, 0x0a /* DOS 1+ -- Buffered String Input */
mov bx, buff
mov dx, bx
mov al, 16
mov [bx], al /* max input length */
mov al, 1
mov [bx+1], al /* zero out the "previous entry" length */
int 0x21
/* terminate the string with a NULL terminator */
xor ax, ax
inc bx
mov al, [bx] /* read length of input string */
mov bx, ax
add bx, dx
mov [bx+2], ah
/* output a \n */
mov ah, 2
mov dl, 0x0A
int 0x21
 
pop dx
pop bx
pop ax
}
if (buff[1] == 0) break; /* empty string = no date change */
if ((cmd_date_parse(buff + 2, &year, &mo, &dy, nls) == 0) && (cmd_date_set(year, mo, dy) == 0)) break;
outputnl("Invalid date");
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/del.c
0,0 → 1,143
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* del/erase
*/
 
static enum cmd_result 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(CMD_OK);
}
 
if (p->argc == 0) {
outputnl("Required parameter missing");
return(CMD_FAIL);
}
 
/* 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(CMD_FAIL);
}
} else if (delspec != NULL) { /* otherwise its a delspec */
outputnl("Too many parameters");
return(CMD_FAIL);
} else {
delspec = p->argv[i];
}
}
 
/* convert path to canonical form */
file_truename(delspec, buff);
 
/* is delspec pointing at a directory? if so, add a \*.* */
i = path_appendbkslash_if_dir(buff);
if (buff[i - 1] == '\\') 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(CMD_FAIL);
}
 
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);
if (err != 0) { /* report the error only if query had no wildcards */
for (i = 0; buff[i] != 0; i++) if (buff[i] == '?') break;
if (buff[i] == 0) nls_outputnl_doserr(err);
break;
}
} else {
if (findnext(dta) != 0) break; /* do not report errors on findnext() */
}
 
/* prep the full path/name of the file in buff */
/* NOTE: buff contained the search pattern but it is no longer needed so I
* can reuse it now */
strcpy(buff + pathlimit, fname);
 
/* ask if confirmation required: PLIK.TXT Delete (Y/N)? */
if (confirmflag) {
output(buff);
output(" \t");
if (askchoice("Delete (Y/N)?", "YN") != 0) continue;
}
 
/* del found file */
_asm {
push ax
push dx
mov ah, 0x41 /* delete a file, DS:DX points to an ASCIIZ filespec (no wildcards allowed) */
mov dx, buff
int 0x21
jnc DONE
mov [err], ax
DONE:
pop dx
pop ax
}
 
if (err != 0) {
output(fname);
output(": ");
nls_outputnl_doserr(err);
break;
}
}
 
if (err == 0) return(CMD_OK);
return(CMD_FAIL);
}
/svarcom/tags/svarcom-2022.0/cmd/dir.c
0,0 → 1,444
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* 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.
*/
 
#define WCOLWIDTH 15 /* width of a column in wide mode output */
 
 
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
* returns DOS ERR code on failure */
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
unsigned short res = 0;
unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
 
_asm {
push ax
push bx
push cx
push dx
 
mov ah, 0x36 /* DOS 2+ -- Get Disk Free Space */
mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
inc dl
int 0x21 /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
jne COMPUTEDF
mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
jmp DONE
 
COMPUTEDF:
/* freebytes = AX * BX * CX */
mov [sects_per_clust], ax
mov [avail_clusts], bx
mov [bytes_per_sect], cx
 
DONE:
pop dx
pop cx
pop bx
pop ax
}
 
/* multiple steps to avoid uint16 overflow */
*freebytes = sects_per_clust;
*freebytes *= avail_clusts;
*freebytes *= bytes_per_sect;
 
return(res);
}
 
 
static void dir_pagination(unsigned short *availrows) {
*availrows -= 1;
if (*availrows == 0) {
press_any_key();
*availrows = screen_getheight() - 1;
}
}
 
 
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
* /AHS -> adds S and H to mandatory attribs ("must")
* /A-S -> removes S from allowed attribs ("may")
* returns non-zero on error. */
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
for (; *arg != 0; arg++) {
unsigned char curattr;
char not;
if (*arg == '-') {
not = 1;
arg++;
} else {
not = 0;
}
switch (*arg) {
case 'd':
case 'D':
curattr = DOS_ATTR_DIR;
break;
case 'r':
case 'R':
curattr = DOS_ATTR_RO;
break;
case 'a':
case 'A':
curattr = DOS_ATTR_ARC;
break;
case 'h':
case 'H':
curattr = DOS_ATTR_HID;
break;
case 's':
case 'S':
curattr = DOS_ATTR_SYS;
break;
default:
return(-1);
}
/* update res bitfield */
if (not) {
*attrfilter_may &= ~curattr;
} else {
*attrfilter_must |= curattr;
}
}
return(0);
}
 
 
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
 
static enum cmd_result 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 */
unsigned short i;
unsigned short availrows; /* counter of available rows on display (used for /P) */
unsigned short wcols = screen_getwidth() / WCOLWIDTH; /* number of columns in wide mode */
unsigned char wcolcount;
struct nls_patterns *nls = (void *)(p->BUFFER + (p->BUFFERSZ / 2));
char *buff2 = p->BUFFER + (p->BUFFERSZ / 2) + sizeof(*nls);
unsigned long summary_fcount = 0;
unsigned long summary_totsz = 0;
unsigned char drv = 0;
unsigned char attrfilter_may = DIR_ATTR_DEFAULT;
unsigned char attrfilter_must = 0;
 
#define DIR_FLAG_PAUSE 1
#define DIR_FLAG_RECUR 4
#define DIR_FLAG_LCASE 8
unsigned char flags = 0;
 
#define DIR_OUTPUT_NORM 1
#define DIR_OUTPUT_WIDE 2
#define DIR_OUTPUT_BARE 3
unsigned char format = DIR_OUTPUT_NORM;
 
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");
return(CMD_OK);
}
 
i = nls_getpatterns(nls);
if (i != 0) nls_outputnl_doserr(i);
 
/* parse command line */
for (i = 0; i < p->argc; i++) {
if (p->argv[i][0] == '/') {
const char *arg = p->argv[i] + 1;
char neg = 0;
/* detect negations and get actual argument */
if (*arg == '-') {
neg = 1;
arg++;
}
/* */
switch (*arg) {
case 'a':
case 'A':
arg++;
/* preset defaults */
attrfilter_may = DIR_ATTR_DEFAULT;
attrfilter_must = 0;
/* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
if (neg) {
if (*arg != 0) {
nls_outputnl_err(0, 2); /* invalid switch */
return(CMD_FAIL);
}
} else {
/* start with "allow everything" */
attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
if (dir_parse_attr_list(arg, &attrfilter_may, &attrfilter_must) != 0) {
nls_outputnl_err(0, 3); /* invalid parameter format */
return(CMD_FAIL);
}
}
break;
case 'b':
case 'B':
format = DIR_OUTPUT_BARE;
break;
case 'l':
case 'L':
flags |= DIR_FLAG_LCASE;
break;
case 'o':
case 'O':
/* TODO */
outputnl("/O NOT IMPLEMENTED YET");
return(CMD_FAIL);
break;
case 'p':
case 'P':
flags |= DIR_FLAG_PAUSE;
if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
break;
case 's':
case 'S':
/* TODO */
outputnl("/S NOT IMPLEMENTED YET");
return(CMD_FAIL);
break;
case 'w':
case 'W':
format = DIR_OUTPUT_WIDE;
break;
default:
nls_outputnl_err(0, 2); /* invalid switch */
return(CMD_FAIL);
}
} else { /* filespec */
if (filespecptr != NULL) {
nls_outputnl_err(0, 4); /* too many parameters */
return(CMD_FAIL);
}
filespecptr = p->argv[i];
}
}
 
if (filespecptr == NULL) filespecptr = ".";
 
availrows = screen_getheight() - 2;
 
/* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
p->BUFFER[0] = filespecptr[0] - ('a' - 1);
} else {
p->BUFFER[0] = filespecptr[0] - ('A' - 1);
}
i = curpathfordrv(p->BUFFER, p->BUFFER[0]);
} else {
i = file_truename(filespecptr, p->BUFFER);
}
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
if (format != DIR_OUTPUT_BARE) {
drv = p->BUFFER[0];
if (drv >= 'a') {
drv -= 'a';
} else {
drv -= 'A';
}
cmd_vol_internal(drv, buff2);
sprintf(buff2, "Directory of %s", p->BUFFER);
/* trim at first '?', if any */
for (i = 0; buff2[i] != 0; i++) if (buff2[i] == '?') buff2[i] = 0;
outputnl(buff2);
outputnl("");
availrows -= 3;
}
 
/* if dir: append a backslash (also get its len) */
i = path_appendbkslash_if_dir(p->BUFFER);
 
/* if ends with a \ then append ????????.??? */
if (p->BUFFER[i - 1] == '\\') strcat(p->BUFFER, "????????.???");
 
/* ask DOS for list of files, but only with allowed attribs */
i = findfirst(dta, p->BUFFER, attrfilter_may);
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
wcolcount = 0; /* may be used for columns counting with wide mode */
 
do {
/* if mandatory attribs are requested, filter them now */
if ((attrfilter_must & dta->attr) != attrfilter_must) continue;
 
/* if file contains attributes that are not allowed -> skip */
if ((~attrfilter_may & dta->attr) != 0) continue;
 
/* turn string lcase (/L) */
if (flags & DIR_FLAG_LCASE) _strlwr(dta->fname); /* OpenWatcom extension, probably does not care about NLS so results may be odd with non-A-Z characters... */
 
summary_fcount++;
if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
 
switch (format) {
case DIR_OUTPUT_NORM:
/* print fname-space-extension (unless it's "." or "..", then print as-is) */
if (dta->fname[0] == '.') {
output(dta->fname);
i = strlen(dta->fname);
while (i++ < 12) output(" ");
} else {
file_fname2fcb(buff2, dta->fname);
memmove(buff2 + 9, buff2 + 8, 4);
buff2[8] = ' ';
output(buff2);
}
output(" ");
/* either <DIR> or right aligned 10-chars byte size */
memset(buff2, ' ', 10);
if (dta->attr & DOS_ATTR_DIR) {
strcpy(buff2 + 10, "<DIR>");
} else {
_ultoa(dta->size, buff2 + 10, 10); /* OpenWatcom extension */
}
output(buff2 + strlen(buff2) - 10);
/* two spaces and NLS DATE */
buff2[0] = ' ';
buff2[1] = ' ';
nls_format_date(buff2 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, nls);
output(buff2);
 
/* one space and NLS TIME */
nls_format_time(buff2 + 1, dta->time_hour, dta->time_min, 0xff, nls);
outputnl(buff2);
break;
 
case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
i = strlen(dta->fname);
if (dta->attr & DOS_ATTR_DIR) {
i += 2;
output("[");
output(dta->fname);
output("]");
} else {
output(dta->fname);
}
while (i++ < WCOLWIDTH) output(" ");
if (++wcolcount == wcols) {
wcolcount = 0;
outputnl("");
} else {
availrows++; /* wide mode is the only one that does not write one line per file */
}
break;
 
case DIR_OUTPUT_BARE:
outputnl(dta->fname);
break;
}
 
if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
 
} while (findnext(dta) == 0);
 
if (wcolcount != 0) {
outputnl(""); /* in wide mode make sure to end on a clear row */
if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
}
 
/* print out summary (unless bare output mode) */
if (format != DIR_OUTPUT_BARE) {
unsigned short alignpos;
/* x file(s) */
memset(buff2, ' ', 13); /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
i = nls_format_number(buff2 + 13, summary_fcount, nls);
alignpos = sprintf(buff2 + 13 + i, " %s ", "file(s)");
output(buff2 + i);
/* xxxx bytes */
i = nls_format_number(buff2 + 13, summary_totsz, nls);
output(buff2 + i);
output(" ");
outputnl("bytes");
if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
/* xxxx bytes free */
i = cmd_dir_df(&summary_totsz, drv);
if (i != 0) nls_outputnl_doserr(i);
alignpos += 13 + 13;
memset(buff2, ' ', alignpos); /* align the freebytes value to same column as totbytes */
i = nls_format_number(buff2 + alignpos, summary_totsz, nls);
output(buff2 + i);
output(" ");
outputnl("bytes free");
if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/echo.c
0,0 → 1,95
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* echo
*/
 
static enum cmd_result cmd_echo(struct cmd_funcparam *p) {
const char *arg = p->cmdline + 5;
 
/* display help only if /? is the only argument */
if ((p->argc == 1) && (imatch(p->argv[0], "/?"))) {
outputnl("Displays messages, or turns command-echoing on or off");
outputnl("");
outputnl("ECHO [ON | OFF]");
outputnl("ECHO [message]");
outputnl("");
outputnl("Type ECHO without parameters to display the current echo setting.");
return(CMD_OK);
}
 
/* ECHO without any parameter: display current state */
if (p->argc == 0) {
if (p->rmod->flags & FLAG_ECHOFLAG) {
outputnl("ECHO is on");
} else {
outputnl("ECHO is off");
}
return(CMD_OK);
}
 
/* ECHO ON */
if ((p->argc == 1) && (imatch(p->argv[0], "on"))) {
p->rmod->flags |= FLAG_ECHOFLAG;
return(CMD_OK);
}
 
/* ECHO OFF */
if ((p->argc == 1) && (imatch(p->argv[0], "off"))) {
p->rmod->flags &= ~FLAG_ECHOFLAG;
return(CMD_OK);
}
 
/* ECHO MSG (start at cmdline+5 since first 5 are "ECHO" + separator) */
_asm {
push ax
push dx
push si
 
mov si, [arg]
cld /* clear direction flag (DF) so lodsb increments SI */
mov ah, 0x02 /* display char from DL */
NEXTYBTE:
lodsb /* load byte at DS:[SI] into AL and inc SI (if DF clear) */
or al, al /* is AL == 0? then end of string reached */
jz DONE
mov dl, al
int 0x21
jmp NEXTYBTE
 
/* output a final CR/LF */
DONE:
mov dl, 0x0D
int 0x21
mov dl, 0x0A
int 0x21
 
pop si
pop dx
pop ax
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/exit.c
0,0 → 1,40
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* exit
*
* Quits the COMMAND.COM program (command interpreter)
*
*/
 
static enum cmd_result cmd_exit(struct cmd_funcparam *p) {
if (cmd_ishlp(p)) {
outputnl("EXIT\r\n");
outputnl("Quits the COMMAND.COM program (command interpreter)");
} else {
sayonara(p->rmod);
}
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/if.c
0,0 → 1,138
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* if [not] exists
* if [not] errorlevel 1
* if [not] string ==string (string==string and string == string works, too)
* if [not] errorlevel == 1 <-- I do NOT support this one, even though
* MSDOS 5 and 6 considers it equivalent to
* IF ERRORLEVEL 1. This is a misleading and
* undocumented syntax (does not actually
* check for equality).
*/
 
 
#define JMP_NEXT_ARG(s) while ((*s != ' ') && (*s != 0)) s++; while (*s == ' ') s++;
 
 
static enum cmd_result cmd_if(struct cmd_funcparam *p) {
unsigned char negflag = 0;
unsigned short i;
const char *s = p->cmdline + p->argoffset;
 
/* help screen ONLY if /? is the only argument - I do not want to output
* help for ex. for "if %1 == /? echo ..." */
if ((p->argc == 1) && (imatch(p->argv[0], "/?"))) {
outputnl("Performs conditional processing in batch programs.");
outputnl("");
outputnl("IF [NOT] ERRORLEVEL num command");
outputnl("IF [NOT] string1==string2 command");
outputnl("IF [NOT] EXIST filename command");
outputnl("");
outputnl("NOT command is executed only if condition is NOT met");
outputnl("ERRORLEVEL num condition: last program returned an exit code >= num");
outputnl("string1==string2 condition: both strings must be equal");
outputnl("EXIST filename condition: filename exists (wildcards accepted)");
outputnl("command command to carry out if condition is met.");
return(CMD_OK);
}
 
/* negation? */
if (imatchlim(s, "NOT ", 4)) {
negflag = 1;
JMP_NEXT_ARG(s);
}
 
/* IF ERRORLEVEL x cmd */
if (imatchlim(s, "ERRORLEVEL ", 11)) {
unsigned char far *rmod_exitcode = MK_FP(p->rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
JMP_NEXT_ARG(s);
if (*s == 0) goto SYNTAX_ERR;
/* convert errorlevel to an uint */
if ((*s < '0') || (*s > '9')) {
i = 0xffff;
} else {
atous(&i, s);
}
/* move s to command */
JMP_NEXT_ARG(s);
/* is errorlevel matching? */
if (i <= *rmod_exitcode) negflag ^= 1;
goto EXEC_S_CMD_IF_NEGFLAG_SET;
}
 
/* IF EXIST fname (or wildcard)
* TODO: checking for a file on an empty diskette drive should NOT lead bother
* the user with the stupid 'retry, abort, fail' query! */
if (imatchlim(s, "EXIST ", 6)) {
struct DTA *dta = (void *)(0x80); /* default dta location */
JMP_NEXT_ARG(s);
/* copy filename to buffer */
for (i = 0; (s[i] != ' ') && (s[i] != 0); i++) p->BUFFER[i] = s[i];
p->BUFFER[i] = 0;
/* move s to command */
JMP_NEXT_ARG(s);
if (*s == 0) goto SYNTAX_ERR; /* check now to avoid moving the diskette drive if syntax bad anyway */
/* does file exist? */
if (findfirst(dta, p->BUFFER, 0) == 0) negflag ^= 1;
goto EXEC_S_CMD_IF_NEGFLAG_SET;
}
 
/* IF str1==str2 ? (and if that's not it, then it's a syntax error) */
if (strstr(s, "==") != NULL) {
/* copy first argument to BUFF, until first '=' or space */
for (i = 0; (s[i] != '=') && (s[i] != ' '); i++) p->BUFFER[i] = s[i];
/* 1st arg cannot be empty */
if (i == 0) goto SYNTAX_ERR;
/* terminate buff string and move s forward to the equality char (or space) */
p->BUFFER[i++] = 0;
s += i;
while (*s == ' ') s++;
/* if second char is not a '=' then syntax error (equality sign is not
* allowed in first string) */
if (*s != '=') goto SYNTAX_ERR;
/* skip all trailing equality chars (MSDOS accepts many of them, ie all
* these are fine: "dupa==dupa", "dupa===dupa", "dupa====dupa", etc) */
while (*s == '=') s++;
while (*s == ' ') s++; /* skip any leading spaces */
/* move along until space or NULL terminator, checking equality */
for (i = 0; (p->BUFFER[i] != 0) && (p->BUFFER[i] == s[i]); i++);
if ((p->BUFFER[i] == 0) && (s[i] == ' ')) negflag ^= 1;
JMP_NEXT_ARG(s);
goto EXEC_S_CMD_IF_NEGFLAG_SET;
}
 
/* invalid syntax */
SYNTAX_ERR:
outputnl("Syntax error");
return(CMD_FAIL);
 
/* let's exec command (write it to start of cmdline and parse again) */
EXEC_S_CMD_IF_NEGFLAG_SET:
if (*s == 0) goto SYNTAX_ERR;
if (negflag == 0) return(CMD_OK);
memmove((void *)(p->cmdline), s, strlen(s) + 1); /* cmdline and s share the same memory! */
return(CMD_CHANGED);
}
/svarcom/tags/svarcom-2022.0/cmd/ln.c
0,0 → 1,257
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
/*
* ln add linkname linkdir
* ln del linkname
* ln list [pattern]
*/
 
static enum cmd_result cmd_lnadd(char *BUFFER, const char *linkname, const char *targetdir, unsigned short env_seg) {
const char *ext;
char *realdirname = BUFFER;
unsigned short realdirnamelen;
char *buff = BUFFER + 256;
unsigned short doserr;
 
/* convert dirname to realpath */
doserr = file_truename(targetdir, realdirname);
if (doserr != 0) {
nls_outputnl_doserr(doserr);
return(CMD_FAIL);
}
 
/* does EXENAME in DIRECTORY exist? */
if (lookup_cmd(buff, linkname, realdirname, &ext) != 0) {
outputnl("No matching executable found in given path.");
return(CMD_FAIL);
}
 
/* open DOSDIR\CFG\LINKS.DB and write realdirname to */
if (link_computefname(buff, linkname, env_seg) != 0) return(CMD_FAIL);
 
realdirnamelen = strlen(realdirname);
 
/* open file *only if it does not exist yet* and write realdirname to it */
_asm {
push ax
push bx
push cx
push dx
 
mov ax, 0x6c00 /* extended OPEN */
mov bx, 1 /* open for WRITE */
xor cx, cx /* create the file with no attributes */
mov dx, 0x0010 /* create file if it does not exists, otherwise fail */
mov si, buff /* file name */
int 0x21
jnc WRITE
mov doserr, ax
jmp DONE
 
WRITE:
mov bx, ax /* file handle */
mov ah, 0x40 /* write to file */
mov cx, realdirnamelen
mov dx, realdirname
int 0x21
 
mov ah, 0x3e /* close file in BX */
int 0x21
 
DONE:
 
pop dx
pop cx
pop bx
pop ax
}
 
if (doserr != 0) {
nls_outputnl_doserr(doserr);
return(CMD_FAIL);
}
 
return(CMD_OK);
}
 
 
static enum cmd_result cmd_lndel(char *buff, const char *linkname, unsigned short env_seg) {
unsigned short i;
 
/* is the argument valid? (must not contain any dot nor backslash) */
for (i = 0; linkname[i] != 0; i++) {
if ((linkname[i] == '.') || (linkname[i] == '/') || (linkname[i] == '\\')) {
outputnl("Bad link name");
return(CMD_OK);
}
}
 
/* prep link filename to look at */
if (link_computefname(buff, linkname, env_seg) != 0) return(CMD_FAIL);
 
/* try removing it */
i = 0;
_asm {
push ax
push bx
push cx
push dx
 
mov ah, 0x41
mov dx, buff
int 0x21
jnc SUCCESS
mov i, ax
SUCCESS:
 
pop dx
pop cx
pop bx
pop ax
}
 
if (i != 0) nls_outputnl_doserr(i);
 
return(CMD_OK);
}
 
 
static enum cmd_result cmd_lnlist(char *buff, const char *linkname, unsigned short env_seg) {
unsigned short i, pathlen;
struct DTA *dta = (void *)0x80;
char *buff128 = buff + 256;
char *buff16 = buff128 + 128;
 
if (linkname != NULL) {
/* make sure link pattern is valid (must not contain '.' or '\\' or '/') */
for (i = 0; linkname[i] != 0; i++) {
switch (linkname[i]) {
case '.':
case '/':
case '\\':
outputnl("Invalid link pattern");
return(CMD_FAIL);
}
}
} else {
linkname = "*";
}
 
/* fetch %DOSDIR% */
pathlen = env_lookup_valcopy(buff, 128, env_seg, "DOSDIR");
if (pathlen == 0) {
outputnl("%DOSDIR% not defined");
return(CMD_FAIL);
}
 
/* prep DOSDIR\LINKS\pattern */
if (buff[pathlen - 1] == '\\') pathlen--;
pathlen += sprintf(buff + pathlen, "\\LINKS\\");
sprintf(buff + pathlen, "%s.LNK", linkname);
 
if (findfirst(dta, buff, DOS_ATTR_RO | DOS_ATTR_ARC) != 0) return(CMD_OK);
 
do {
/* print link file name (but trim ".lnk") */
for (i = 0; (dta->fname[i] != 0) && (dta->fname[i] != '.'); i++) buff16[i] = dta->fname[i];
if (i < 8) buff16[i++] = '\t';
buff16[i] = 0;
output(buff16);
output(" @ ");
/* prep full link filename */
strcpy(buff + pathlen, dta->fname);
/* read up to 128 bytes from link file to buff and display it */
i = 0;
_asm {
push ax
push bx
push cx
push dx
 
/* open file */
mov ax, 0x3d00
mov dx, buff /* filename */
int 0x21
jc FAIL_FOPEN
/* read from file */
mov bx, ax
mov ah, 0x3f
mov cx, 128
mov dx, buff128
int 0x21
jc FAIL_FREAD
mov i, ax
/* close file */
FAIL_FREAD:
mov ah, 0x3e
int 0x21
FAIL_FOPEN:
 
pop dx
pop cx
pop bx
pop ax
}
buff128[i] = 0;
/* make sure no cr or lf is present */
for (i = 0; buff128[i] != 0; i++) {
if ((buff128[i] == '\r') || (buff128[i] == '\n')) {
buff128[i] = 0;
break;
}
}
outputnl(buff128);
} while (findnext(dta) == 0);
 
return(CMD_OK);
}
 
 
static enum cmd_result cmd_ln(struct cmd_funcparam *p) {
if (cmd_ishlp(p)) {
outputnl("Adds, deletes or displays executable links.");
outputnl("");
outputnl("LN ADD linkname targetdir");
outputnl("LN DEL linkname");
outputnl("LN LIST [pattern]");
return(CMD_OK);
}
 
if (p->argc == 0) {
outputnl("Not enough parameters");
return(CMD_OK);
}
 
/* detect what subfunction the user wants */
if ((imatch(p->argv[0], "add")) && (p->argc == 3)) return(cmd_lnadd(p->BUFFER, p->argv[1], p->argv[2], p->env_seg));
if ((imatch(p->argv[0], "del")) && (p->argc == 2)) return(cmd_lndel(p->BUFFER, p->argv[1], p->env_seg));
if (imatch(p->argv[0], "list")) {
if (p->argc == 1) return(cmd_lnlist(p->BUFFER, NULL, p->env_seg));
if (p->argc == 2) return(cmd_lnlist(p->BUFFER, p->argv[1], p->env_seg));
}
 
outputnl("Invalid argument");
return(CMD_FAIL);
}
/svarcom/tags/svarcom-2022.0/cmd/mkdir.c
0,0 → 1,77
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* mkdir
*/
 
static enum cmd_result 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(CMD_OK);
}
 
if (p->argc == 0) {
outputnl("Required parameter missing");
return(CMD_FAIL);
}
 
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
if (p->argv[0][0] == '/') {
outputnl("Invalid parameter");
return(CMD_FAIL);
}
 
_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) {
nls_outputnl_doserr(err);
return(CMD_FAIL);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/path.c
0,0 → 1,90
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* path
*
* Displays or sets a search path for executable files.
*/
 
static enum cmd_result 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(CMD_OK);
}
 
/* 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(CMD_FAIL);
}
 
/* more than 1 parameter */
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
/* 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(CMD_OK);
}
 
/* 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] == 0) break;
}
env_setvar(p->env_seg, buff);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/pause.c
0,0 → 1,38
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* pause
*/
 
 
static enum cmd_result cmd_pause(struct cmd_funcparam *p) {
if (cmd_ishlp(p)) {
nls_outputnl(15, 0);
outputnl("\r\nPAUSE");
} else {
press_any_key();
}
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/prompt.c
0,0 → 1,60
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* prompt
*
* Changes the DOS command prompt.
*
*/
 
static enum cmd_result cmd_prompt(struct cmd_funcparam *p) {
 
if (cmd_ishlp(p)) {
outputnl("Changes the DOS command prompt.");
outputnl("");
outputnl("PROMPT [new command prompt specification]");
return(CMD_OK);
}
 
/* no parameter - restore default prompt path */
if (p->argc == 0) {
env_dropvar(p->env_seg, "PROMPT");
return(CMD_OK);
}
 
/* 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] == 0) break;
}
env_setvar(p->env_seg, buff);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/rem.c
0,0 → 1,38
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* rem
*/
 
static enum cmd_result cmd_rem(struct cmd_funcparam *p) {
/* help screen ONLY if /? is the only argument - I do not want to output
* help for ex. for "REM mouse.com /?" */
if ((p->argc == 1) && (imatch(p->argv[0], "/?"))) {
outputnl("Records comments (remarks) in a batch file or CONFIG.SYS");
outputnl("");
outputnl("REM [comment]");
}
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/rename.c
0,0 → 1,163
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* rename/ren
*/
 
static enum cmd_result cmd_rename(struct cmd_funcparam *p) {
char *src = p->BUFFER;
char *dst = p->BUFFER + 256;
char *buff1 = p->BUFFER + 512;
char *buff2 = p->BUFFER + 1024;
unsigned short i, fnameoffset;
struct DTA *dta = (void *)0x80; /* use default DTA in PSP */
 
if (cmd_ishlp(p)) {
outputnl("Renames a file or files");
outputnl("");
outputnl("RENAME [drive:][path]filename1 filename2");
outputnl("REN [drive:][path]filename1 filename2");
outputnl("");
outputnl("Note that you cannot specify a new drive or path for your destination file.");
outputnl("Use MOVE to rename a directory, or to move files from one directory to another.");
return(CMD_OK);
}
 
/* I expect exactly two arguments */
if (p->argc != 2) {
outputnl("Invalid syntax");
return(CMD_FAIL);
}
 
/* convert src to truename format */
i = file_truename(p->argv[0], src);
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
/* copy src path to buffers and remember where the filename starts */
fnameoffset = 0;
for (i = 0;; i++) {
buff1[i] = src[i];
buff2[i] = src[i];
if (buff1[i] == '\\') fnameoffset = i + 1;
if (buff1[i] == 0) break;
}
 
/* now append dst filename to the buffer and validate it: cannot contain backslash, slash or ':' */
for (i = 0;; i++) {
switch (p->argv[1][i]) {
case ':':
case '\\':
case '/':
outputnl("Invalid destination");
return(CMD_FAIL);
}
buff1[fnameoffset + i] = p->argv[1][i];
if (buff1[fnameoffset + i] == 0) break;
}
 
/* apply truename to dest to normalize wildcards into ? chars */
i = file_truename(buff1, dst);
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
/* we're good to go, src and dst should look somehow like that now:
* src = C:\TEMP\PATH\FILE????.TXT
* dst = C:\TEMP\PATH\FILE????.DOC
* buff1 = C:\TEMP\PATH\
* buff2 = C:\TEMP\PATH\
* fnameoffset = 13
*
* src is used for FindFirst/FindNext iterations, then buff1 is filled with
* the source filename found by FindFirst/FindNext and buff2 is filled with
* the destination file (with ?'s replaced by whatever is found at the same
* location in buff1).
*/
 
i = findfirst(dta, src, 0);
if (i != 0) nls_outputnl_doserr(i);
 
while (i == 0) {
/* write found fname into buff1 and dst fname into buff2 - both in FCB
* format (MYFILE EXT) so it is easy to compare them */
file_fname2fcb(buff1 + fnameoffset, dta->fname);
file_fname2fcb(buff2 + fnameoffset, dst + fnameoffset);
 
/* scan buff2 fname for '?' and replace them with whatever is in buff1 */
for (i = fnameoffset; buff2[i] != 0; i++) {
if (buff2[i] == '?') buff2[i] = buff1[i];
}
 
/* fill buff1 with the 8+3 found file and convert the one in buff2 to 8+3 as well */
file_fcb2fname(buff1 + fnameoffset, buff2 + fnameoffset);
strcpy(buff2 + fnameoffset, buff1 + fnameoffset);
strcpy(buff1 + fnameoffset, dta->fname);
 
/* buff1 contains now a fully resolved source and buff2 a proper destination */
#if 0 /* DEBUG ("if 1" to enable) */
output(buff1);
output(" -> ");
outputnl(buff2);
#endif
/* call DOS to do the actual job */
i = 0;
_asm {
push ax
push di
push dx
push es
 
mov ah, 0x56 /* rename file: DS:DX=ASCIIZ of src ES:DI=ASCIIZ of dst */
push ds
pop es
mov dx, buff1
mov di, buff2
int 0x21 /* CF clear on success, otherwise err code in AX */
jnc DONE
mov [i], ax /* copy error code to i */
DONE:
 
pop es
pop dx
pop di
pop ax
}
if (i != 0) {
output(buff1 + fnameoffset);
output(" -> ");
output(buff2 + fnameoffset);
output(" ");
nls_outputnl_doserr(i);
}
/* next please */
i = findnext(dta);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/rmdir.c
0,0 → 1,77
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* rmdir
*/
 
static enum cmd_result 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(CMD_OK);
}
 
if (p->argc == 0) {
outputnl("Required parameter missing");
return(CMD_FAIL);
}
 
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
if (p->argv[0][0] == '/') {
outputnl("Invalid parameter");
return(CMD_FAIL);
}
 
_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) {
nls_outputnl_doserr(err);
return(CMD_FAIL);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/set.c
0,0 → 1,100
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* set [varname[=value]]
*
* value cannot contain any '=' character, but it can contain spaces
* varname can also contain spaces
*/
 
 
static enum cmd_result 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.");
return(CMD_OK);
}
 
/* 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 == 0) goto syntax_err;
buff[i] = *ptr;
if ((buff[i] >= 'a') && (buff[i] <= 'z')) buff[i] -= ('a' - 'A');
i++;
}
 
/* copy value now */
while (*ptr != 0) {
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(CMD_FAIL);
}
}
return(CMD_OK);
 
syntax_err:
 
outputnl("Syntax error");
return(CMD_FAIL);
}
/svarcom/tags/svarcom-2022.0/cmd/shift.c
0,0 → 1,52
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* shift
*/
 
static enum cmd_result cmd_shift(struct cmd_funcparam *p) {
char far *batargv = p->rmod->batargv;
char far *nextarg;
 
if (cmd_ishlp(p)) {
nls_outputnl(16, 0);
nls_outputnl(16, 1);
outputnl("");
outputnl("SHIFT");
return(CMD_OK);
}
 
/* abort if batargv is empty */
if (*batargv == 0) return(CMD_OK);
 
/* find the next argument in batargv */
for (nextarg = batargv + 1; *nextarg != 0; nextarg++);
nextarg++; /* move ptr past the zero terminator */
 
/* move down batargv so 2nd argument is at the head now */
_fmemmove(batargv, nextarg, sizeof(p->rmod->batargv) - (nextarg - batargv));
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/time.c
0,0 → 1,254
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* time [time]
*/
 
 
/* read a one or two digit number and write it to buff */
static int cmd_time_get_item(char *buff, const char *s) {
unsigned short i;
 
for (i = 0; i < 3; i++) {
if ((s[i] < '0') || (s[i] > '9')) {
buff[i] = 0;
return(0);
}
buff[i] = s[i];
}
 
/* err */
*buff = 0;
return(-1);
}
 
 
/* parse a NULL-terminated string int hour, minutes and seconds, returns 0 on success
* valid inputs: 0, 7, 5:5, 23:23, 17:54:45, 9p, 9:05, ...
*/
static int cmd_time_parse(const char *s, signed char *ho, signed char *mi, signed char *se, struct nls_patterns *nls) {
unsigned short i;
const char *ptrs[2] = {NULL, NULL}; /* minutes, seconds */
char buff[3];
char ampm = 0;
 
*ho = -1;
*mi = 0;
*se = 0;
 
/* validate input - must contain only chars 0-9, time separator and 'a' or 'p' */
for (i = 0; s[i] != 0; i++) {
switch (s[i]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'a':
case 'A':
case 'p':
case 'P':
/* these can be only at last position and never at the first */
if ((s[i + 1] != 0) || (i == 0)) return(-1);
ampm = s[i];
if (ampm >= 'a') ampm -= ('a' - 'A');
break;
default:
if ((s[i] != nls->timesep[0]) || (i == 0)) return(-1);
if (ptrs[0] == NULL) {
ptrs[0] = s + i + 1;
} else if (ptrs[1] == NULL) {
ptrs[1] = s + i + 1;
} else { /* too many separators */
return(-1);
}
break;
}
}
 
/* read hour */
if (cmd_time_get_item(buff, s) != 0) goto FAIL;
if (atous(&i, buff) != 0) goto FAIL;
*ho = i;
 
/* if minutes provided, read them */
if (ptrs[0] != NULL) {
if (cmd_time_get_item(buff, ptrs[0]) != 0) goto FAIL;
if (atous(&i, buff) != 0) goto FAIL;
*mi = i;
}
 
/* if seconds provided, read them */
if (ptrs[1] != NULL) {
if (cmd_time_get_item(buff, ptrs[1]) != 0) goto FAIL;
if (atous(&i, buff) != 0) goto FAIL;
*se = i;
}
 
/* validate ranges */
if ((*ho > 23) || (*mi > 59) || (*se > 59)) goto FAIL;
 
/* am? */
if ((ampm == 'A') && (*ho > 12)) goto FAIL;
if ((ampm == 'A') && (*ho == 12)) *ho = 0; /* 12:00am is 00:00 (midnight) */
 
/* pm? */
if (ampm == 'P') {
if (*ho > 12) goto FAIL;
if (*ho < 12) *ho += 12;
}
 
return(0);
 
FAIL:
*ho = -1;
return(-1);
}
 
 
static enum cmd_result cmd_time(struct cmd_funcparam *p) {
struct nls_patterns *nls = (void *)(p->BUFFER);
char *buff = p->BUFFER + sizeof(*nls);
unsigned short i;
signed char ho = -1, mi = -1, se = -1;
 
if (cmd_ishlp(p)) {
outputnl("Displays or sets the system time.");
outputnl("");
outputnl("TIME [time]");
outputnl("");
outputnl("Type TIME with no parameters to display the current time and a prompt for a");
outputnl("new one. Press ENTER to keep the same time.");
return(CMD_OK);
}
 
i = nls_getpatterns(nls);
if (i != 0) {
nls_outputnl_doserr(i);
return(CMD_FAIL);
}
 
/* display current time if no args */
if (p->argc == 0) {
/* get cur time */
_asm {
push ax
push bx
push cx
push dx
 
mov ah, 0x2c /* DOS 1+ -- Query DOS Time */
int 0x21 /* CH=hour CL=minutes DH=seconds DL=1/100sec */
mov [ho], ch
mov [mi], cl
mov [se], dh
 
pop dx
pop cx
pop bx
pop ax
}
buff[0] = ' ';
nls_format_time(buff + 1, ho, mi, se, nls);
output("Current time is");
outputnl(buff);
ho = -1;
} else { /* parse time if provided */
if (cmd_time_parse(p->argv[0], &ho, &mi, &se, nls) != 0) {
outputnl("Invalid time");
ho = -1;
}
}
 
/* ask for time if not provided or if input was malformed */
while (ho < 0) {
output("Enter new time:");
output(" ");
/* collect user input into buff */
_asm {
push ax
push bx
push dx
 
mov ah, 0x0a /* DOS 1+ -- Buffered String Input */
mov bx, buff
mov dx, bx
mov al, 16
mov [bx], al /* max input length */
mov al, 1
mov [bx+1], al /* zero out the "previous entry" length */
int 0x21
/* terminate the string with a NULL terminator */
xor ax, ax
inc bx
mov al, [bx] /* read length of input string */
mov bx, ax
add bx, dx
mov [bx+2], ah
/* output a \n */
mov ah, 2
mov dl, 0x0A
int 0x21
 
pop dx
pop bx
pop ax
}
if (buff[1] == 0) break; /* empty string = do not change time */
if (cmd_time_parse(buff + 2, &ho, &mi, &se, nls) == 0) break;
outputnl("Invalid time");
return(CMD_FAIL);
}
 
if (ho >= 0) {
/* set time */
_asm {
push ax
push bx
push cx
push dx
 
mov ah, 0x2d /* DOS 1+ -- Set DOS Time */
mov ch, [ho] /* hour (0-23) */
mov cl, [mi] /* minutes (0-59) */
mov dh, [se] /* seconds (0-59) */
mov dl, 0 /* 1/100th seconds (0-99) */
int 0x21
 
pop dx
pop cx
pop bx
pop ax
}
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/type.c
0,0 → 1,113
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* type
*/
 
static enum cmd_result 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(CMD_OK);
}
 
if (p->argc == 0) {
outputnl("Required parameter missing");
return(CMD_FAIL);
}
 
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
/* 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) {
nls_outputnl_doserr(err);
return(CMD_FAIL);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/ver.c
0,0 → 1,126
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
/*
* ver
*/
 
#define PVER "2022.0"
#define COPYRDATE "2021-2022"
 
static enum cmd_result 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.");
outputnl("");
outputnl("ver [/about]");
#ifdef VERDBG
outputnl("ver /dbg");
#endif
return(CMD_OK);
}
 
#ifdef VERDBG
if ((p->argc == 1) && (imatch(p->argv[0], "/dbg"))) {
unsigned short far *rmod_envseg = MK_FP(p->rmod->rmodseg, RMOD_OFFSET_ENVSEG);
unsigned char far *rmod_exitcode = MK_FP(p->rmod->rmodseg, RMOD_OFFSET_LEXITCODE);
unsigned short far *rmod_comspecptr = MK_FP(p->rmod->rmodseg, RMOD_OFFSET_COMSPECPTR);
char far *fptr;
unsigned short i;
printf("rmod->rmodseg = 0x%04X\r\n", p->rmod->rmodseg);
printf("rmod->origparent = %04X:%04X\r\n", p->rmod->origparent >> 16, p->rmod->origparent & 0xffff);
printf("rmod->origenvseg = 0x%04X\r\n", p->rmod->origenvseg);
printf("rmod->flags = 0x%02X\r\n", p->rmod->flags);
printf("[rmod:RMOD_OFFSET_ENVSEG] = 0x%04X\r\n", *rmod_envseg);
printf("environment allocated size: %u bytes\r\n", env_allocsz(*rmod_envseg));
for (fptr = MK_FP(p->rmod->rmodseg, RMOD_OFFSET_BOOTDRIVE), i = 0; *fptr != 0; fptr++) buff[i++] = *fptr;
buff[i] = 0;
printf("[rmod:RMOD_OFFSET_BOOTCOMSPEC] = '%s'\r\n", buff);
if (*rmod_comspecptr == 0) {
sprintf(buff, "NULL");
} else {
for (fptr = MK_FP(*rmod_envseg, *rmod_comspecptr), i = 0; *fptr != 0; fptr++) buff[i++] = *fptr;
buff[i] = 0;
}
printf("[rmod:RMOD_OFFSET_COMSPECPTR] = '%s'\r\n", buff);
printf("[rmod:RMOD_OFFSET_LEXITCODE] = %u\r\n", *rmod_exitcode);
printf("rmod dump (first 64 bytes at [rmodseg:0100h]):\r\n");
fptr = MK_FP(p->rmod->rmodseg, 0x100);
for (i = 0; i < 64; i += 16) {
int ii;
for (ii = i; ii < i + 16; ii++) printf(" %02X", fptr[ii]);
printf(" ");
for (ii = i; ii < i + 16; ii++) {
if (fptr[ii] < ' ') {
printf(".");
} else {
printf("%c", fptr[ii]);
}
}
printf("\r\n");
}
 
return(CMD_OK);
}
#endif
 
if ((p->argc == 1) && (imatch(p->argv[0], "/about"))) {
outputnl("SvarCOM is a shell interpreter for DOS kernels compatible with MS-DOS 5+.");
outputnl("");
outputnl("This software is distributed under the terms of the MIT license.");
outputnl("Copyright (C) " COPYRDATE " Mateusz Viste");
outputnl("");
outputnl("Program ten dedykuje Milenie i Mojmirowi. Zycze wam, byscie w swoim zyciu");
outputnl("potrafili docenic wartosci minionych pokolen, jednoczesnie czerpiac radosc");
outputnl("z prostych przyjemnosci dnia codziennego. Lair, jesien 2021.");
return(CMD_OK);
}
 
if (p->argc != 0) {
outputnl("Invalid parameter");
return(CMD_FAIL);
}
 
_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(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/verify.c
0,0 → 1,87
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* verify
*/
 
static enum cmd_result 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(CMD_OK);
}
 
if (p->argc > 1) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
 
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(CMD_OK);
}
 
/* 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(CMD_FAIL);
}
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd/vol.c
0,0 → 1,150
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* vol [drive:]
*/
 
static void cmd_vol_internal(unsigned char drv, char *buff) {
unsigned short *buff16 = (void *)(buff);
unsigned short err = 0;
struct DTA *dta = (void *)0x80; /* use the default DTA at location 80h in PSP */
 
outputnl(""); /* start with an empty line to mimic MS-DOS */
 
/* look for volume label in root dir via a FindFirst call */
sprintf(buff, "%c:\\????????.???", drv + 'A');
_asm {
push ax
push cx
push dx
mov [err], 0 /* preset errflag to zero */
mov ah, 0x4e /* FindFirst */
mov dx, buff
mov cx, 0x08 /* match volume labels only */
int 0x21 /* dta filled or CF set on error */
jnc DONE
mov [err], ax
DONE:
pop dx
pop cx
pop ax
}
 
if (err != 0) {
sprintf(buff, "Volume in drive %c has no label", drv + 'A');
} else {
/* if label > 8 chars then drop the dot (DRIVE_LA.BEL -> DRIVE_LABEL) */
if (strlen(dta->fname) > 8) memmove(dta->fname + 8, dta->fname + 9, 4);
sprintf(buff, "Volume in drive %c is %s", drv + 'A', dta->fname);
}
outputnl(buff);
 
/* try to fetch the disk's serial number (DOS 4+ internal call) */
err = 0;
_asm {
push ax
push bx
push dx
mov ax, 0x6900
mov bl, drv /* A=1, B=2, etc */
inc bl /* adjust BL to +1 since drv is 0-based (A=0, B=1, etc) */
xor bh, bh /* "info level", must be 0 */
mov dx, buff /* pointer to a location where a DiskInfo struct will be written */
int 0x21
jnc DONE
mov [err], ax /* err code */
DONE:
pop dx
pop bx
pop ax
}
/* Format of DiskInfo struct (source: RBIL)
Offset Size Description (Table 01766)
00h WORD 0000h (info level)
02h DWORD disk serial number (binary)
06h 11 BYTEs volume label or "NO NAME " if none present
11h 8 BYTEs filesystem type */
if ((err == 0) && (buff16[1] | buff16[2])) {
sprintf(buff + 64, "Volume Serial Number is %04X-%04X", buff16[2], buff16[1]);
outputnl(buff + 64);
}
}
 
 
static enum cmd_result cmd_vol(struct cmd_funcparam *p) {
char drv = 0;
char curdrv = 0;
unsigned short i;
 
if (cmd_ishlp(p)) {
outputnl("Displays the disk volume label and serial number, if they exist.");
outputnl("");
outputnl("VOL [drive:]");
return(CMD_OK);
}
 
for (i = 0; i < p->argc; i++) {
if (p->argv[i][0] == '/') {
outputnl("Invalid switch");
return(CMD_FAIL);
}
if (drv != 0) {
outputnl("Too many parameters");
return(CMD_FAIL);
}
if ((p->argv[i][0] == 0) || (p->argv[i][1] != ':') || (p->argv[i][2] != 0)) {
outputnl("Invalid parameter format");
return(CMD_FAIL);
}
drv = p->argv[i][0];
/* convert drive letter to a value 1..x (1=A, 2=B, etc) */
if ((drv >= 'a') && (drv <= 'z')) {
drv -= 'a';
} else {
drv -= 'A';
}
}
 
/* fetch current drive */
_asm {
push ax
mov ah, 0x19 /* query default (current) disk */
int 0x21 /* drive in AL (0=A, 1=B, etc) */
mov [curdrv], al
pop ax
}
 
/* if no drive specified, use the default one */
if (drv == 0) {
drv = curdrv;
} else if (!isdrivevalid(drv)) { /* is specified drive valid? */
outputnl("Invalid drive");
return(CMD_FAIL);
}
 
cmd_vol_internal(drv, p->BUFFER);
 
return(CMD_OK);
}
/svarcom/tags/svarcom-2022.0/cmd.c
0,0 → 1,285
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
/* 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 "env.h"
#include "helpers.h"
#include "redir.h"
#include "rmodinit.h"
#include "sayonara.h"
 
#include "cmd.h"
 
 
struct cmd_funcparam {
int argc; /* number of arguments */
const char *argv[128]; /* pointers to each argument */
char argvbuf[256]; /* buffer that hold data pointed out by argv[] */
unsigned short env_seg; /* segment of environment block */
struct rmod_props far *rmod; /* rmod settings */
unsigned short argoffset; /* offset of cmdline where first argument starts */
const char *cmdline; /* original cmdline (terminated by a NULL) */
unsigned short BUFFERSZ; /* avail space in BUFFER */
char BUFFER[1]; /* a buffer for whatever is needed (must be last) */
};
 
/* 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/break.c"
#include "cmd/cd.c"
#include "cmd/chcp.c"
#include "cmd/cls.c"
#include "cmd/copy.c"
#include "cmd/date.c"
#include "cmd/del.c"
#include "cmd/if.c"
#include "cmd/vol.c" /* must be included before dir.c due to dependency */
#include "cmd/dir.c"
#include "cmd/echo.c"
#include "cmd/exit.c"
#include "cmd/ln.c"
#include "cmd/mkdir.c"
#include "cmd/path.c"
#include "cmd/pause.c"
#include "cmd/prompt.c"
#include "cmd/rem.c"
#include "cmd/rename.c"
#include "cmd/rmdir.c"
#include "cmd/set.c"
#include "cmd/shift.c"
#include "cmd/time.c"
#include "cmd/type.c"
#include "cmd/ver.c"
#include "cmd/verify.c"
 
 
struct CMD_ID {
const char *cmd;
enum cmd_result (*func_ptr)(struct cmd_funcparam *); /* pointer to handling function */
};
 
const struct CMD_ID INTERNAL_CMDS[] = {
{"BREAK", cmd_break},
{"CD", cmd_cd},
{"CHCP", cmd_chcp},
{"CHDIR", cmd_cd},
{"CLS", cmd_cls},
{"COPY", cmd_copy},
{"CTTY", cmd_notimpl},
{"DATE", cmd_date},
{"DEL", cmd_del},
{"DIR", cmd_dir},
{"ECHO", cmd_echo},
{"ERASE", cmd_del},
{"EXIT", cmd_exit},
{"IF", cmd_if},
{"LH", cmd_notimpl},
{"LN", cmd_ln},
{"LOADHIGH",cmd_notimpl},
{"MD", cmd_mkdir},
{"MKDIR", cmd_mkdir},
{"PAUSE", cmd_pause},
{"PATH", cmd_path},
{"PROMPT", cmd_prompt},
{"RD", cmd_rmdir},
{"REM", cmd_rem},
{"REN", cmd_rename},
{"RENAME", cmd_rename},
{"RMDIR", cmd_rmdir},
{"SET", cmd_set},
{"SHIFT", cmd_shift},
{"TIME", cmd_time},
{"TYPE", cmd_type},
{"VER", cmd_ver},
{"VERIFY", cmd_verify},
{"VOL", cmd_vol},
{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 *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.
* if argvlist is not NULL, it will be filled with pointers that point to buff
* locations. buff is filled with all the arguments, each argument being
* zero-separated. buff is terminated with an empty argument to mark the end
* of arguments.
* 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 */
if (argvlist) argvlist[argc] = buff + i;
argc++;
/* find next arg delimiter (spc, null, slash or plus) while copying arg to local buffer */
do {
buff[i++] = s[si++];
} while (s[si] != ' ' && s[si] != 0 && s[si] != '/' && s[si] != '+');
buff[i++] = 0;
/* is this end of string? */
if (s[si] == 0) break;
}
buff[i] = 0; /* terminate with one extra zero to tell "this is the end of list" */
if (argvlist) argvlist[argc] = NULL;
return(argc);
}
 
 
enum cmd_result cmd_process(struct rmod_props far *rmod, unsigned short env_seg, const char *cmdline, void *BUFFER, unsigned short BUFFERSZ, const struct redir_data *redir, unsigned char delstdin) {
const struct CMD_ID *cmdptr;
unsigned short argoffset;
enum cmd_result cmdres;
struct cmd_funcparam *p = (void *)BUFFER;
p->BUFFERSZ = BUFFERSZ - sizeof(*p);
 
/* 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) return(CMD_OK);
nls_outputnl_doserr(0x0f);
return(CMD_FAIL);
}
}
 
/* try matching an internal command */
cmdptr = cmd_match(cmdline, &argoffset);
if (cmdptr == NULL) return(CMD_NOTFOUND); /* command is not recognized as internal */
 
/* printf("recognized internal command: '%s', tail of command at offset %u\r\n", cmdptr->cmd, argoffset); */
 
/* apply redirections (if any) */
if (redir_apply(redir) != 0) return(CMD_FAIL);
 
/* prepare function parameters and feed it to the cmd handling function */
p->argc = cmd_explode(p->argvbuf, cmdline + argoffset, p->argv);
p->env_seg = env_seg;
p->rmod = rmod;
p->argoffset = argoffset;
p->cmdline = cmdline;
 
cmdres = (cmdptr->func_ptr)(p);
 
/* cancel redirections */
redir_revert();
 
/* delete stdin temporary file */
if (delstdin) {
const char *fname = redir->stdinfile;
unsigned short doserr = 0;
_asm {
push ax
push dx
mov ah, 0x41 /* delete a file */
mov dx, fname /* DS:DX - filename to delete */
int 0x21
jnc DONE
mov doserr, ax
DONE:
pop dx
pop ax
}
if (doserr) {
output(fname);
output(": ");
nls_outputnl_doserr(doserr);
}
}
 
return(cmdres);
}
/svarcom/tags/svarcom-2022.0/cmd.h
0,0 → 1,49
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
#ifndef CMD_H
#define CMD_H
 
#include "rmodinit.h"
 
/* what cmd_process may return */
enum cmd_result {
CMD_OK, /* command executed and succeeded */
CMD_FAIL, /* command executed and failed */
CMD_NOTFOUND, /* no such command (not an internal command) */
CMD_CHANGED /* command-line transformed, please reparse it */
};
 
/* process internal commands */
enum cmd_result cmd_process(struct rmod_props far *rmod, unsigned short env_seg, const char *cmdline, void *BUFFER, unsigned short BUFFERSZ, const struct redir_data *r, unsigned char delstdin);
 
/* explodes a command into an array of arguments where last arg is NULL.
* if argvlist is not NULL, it will be filled with pointers that point to buff
* locations. buff is filled with all the arguments, each argument being
* zero-separated. buff is terminated with an empty argument to mark the end
* of arguments.
* returns number of args */
unsigned short cmd_explode(char *buff, const char far *s, char const **argvlist);
 
#endif
/svarcom/tags/svarcom-2022.0/command.c
0,0 → 1,906
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
#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 "redir.h"
#include "rmodinit.h"
#include "sayonara.h"
 
#include "rmodcore.h" /* rmod binary inside a BUFFER array */
 
/* this version byte is used to tag RMOD so I can easily make sure that
* the RMOD struct I find in memory is one that I know. Should the version
* mismatch, then it would likely mean that SvarCOM has been upgraded and
* RMOD should not be accessed as its structure might no longer be in sync
* with what I think it is.
* *** INCREMENT THIS AT EACH NEW SVARCOM RELEASE! *** */
#define BYTE_VERSION 3
 
 
struct config {
unsigned char flags; /* command.com flags, as defined in rmodinit.h */
char *execcmd;
unsigned short envsiz;
};
 
/* max length of the cmdline storage (bytes) - includes also max length of
* line loaded from a BAT file (no more than 255 bytes!) */
#define CMDLINE_MAXLEN 255
 
 
/* sets guard values at a few places in memory for later detection of
* overflows via memguard_check() */
static void memguard_set(char *cmdlinebuf) {
BUFFER[sizeof(BUFFER) - 1] = 0xC7;
cmdlinebuf[CMDLINE_MAXLEN] = 0xC7;
}
 
 
/* checks for valguards at specific memory locations, returns 0 on success */
static int memguard_check(unsigned short rmodseg, char *cmdlinebuf) {
/* check RMOD signature (would be overwritten in case of stack overflow */
static char msg[] = "!! MEMORY CORRUPTION ## DETECTED !!";
unsigned short far *rmodsig = MK_FP(rmodseg, 0x100 + 6);
unsigned char far *rmod = MK_FP(rmodseg, 0);
 
if (*rmodsig != 0x2019) {
msg[22] = '1';
goto FAIL;
}
 
/* check last BUFFER byte */
if (BUFFER[sizeof(BUFFER) - 1] != 0xC7) {
msg[22] = '2';
goto FAIL;
}
 
/* check last cmdlinebuf byte */
if (cmdlinebuf[CMDLINE_MAXLEN] != 0xC7) {
msg[22] = '3';
goto FAIL;
}
 
/* check rmod exec buf */
if (rmod[RMOD_OFFSET_EXECPROG + 127] != 0) {
msg[22] = '4';
goto FAIL;
}
 
/* check rmod exec stdin buf */
if (rmod[RMOD_OFFSET_STDINFILE + 127] != 0) {
msg[22] = '5';
goto FAIL;
}
 
/* check rmod exec stdout buf */
if (rmod[RMOD_OFFSET_STDOUTFILE + 127] != 0) {
msg[22] = '6';
goto FAIL;
}
 
/* else all good */
return(0);
 
/* error handling */
FAIL:
outputnl(msg);
return(1);
}
 
 
/* parses command line the hard way (directly from PSP) */
static void parse_argv(struct config *cfg) {
const unsigned char *cmdlinelen = (void *)0x80;
char *cmdline = (void *)0x81;
 
memset(cfg, 0, sizeof(*cfg));
 
/* set a NULL terminator on cmdline */
cmdline[*cmdlinelen] = 0;
 
while (*cmdline != 0) {
 
/* skip over any leading spaces */
if (*cmdline == ' ') {
cmdline++;
continue;
}
 
if (*cmdline != '/') {
output("Invalid parameter: ");
outputnl(cmdline);
goto SKIP_TO_NEXT_ARG;
}
 
/* got a slash */
cmdline++; /* skip the slash */
switch (*cmdline) {
case 'c': /* /C = execute command and quit */
case 'C':
cfg->flags |= FLAG_EXEC_AND_QUIT;
/* FALLTHRU */
case 'k': /* /K = execute command and keep running */
case 'K':
cfg->execcmd = cmdline + 1;
return;
 
case 'd': /* /D = skip autoexec.bat processing */
case 'D':
cfg->flags |= FLAG_SKIP_AUTOEXEC;
break;
 
case 'e': /* preset the initial size of the environment block */
case 'E':
cmdline++;
if (*cmdline == ':') cmdline++; /* could be /E:size */
atous(&(cfg->envsiz), cmdline);
if (cfg->envsiz < 64) cfg->envsiz = 0;
break;
 
case 'p': /* permanent shell (can't exit + run autoexec.bat) */
case 'P':
cfg->flags |= FLAG_PERMANENT;
break;
 
case '?':
outputnl("Starts the SvarCOM command interpreter");
outputnl("");
outputnl("COMMAND /E:nnn [/[C|K] [/P] [/D] command]");
outputnl("");
outputnl("/D Skip AUTOEXEC.BAT processing (makes sense only with /P)");
outputnl("/E:nnn Sets the environment size to nnn bytes");
outputnl("/P Makes the new command interpreter permanent and run AUTOEXEC.BAT");
outputnl("/C Executes the specified command and returns");
outputnl("/K Executes the specified command and continues running");
exit(1);
break;
 
default:
output("Invalid switch: /");
outputnl(cmdline);
break;
}
 
/* move to next argument or quit processing if end of cmdline */
SKIP_TO_NEXT_ARG:
while ((*cmdline != 0) && (*cmdline != ' ') && (*cmdline != '/')) cmdline++;
}
}
 
 
/* builds the prompt string and displays it. buff is filled with a zero-terminated copy of the prompt. */
static void build_and_display_prompt(char *buff, unsigned short envseg) {
char *s = buff;
/* locate the prompt variable or use the default pattern */
const char far *fmt = env_lookup_val(envseg, "PROMPT");
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
jc DONE /* leave path empty on error */
/* move s ptr forward to end (0-termintor) of pathname */
NEXTBYTE:
mov si, s
cmp byte ptr [si], 0
je DONE
inc s
jmp NEXTBYTE
DONE:
}
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 = 0;
output(buff);
}
 
 
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod, struct redir_data *redir, unsigned char delete_stdin_file) {
char *cmdfile = buff + 512;
const char far *pathptr;
int lookup;
unsigned short i;
const char *ext;
char *cmd = buff + 1024;
const char *cmdtail;
char far *rmod_execprog = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPROG);
char far *rmod_cmdtail = MK_FP(rmod->rmodseg, 0x81);
_Packed struct {
unsigned short envseg;
unsigned long cmdtail;
unsigned long fcb1;
unsigned long fcb2;
} far *ExecParam = MK_FP(rmod->rmodseg, RMOD_OFFSET_EXECPARAM);
 
/* find cmd and cmdtail */
i = 0;
cmdtail = cmdline;
while (*cmdtail == ' ') cmdtail++; /* skip any leading spaces */
while ((*cmdtail != ' ') && (*cmdtail != '/') && (*cmdtail != '+') && (*cmdtail != 0)) {
cmd[i++] = *cmdtail;
cmdtail++;
}
cmd[i] = 0;
 
/* is this a command in curdir? */
lookup = lookup_cmd(cmdfile, cmd, NULL, &ext);
if (lookup == 0) {
/* printf("FOUND LOCAL EXEC FILE: '%s'\r\n", cmdfile); */
goto RUNCMDFILE;
} else if (lookup == -2) {
/* puts("NOT FOUND"); */
return;
}
 
/* try matching something in PATH */
pathptr = env_lookup_val(envseg, "PATH");
 
/* try each path in %PATH% */
while (pathptr) {
for (i = 0;; i++) {
buff[i] = *pathptr;
if ((buff[i] == 0) || (buff[i] == ';')) break;
pathptr++;
}
buff[i] = 0;
lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
if (lookup == 0) goto RUNCMDFILE;
if (lookup == -2) return;
if (*pathptr == ';') {
pathptr++;
} else {
break;
}
}
 
/* last chance: is it an executable link? (trim extension from cmd first) */
for (i = 0; (cmd[i] != 0) && (cmd[i] != '.') && (i < 9); i++) buff[128 + i] = cmd[i];
buff[128 + i] = 0;
if ((i < 9) && (link_computefname(buff, buff + 128, envseg) == 0)) {
/* try opening the link file (if it exists) and read it into buff */
i = 0;
_asm {
push ax
push bx
push cx
push dx
 
mov ax, 0x3d00 /* DOS 2+ - OPEN EXISTING FILE, READ-ONLY */
mov dx, buff /* file name */
int 0x21
jc ERR_FOPEN
/* file handle in AX, read from file now */
mov bx, ax /* file handle */
mov ah, 0x3f /* Read from file via handle bx */
mov cx, 128 /* up to 128 bytes */
/* mov dx, buff */ /* dest buffer (already set) */
int 0x21 /* read up to 256 bytes from file and write to buff */
jc ERR_READ
mov i, ax
ERR_READ:
mov ah, 0x3e /* close file handle in BX */
int 0x21
ERR_FOPEN:
 
pop dx
pop cx
pop bx
pop ax
}
 
/* did I read anything? */
if (i != 0) {
buff[i] = 0;
/* trim buff at first \n or \r, just in case someone fiddled with the
* link file using a text editor */
for (i = 0; (buff[i] != 0) && (buff[i] != '\r') && (buff[i] != '\n'); i++);
buff[i] = 0;
/* lookup check */
if (buff[0] != 0) {
lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
if (lookup == 0) goto RUNCMDFILE;
}
}
}
 
/* all failed (ie. executable file not found) */
return;
 
RUNCMDFILE:
 
/* special handling of batch files */
if ((ext != NULL) && (imatch(ext, "bat"))) {
/* copy truename of the bat file to rmod buff */
_fstrcpy(rmod->batfile, cmdfile);
 
/* explode args of the bat file and store them in rmod buff */
cmd_explode(buff, cmdline, NULL);
_fmemcpy(rmod->batargv, buff, sizeof(rmod->batargv));
 
/* reset the 'next line to execute' counter */
rmod->batnextline = 0;
/* remember the echo flag (in case bat file disables echo) */
rmod->flags &= ~FLAG_ECHO_BEFORE_BAT;
if (rmod->flags & FLAG_ECHOFLAG) rmod->flags |= FLAG_ECHO_BEFORE_BAT;
return;
}
 
/* copy full filename to execute, along with redirected files (if any) */
_fstrcpy(rmod_execprog, cmdfile);
 
/* copy stdin file if a redirection is needed */
if (redir->stdinfile) {
char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDINFILE);
char far *delstdin = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDIN_DEL);
_fstrcpy(farptr, redir->stdinfile);
if (delete_stdin_file) {
*delstdin = redir->stdinfile[0];
} else {
*delstdin = 0;
}
}
 
/* same for stdout file */
if (redir->stdoutfile) {
char far *farptr = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTFILE);
unsigned short far *farptr16 = MK_FP(rmod->rmodseg, RMOD_OFFSET_STDOUTAPP);
_fstrcpy(farptr, redir->stdoutfile);
/* openflag */
*farptr16 = redir->stdout_openflag;
}
 
/* copy cmdtail to rmod's PSP and compute its len */
for (i = 0; cmdtail[i] != 0; i++) rmod_cmdtail[i] = cmdtail[i];
rmod_cmdtail[i] = '\r';
rmod_cmdtail[-1] = i;
 
/* set up rmod to execute the command */
 
ExecParam->envseg = envseg;
ExecParam->cmdtail = (unsigned long)MK_FP(rmod->rmodseg, 0x80); /* farptr, must be in PSP format (lenbyte args \r) */
ExecParam->fcb1 = 0; /* TODO farptr */
ExecParam->fcb2 = 0; /* TODO farptr */
exit(0); /* let rmod do the job now */
}
 
 
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);
}
 
 
/* wait for user input */
static void cmdline_getinput(unsigned short inpseg, unsigned short inpoff) {
_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 to inpseg:inpoff */
mov ax, inpseg
push ax
pop ds
mov dx, inpoff
 
/* 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:
/* terminate command with a CR/LF */
mov ah, 0x02 /* display character in dl */
mov dl, 0x0d
int 0x21
mov dl, 0x0a
int 0x21
 
pop ds
pop dx
pop cx
pop bx
pop ax
}
}
 
 
/* fetches a line from batch file and write it to buff (NULL-terminated),
* increments rmod counter and returns 0 on success. */
static int getbatcmd(char *buff, unsigned char buffmaxlen, struct rmod_props far *rmod) {
unsigned short i;
unsigned short batname_seg = FP_SEG(rmod->batfile);
unsigned short batname_off = FP_OFF(rmod->batfile);
unsigned short filepos_cx = rmod->batnextline >> 16;
unsigned short filepos_dx = rmod->batnextline & 0xffff;
unsigned char blen = 0;
unsigned short errv = 0;
 
/* open file, jump to offset filpos, and read data into buff.
* result in blen (unchanged if EOF or failure). */
_asm {
push ax
push bx
push cx
push dx
 
/* open file (read-only) */
mov bx, 0xffff /* preset BX to 0xffff to detect error conditions */
mov dx, batname_off
mov ax, batname_seg
push ds /* save DS */
mov ds, ax
mov ax, 0x3d00
int 0x21 /* handle in ax on success */
pop ds /* restore DS */
jc ERR
mov bx, ax /* save handle to bx */
 
/* jump to file offset CX:DX */
mov ax, 0x4200
mov cx, filepos_cx
mov dx, filepos_dx
int 0x21 /* CF clear on success, DX:AX set to cur pos */
jc ERR
 
/* read the line into buff */
mov ah, 0x3f
xor ch, ch
mov cl, buffmaxlen
mov dx, buff
int 0x21 /* CF clear on success, AX=number of bytes read */
jc ERR
mov blen, al
jmp CLOSEANDQUIT
 
ERR:
mov errv, ax
 
CLOSEANDQUIT:
/* close file (if bx contains a handle) */
cmp bx, 0xffff
je DONE
mov ah, 0x3e
int 0x21
 
DONE:
pop dx
pop cx
pop bx
pop ax
}
 
/* printf("blen=%u filepos_cx=%u filepos_dx=%u\r\n", blen, filepos_cx, filepos_dx); */
 
if (errv != 0) nls_outputnl_doserr(errv);
 
/* on EOF - abort processing the bat file */
if (blen == 0) goto OOPS;
 
/* find nearest \n to inc batch offset and replace \r by NULL terminator
* I support all CR/LF, CR- and LF-terminated batch files */
for (i = 0; i < blen; i++) {
if ((buff[i] == '\r') || (buff[i] == '\n')) {
if ((buff[i] == '\r') && ((i+1) < blen) && (buff[i+1] == '\n')) rmod->batnextline += 1;
break;
}
}
buff[i] = 0;
rmod->batnextline += i + 1;
 
return(0);
 
OOPS:
rmod->batfile[0] = 0;
rmod->batnextline = 0;
return(-1);
}
 
 
/* replaces %-variables in a BAT line with resolved values:
* %PATH% -> replaced by the contend of the PATH env variable
* %UNDEFINED% -> undefined variables are replaced by nothing ("")
* %NOTCLOSED -> NOTCLOSED
* %1 -> first argument of the batch file (or nothing if no arg) */
static void batpercrepl(char *res, unsigned short ressz, const char *line, const struct rmod_props far *rmod, unsigned short envseg) {
unsigned short lastperc = 0xffff;
unsigned short reslen = 0;
 
if (ressz == 0) return;
ressz--; /* reserve one byte for the NULL terminator */
 
for (; (reslen < ressz) && (*line != 0); line++) {
/* if not a percent, I don't care */
if (*line != '%') {
res[reslen++] = *line;
continue;
}
 
/* *** perc char handling *** */
 
/* closing perc? */
if (lastperc != 0xffff) {
/* %% is '%' */
if (lastperc == reslen) {
res[reslen++] = '%';
} else { /* otherwise variable name */
const char far *ptr;
res[reslen] = 0;
reslen = lastperc;
ptr = env_lookup_val(envseg, res + reslen);
if (ptr != NULL) {
while ((*ptr != 0) && (reslen < ressz)) {
res[reslen++] = *ptr;
ptr++;
}
}
}
lastperc = 0xffff;
continue;
}
 
/* digit? (bat arg) */
if ((line[1] >= '0') && (line[1] <= '9')) {
unsigned short argid = line[1] - '0';
unsigned short i;
const char far *argv = rmod->batargv;
 
/* locate the proper arg */
for (i = 0; i != argid; i++) {
/* if string is 0, then end of list reached */
if (*argv == 0) break;
/* jump to next arg */
while (*argv != 0) argv++;
argv++;
}
 
/* copy the arg to result */
for (i = 0; (argv[i] != 0) && (reslen < ressz); i++) {
res[reslen++] = argv[i];
}
line++; /* skip the digit */
continue;
}
 
/* opening perc */
lastperc = reslen;
 
}
 
res[reslen] = 0;
}
 
 
int main(void) {
static struct config cfg;
static unsigned short far *rmod_envseg;
static unsigned short far *lastexitcode;
static struct rmod_props far *rmod;
static char cmdlinebuf[CMDLINE_MAXLEN + 2]; /* 1 extra byte for 0-terminator and another for memguard */
static char *cmdline;
static struct redir_data redirprops;
static enum cmd_result cmdres;
static unsigned short i; /* general-purpose variable for short-lived things */
static unsigned char delete_stdin_file;
 
rmod = rmod_find(BUFFER_len);
if (rmod == NULL) {
/* look at command line parameters (in case env size if set there) */
parse_argv(&cfg);
rmod = rmod_install(cfg.envsiz, BUFFER, BUFFER_len);
if (rmod == NULL) {
outputnl("ERROR: rmod_install() failed");
return(1);
}
/* copy flags to rmod's storage (and enable ECHO) */
rmod->flags = cfg.flags | FLAG_ECHOFLAG;
/* printf("rmod installed at %Fp\r\n", rmod); */
rmod->version = BYTE_VERSION;
} else {
/* printf("rmod found at %Fp\r\n", rmod); */
/* if I was spawned by rmod and FLAG_EXEC_AND_QUIT is set, then I should
* die asap, because the command has been executed already, so I no longer
* have a purpose in life */
if (rmod->flags & FLAG_EXEC_AND_QUIT) sayonara(rmod);
/* */
if (rmod->version != BYTE_VERSION) {
outputnl("SVARCOM VERSION CHANGED. SYSTEM HALTED. PLEASE REBOOT YOUR COMPUTER.");
_asm {
HALT:
hlt
jmp HALT
}
}
}
 
/* install a few guardvals in memory to detect some cases of overflows */
memguard_set(cmdlinebuf);
 
rmod_envseg = MK_FP(rmod->rmodseg, RMOD_OFFSET_ENVSEG);
lastexitcode = MK_FP(rmod->rmodseg, 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->rmodseg, RMOD_OFFSET_INPBUFF, *rmod_envseg, envsiz);
}*/
 
/* on /P check for the presence of AUTOEXEC.BAT and execute it if found,
* but skip this check if /D was also passed */
if ((cfg.flags & (FLAG_PERMANENT | FLAG_SKIP_AUTOEXEC)) == FLAG_PERMANENT) {
if (file_getattr("AUTOEXEC.BAT") >= 0) cfg.execcmd = "AUTOEXEC.BAT";
}
 
do {
/* terminate previous command with a CR/LF if ECHO ON (but not during BAT processing) */
if ((rmod->flags & FLAG_ECHOFLAG) && (rmod->batfile[0] == 0)) outputnl("");
 
SKIP_NEWLINE:
 
/* memory check */
memguard_check(rmod->rmodseg, cmdlinebuf);
 
/* preset cmdline to point at the dedicated buffer */
cmdline = cmdlinebuf;
 
/* (re)load translation strings if needed */
nls_langreload(BUFFER, *rmod_envseg);
 
/* load awaiting command, if any (used to run piped commands) */
if (rmod->awaitingcmd[0] != 0) {
_fstrcpy(cmdline, rmod->awaitingcmd);
rmod->awaitingcmd[0] = 0;
delete_stdin_file = 1;
goto EXEC_CMDLINE;
} else {
delete_stdin_file = 0;
}
 
/* skip user input if I have a command to exec (/C or /K) */
if (cfg.execcmd != NULL) {
cmdline = cfg.execcmd;
cfg.execcmd = NULL;
goto EXEC_CMDLINE;
}
 
/* if batch file is being executed -> fetch next line */
if (rmod->batfile[0] != 0) {
if (getbatcmd(BUFFER, CMDLINE_MAXLEN, rmod) != 0) { /* end of batch */
/* restore echo flag as it was before running the bat file */
rmod->flags &= ~FLAG_ECHOFLAG;
if (rmod->flags & FLAG_ECHO_BEFORE_BAT) rmod->flags |= FLAG_ECHOFLAG;
continue;
}
/* %-decoding of variables (%PATH%, %1, %%...), result in cmdline */
batpercrepl(cmdline, CMDLINE_MAXLEN, BUFFER, rmod, *rmod_envseg);
/* skip any leading spaces */
while (*cmdline == ' ') cmdline++;
/* output prompt and command on screen if echo on and command is not
* inhibiting it with the @ prefix */
if ((rmod->flags & FLAG_ECHOFLAG) && (cmdline[0] != '@')) {
build_and_display_prompt(BUFFER, *rmod_envseg);
outputnl(cmdline);
}
/* skip the @ prefix if present, it is no longer useful */
if (cmdline[0] == '@') cmdline++;
} else {
/* interactive mode: display prompt (if echo enabled) and wait for user
* command line */
if (rmod->flags & FLAG_ECHOFLAG) build_and_display_prompt(BUFFER, *rmod_envseg);
/* collect user input */
cmdline_getinput(FP_SEG(rmod->inputbuf), FP_OFF(rmod->inputbuf));
/* copy it to local cmdline */
if (rmod->inputbuf[1] != 0) _fmemcpy(cmdline, rmod->inputbuf + 2, rmod->inputbuf[1]);
cmdline[(unsigned)(rmod->inputbuf[1])] = 0; /* zero-terminate local buff (oriignal is '\r'-terminated) */
}
 
/* if nothing entered, loop again (but without appending an extra CR/LF) */
if (cmdline[0] == 0) goto SKIP_NEWLINE;
 
/* I jump here when I need to exec an initial command (/C or /K) */
EXEC_CMDLINE:
 
/* 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->rmodseg, *rmod_envseg);
 
/* handle redirections (if any) */
i = redir_parsecmd(&redirprops, cmdline, rmod->awaitingcmd, *rmod_envseg);
if (i != 0) {
nls_outputnl_doserr(i);
rmod->awaitingcmd[0] = 0;
continue;
}
 
/* try matching (and executing) an internal command */
cmdres = cmd_process(rmod, *rmod_envseg, cmdline, BUFFER, sizeof(BUFFER), &redirprops, delete_stdin_file);
if ((cmdres == CMD_OK) || (cmdres == CMD_FAIL)) {
/* internal command executed */
continue;
} else if (cmdres == CMD_CHANGED) { /* cmdline changed, needs to be reprocessed */
goto EXEC_CMDLINE;
} else if (cmdres == CMD_NOTFOUND) {
/* this was not an internal command, try matching an external command */
run_as_external(BUFFER, cmdline, *rmod_envseg, rmod, &redirprops, delete_stdin_file);
/* perhaps this is a newly launched BAT file */
if ((rmod->batfile[0] != 0) && (rmod->batnextline == 0)) goto SKIP_NEWLINE;
/* run_as_external() does not return on success, if I am still alive then
* external command failed to execute */
outputnl("Bad command or file name");
continue;
}
 
/* I should never ever land here */
outputnl("INTERNAL ERR: INVALID CMDRES");
 
} while ((rmod->flags & FLAG_EXEC_AND_QUIT) == 0);
 
sayonara(rmod);
return(0);
}
/svarcom/tags/svarcom-2022.0/env.c
0,0 → 1,167
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
/*
* 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);
}
}
 
 
/* almost identical to env_lookup(), but instead of returning a pointer
* to the 'NAME=value' string, it returns a pointer to value (or NULL if
* var not found) */
char far *env_lookup_val(unsigned short env_seg, const char *varname) {
char far *r = env_lookup(env_seg, varname);
if (r == NULL) return(NULL);
/* find '=' or end of string */
for (;;) {
if (*r == '=') return(r + 1);
if (*r == 0) return(r);
r++;
}
}
 
 
/* locates the value of env variable varname and copies it to result, up to
* ressz bytes (incl. the NULL terminator). returns the length of the value on
* success, 0 if var not found or couldn't fit in ressz). */
unsigned short env_lookup_valcopy(char *res, unsigned short ressz, unsigned short env_seg, const char *varname) {
unsigned short i;
char far *v = env_lookup_val(env_seg, varname);
if (v == NULL) return(0);
for (i = 0;; i++) {
if (ressz-- == 0) return(0);
res[i] = v[i];
if (res[i] == 0) return(i);
}
}
 
 
/* 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/tags/svarcom-2022.0/env.h
0,0 → 1,69
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
/*
* routines used to manipulate the environment block
*/
 
#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);
 
/* almost identical to env_lookup(), but instead of returning a pointer
* to the 'NAME=value' string, it returns a pointer to value (or NULL if
* var not found) */
char far *env_lookup_val(unsigned short env_seg, const char *varname);
 
/* locates the value of env variable varname and copies it to result, up to
* ressz bytes (incl. the NULL terminator). returns the length of the value on
* success, 0 if var not found or couldn't fit in ressz). */
unsigned short env_lookup_valcopy(char *res, unsigned short ressz, 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/tags/svarcom-2022.0/file2c.c
0,0 → 1,93
/*
* 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>
 
 
static void help(void) {
puts("usage: file2c [/c] [/lxxx] infile.dat outfile.c varname");
puts("");
puts("/c - define the output array as CONST");
puts("/s - define the output array as STATIC");
puts("/lxxx - enforces the output array to be xxx bytes big");
}
 
 
int main(int argc, char **argv) {
char *fnamein = NULL, *fnameout = NULL, *varname = NULL;
char stortype = 0; /* 'c' = const ; 's' = static */
char *flag_l = "";
FILE *fdin, *fdout;
unsigned long len;
int c;
 
for (c = 1; c < argc; c++) {
if ((argv[c][0] == '/') && (argv[c][1] == 'l')) {
flag_l = argv[c] + 2;
continue;
}
if ((argv[c][0] == '/') && (argv[c][1] == 'c')) {
stortype = 'c';
continue;
}
if ((argv[c][0] == '/') && (argv[c][1] == 's')) {
stortype = 's';
continue;
}
if (argv[c][0] == '/') {
help();
return(1);
}
/* not a switch - so it's either infile, outfile or varname */
if (fnamein == NULL) {
fnamein = argv[c];
} else if (fnameout == NULL) {
fnameout = argv[c];
} else if (varname == NULL) {
varname = argv[c];
} else {
help();
return(1);
}
}
 
if (varname == NULL) {
help();
return(1);
}
 
fdin = fopen(fnamein, "rb");
if (fdin == NULL) {
puts("ERROR: failed to open input file");
return(1);
}
 
fdout = fopen(fnameout, "wb");
if (fdout == NULL) {
fclose(fdin);
puts("ERROR: failed to open output file");
return(1);
}
 
if (stortype == 'c') fprintf(fdout, "const ");
if (stortype == 's') fprintf(fdout, "static ");
fprintf(fdout, "char %s[%s] = {", varname, flag_l);
 
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", varname, len);
 
fclose(fdin);
fclose(fdout);
return(0);
}
/svarcom/tags/svarcom-2022.0/freecom.txt
0,0 → 1,73
 
=== SVARCOM vs FREECOM ===
 
 
SvarCOM is a DOS command interpreter (shell), similar to COMMAND.COM in MS-DOS
and FreeCOM in FreeDOS. But why not using FreeCOM in the first place?
 
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
issues through a completely new implementation. SvarCOM is composed of
entirely original code and does not borrow any code from MS-DOS or FreeCOM.
 
 
=== 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.
 
SvarCOM does not rely on XMS and performs runtime swapping that works on any
IBM PC compatible machine.
 
 
=== NLS RESSOURCES ===========================================================
 
FreeCOM requires custom NLS ressources. While the vast majority of FreeDOS
programs use a single "standard" (CATS), FreeCOM uses a different approach
with NLS strings built into the binary. This makes it necessary to distribute
as many binary blobs as there are supported languages. Another consequence is
that FreeCOM is unable to switch its language dynamically (ie. following
changes made to the LANG environment variable). It also makes the translation
more difficult.
 
SvarCOM uses CATS-style translations and supports dynamic language changes
through the %LANG% environment variable.
 
 
=== 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.
 
SvarCOM, on the other hand, is meant to be simple and universal. It is
compiled with OpenWatcom only, which makes a ton of IFDEF's go away. It also
does not integrate extra features that can be reasonably implemented through
external tools (typically: DOSKEY). It strives to reimplement the baseline
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 the software,
it only asks for credits to be provided where credit is due.
 
I am aware that this section, and its slightly provocating title, may trigger
reactions from GPL enthusiasts. Let me explain with more words. I enjoy
creating software and I publish it for others to use for free. Should someone
wish to extend SvarCOM with an extra feature and decide not to publish the
source code along with the modified version, that's fine by me. My code is
still open and free. Theirs is not, but its their work, so I find it fair that
they have the freedom to decide how to distribute it. I certainly do not want
to impose my views on others.
 
 
====================================================================== EOF ===
/svarcom/tags/svarcom-2022.0/helpers.c
0,0 → 1,851
/* This file is part of the SvarCOM project and 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.
*/
 
/*
* a variety of helper functions
* Copyright (C) 2021 Mateusz Viste
*/
 
#include <i86.h> /* MK_FP() */
#include <stdio.h> /* sprintf() */
#include <string.h> /* memcpy() */
 
#include "deflang.h"
 
#include "env.h"
 
#include "helpers.h"
 
 
/* case-insensitive comparison of strings, compares up to maxlen characters.
* returns non-zero on equality. */
int imatchlim(const char *s1, const char *s2, unsigned short maxlen) {
while (maxlen--) {
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) break;
s1++;
s2++;
}
return(1);
}
 
 
/* 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 handle (1=stdout 2=stderr) */
void output_internal(const char *s, unsigned char nl, unsigned char handle) {
const static unsigned char *crlf = "\r\n";
_asm {
push ds
pop es /* make sure es=ds (scasb uses es) */
/* get length of s into CX */
mov ax, 0x4000 /* ah=DOS "write to file" and AL=0 for NULL matching */
mov dx, s /* set dx to string (required for later) */
mov di, dx /* set di to string (for NULL matching) */
mov cx, 0xffff /* preset cx to 65535 (-1) */
cld /* clear DF so scasb increments DI */
repne scasb /* cmp al, es:[di], inc di, dec cx until match found */
/* CX contains (65535 - strlen(s)) now */
not cx /* reverse all bits so I get (strlen(s) + 1) */
dec cx /* this is CX length */
jz WRITEDONE /* do nothing for empty strings */
 
/* output by writing to stdout */
/* mov ah, 0x40 */ /* DOS 2+ -- write to file via handle */
xor bh, bh
mov bl, handle /* set handle (1=stdout 2=stderr) */
/* mov cx, xxx */ /* write CX bytes */
/* mov dx, s */ /* DS:DX is the source of bytes to "write" */
int 0x21
WRITEDONE:
 
/* print out a CR/LF trailer if nl set */
test byte ptr [nl], 0xff
jz FINITO
/* bx still contains handle */
mov ah, 0x40 /* "write to file" */
mov cx, 2
mov dx, crlf
int 0x21
FINITO:
}
}
 
 
static const char *nlsblock_findstr(unsigned short id) {
const char *ptr = langblock + 4; /* first 4 bytes are lang id and lang len */
/* find the string id in langblock memory */
for (;;) {
if (((unsigned short *)ptr)[0] == id) {
ptr += 3;
return(ptr);
}
if (ptr[2] == 0) return(NULL);
ptr += ptr[2] + 3;
}
}
 
 
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle) {
const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
const char *ptr = nlsblock_findstr(id);
if (ptr == NULL) ptr = NOTFOUND;
output_internal(ptr, nl, handle);
}
 
 
/* output DOS error e to stderr */
void nls_outputnl_doserr(unsigned short e) {
static char errstr[16];
const char *ptr = NULL;
/* find string in nls block */
if (e < 0xff) ptr = nlsblock_findstr(0xff00 | e);
/* if not found, use a fallback */
if (ptr == NULL) {
sprintf(errstr, "DOS ERR %u", e);
ptr = errstr;
}
/* display */
output_internal(ptr, 1, hSTDERR);
}
 
 
/* 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, returns 0 on success
* or DOS err on failure (invalid drive) */
unsigned short file_truename(const char *src, char *dst) {
unsigned short res = 0;
_asm {
push es
mov ah, 0x60 /* query truename, DS:SI=src, ES:DI=dst */
push ds
pop es
mov si, src
mov di, dst
int 0x21
jnc DONE
mov [res], ax
DONE:
pop es
}
return(res);
}
 
 
/* 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) {
nls_output(15, 1); /* 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
}
}
 
 
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
int isdrivevalid(unsigned char drv) {
_asm {
mov ah, 0x19 /* query default (current) disk */
int 0x21 /* drive in AL (0=A, 1=B, etc) */
mov ch, al /* save current drive to ch */
/* try setting up the drive as current */
mov ah, 0x0E /* select default drive */
mov dl, [drv] /* 0=A, 1=B, etc */
int 0x21
/* this call does not set CF on error, I must check cur drive to look for success */
mov ah, 0x19 /* query default (current) disk */
int 0x21 /* drive in AL (0=A, 1=B, etc) */
mov [drv], 1 /* preset result as success */
cmp al, dl /* is eq? */
je DONE
mov [drv], 0 /* fail */
jmp FAILED
DONE:
/* set current drive back to what it was initially */
mov ah, 0x0E
mov dl, ch
int 0x21
FAILED:
}
return(drv);
}
 
 
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE EXT) */
void file_fname2fcb(char *dst, const char *src) {
unsigned short i;
 
/* fill dst with 11 spaces and a NULL terminator */
for (i = 0; i < 11; i++) dst[i] = ' ';
dst[11] = 0;
 
/* copy fname until dot (.) or 8 characters */
for (i = 0; i < 8; i++) {
if ((src[i] == '.') || (src[i] == 0)) break;
dst[i] = src[i];
}
 
/* advance src until extension or end of string */
src += i;
for (;;) {
if (*src == '.') {
src++; /* next character is extension */
break;
}
if (*src == 0) break;
}
 
/* copy extension to dst (3 chars max) */
dst += 8;
for (i = 0; i < 3; i++) {
if (src[i] == 0) break;
dst[i] = src[i];
}
}
 
 
/* converts a 11-bytes FCB filename (MYFILE EXT) into 8+3 format (MYFILE.EXT) */
void file_fcb2fname(char *dst, const char *src) {
unsigned short i, end = 0;
 
for (i = 0; i < 8; i++) {
dst[i] = src[i];
if (dst[i] != ' ') end = i + 1;
}
 
/* is there an extension? */
if (src[8] == ' ') {
dst[end] = 0;
} else { /* found extension: copy it until first space */
dst[end++] = '.';
for (i = 8; i < 11; i++) {
if (src[i] == ' ') break;
dst[end++] = src[i];
}
dst[end] = 0;
}
}
 
 
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
* on error, result will contain all valid digits that were read until
* error occurred (0 on overflow or if parsing failed immediately) */
int atous(unsigned short *r, const char *s) {
int err = 0;
 
_asm {
mov si, s
xor ax, ax /* general purpose register */
xor cx, cx /* contains the result */
mov bx, 10 /* used as a multiplicative step */
 
NEXTBYTE:
xchg cx, ax /* move result into cx temporarily */
lodsb /* AL = DS:[SI++] */
/* is AL 0? if so we're done */
test al, al
jz DONE
/* validate that AL is in range '0'-'9' */
sub al, '0'
jc FAIL /* invalid character detected */
cmp al, 9
jg FAIL /* invalid character detected */
/* restore result into AX (CX contains the new digit) */
xchg cx, ax
/* multiply result by 10 and add cl */
mul bx /* DX AX = AX * BX(10) */
jc OVERFLOW /* overflow */
add ax, cx
/* if CF is set then overflow occurred (overflow part lands in DX) */
jnc NEXTBYTE
 
OVERFLOW:
xor cx, cx /* make sure result is zeroed in case overflow occured */
 
FAIL:
inc [err]
 
DONE: /* save result (CX) into indirect memory address r */
mov bx, [r]
mov [bx], cx
}
return(err);
}
 
 
/* appends a backslash if path is a directory
* returns the (possibly updated) length of path */
unsigned short path_appendbkslash_if_dir(char *path) {
unsigned short len;
int attr;
for (len = 0; path[len] != 0; len++);
if (len == 0) return(0);
if (path[len - 1] == '\\') return(len);
/* */
attr = file_getattr(path);
if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
path[len++] = '\\';
path[len] = 0;
}
return(len);
}
 
 
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
* returns 0 on success, doserr otherwise */
unsigned short curpathfordrv(char *buff, unsigned char d) {
unsigned short r = 0;
 
_asm {
/* is d == 0? then I need to resolve current drive */
cmp byte ptr [d], 0
jne GETCWD
/* resolve cur drive */
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 [d], al
 
GETCWD:
/* prepend buff with drive:\ */
mov si, buff
mov dl, [d]
mov [si], dl
add byte ptr [si], 'A' - 1
inc si
mov [si], ':'
inc si
mov [si], '\\'
inc si
 
mov ah, 0x47 /* get current directory of drv DL into DS:SI */
int 0x21
jnc DONE
mov [r], ax /* copy result from ax */
 
DONE:
}
 
return(r);
}
 
 
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
unsigned short nls_getpatterns(struct nls_patterns *p) {
unsigned short r = 0;
 
_asm {
mov ax, 0x3800 /* DOS 2+ -- Get Country Info for current country */
mov dx, p /* DS:DX points to the CountryInfoRec buffer */
int 0x21
jnc DONE
mov [r], ax /* copy DOS err code to r */
DONE:
}
 
return(r);
}
 
 
/* computes a formatted date based on NLS patterns found in p
* returns length of result */
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
unsigned short items[3];
/* preset date/month/year in proper order depending on date format */
switch (p->dateformat) {
case 0: /* USA style: m d y */
items[0] = mo;
items[1] = dy;
items[2] = yr;
break;
case 1: /* EU style: d m y */
items[0] = dy;
items[1] = mo;
items[2] = yr;
break;
case 2: /* Japan-style: y m d */
default:
items[0] = yr;
items[1] = mo;
items[2] = dy;
break;
}
/* compute the string */
return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
}
 
 
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
* returns length of result */
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
char ampm = 0;
unsigned short res;
 
if (p->timefmt == 0) {
if (ho == 12) {
ampm = 'p';
} else if (ho > 12) {
ho -= 12;
ampm = 'p';
} else { /* ho < 12 */
if (ho == 0) ho = 12;
ampm = 'a';
}
res = sprintf(s, "%2u", ho);
} else {
res = sprintf(s, "%02u", ho);
}
 
/* append separator and minutes */
res += sprintf(s + res, "%s%02u", p->timesep, mn);
 
/* if seconds provided, append them, too */
if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
 
/* finally append AM/PM char */
if (ampm != 0) s[res++] = ampm;
s[res] = 0;
 
return(res);
}
 
 
/* computes a formatted integer number based on NLS patterns found in p
* returns length of result */
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
unsigned short sl = 0, i;
unsigned char thcount = 0;
 
/* write the value (reverse) with thousand separators (if any defined) */
do {
if ((thcount == 3) && (p->thousep[0] != 0)) {
s[sl++] = p->thousep[0];
thcount = 0;
}
s[sl++] = '0' + num % 10;
num /= 10;
thcount++;
} while (num > 0);
 
/* terminate the string */
s[sl] = 0;
 
/* reverse the string now (has been built in reverse) */
for (i = sl / 2 + (sl & 1); i < sl; i++) {
thcount = s[i];
s[i] = s[sl - (i + 1)]; /* abc'de if i=3 then ' <-> c */
s[sl - (i + 1)] = thcount;
}
 
return(sl);
}
 
 
/* reload nls ressources from svarcom.lng into langblock */
void nls_langreload(char *buff, unsigned short env) {
unsigned short i;
const char far *nlspath;
char *langblockptr = langblock;
unsigned short lang;
unsigned short errcode = 0;
 
/* look up the LANG env variable, upcase it and copy to lang */
nlspath = env_lookup_val(env, "LANG");
if ((nlspath == NULL) || (nlspath[0] == 0)) return;
buff[0] = nlspath[0];
buff[1] = nlspath[1];
buff[2] = 0;
 
if (buff[0] >= 'a') buff[0] -= 'a' - 'A';
if (buff[1] >= 'a') buff[1] -= 'a' - 'A';
memcpy(&lang, buff, 2);
 
/* check if there is need to reload at all */
if (((unsigned short *)langblock)[0] == lang) return;
 
/* printf("NLS RELOAD (curlang=%04X ; toload=%04X\r\n", ((unsigned short *)langblock)[0], lang); */
 
nlspath = env_lookup_val(env, "NLSPATH");
if ((nlspath == NULL) || (nlspath[0] == 0)) return;
 
/* copy NLSPATH(far) to buff */
for (i = 0; nlspath[i] != 0; i++) buff[i] = nlspath[i];
 
/* terminate with a bkslash, if not already the case */
if (buff[i - 1] != '\\') buff[i++] = '\\';
 
/* append "svarcom.lng" */
strcpy(buff + i, "SVARCOM.LNG");
 
/* copy file content to langblock */
_asm {
push ax
push bx
push cx
push dx
push si
push di
 
/* make sure ES=DS and clear DF (will be useful for string matching) */
push ds
pop es
cld
 
/* preset SI to buff */
mov si, buff
 
/* Open File */
mov bx, 0xffff /* bx holds the file handle (0xffff = not set) */
mov ax, 0x3d00 /* DOS 2+ -- Open File for read */
mov dx, si /* fname */
int 0x21 /* cf set on error, otherwise handle in AX */
jnc OPENOK
jmp FAIL
OPENOK:
mov bx, ax /* save file handle to bx */
 
/* read hdr */
mov ah, 0x3f /* DOS 2+ -- Read from File via Handle in BX */
mov cx, 4 /* read 4 bytes */
mov dx, si
int 0x21
jnc READHDROK
jmp FAIL
 
READHDROK:
 
cmp ax, cx /* hdr must be 4 bytes long (SvL\x1b) */
jne FAIL
/* check that sig is Svl\x1b */
mov di, si
cld /* scasw must inc DI */
mov ax, 'vS'
scasw /* cmp ax, ES:[DI] and DI += 2*/
jne FAIL
mov ax, 0x1B4C
scasw /* cmp ax, ES:[DI] and DI += 2*/
jne FAIL
 
READLANGID:
/* read lang id */
mov ah, 0x3f /* Read from File via Handle in BX */
/* mov bx, [i] already set */
mov cx, 4
mov dx, si
int 0x21
jc FAIL
cmp ax, cx
jne FAIL
/* is this the LANG I am looking for? */
mov ax, [lang]
mov di, si
scasw /* cmp ax, ES:[DI] and DI += 2*/
je LOADSTRINGS
/* skip to next lang */
mov ax, 0x4201 /* move file pointer CX:DX bytes forward */
/* mov bx, [i] file handle */
xor cx, cx
mov dx, [di]
int 0x21
jc FAIL
jmp READLANGID
 
LOADSTRINGS:
 
/* copy langid and langlen to langblockptr */
mov di, langblockptr
mov ax, [si]
stosw /* mov [di], ax and di += 2 */
mov ax, [si+2]
stosw
/* read strings (buff+2 bytes) into langblock */
mov ah, 0x3f /* Read from File via Handle in BX */
mov cx, [si+2]
mov dx, di
int 0x21
jnc DONE
 
/* on error make sure to zero out langblock's header */
xor cx, cx
mov [di], cx /* langblock id*/
mov [di + 2], cx /* langblock len */
mov [di + 4], cx /* 1st string id */
mov [di + 6], cx /* 1st string len */
 
/* cleanup and quit */
FAIL:
mov [errcode], ax
DONE:
/* close file handle if set */
cmp bx, 0xffff
je FNOTOPEN
mov ah, 0x3e /* DOS 2+ -- Close a File Handle (Handle in BX) */
int 0x21
FNOTOPEN:
 
pop di
pop si
pop dx
pop cx
pop bx
pop ax
}
 
if (errcode != 0) printf("AX=%04x\r\n", errcode);
}
 
 
/* locates executable fname in path and fill res with result. returns 0 on success,
* -1 on failed match and -2 on failed match + "don't even try with other paths"
* extptr is filled with a ptr to the extension in fname (NULL if no extension) */
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
unsigned short lastbslash = 0;
unsigned short i, len;
unsigned char explicitpath = 0;
 
/* does the original fname has an explicit path prefix or explicit ext? */
*extptr = NULL;
for (i = 0; fname[i] != 0; i++) {
switch (fname[i]) {
case ':':
case '\\':
explicitpath = 1;
*extptr = NULL; /* extension is the last dot AFTER all path delimiters */
break;
case '.':
*extptr = fname + i + 1;
break;
}
}
 
/* normalize filename */
if (file_truename(fname, res) != 0) return(-2);
 
/* printf("truename: %s\r\n", res); */
 
/* figure out where the command starts and if it has an explicit extension */
for (len = 0; res[len] != 0; len++) {
switch (res[len]) {
case '?': /* abort on any wildcard character */
case '*':
return(-2);
case '\\':
lastbslash = len;
break;
}
}
 
/* printf("lastbslash=%u\r\n", lastbslash); */
 
/* if no path prefix was found in fname (no colon or backslash) AND we have
* a path arg, then assemble path+filename */
if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
i = strlen(path);
if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
/* move the filename at the place where path will end */
memmove(res + i, res + lastbslash + 1, len - lastbslash);
/* copy path in front of the filename and make sure there is a bkslash sep */
memmove(res, path, i);
res[i - 1] = '\\';
}
 
/* if no extension was initially provided, try matching COM, EXE, BAT */
if (*extptr == NULL) {
const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
len = strlen(res);
for (i = 0; ext[i] != NULL; i++) {
strcpy(res + len, ext[i]);
/* printf("? '%s'\r\n", res); */
*extptr = ext[i] + 1;
if (file_getattr(res) >= 0) return(0);
}
} else { /* try finding it as-is */
/* printf("? '%s'\r\n", res); */
if (file_getattr(res) >= 0) return(0);
}
 
/* not found */
if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
return(-1);
}
 
 
/* fills fname with the path and filename to the linkfile related to the
* executable link "linkname". returns 0 on success. */
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
unsigned short pathlen;
 
/* fetch %DOSDIR% */
pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
if (pathlen == 0) {
outputnl("%DOSDIR% not defined");
return(-1);
}
 
/* prep filename: %DOSDIR%\LINKS\PKG.LNK */
if (fname[pathlen - 1] == '\\') pathlen--;
sprintf(fname + pathlen, "\\LINKS\\%s.LNK", linkname);
 
return(0);
}
/svarcom/tags/svarcom-2022.0/helpers.h
0,0 → 1,197
/* This file is part of the SvarCOM project and 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.
*/
 
#ifndef HELPERS_H
#define HELPERS_H
 
/* case-insensitive comparison of strings, compares up to maxlen characters.
* returns non-zero on equality. */
int imatchlim(const char *s1, const char *s2, unsigned short maxlen);
 
#define imatch(a,b) imatchlim(a,b,0xffff)
 
/* returns zero if s1 starts with s2 */
int strstartswith(const char *s1, const char *s2);
 
/* outputs a NULL-terminated string to handle (hSTDOUT or hSTDERR) */
void output_internal(const char *s, unsigned char nl, unsigned char handle);
 
/* outputs a NULL-terminated NLS string to stdout */
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle);
 
#define hSTDOUT 1
#define hSTDERR 2
 
#define output(x) output_internal(x, 0, hSTDOUT)
#define outputnl(x) output_internal(x, 1, hSTDOUT)
#define nls_output(x,y) nls_output_internal((x << 8) | y, 0, hSTDOUT)
#define nls_outputnl(x,y) nls_output_internal((x << 8) | y, 1, hSTDOUT)
#define nls_outputnl_err(x,y) nls_output_internal((x << 8) | y, 1, hSTDERR)
 
/* output DOS error e to stderr */
void nls_outputnl_doserr(unsigned short e);
 
/*
* 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_sec2:5;
unsigned short time_min:6;
unsigned short time_hour:5;
unsigned short date_dy:5;
unsigned short date_mo:4;
unsigned short date_yr:7;
unsigned long size;
char fname[13];
};
 
 
/* this is also known as the "Country Info Block" or "CountryInfoRec":
* offset size desc
* +0 2 wDateFormat 0=USA (m d y), 1=Europe (d m y), 2=Japan (y m d)
* +2 5 szCrncySymb currency symbol (ASCIIZ)
* +7 2 szThouSep thousands separator (ASCIIZ)
* +9 2 szDecSep decimal separator (ASCIIZ)
* +0bH 2 szDateSep date separator (ASCIIZ)
* +0dH 2 szTimeSep time separator (ASCIIZ)
* +0fH 1 bCrncyFlags currency format flags
* +10H 1 bCrncyDigits decimals digits in currency
* +11H 1 bTimeFormat time format 0=12h 1=24h
* +12H 4 pfCasemap Casemap FAR call address
* +16H 2 szDataSep data list separator (ASCIIZ)
* +18H 10 res reserved zeros
* 34 total length
*/
_Packed struct nls_patterns {
unsigned short dateformat;
char currency[5];
char thousep[2];
char decsep[2];
char datesep[2];
char timesep[2];
unsigned char currflags;
unsigned char currdigits;
unsigned char timefmt;
void far *casemapfn;
char datalistsep[2];
char reserved[10];
};
 
 
#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
* attr contains DOS attributes that files MAY have (ie attr=0 will match only
* files that have no attributes at all)
* 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, returns 0 on success
* or DOS err on failure (invalid drive) */
unsigned short 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);
 
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
int isdrivevalid(unsigned char drv);
 
/* converts a filename into FCB format (FILENAMEEXT) */
void file_fname2fcb(char *dst, const char *src);
 
/* converts a FCB filename (FILENAMEEXT) into normal format (FILENAME.EXT) */
void file_fcb2fname(char *dst, const char *src);
 
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
* on error, result will contain all valid digits that were read until
* error occurred (0 on overflow or if parsing failed immediately) */
int atous(unsigned short *r, const char *s);
 
/* appends a backslash if path is a directory
* returns the (possibly updated) length of path */
unsigned short path_appendbkslash_if_dir(char *path);
 
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
* returns 0 on success, doserr otherwise */
unsigned short curpathfordrv(char *buff, unsigned char d);
 
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
unsigned short nls_getpatterns(struct nls_patterns *p);
 
/* computes a formatted date based on NLS patterns found in p
* returns length of result */
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p);
 
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
* returns length of result */
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p);
 
/* computes a formatted integer number based on NLS patterns found in p
* returns length of result */
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p);
 
/* reload nls ressources from svarcom.lng into langblock */
void nls_langreload(char *buff, unsigned short env);
 
/* locates executable fname in path and fill res with result. returns 0 on success,
* -1 on failed match and -2 on failed match + "don't even try with other paths"
* extptr is filled with a ptr to the extension in fname (NULL if no extension) */
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr);
 
/* fills fname with the path and filename to the linkfile related to the
* executable link "linkname". returns 0 on success. */
int link_computefname(char *fname, const char *linkname, unsigned short env_seg);
 
#endif
/svarcom/tags/svarcom-2022.0/history.txt
0,0 → 1,23
 
 
=== SvarCOM's history / changelog ===
 
 
=== ver 2022.0 (01.02.2022) ==================================================
 
- added "global executable links" support (new command: LN)
- prompt fixed when current drive becomes invalid (eg. empty diskette drive)
- piping support (like dir/b | sort)
- DIR: fixed /P pagination in wide mode
- DIR: implemented /A
- implemented IF command (IF EXIST, IF ERRORLEVEL, IF str==str)
- added a break handler (running application can be aborted with CTRL+C)
- DOS error messages are output to stderr
 
 
=== ver 2021.0 (24.11.2021) ==================================================
 
- first public release, after roughly a month of work
 
 
====================================================================== EOF ===
/svarcom/tags/svarcom-2022.0/internal.txt
0,0 → 1,73
 
 
=== SvarCOM implementation notes ===
 
 
=== SWAPPING =================================================================
 
While conventional RAM is scarce, a command line interpreter must make effort
to reduce its memory footprint when launching applications. SvarCOM does that
by installing a small executable module in memory, called RMOD (for Resident
MODule). SvarCOM pre-sets RMOD so knows how to execute the external program
and removes itself from memory, letting RMOD do the job. RMOD executes the
application, waits for it to finish and then calls back SvarCOM.
 
 
=== NLS STRINGS ==============================================================
 
SvarCOM can output information in many languages. To do so, it relies on a
precompiled resource file named SVARCOM.LNG. When SvarCOM starts, it looks
for this file in the %NLSPATH% directory and loads from it the part that
contains the %LANG% language. All this is done by nls_langreload().
 
The SVARCOM.LNG file is compiled by a separate tool: TLUMACZ. It takes
CATS-style language files as input and compiles them into a single SVARCOM.LNG
resource file. It also produces a DEFAULT.LNG with english strings only, this
one is embedded into the SvarCOM executable to display english text in case
SVARCOM.LNG is unavailable.
 
 
=== BATCH FILES SUPPORT ======================================================
 
When SvarCOM executes a command, it checks first if it has a *.BAT extension.
If so, it switches into 'batch-processing' mode:
 
- Writes the batch filename into its persistent (rmod-owned) buffer, along
with a counter that holds the offset of the next line to be executed.
 
When the batch filename buffer is non-empty, SvarCOM does not ask the user for
a command. Instead, it opens the batch file, jumps to the "next line to be
executed" and loads the command from there, incrementing the line counter in
the process.
 
 
=== PIPING COMMANDS ==========================================================
 
Piping a command means redirecting its standard output (stdout) to the
standard input (stdin) of another command. While redirection of file handles
is a concept well supported by the DOS kernels, piping is not, in part due to
the mono-task nature of DOS. SvarCOM provides piping support through following
logic:
1. user-entered (or batch-acquired) command line is analyzed for any kind of
redirections (incl. pipes) by redir_parsecmd(). If the command appears to
be piped, then redir_parsecmd() enforces a stdout redirection to a
temporary file and moves all the pipe chain to an RMOD-owned buffer named
"awaitingcmd", appending an stdin redirection so the next command's stdin
is fed from the temporary file. The command is then executed.
2. before further execution, SvarCOM looks into its "awaitingcmd" buffer, and
if it is non-empty, it runs its content.
3. when loading commands from the awaitingcmd, SvarCOM sets a special
"delete_stdin_file" flag and passes it to command-executing functions so
these remember to delete the stdin-redirected file.
 
 
=== GLOBAL EXECUTABLE LINKS ==================================================
 
SvarCOM features special support for "global executable links". This allows to
run selected programs from any directory, without the need to copy these
programs to a directory in %PATH%. Executable links are flat files written in
%DOSDIR%\LINKS\. Each file there contains the directory where the matching
program should be looked for.
 
 
===================================================================== EOF ====
/svarcom/tags/svarcom-2022.0/makefile
0,0 → 1,81
#
# This is a makefile to build the SVARCOM command interpreter (COMMAND.COM)
# using OpenWatcom and nasm.
#
# You can use following targets:
#
# wmake - compiles the program
# wmake clean - cleans up all non-source files
#
 
FEATURES =
FEATURES += -DVERDBG
 
LDFLAGS = -0 -y -wx -mt -lr -we -d0 -ox -fm=command.map
CFLAGS = -0 -wx -ms -we -d0 -ox $(FEATURES)
# -0 generate 8086 compatible code
# -y ignore %WCL% if present
# -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
#
# NOTE: wcc does not understand -mt, that is why -ms must be passed instead
 
all: command.com
 
command.com: rmodcore.h command.obj cmd.obj env.obj redir.obj rmodinit.obj sayonara.obj helpers.obj
*wcl $(LDFLAGS) command.obj cmd.obj env.obj redir.obj rmodinit.obj sayonara.obj helpers.obj
 
cmd.obj: cmd.c cmd\*.c
wcc $(CFLAGS) cmd.c
 
command.obj: command.c rmodcore.h
wcc $(CFLAGS) command.c
 
helpers.obj: helpers.c deflang.h
wcc $(CFLAGS) helpers.c
 
.c.obj:
wcc $(CFLAGS) $<
 
rmodcore.h: file2c.com rmod.bin
file2c /s /l4096 rmod.bin rmodcore.h BUFFER
 
deflang.h: file2c.com tlumacz\default.lng
file2c /l4096 tlumacz\default.lng deflang.h langblock
 
file2c.com: file2c.c
wcl $(LDFLAGS) file2c.c
 
rmod.bin: rmod.asm
nasm -f bin -l rmod.lst -o rmod.bin rmod.asm
 
clean: .SYMBOLIC
del *.com
del *.obj
del rmod.bin
del rmod.lst
del rmodcore.h
del deflang.h
del command.map
 
pkg: svarcom.zip
 
svarcom.zip: command.com
zip -9kDX -j svarcom.zip command.com svarcom.txt todo.txt freecom.txt history.txt tlumacz\svarcom.lng
mkdir source
copy makefile source\
copy *.c source\
copy *.h source\
copy *.txt source\
copy *.asm source\
mkdir source\cmd
copy cmd\*.* source\cmd\
mkdir source\tlumacz
copy tlumacz\*.* source\tlumacz\
mkdir source\tlumacz\lang
copy tlumacz\lang\*.* source\tlumacz\lang\
zip -9rkDX -m svarcom.zip source
/svarcom/tags/svarcom-2022.0/redir.c
0,0 → 1,243
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021-2022 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.
*/
 
#include <string.h> /* memset() */
 
#include "env.h"
#include "helpers.h"
#include "rmodinit.h"
 
#include "redir.h"
 
static unsigned short oldstdout = 0xffff;
 
 
/* compute a filename to be used for pipes */
static unsigned short gentmpfile(char *s, unsigned short envseg) {
unsigned short err = 0;
unsigned short i;
 
/* do I have a %temp% path? */
i = env_lookup_valcopy(s, 116, envseg, "TEMP");
if (i > 0) {
/* make sure it is terminated by a backslash (required by int 0x21, ah=5a) */
if (s[i - 1] != '\\') {
s[i++] = '\\';
s[i] = 0;
}
} else {
/* if fails, then use truename(.\) */
if (file_truename(".\\", s) != 0) *s = 0;
}
 
/* create file */
_asm {
mov ah, 0x5a
mov dx, s
xor cx, cx /* file attributes */
int 0x21
jnc CLOSEFILE
mov err, ax
jmp DONE
/* close file handle (handle still in BX) */
CLOSEFILE:
mov bx, ax
mov ah, 0x3e
int 0x21
DONE:
}
return(err);
}
 
 
/* parse commandline and performs necessary redirections. cmdline is
* modified so all redirections are cut out.
* piped commands are moved to awaitingcmd for later execution
* returns 0 on success, DOS err on failure */
unsigned short redir_parsecmd(struct redir_data *d, char *cmdline, char far *awaitingcmd, unsigned short envseg) {
unsigned short i;
unsigned short pipescount = 0;
 
/* NOTES:
*
* 1. while it is possible to type a command with multiple
* redirections, MSDOS executes only the last redirection.
*
* 2. the order in which >, < and | are provided on the command line does
* not seem to matter for MSDOS. piped commands are executed first (in
* the order they appear) and the result of the last one is redirected to
* whenever the last > points at.
* stdin redirection (<) is (obviously) applied to the first command only
*/
 
/* preset oldstdout to 0xffff in case no redirection is required */
oldstdout = 0xffff;
 
/* clear out the redir_data struct */
memset(d, 0, sizeof(*d));
 
*awaitingcmd = 0;
 
/* parse the command line and fill struct with pointers */
for (i = 0;; i++) {
if (cmdline[i] == '>') {
cmdline[i] = 0;
if (cmdline[i + 1] == '>') {
i++;
d->stdout_openflag = 0x11; /* used during int 21h,AH=6C */
} else {
d->stdout_openflag = 0x12;
}
d->stdoutfile = cmdline + i + 1;
while (d->stdoutfile[0] == ' ') d->stdoutfile++;
} else if (cmdline[i] == '<') {
cmdline[i] = 0;
d->stdinfile = cmdline + i + 1;
while (d->stdinfile[0] == ' ') d->stdinfile++;
} else if (cmdline[i] == '|') {
cmdline[i] = 0;
if (pipescount < REDIR_MAX_PIPES) {
d->pipes[pipescount++] = cmdline + i + 1;
while (d->pipes[pipescount][0] == ' ') d->pipes[pipescount]++;
}
} else if (cmdline[i] == 0) {
break;
}
}
 
/* if pipes present, write them to awaitingcmd (and stdout redirection too) */
if (pipescount != 0) {
static char tmpfile[130];
for (i = 0; i < pipescount; i++) {
if (i != 0) _fstrcat(awaitingcmd, "|");
_fstrcat(awaitingcmd, d->pipes[i]);
}
/* append stdout redirection so I don't forget about it for the last command of the pipe queue */
if (d->stdoutfile != NULL) {
if (d->stdout_openflag == 0x11) {
_fstrcat(awaitingcmd, ">>");
} else {
_fstrcat(awaitingcmd, ">");
}
d->stdoutfile = NULL;
}
/* redirect stdin of next command from a temp file (that is used as my output) */
_fstrcat(awaitingcmd, "<");
i = gentmpfile(tmpfile, envseg);
if (i != 0) return(i);
_fstrcat(awaitingcmd, tmpfile);
/* same file is used as my stdout */
d->stdoutfile = tmpfile;
d->stdout_openflag = 0x12;
}
return(0);
}
 
 
/* apply stdout redirections defined in redir_data, returns 0 on success */
int redir_apply(const struct redir_data *d) {
 
if (d->stdoutfile != NULL) {
unsigned short openflag = d->stdout_openflag;
unsigned short errcode = 0;
unsigned short handle = 0;
const char *myptr = d->stdoutfile;
 
/* */
_asm {
push ax
push bx
push cx
push dx
push si
mov ax, 0x6c00 /* Extended Open/Create */
mov bx, 1 /* access mode (0=read, 1=write, 2=r+w */
xor cx, cx /* attributes when(if) creating the file (0=normal) */
mov dx, [openflag] /* action if file exists (0x11=open, 0x12=truncate)*/
mov si, myptr /* ASCIIZ filename */
int 0x21 /* AX=handle on success (CF clear), otherwise dos err */
mov handle, ax /* save the file handler */
jnc JMPEOF
mov errcode, ax
jmp DONE
 
JMPEOF:
cmp openflag, word ptr 0x11
jne DUPSTDOUT
/* jump to the end of the file (required for >> redirections) */
mov ax, 0x4202 /* jump to position EOF - CX:DX in handle BX */
mov bx, handle
xor cx, cx
xor dx, dx
int 0x21
 
/* save (duplicate) current stdout so I can revert it later */
DUPSTDOUT:
mov ah, 0x45 /* duplicate file handle */
mov bx, 1 /* handle to dup (1=stdout) */
int 0x21 /* ax = new file handle */
mov oldstdout, ax
/* redirect the stdout handle */
mov bx, handle /* dst handle */
mov cx, 1 /* src handle (1=stdout) */
mov ah, 0x46 /* redirect a handle */
int 0x21
/* close the original file handle (no longer needed) */
mov ah, 0x3e /* close a file handle (handle in BX) */
int 0x21
DONE:
pop si
pop dx
pop cx
pop bx
pop ax
}
if (errcode != 0) {
nls_outputnl_doserr(errcode);
return(-1);
}
}
 
return(0);
}
 
 
/* restores previous stdout handle if is has been redirected */
void redir_revert(void) {
_asm {
/* if oldstdout is 0xffff then not redirected */
mov bx, [oldstdout] /* dst handle */
cmp bx, 0xffff
je DONE
/* redirect the stdout handle (handle already in BX) */
mov cx, 1 /* src handle (1=stdout) */
mov ah, 0x46 /* redirect a handle */
int 0x21
/* close old handle (in bx already) */
mov ah, 0x3e
int 0x21
mov [oldstdout], 0xffff
DONE:
}
}
/svarcom/tags/svarcom-2022.0/redir.h
0,0 → 1,49
/* This file is part of the SvarCOM project and 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.
*/
 
#ifndef REDIR_H
#define REDIR_H
 
#define REDIR_MAX_PIPES 15
 
struct redir_data {
char *pipes[REDIR_MAX_PIPES + 1];
char *stdinfile;
char *stdoutfile;
unsigned short stdout_openflag; /* 0x11 or 0x12, used for the 'extended open' call */
};
 
/* parse commandline and performs necessary redirections. cmdline is
* modified so all redirections are cut out.
* piped commands are moved to awaitingcmd for later execution and a temporary file is created (either in current directory or in %TEMP%, if the latter is defined)
* returns 0 on success, DOS err on failure */
unsigned short redir_parsecmd(struct redir_data *d, char *cmdline, char far *awaitingcmd, unsigned short envseg);
 
/* apply stdin/stdout redirections defined in redir_data, returns 0 on success */
int redir_apply(const struct redir_data *d);
 
/* restores previous stdout/stdin handlers if they have been redirected */
void redir_revert(void);
 
#endif
/svarcom/tags/svarcom-2022.0/rmod.asm
0,0 → 1,307
;
; rmod - resident module of the SvarCOM command interpreter (NASM code)
;
; Copyright (C) 2021-2022 Mateusz Viste
; MIT license
;
; this is installed in memory by the transient part of SvarCOM. it has only
; two jobs: providing a resident buffer for command history, environment, etc
; and respawning COMMAND.COM whenever necessary.
 
CPU 8086
org 0x100
 
PSP_ENVSEG equ 0x2C
 
section .text ; all goes into code segment
 
; offset
SIG1 dw 0x1983 ; +0
SIG2 dw 0x1985 ; +2
SIG3 dw 0x2017 ; +4
SIG4 dw 0x2019 ; +6 this acts also as a guardval to detect stack overflows
 
; DOS int 21h functions that I use require at least 40 bytes of stack under
; DOS-C (FreeDOS) kernel, so here I reserve 64 bytes juste to be sure
STACKBUF db "XXX SVARCOM RMOD BY MATEUSZ VISTE XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
STACKPTR dw 0
 
; offset of the COMSPEC variable in the environment block, 0 means "use
; boot drive". this value is patched by the transient part of COMMAND.COM
COMSPECPTR dw 0 ; +4Ah
 
; fallback COMSPEC string used if no COMPSEC is present in the environment
; drive. drive is patched by the transient part of COMMAND.COM
COMSPECBOOT db "@:\COMMAND.COM", 0 ; +4Ch
 
; exit code of last application
LEXCODE db 0 ; +5Bh
 
; ExecParamRec used by INT 21h, AX=4b00 (load and execute program), 14 bytes:
; offset size content
; +0 2 segment of environment for child (0 = current)
; +2 4 address of command line to place at PSP:0080
; +6 4 address of an FCB to be placed at PSP:005c
; +0Ah 4 address of an FCB to be placed at PSP:006c
EXEC_PARAM_REC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; +5Ch
 
; Program to execute, preset by SvarCOM (128 bytes, ASCIIZ)
EXECPROG: times 128 db 0 ; +6Ah
 
; File where stdin and stdout should be redirected (0 = no redirection)
REDIR_INFIL: times 128 db 0 ; +EAh
REDIR_OUTFIL: times 128 db 0 ; +16Ah
REDIR_OUTAPPEND: dw 0 ; +1EAh
REDIR_DEL_STDIN: db 0 ; +1ECh indicates that the stdin file
; should be deleted (pipes). This
; MUST contain the 1st char of
; REDIR_INFIL!
 
; CTRL+BREAK (int 23h) handler
; According to the TechHelp! Manual: "If you want to abort (exit to the parent
; process), then set the carry flag and return via a FAR RET. This causes DOS
; to perform normal cleanup and exit to the parent." (otherwise use iret)
BREAK_HANDLER: ; +1EDh
stc
retf
 
 
skipsig: ; +1EFh
 
; set up CS=DS=SS and point SP to my private stack buffer
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, STACKPTR
 
; set up myself as break handler
mov ax, 0x2523 ; set int vector 23h
mov dx, BREAK_HANDLER
int 0x21
 
; revert stdin/stdout redirections (if any) to their initial state
call REVERT_REDIR_IF_ANY
 
; redirect stdin and/or stdout if required
call REDIR_INOUTFILE_IF_REQUIRED
 
; should I executed command.com or a pre-set application?
or [EXECPROG], byte 0
jz EXEC_COMMAND_COM
 
; TODO: perhaps I should call the DOS SetPSP function here? But if I do, the
; int 21h, ah=50h call freezes...
;mov ah, 0x50 ; DOS 2+ -- Set PSP
;mov bx, cs
;int 0x21
 
; exec an application preset (by SvarCOM) in the ExecParamRec
mov ax, 0x4B00 ; DOS 2+ - load & execute program
mov dx, EXECPROG ; DS:DX - ASCIZ program name (preset at PSP[already)
mov bx, EXEC_PARAM_REC ; ES:BX - parameter block pointer
int 0x21
mov [cs:EXECPROG], byte 0 ; do not run app again (+DS might have been changed)
 
jmp short skipsig ; enforce valid ds/ss/etc (can be lost after int 21,4b)
 
EXEC_COMMAND_COM:
 
; collect the exit code of previous application
mov ah, 0x4D
int 0x21
mov [LEXCODE], al
 
; zero out the exec param block (14 bytes)
mov al, 0 ; byte to write
mov cx, 14 ; how many times
mov di, EXEC_PARAM_REC ; ES:DI = destination
cld ; stosb must move forward
rep stosb ; repeat cx times
 
; preset the default COMSPEC pointer to ES:DX (ES is already set to DS)
mov dx, COMSPECBOOT
 
; do I have a valid COMSPEC?
or [COMSPECPTR], word 0
jz USEDEFAULTCOMSPEC
; set ES:DX to actual COMSPEC (in env segment)
mov es, [PSP_ENVSEG]
mov dx, [COMSPECPTR]
USEDEFAULTCOMSPEC:
 
; prepare the exec param block
mov ax, [PSP_ENVSEG]
mov [EXEC_PARAM_REC], ax
mov [EXEC_PARAM_REC+2], word CMDTAIL
mov [EXEC_PARAM_REC+4], cs
 
; execute command.com
mov ax, 0x4B00 ; DOS 2+ - load & execute program
push es ;
pop ds ;
;mov dx, COMSPEC ; DS:DX - ASCIZ program name (preset already)
push cs
pop es
mov bx, EXEC_PARAM_REC ; ES:BX - parameter block pointer
int 0x21
 
; if all went well, jump back to start
jnc skipsig
 
; restore DS=CS
mov bx, cs
mov ds, bx
 
; update error string so it contains the error number
add al, '0'
mov [ERRLOAD + 4], al
 
; display error message
mov ah, 0x09
mov dx, ERRLOAD
int 0x21
 
; wait for keypress
mov ah, 0x08
int 0x21
 
; back to program start
jmp skipsig
 
; command.com tail arguments, in PSP format: length byte followed by args and
; terminated with \r) - a single 0x0A byte is passed so SvarCOM knows it is
; called as respawn (as opposed to being invoked as a normal application)
; this allows multiple copies of SvarCOM to stack upon each other.
CMDTAIL db 0x01, 0x0A, 0x0D
 
ERRLOAD db "ERR x, FAILED TO LOAD COMMAND.COM", 13, 10, '$'
 
; variables used to revert stdin/stdout to their initial state
OLD_STDOUT dw 0xffff
OLD_STDIN dw 0xffff
 
 
; ****************************************************************************
; *** ROUTINES ***************************************************************
; ****************************************************************************
 
; ----------------------------------------------------------------------------
; revert stdin/stdout redirections (if any) to their initial state
REVERT_REDIR_IF_ANY:
; is stdout redirected?
mov bx, [OLD_STDOUT]
cmp bx, 0xffff
je STDOUT_DONE
; revert the stdout handle (dst in BX already)
mov cx, 1 ; src handle (1=stdout)
mov ah, 0x46 ; redirect a handle
int 0x21
; close the old handle (still in bx)
mov ah, 0x3e
int 0x21
mov [OLD_STDOUT], word 0xffff ; mark stdout as "not redirected"
STDOUT_DONE:
 
; is stdin redirected?
mov bx, [OLD_STDIN]
cmp bx, 0xffff
je STDIN_DONE
; revert the stdin handle (dst in BX already)
xor cx, cx ; src handle (0=stdin)
mov ah, 0x46 ; redirect a handle
int 0x21
; close the old handle (still in bx)
mov ah, 0x3e
int 0x21
mov [OLD_STDIN], word 0xffff ; mark stdin as "not redirected"
 
; delete stdin file if required
cmp [REDIR_DEL_STDIN], byte 0
je STDIN_DONE
; revert the original file and delete it
mov ah, [REDIR_DEL_STDIN]
mov [REDIR_INFIL], ah
mov ah, 0x41 ; DOS 2+ - delete file pointed at by DS:DX
mov dx, REDIR_INFIL
int 0x21
mov [REDIR_INFIL], byte 0
mov [REDIR_DEL_STDIN], byte 0
 
STDIN_DONE:
 
ret
; ----------------------------------------------------------------------------
 
 
; ----------------------------------------------------------------------------
; redirect stdout if REDIR_OUTFIL points to something
REDIR_INOUTFILE_IF_REQUIRED:
cmp [REDIR_OUTFIL], byte 0
je NO_STDOUT_REDIR
mov si, REDIR_OUTFIL ; si = output file
mov ax, 0x6c00 ; Extended Open/Create
mov bx, 1 ; access mode (0=read, 1=write, 2=r+w)
xor cx, cx ; file attribs when(if) file is created (0=normal)
mov dx, [REDIR_OUTAPPEND] ; action if file exist (0x11=open, 0x12=truncate)
int 0x21 ; ax=handle on success (CF clear)
mov [REDIR_OUTFIL], byte 0
jc NO_STDOUT_REDIR ; TODO: abort with an error message instead
 
; jump to end of file if flag was 0x11 (required for >> redirections)
cmp [REDIR_OUTAPPEND], word 0x11
jne SKIP_JMPEOF
mov bx, ax
mov ax, 0x4202 ; jump to position EOF - CX:DX in handle BX
xor cx, cx
xor dx, dx
int 0x21
mov ax, bx ; put my handle back in ax, as expected by later code
SKIP_JMPEOF:
 
; duplicate current stdout so I can revert it later
push ax ; save my file handle in stack
mov ah, 0x45 ; duplicate file handle BX
mov bx, 1 ; 1 = stdout
int 0x21 ; ax=new (duplicated) file handle
mov [OLD_STDOUT], ax ; save the old handle in memory
 
; redirect stdout to my file
pop bx ; dst handle
mov cx, 1 ; src handle (1=stdout)
mov ah, 0x46 ; "redirect a handle"
int 0x21
 
; close the original file handle, I no longer need it
mov ah, 0x3e ; close a file handle (handle in BX)
int 0x21
NO_STDOUT_REDIR:
 
; *** redirect stdin if REDIR_INFIL points to something ***
cmp [REDIR_INFIL], byte 0
je NO_STDIN_REDIR
mov dx, REDIR_INFIL ; dx:dx = file
mov ax, 0x3d00 ; open file for read
int 0x21 ; ax=handle on success (CF clear)
mov [REDIR_INFIL], byte 0
jc NO_STDIN_REDIR ; TODO: abort with an error message instead
 
; duplicate current stdin so I can revert it later
push ax ; save my file handle in stack
mov ah, 0x45 ; duplicate file handle BX
xor bx, bx ; 0=stdin
int 0x21 ; ax=new (duplicated) file handle
mov [OLD_STDIN], ax ; save the old handle in memory
 
; redirect stdout to my file
pop bx ; dst handle
xor cx, cx ; src handle (0=stdin)
mov ah, 0x46 ; "redirect a handle"
int 0x21
 
; close the original file handle, I no longer need it
mov ah, 0x3e ; close a file handle (handle in BX)
int 0x21
NO_STDIN_REDIR:
ret
; ----------------------------------------------------------------------------
/svarcom/tags/svarcom-2022.0/rmodinit.c
0,0 → 1,227
/* This file is part of the SvarCOM project and 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.
*/
 
#include <i86.h>
#include <string.h>
 
#include "env.h"
#include "helpers.h"
 
#include "rmodinit.h"
 
 
/* returns far pointer to rmod's settings block on success */
struct rmod_props far *rmod_install(unsigned short envsize, unsigned char *rmodcore, unsigned short rmodcore_len) {
char far *myptr, far *mcb;
unsigned short far *owner;
const unsigned short sizeof_rmodandprops_paras = (0x100 + rmodcore_len + sizeof(struct rmod_props) + 15) / 16;
unsigned short rmodseg = 0xffff;
unsigned short envseg, origenvseg;
struct rmod_props far *res;
 
/* read my current env segment from PSP and save it */
envseg = *((unsigned short *)0x2c);
origenvseg = envseg;
 
/* printf("original (PSP) env buffer at %04X\r\n", envseg); */
 
/* if envseg is zero, then enforce our own one (MSDOS 5 does not provide a default env) */
if ((envseg == 0) && (envsize == 0)) envsize = 256;
 
/* 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, sizeof_rmodandprops_paras
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(NULL);
}
 
/* copy rmod to its destination, prefixed with a copy of my own PSP */
myptr = MK_FP(rmodseg, 0);
{
unsigned short i;
char *mypsp = (void *)0;
for (i = 0; i < 0x100; i++) myptr[i] = mypsp[i];
}
myptr = MK_FP(rmodseg, 0x100);
_fmemcpy(myptr, rmodcore, rmodcore_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 "owned by rmod" */
mcb = MK_FP(envseg - 1, 0);
owner = (void far *)(mcb + 1);
*owner = rmodseg;
_fmemcpy(mcb + 8, "SVARENV", 8);
 
/* if env block is newly allocated, fill it with a few NULLs */
if (envsize != 0) {
owner = MK_FP(envseg, 0);
owner[0] = 0;
owner[1] = 0;
}
 
/* set CTRL+BREAK handler to rmod */
_asm {
push ax
push dx
push ds
mov ax, 0x2523
mov ds, rmodseg
mov dx, RMOD_OFFSET_BRKHANDLER
int 0x21
pop ds
pop dx
pop ax
}
 
/* prepare result (rmod props) */
res = MK_FP(rmodseg, 0x100 + rmodcore_len);
_fmemset(res, 0, sizeof(*res)); /* zero out */
res->rmodseg = rmodseg; /* rmod segment */
res->inputbuf[0] = 128; /* input buffer for INT 0x21, AH=0Ah*/
res->origenvseg = origenvseg; /* original environment segment */
 
/* write env segment to rmod's PSP */
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
}
 
/* save my original parent in rmod's memory */
res->origparent = *((unsigned long *)0x0a); /* original parent seg:off is at 0x0a of my PSP */
 
/* set the int22 handler in my PSP to rmod so DOS jumps to rmod after I
* terminate and save the original handler in rmod's memory */
{
unsigned short *ptr = (void *)0x0a; /* int22 handler is at 0x0A of the PSP */
ptr[0] = RMOD_OFFSET_ROUTINE;
ptr[1] = rmodseg;
}
 
return(res);
}
 
 
/* look up my parent: if it's rmod then return a ptr to its props struct,
* otherwise return NULL */
struct rmod_props far *rmod_find(unsigned short rmodcore_len) {
unsigned short *parent = (void *)0x0C; /* parent's seg in PSP[Ch] ("prev. int22 handler") */
unsigned short far *ptr;
const unsigned short sig[] = {0x1983, 0x1985, 0x2017, 0x2019};
unsigned char *cmdtail = (void *)0x80;
unsigned char i;
/* is it rmod? */
ptr = MK_FP(*parent, 0x100);
for (i = 0; i < 4; i++) if (ptr[i] != sig[i]) return(NULL);
/* match successfull (rmod is my parent) - but is it really a respawn?
* command-line tail should contain a single character '\r' */
if ((cmdtail[0] != 1) || (cmdtail[1] != '\n')) return(NULL);
cmdtail[0] = 0;
cmdtail[1] = '\r';
/* */
return(MK_FP(*parent, 0x100 + rmodcore_len));
}
 
 
/* 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_val(env_seg, "COMSPEC");
if (comspecfp != NULL) {
*comspecptr = FP_OFF(comspecfp);
} else {
*comspecptr = 0;
}
}
/svarcom/tags/svarcom-2022.0/rmodinit.h
0,0 → 1,64
/* This file is part of the SvarCOM project and 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.
*/
 
#ifndef RMODINIT_H
#define RMODINIT_H
 
#define FLAG_EXEC_AND_QUIT 1
#define FLAG_PERMANENT 2
#define FLAG_ECHOFLAG 4
#define FLAG_ECHO_BEFORE_BAT 8
#define FLAG_SKIP_AUTOEXEC 16
 
struct rmod_props {
char inputbuf[130]; /* input buffer for INT 21, AH=0x0A */
unsigned short rmodseg; /* segment where rmod is loaded */
unsigned long origparent; /* original parent (far ptr) of the shell */
unsigned short origenvseg; /* original environment segment */
unsigned char flags; /* command line parameters */
unsigned char version; /* used to detect mismatch between rmod and SvarCOM */
char batfile[130]; /* truename of batch file being processed */
char batargv[130]; /* args of the batch call (0-separated) */
unsigned long batnextline; /* offset in file of next bat line to process */
char awaitingcmd[130]; /* command to exec next time (if any) */
};
 
#define RMOD_OFFSET_ENVSEG 0x2C /* stored in rmod's PSP */
#define RMOD_OFFSET_COMSPECPTR (0x100 + 0x4A)
#define RMOD_OFFSET_BOOTDRIVE (0x100 + 0x4C)
#define RMOD_OFFSET_LEXITCODE (0x100 + 0x5B)
#define RMOD_OFFSET_EXECPARAM (0x100 + 0x5C)
#define RMOD_OFFSET_EXECPROG (0x100 + 0x6A)
#define RMOD_OFFSET_STDINFILE (0x100 + 0xEA)
#define RMOD_OFFSET_STDOUTFILE (0x100 + 0x16A)
#define RMOD_OFFSET_STDOUTAPP (0x100 + 0x1EA)
#define RMOD_OFFSET_STDIN_DEL (0x100 + 0x1EC)
#define RMOD_OFFSET_BRKHANDLER (0x100 + 0x1ED)
#define RMOD_OFFSET_ROUTINE (0x100 + 0x1EF)
 
struct rmod_props far *rmod_install(unsigned short envsize, unsigned char *rmodcore, unsigned short rmodcore_len);
struct rmod_props far *rmod_find(unsigned short rmodcore_len);
void rmod_updatecomspecptr(unsigned short rmod_seg, unsigned short env_seg);
 
#endif
/svarcom/tags/svarcom-2022.0/sayonara.c
0,0 → 1,63
/* This file is part of the SvarCOM project and 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.
*/
 
#include <i86.h>
 
#include "rmodinit.h"
 
#include "sayonara.h"
 
 
/* rewires my parent pointer, uninstalls rmod let DOS terminate me, UNLESS
* my parent is unknown */
void sayonara(struct rmod_props far *rmod) {
unsigned short rmodseg = rmod->rmodseg;
unsigned long *myparent = (void *)0x0A;
unsigned short far *rmodenv_ptr = MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
unsigned short rmodenv = *rmodenv_ptr;
 
/* detect "I am the origin shell" situations */
if (rmod->flags & FLAG_PERMANENT) return; /* COMMAND.COM /P */
if ((rmod->origparent >> 16) == 0xffff) return; /* original parent seg set to 0xffff (DOS-C / FreeDOS) */
if (rmod->origenvseg == 0) return; /* no original environment (MSDOS 5/6) */
 
/* set my parent back to original value */
*myparent = rmod->origparent;
 
_asm {
/* free RMOD's code segment and env segment */
mov ah, 0x49 /* DOS 2+ -- Free Memory Block */
mov es, [rmodseg]
int 0x21
 
/* free RMOD's env segment */
mov ah, 0x49 /* DOS 2+ -- Free Memory Block */
mov es, [rmodenv]
int 0x21
 
/* gameover */
mov ax, 0x4C00 /* DOS 2+ -- Terminate with exit code 0 */
int 0x21
}
}
/svarcom/tags/svarcom-2022.0/sayonara.h
0,0 → 1,34
/* This file is part of the SvarCOM project and 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.
*/
 
#ifndef SAYONARA_H
#define SAYONARA_H
 
#include "rmodinit.h"
 
/* rewires my parent pointer, uninstalls rmod let DOS terminate me, UNLESS
* my parent is unknown */
void sayonara(struct rmod_props far *rmod);
 
#endif
/svarcom/tags/svarcom-2022.0/svarcom.txt
0,0 → 1,107
 
 
=== 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.
 
The goal is to make SvarCOM the default SvarDOS shell, replacing FreeCOM.
Why replacing FreeCOM, you ask? See FREECOM.TXT for details.
 
SvarCOM is a work-in-progress effort. As such, it still lacks a few things:
- a few internal commands missing: CALL, CTTY, GOTO, LH
- DIR misses two switches: /S, /O
 
SvarCOM is minimalist and I'd like to keep it that way. It aims to be
functionaly equivalent to COMMAND.COM from MS-DOS 5.x/6.x. No LFN support.
 
As of version 2022.0, SvarCOM's resident footprint is under 2 KiB.
 
Translation strings are stored in the file SVARCOM.LNG, which should be
placed in a directory pointed at by %NLSPATH% for SvarCOM to be able to output
messages in non-english languages. SvarCOM's language is controlled by the
%LANG% environment variable. NOTE: at this time translations aren't available
yet, with the proof-of-concept exceptions of the 'PAUSE' and 'SHIFT' commands.
 
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.
 
BREAK - sets or clears extended CTRL+C checking
CD/CHDIR - displays the name of or changes the current directory
CHCP - displays or sets the active code page number
CLS - clears the screen
COPY - copies one or more files to another location
DATE - displays or sets the system date
DEL/ERASE - deletes one or more files
DIR - displays a list of files and subdirectories in a directory
ECHO - displays messages, or turns command-echoing on or off
EXIT - quits the command.com program (command interpreter)
IF - performs conditional processing in batch programs
LN - adds, deletes and displays global executable links
MD/MKDIR - creates a directory
PATH - displays or sets a search path for executable files
PAUSE - suspends processing of a batch program
PROMPT - changes the DOS command prompt
REM - records comments (remarks) in a batch file or CONFIG.SYS
REN/RENAME - renames a file or files
RMDIR - removes (deletes) a directory
SET - displays, sets or removes DOS environment variables
SHIFT - changes the position of arguments in a batch file
TIME - displays or sets the system time
TYPE - displays the contents of a text file
VER - displays the DOS version
VERIFY - tells DOS whether to verify that files are written correctly
VOL - displays the disk volume label and serial number
 
 
=== INSTALLATION =============================================================
 
Installing SvarCOM requires to either copy it to the root of your boot drive
replacing your current COMMAND.COM, or adding a SHELL directive to your
CONFIG.SYS file to instruct DOS that it should load SvarCOM as its shell, eg.:
 
SHELL=C:\SVARCOM\COMMAND.COM
 
Some DOSes support a SHELLHIGH directive that loads the shell in high memory.
It is pointless to use it to load SvarCOM. SvarCOM will load its resident part
as high as possible on its own.
 
SvarCOM supports multiple languages. To enable SvarCOM using them it is
necessary to define an NLSPATH environment variable that would point to a
directory and copy the SVARCOM.LNG file there. Once done, SvarCOM will try
loading whatever language is being set up in the LANG environment variable.
 
 
=== LICENSE ==================================================================
 
SvarCOM is published under the terms of the MIT license.
 
Copyright (C) 2021-2022 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 ===
/svarcom/tags/svarcom-2022.0/tlumacz/lang/english.txt
0,0 → 1,60
#
# SvarCOM translation file
# Lang: ENGLISH
#
# Translator: Mateusz Viste
#
 
# GENERIC MESSAGES USED BY MULTIPLE INTERNAL COMMANDS
0.1:Invalid syntax
0.2:Invalid switch
0.3:Invalid parameter format
0.4:Too many parameters
 
# PAUSE
15.0:Suspends the execution of a batch script
15.1:Press any key to continue...
 
# SHIFT
16.0:Changes the position of arguments in a batch file:
16.1:Argument %1 becomes %0, argument %2 becomes %1, etc.
 
# DOS ERRORS
255.1:Function number invalid
255.2:File not found
255.3:Path not found
255.4:Too many open files (no handles available)
255.5:Access denied
255.6:Invalid handle
255.7:Memory control block destroyed
255.8:Insufficient memory
255.9:Memory block address invalid
255.10:Environment invalid
255.11:Format invalid
255.12:Access code invalid
255.13:Data invalid
255.15:Invalid drive
255.16:Attempted to remove current directory
255.17:Not same device
255.18:No more files
255.19:Disk write-protected
255.20:Unknown unit
255.21:Drive not ready
255.22:Unknown command
255.23:Data error (CRC)
255.24:Bad request structure length
255.25:Seek error
255.26:Unknown media type (non-DOS disk)
255.27:Sector not found
255.28:Printer out of paper
255.29:Write fault
255.30:Read fault
255.31:General failure
255.32:Sharing violation
255.33:Lock violation
255.34:Disk change invalid
255.35:FCB unavailable
255.36:Sharing buffer overflow
255.37:Code page mismatch
255.38:Cannot complete file operations (EOF / out of input)
255.39:Insufficient disk space
/svarcom/tags/svarcom-2022.0/tlumacz/lang/french.txt
0,0 → 1,14
#
# SvarCOM translation file
# Lang: FRENCH
#
# Translator: Mateusz Viste
#
 
# PAUSE
15.0:Suspends l'exécution d'un programme batch
15.1:Appuyez sur une touche pour continuer...
 
# SHIFT
16.0:Change la position des arguments dans un fichier batch:
16.1:L'argument %1 devient %0, l'argument %2 devient %1, etc.
/svarcom/tags/svarcom-2022.0/tlumacz/lang/polish.txt
0,0 → 1,54
#
# SvarCOM translation file
# Lang: POLISH
#
# Translator: Mateusz Viste
#
 
# PAUSE
15.0:Wstrzymuje przetwarzanie pliku wsadowego
15.1:Naciśnij dowolny klawisz aby kontynuować...
 
# SHIFT
16.0:Zmienia pozycję argumentów w pliku wsadowym:
16.1:Argument %1 staje się %0, argument %2 staje się %1, itd.
 
# DOS ERRORS
255.1:Błędny numer funkcji
255.2:Nie znaleziono pliku
255.3:Nie znaleziono ścieżki
255.4:Zbyt wiele otwartych plików (brak dostępnych uchwytów)
255.5:Brak dostępu
255.6:Nieprawidłowy uchwyt
255.7:Zniszczony blok kontroli pamięci
255.8:Niewystarczająca pamięć
255.9:Nieprawidłowy adres bloku pamięci
255.10:Nieprawidłowe środowisko
255.11:Nieprawidłowy format
255.12:Nieprawidłowy kod dostepu
255.13:Nieprawidłowe dane
255.15:Nieprawidłowy napęd
255.16:Dokonano próby usunięcia bieżącego katalogu
255.17:Nie to samo urządzenie
255.18:Brak dalszych plików
255.19:Dysk chroniony przed zapisem
255.20:Nieznana jednostka
255.21:Napęd nie jest gotowy
255.22:Nieznane polecenie
255.23:Błąd danych (CRC)
255.24:Nieprawidłowa długość struktury zapytania
255.25:Błąd wyszukiwania
255.26:Nieznany typ nośnika (dysk niezgodny z DOS)
255.27:Nie znaleziono sektora
255.28:Brak papieru w drukarce
255.29:Błąd zapisu
255.30:Błąd odczytu
255.31:Ogólna awaria
255.32:Naruszenie zasad współdzielenia
255.33:Naruszenie blokady
255.34:Nieprawidłowa zmiana dysku
255.35:Niedostępne FCB
255.36:Przepełnienie bufora udostępniania
255.37:Niezgodność strony kodowej
255.38:Nie można ukończyć operacji na pliku (EOF / brak danych wejściowych)
255.39:Za mało miejsca na dysku
/svarcom/tags/svarcom-2022.0/tlumacz/makefile
0,0 → 1,14
#
# make instructions to build tlumacz.exe with OpenWatcom
# Copyright (C) 2021 Mateusz Viste
#
 
all: tlumacz.exe
 
tlumacz.exe: tlumacz.c
wcl -0 -y -cc -wx -mc -lr -we -ox tlumacz.c
del *.obj
 
clean: .symbolic
del *.exe
del *.obj
/svarcom/tags/svarcom-2022.0/tlumacz/nlsgen.bat
0,0 → 1,17
@ECHO OFF
 
MKDIR NLS
 
REM copy english translations as-is
COPY LANG\ENGLISH.TXT NLS\SVARCOM.EN
 
REM GENERATE CODEPAGE-SPECIFIG VERSIONS OUT OF UTF-8 FILES
utf8tocp 850 LANG\FRENCH.TXT > NLS\SVARCOM.FR
utf8tocp maz LANG\POLISH.TXT > NLS\SVARCOM.PL
 
REM COMPILE ALL RESOURCES TOGETHER
tlumacz en fr pl
 
REM CLEAN UP
DEL NLS\SVARCOM.*
RMDIR NLS
/svarcom/tags/svarcom-2022.0/tlumacz/tlumacz.c
0,0 → 1,269
/*
* Copyright (C) 2021 Mateusz Viste
*
* usage: tlumacz en fr pl etc
*
* computes a svarcom.lng file that contains all language ressources found
* inside dirname.
*
* DAT format:
*
* 4-bytes signature:
* "SvL\x1b"
*
* Then "LANG BLOCKS" follow. Each LANG BLOCK is prefixed with 4 bytes:
* II LL - II is the LANG identifier ("EN", "PL", etc) and LL is the size
* of the block (65535 bytes max).
*
* Inside a LANG BLOCK is a set of strings:
*
* II L S where II is the string's 16-bit identifier, L is its length (1-255)
* and S is the actual string. All strings are ASCIIZ-formatted (ie.
* end with a NULL terminator).
*
* The list of strings ends with a single 0-long string.
*/
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
 
 
struct bitmap {
unsigned char bits[8192];
};
 
static void bitmap_set(struct bitmap *b, unsigned short id) {
b->bits[id / 8] |= 1 << (id & 7);
}
 
static int bitmap_get(const struct bitmap *b, unsigned short id) {
return(b->bits[id / 8] & (1 << (id & 7)));
}
 
static void bitmap_init(struct bitmap *b) {
memset(b, 0, sizeof(struct bitmap));
}
 
 
 
/* read a single line from fd and fills it into dst, returns line length
* ending CR/LF is trimmed, as well as any trailing spaces */
static unsigned short readl(char *dst, size_t dstsz, FILE *fd) {
unsigned short l, lastnonspace = 0;
 
if (fgets(dst, dstsz, fd) == NULL) return(0xffff); /* EOF */
/* trim at first CR or LF and return len */
for (l = 0; (dst[l] != 0) && (dst[l] != '\r') && (dst[l] != '\n'); l++) {
if (dst[l] != ' ') lastnonspace = l;
}
 
if (lastnonspace < l) l = lastnonspace + 1; /* rtrim */
dst[l] = 0;
 
return(l);
}
 
 
/* parse a line in format "1.50:somestring". fills id and returns a pointer to
* the actual string part on success, or NULL on error */
static char *parseline(unsigned short *id, char *s) {
int i;
int dotpos = 0, colpos = 0, gotdigits = 0;
 
/* I must have a . and a : in the first 9 bytes */
for (i = 0;; i++) {
if (s[i] == '.') {
if ((dotpos != 0) || (gotdigits == 0)) break;
dotpos = i;
gotdigits = 0;
} else if (s[i] == ':') {
if (gotdigits != 0) colpos = i;
break;
} else if ((s[i] < '0') || (s[i] > '9')) {
break;
}
gotdigits++;
}
/* did I collect everything? */
if ((dotpos == 0) || (colpos == 0)) return(NULL);
if (s[colpos + 1] == 0) return(NULL);
 
*id = atoi(s);
*id <<= 8;
*id |= atoi(s + dotpos + 1);
 
/* printf("parseline(): %04X = '%s'\r\n", *id, s + colpos + 1); */
 
return(s + colpos + 1);
}
 
 
/* opens a CATS-style file and compiles it into a ressources lang block */
static unsigned short gen_langstrings(unsigned char *buff, const char *langid, struct bitmap *b, const struct bitmap *refb, const unsigned char *refblock) {
unsigned short len = 0, linelen;
FILE *fd;
char fname[] = "NLS\\SVARCOM.XX";
char linebuf[512];
char *ptr;
unsigned short id, linecount;
 
bitmap_init(b);
 
strcpy(fname + strlen(fname) - 2, langid);
 
fd = fopen(fname, "rb");
if (fd == NULL) {
printf("ERROR: FAILED TO OPEN '%s'\r\n", fname);
return(0);
}
 
for (linecount = 1;; linecount++) {
 
linelen = readl(linebuf, sizeof(linebuf), fd);
if (linelen == 0xffff) break; /* EOF */
if ((linelen == 0) || (linebuf[0] == '#')) continue;
 
/* read id and get ptr to actual string ("1.15:string") */
ptr = parseline(&id, linebuf);
if (ptr == NULL) {
printf("ERROR: line #%u of %s is malformed\r\n", linecount, fname);
len = 0;
break;
}
 
/* write string into block (II L S) */
memcpy(buff + len, &id, 2);
len += 2;
buff[len++] = strlen(ptr) + 1;
memcpy(buff + len, ptr, strlen(ptr) + 1);
len += strlen(ptr) + 1;
 
/* if reference bitmap provided: check that the id is valid */
if ((refb != NULL) && (bitmap_get(refb, id) == 0)) {
printf("WARNING: %s[#%u] has an invalid id (%u.%u not present in ref lang)\r\n", fname, linecount, id >> 8, id & 0xff);
}
 
/* make sure this id is not already present */
if (bitmap_get(b, id) == 0) {
/* set bit in bitmap to remember I have this string */
bitmap_set(b, id);
} else {
printf("WARNING: %s[#%u] has a duplicated id (%u.%u)\r\n", fname, linecount, id >> 8, id & 0xff);
}
}
 
fclose(fd);
 
/* if refblock provided, pull missing strings from it */
if (refblock != NULL) {
for (;;) {
id = *((unsigned short *)refblock);
if ((id == 0) && (refblock[2] == 0)) break;
if (bitmap_get(b, id) == 0) {
printf("WARNING: %s is missing string %u.%u (pulled from ref lang)\r\n", fname, id >> 8, id & 0xff);
/* copy missing string from refblock */
memcpy(buff + len, refblock, refblock[2] + 3);
len += refblock[2] + 3;
}
refblock += refblock[2] + 3;
}
}
 
/* write the block terminator (0-long string) */
buff[len++] = 0; /* id */
buff[len++] = 0; /* id */
buff[len++] = 0; /* len */
 
return(len);
}
 
 
#define MEMBLOCKSZ 65500
 
int main(int argc, char **argv) {
FILE *fd;
int ecode = 0;
char *buff, *refblock;
static struct bitmap bufbitmap;
static struct bitmap refbitmap;
unsigned short i;
 
if (argc < 2) {
puts("usage: tlumacz en fr pl etc");
return(1);
}
 
buff = malloc(MEMBLOCKSZ);
refblock = malloc(MEMBLOCKSZ);
if ((buff == NULL) || (refblock == NULL)) {
puts("out of memory");
return(1);
}
 
fd = fopen("svarcom.lng", "wb");
if (fd == NULL) {
puts("ERR: failed to open or create SVARCOM.LNG");
return(1);
}
 
/* write sig */
fwrite("SvL\x1b", 1, 4, fd);
 
/* write lang blocks */
for (i = 1; i < argc; i++) {
unsigned short sz;
char id[3];
 
if (strlen(argv[i]) != 2) {
printf("INVALID LANG SPECIFIED: %s\r\n", argv[i]);
ecode = 1;
break;
}
 
id[0] = argv[i][0];
id[1] = argv[i][1];
id[2] = 0;
if (id[0] >= 'a') id[0] -= 'a' - 'A';
if (id[1] >= 'a') id[1] -= 'a' - 'A';
 
sz = gen_langstrings(buff, id, &bufbitmap, (i != 1)?&refbitmap:NULL, (i != 1)?refblock:NULL);
if (sz == 0) {
printf("ERROR COMPUTING LANG '%s'\r\n", id);
ecode = 1;
break;
} else {
printf("computed %s lang block of %u bytes\r\n", id, sz);
}
/* write lang ID to file, followed by block size and then the actual block */
if ((fwrite(id, 1, 2, fd) != 2) ||
(fwrite(&sz, 1, 2, fd) != 2) ||
(fwrite(buff, 1, sz, fd) != sz)) {
printf("ERROR WRITING TO OUTPUT FILE\r\n");
ecode = 1;
break;
}
/* compute the default block for reference language */
if (i == 1) {
FILE *fd2;
fd2 = fopen("DEFAULT.LNG", "wb");
if (fd2 == NULL) {
puts("ERROR: FAILED TO OPEN OR CREATE DEFAULT.LNG");
break;
}
fwrite(id, 1, 2, fd2); /* lang block id */
fwrite(&sz, 1, 2, fd2); /* lang block size */
fwrite(buff, 1, sz, fd2); /* langblock content (strings) */
fclose(fd2);
/* remember reference data for other languages */
memcpy(refblock, buff, MEMBLOCKSZ);
memcpy(&refbitmap, &bufbitmap, sizeof(struct bitmap));
}
}
 
fclose(fd);
 
return(ecode);
}
/svarcom/tags/svarcom-2022.0/tlumacz/tlumacz.txt
0,0 → 1,14
tlumacz is a "compiler" that builds the SvarCOM multi-lang ressource file out
of a collection of CATS/Kitten text files.
 
usage: tlumacz en fr pl ...
 
tlumacz generates a SVARCOM.LNG file that contains all language ressources.
This file must be then placed in the %NLSPATH% directory so SvarCOM can find it.
SvarCOM's language is controlled by the LANG variable (eg. "SET LANG=PL").
 
the first language acts as a reference, its strings will be used to fill any
missing translations in other files.
 
a DEFAULT.LNG file is also generated, it contains only translation strings for
the reference language. This file is only useful for building SvarCOM.
/svarcom/tags/svarcom-2022.0/todo.txt
0,0 → 1,42
 
=== SVARCOM TODO LIST ===
 
 
This is a loose list of things that I'd like to implement within SvarCOM.
 
 
=== HIGH PRIORITY ============================================================
 
int 24h handler (abort, retry, fail, ignore)
advanced batch constructs: CALL, FOR, GOTO
IF EXIST on an empty drive should not lead to the 'Abort, Retry, Fail' prompt
 
 
=== MEDIUM PRIORITY ==========================================================
 
provide unopened FCBs to launched applications
complete EN translation strings
translations should be cached in rmod-owned memory
if translations reloading fails, do not retry after every command
DIR /O
DIR /S (must use multiple DTAs recursively)
prompt $t, $d, $v
 
 
=== NICE TO HAVE =============================================================
 
respawn COMMAND.COM entirely from memory if it survived last application
DIR %DIRCMD% lookup
COPY /V
COPY /Y
COPY ASCII mode support?
"notify pending command" support (INT 2F,AX=AE00)
INT 2F,AX=5500 support
COMSPEC self-setting does not work under MS-DOS 5/6 (no exepath in env block)
compression of NLS strings and rmod core (lz4?)
dynamic resizing of environment space
CTTY
LOADHIGH/LH
 
 
====================================================================== EOF ===
/svarcom/tags/svarcom-2022.0/toys/erlev.c
0,0 → 1,7
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char **argv) {
int d = atoi(argv[1]);
return(d);
}
/svarcom/tags/svarcom-2022.0/toys/hackz.c
0,0 → 1,41
/*
* reads stdin and writes to stdout after hacker-like conversion
*/
 
#include <stdio.h>
 
int main(void) {
int c;
 
while ((c = getchar()) != EOF) {
switch (c) {
case 'o':
case 'O':
c = '0';
break;
case 'i':
case 'I':
c = '1';
break;
case 'A':
c = '4';
break;
case 'a':
c = '@';
break;
case 'S':
c = '$';
break;
case 'e':
case 'E':
c = '3';
break;
case '0':
c = 'o';
break;
}
putchar(c);
}
 
return(0);
}
/svarcom/tags/svarcom-2022.0/toys/makefile
0,0 → 1,13
 
CFLAGS = -0 -mt -wx -lr -we -ox
 
all: erlev.com upcase.com hackz.com
 
erlev.com: erlev.c
*wcl $(CFLAGS) $<
 
upcase.com: upcase.c
*wcl $(CFLAGS) $<
 
hackz.com: hackz.c
*wcl $(CFLAGS) $<
/svarcom/tags/svarcom-2022.0/toys/readme.txt
0,0 → 1,2
This directory contains tiny applications that are overall not very useful.
Their job is only to help me test SvarCOM.
/svarcom/tags/svarcom-2022.0/toys/upcase.c
0,0 → 1,16
/*
* reads stdin and writes to stdout after upcasing
*/
 
#include <stdio.h>
 
int main(void) {
int c;
 
while ((c = getchar()) != EOF) {
if ((c >= 'a') && (c <= 'z')) c -= ('a' - 'A');
putchar(c);
}
 
return(0);
}