//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 |