Subversion Repositories SvarDOS

Compare Revisions

Ignore whitespace Rev 570 → Rev 571

/svarcom/trunk/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/trunk/cmd.c
1,7 → 1,7
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021 Mateusz Viste
* 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"),
76,6 → 76,7
#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"
112,6 → 113,7
{"EXIT", cmd_exit},
{"IF", cmd_if},
{"LH", cmd_notimpl},
{"LN", cmd_ln},
{"LOADHIGH",cmd_notimpl},
{"MD", cmd_mkdir},
{"MKDIR", cmd_mkdir},
/svarcom/trunk/command.c
1,7 → 1,7
/* This file is part of the SvarCOM project and is published under the terms
* of the MIT license.
*
* Copyright (C) 2021 Mateusz Viste
* 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"),
306,81 → 306,6
}
 
 
/* tries locating 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 contains a ptr to the extension in fname (NULL if not found) */
static 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);
}
 
 
static void run_as_external(char *buff, const char *cmdline, unsigned short envseg, struct rmod_props far *rmod, struct redir_data *redir) {
char *cmdfile = buff + 512;
const char far *pathptr;
420,10 → 345,9
 
/* try matching something in PATH */
pathptr = env_lookup_val(envseg, "PATH");
if (pathptr == NULL) return;
 
/* try each path in %PATH% */
for (;;) {
while (pathptr) {
for (i = 0;; i++) {
buff[i] = *pathptr;
if ((buff[i] == 0) || (buff[i] == ';')) break;
431,15 → 355,68
}
buff[i] = 0;
lookup = lookup_cmd(cmdfile, cmd, buff, &ext);
if (lookup == 0) break;
if (lookup == 0) goto RUNCMDFILE;
if (lookup == -2) return;
if (*pathptr == ';') {
pathptr++;
} else {
return;
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 */
/svarcom/trunk/helpers.c
754,3 → 754,98
 
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/trunk/helpers.h
185,4 → 185,13
/* 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/trunk/history.txt
3,8 → 3,9
=== SvarCOM's history / changelog ===
 
 
=== ver 2021.1 (xx.xx.2021) ==================================================
=== ver 2022.0 (xx.xx.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
/svarcom/trunk/internal.txt
41,4 → 41,13
the process.
 
 
=== EXECUTABLE LINKS =========================================================
 
SvarCOM features special support for "executable links". This allows to run
selected programs from any directory, but 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/trunk/svarcom.txt
17,7 → 17,7
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 2021.1, SvarCOM's resident footprint is under 2 KiB.
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
44,6 → 44,7
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
64,7 → 65,7
 
SvarCOM is published under the terms of the MIT license.
 
Copyright (C) 2021 Mateusz Viste
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