Move the common parent path extraction helper into tools.c and declare it in net.h. Replace the local implementations in RIGHTS, NDIR and NWTESTS with tool_parent_path(). The helper keeps the existing behavior of uppercasing the path and preserving a trailing volume colon for parent paths such as SYS:. No behavior change.
1068 lines
22 KiB
C
1068 lines
22 KiB
C
/*
|
|
* mars-nwe-dosutils - NetWare/DOS utility tools.
|
|
*
|
|
* Copyright (C) 2026 Mario Fetka
|
|
* Copyright (C) 1993,1996 Martin Stover, Marburg, Germany
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Purpose: Shared utility functions used by the DOS command implementations.
|
|
* Depends on: net.h and the command modules that call common parsing, path, memory and terminal helpers.
|
|
*/
|
|
|
|
#include "net.h"
|
|
|
|
/*
|
|
* tool_put_word_lh
|
|
*
|
|
* Purpose:
|
|
* Stores a 16-bit value in NetWare low-high (little-endian) order.
|
|
*/
|
|
void tool_put_word_lh(uint8 *p, uint16 v)
|
|
{
|
|
p[0] = (uint8)(v & 0xff);
|
|
p[1] = (uint8)((v >> 8) & 0xff);
|
|
}
|
|
|
|
/*
|
|
* tool_put_dword_lh
|
|
*
|
|
* Purpose:
|
|
* Stores a 32-bit value in NetWare low-high (little-endian) order.
|
|
*/
|
|
void tool_put_dword_lh(uint8 *p, uint32 v)
|
|
{
|
|
p[0] = (uint8)(v & 0xff);
|
|
p[1] = (uint8)((v >> 8) & 0xff);
|
|
p[2] = (uint8)((v >> 16) & 0xff);
|
|
p[3] = (uint8)((v >> 24) & 0xff);
|
|
}
|
|
|
|
/*
|
|
* tool_get_word_lh
|
|
*
|
|
* Purpose:
|
|
* Reads a 16-bit value stored in NetWare low-high order.
|
|
*/
|
|
uint16 tool_get_word_lh(uint8 *p)
|
|
{
|
|
return((uint16)p[0] | ((uint16)p[1] << 8));
|
|
}
|
|
|
|
/*
|
|
* tool_get_dword_lh
|
|
*
|
|
* Purpose:
|
|
* Reads a 32-bit value stored in NetWare low-high order.
|
|
*/
|
|
uint32 tool_get_dword_lh(uint8 *p)
|
|
{
|
|
return((uint32)p[0] |
|
|
((uint32)p[1] << 8) |
|
|
((uint32)p[2] << 16) |
|
|
((uint32)p[3] << 24));
|
|
}
|
|
|
|
/*
|
|
* tool_put_word_hl
|
|
*
|
|
* Purpose:
|
|
* Stores a 16-bit value in NetWare high-low (big-endian) order.
|
|
*/
|
|
void tool_put_word_hl(uint8 *p, uint16 v)
|
|
{
|
|
p[0] = (uint8)((v >> 8) & 0xff);
|
|
p[1] = (uint8)(v & 0xff);
|
|
}
|
|
|
|
/*
|
|
* tool_put_dword_hl
|
|
*
|
|
* Purpose:
|
|
* Stores a 32-bit value in NetWare high-low (big-endian) order.
|
|
*/
|
|
void tool_put_dword_hl(uint8 *p, uint32 v)
|
|
{
|
|
p[0] = (uint8)((v >> 24) & 0xff);
|
|
p[1] = (uint8)((v >> 16) & 0xff);
|
|
p[2] = (uint8)((v >> 8) & 0xff);
|
|
p[3] = (uint8)(v & 0xff);
|
|
}
|
|
|
|
/*
|
|
* tool_get_word_hl
|
|
*
|
|
* Purpose:
|
|
* Reads a 16-bit value stored in NetWare high-low order.
|
|
*/
|
|
uint16 tool_get_word_hl(uint8 *p)
|
|
{
|
|
return(((uint16)p[0] << 8) | (uint16)p[1]);
|
|
}
|
|
|
|
/*
|
|
* tool_get_dword_hl
|
|
*
|
|
* Purpose:
|
|
* Reads a 32-bit value stored in NetWare high-low order.
|
|
*/
|
|
uint32 tool_get_dword_hl(uint8 *p)
|
|
{
|
|
return(((uint32)p[0] << 24) |
|
|
((uint32)p[1] << 16) |
|
|
((uint32)p[2] << 8) |
|
|
(uint32)p[3]);
|
|
}
|
|
|
|
/*
|
|
* tool_copy_ncp22_name
|
|
*
|
|
* Purpose:
|
|
* Converts one DOS filename component into the uppercase NCP22 name format.
|
|
* The helper rejects paths and keeps the historical 12-byte DOS namespace
|
|
* component limit used by the old directory-entry NCPs.
|
|
*/
|
|
int tool_copy_ncp22_name(uint8 *dst, char *src, uint8 *len_out)
|
|
{
|
|
char tmp[260];
|
|
int len;
|
|
|
|
if (!dst || !len_out)
|
|
return(-1);
|
|
if (!src)
|
|
src = "";
|
|
|
|
tool_upcopy(tmp, src, sizeof(tmp));
|
|
if (strchr(tmp, '\\') || strchr(tmp, '/') || strchr(tmp, ':'))
|
|
return(-1);
|
|
|
|
len = strlen(tmp);
|
|
if (len < 1 || len > 12)
|
|
return(-1);
|
|
|
|
memcpy(dst, tmp, len);
|
|
*len_out = (uint8)len;
|
|
return(0);
|
|
}
|
|
|
|
#ifdef __WATCOMC__
|
|
/*
|
|
* getcurdir
|
|
*
|
|
* Purpose:
|
|
* Borland-compatible wrapper for DOS INT 21h AH=47h. It returns the
|
|
* current directory for the selected drive without the drive letter.
|
|
*
|
|
* Notes:
|
|
* Open Watcom does not provide getcurdir() under the Borland name used by
|
|
* the historical MARS-NWE DOS tools.
|
|
*/
|
|
int getcurdir(int drive, char *directory)
|
|
{
|
|
REGS regs;
|
|
SREGS sregs;
|
|
|
|
regs.h.ah = 0x47; /* DOS: get current directory */
|
|
regs.h.dl = (unsigned char)drive; /* 0=current, 1=A:, 2=B:, ... */
|
|
sregs.ds = FP_SEG(directory);
|
|
regs.x.si = FP_OFF(directory);
|
|
intdosx(®s, ®s, &sregs);
|
|
return(regs.x.cflag ? -1 : 0);
|
|
}
|
|
|
|
/*
|
|
* setdisk
|
|
*
|
|
* Purpose:
|
|
* Borland-compatible wrapper for DOS INT 21h AH=0eh. It selects the DOS
|
|
* default drive using the Borland drive numbering convention.
|
|
*/
|
|
void setdisk(int drive)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x0e; /* DOS: select default drive */
|
|
regs.h.dl = (unsigned char)drive; /* 0=A:, 1=B:, ... */
|
|
intdos(®s, ®s);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* key_pressed
|
|
*
|
|
* Purpose:
|
|
* Checks whether a key is waiting in the BIOS keyboard buffer without
|
|
* consuming it.
|
|
*/
|
|
int key_pressed(void)
|
|
{
|
|
#ifdef __WATCOMC__
|
|
/*
|
|
* Open Watcom's WORDREGS does not expose x.flags. The old Borland
|
|
* code checked the BIOS INT 16h ZF bit after AH=01h. Open Watcom's
|
|
* bios.h provides _bios_keybrd(), which wraps the same BIOS keyboard
|
|
* service and returns 0 when no key is waiting.
|
|
*/
|
|
return(_bios_keybrd(_KEYBRD_READY) != 0);
|
|
#else
|
|
REGS regsin, regsout;
|
|
regsin.h.ah = 0x01; /* read key-press */
|
|
int86(0x16, ®sin, ®sout);
|
|
return((regsout.x.flags & 0x40) ? 0 : 1); /* zeroflag != 0 */
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* clear_kb
|
|
*
|
|
* Purpose:
|
|
* Drains all pending keys from the BIOS keyboard buffer.
|
|
*/
|
|
void clear_kb(void)
|
|
{
|
|
#ifdef __WATCOMC__
|
|
while (key_pressed()) {
|
|
(void)_bios_keybrd(_KEYBRD_READ);
|
|
}
|
|
#else
|
|
REGS regsin, regsout;
|
|
while (key_pressed()) { /* zeroflag != 0 */
|
|
regsin.h.ah = 0x00; /* read key-press */
|
|
int86(0x16, ®sin, ®sout);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* ask_user
|
|
*
|
|
* Purpose:
|
|
* Prints a formatted yes/no question and waits until the user answers with
|
|
* Y/J or N.
|
|
*
|
|
* Returns:
|
|
* 1 for yes, 0 for no.
|
|
*/
|
|
int ask_user(char *p, ...)
|
|
{
|
|
int key;
|
|
int flag = 0;
|
|
va_list argptr;
|
|
va_start(argptr, p);
|
|
vfprintf(stderr, p, argptr);
|
|
va_end(argptr);
|
|
fprintf(stderr, "\n Please answer: Y)es or N)o!");
|
|
while (1) {
|
|
key = getch();
|
|
if (key == 'J' || key == 'j' || key== 'y' || key == 'Y') {
|
|
fprintf(stderr, "Y\n\n");
|
|
flag = 1;
|
|
break;
|
|
}
|
|
if (key == 'N' || key == 'n') {
|
|
fprintf(stderr, "N\n\n");
|
|
flag = 0;
|
|
break;
|
|
}
|
|
}
|
|
clear_kb();
|
|
return(flag);
|
|
}
|
|
|
|
/*
|
|
* xmalloc
|
|
*
|
|
* Purpose:
|
|
* Allocates memory or terminates the utility with a diagnostic.
|
|
*/
|
|
char *xmalloc(uint size)
|
|
{
|
|
char *p = (size) ? (char *)malloc(size) : (char*)NULL;
|
|
if (p == (char *)NULL && size){
|
|
fprintf(stderr, "not enough core, need %d Bytes\n", size);
|
|
exit(1);
|
|
}
|
|
return(p);
|
|
}
|
|
|
|
/*
|
|
* xcmalloc
|
|
*
|
|
* Purpose:
|
|
* Allocates zero-filled memory or terminates the utility with a diagnostic.
|
|
*/
|
|
char *xcmalloc(uint size)
|
|
{
|
|
char *p = xmalloc(size);
|
|
if (size) memset(p, 0, size);
|
|
return(p);
|
|
}
|
|
|
|
/*
|
|
* x_x_xfree
|
|
*
|
|
* Purpose:
|
|
* Frees an allocated pointer and clears the caller's pointer value.
|
|
*/
|
|
void x_x_xfree(char **p)
|
|
{
|
|
if (*p != (char *)NULL){
|
|
free(*p);
|
|
*p = (char*)NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* strmaxcpy
|
|
*
|
|
* Purpose:
|
|
* Copies at most len bytes from source to dest and always terminates dest.
|
|
*
|
|
* Returns:
|
|
* Number of copied source bytes, excluding the terminating zero byte.
|
|
*/
|
|
int strmaxcpy(char *dest, char *source, int len)
|
|
{
|
|
int slen = (source != (char *)NULL) ? min(len, strlen(source)) : 0;
|
|
if (dest == (char *)NULL) return(0);
|
|
if (slen) memcpy(dest, source, slen);
|
|
dest[slen] = '\0';
|
|
return(slen);
|
|
}
|
|
|
|
/*
|
|
* xadd_char
|
|
*
|
|
* Purpose:
|
|
* Appends one character to a bounded C string and keeps it terminated.
|
|
*/
|
|
char *xadd_char(char *s, int c, int maxlen)
|
|
{
|
|
if (s && maxlen) {
|
|
int namlen = strlen(s);
|
|
if (maxlen > -1 && namlen >= maxlen) namlen=maxlen-1;
|
|
s[namlen++] = c;
|
|
s[namlen] = '\0';
|
|
}
|
|
return(s);
|
|
}
|
|
|
|
/*
|
|
* down_char
|
|
*
|
|
* Purpose:
|
|
* Converts one DOS character to lowercase for the ASCII range and the
|
|
* German OEM characters historically handled by MARS-NWE.
|
|
*/
|
|
static uint8 down_char(uint8 ch)
|
|
{
|
|
if (ch > 64 && ch < 91) return(ch + 32);
|
|
switch(ch){
|
|
case 142: ch = 132; break;
|
|
case 153: ch = 148; break;
|
|
case 154: ch = 129; break;
|
|
default :break;
|
|
}
|
|
return(ch);
|
|
}
|
|
|
|
/*
|
|
* up_char
|
|
*
|
|
* Purpose:
|
|
* Converts one DOS character to uppercase for the ASCII range and the
|
|
* German OEM characters historically handled by MARS-NWE.
|
|
*/
|
|
static uint8 up_char(uint8 ch)
|
|
{
|
|
if (ch > 96 && ch < 123) return(ch - 32);
|
|
switch(ch) {
|
|
case 132: ch = 142; break;
|
|
case 148: ch = 153; break;
|
|
case 129: ch = 154; break;
|
|
default : break;
|
|
}
|
|
return(ch);
|
|
}
|
|
|
|
/*
|
|
* upstr
|
|
*
|
|
* Purpose:
|
|
* Uppercases a DOS string in place.
|
|
*/
|
|
uint8 *upstr(uint8 *s)
|
|
{
|
|
if (!s) return((uint8*)NULL);
|
|
for (;*s;s++) *s=up_char(*s);
|
|
return(s);
|
|
}
|
|
|
|
/*
|
|
* deb
|
|
*
|
|
* Purpose:
|
|
* Removes trailing spaces and tabs from a string in place.
|
|
*/
|
|
void deb(uint8 *s)
|
|
{
|
|
if (!s || !*s) return;
|
|
else {
|
|
uint8 *p = s + strlen(s);
|
|
while (p > s && (*--p==32 || *p==9));;
|
|
if (*p==32 || *p==9) *p='\0';
|
|
else *(p+1) = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* leb
|
|
*
|
|
* Purpose:
|
|
* Historical leading-space helper used by the old tools. It keeps the
|
|
* original behavior and shifts the string to the first whitespace boundary.
|
|
*/
|
|
void leb(uint8 *s)
|
|
{
|
|
if (!s || !*s || (*s != 32 && *s != 9)) return;
|
|
else {
|
|
uint8 *p = s;
|
|
for (;*p && *p!=32 && *p!=9;p++);;
|
|
strcpy(s, p);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* korrpath
|
|
*
|
|
* Purpose:
|
|
* Normalizes a DOS/NetWare path for the older tools by converting backslash
|
|
* separators to slash and lowercasing DOS characters.
|
|
*/
|
|
void korrpath(char *s)
|
|
{
|
|
if (!s) return;
|
|
for (;*s;s++) {
|
|
if (*s=='\\') *s='/';
|
|
else *s=down_char(*s);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get_path_fn
|
|
*
|
|
* Purpose:
|
|
* Splits a historical slash/colon path into directory part and file name.
|
|
*/
|
|
void get_path_fn(char *s, char *p, char *fn)
|
|
{
|
|
int j= strlen(s);
|
|
if (p != (char *)NULL) p[0] = 0;
|
|
if (fn != (char*) NULL) fn[0] = 0;
|
|
if (!j) return;
|
|
if (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0) ) ) {
|
|
if (p != (char *)NULL) {
|
|
strcpy(p, s);
|
|
strcat(p, "/");
|
|
}
|
|
if (fn != (char *)NULL) fn[0] = 0;
|
|
return;
|
|
}
|
|
while (j--){
|
|
if ((s[j] == '/') || (s[j] == ':') ) {
|
|
if (fn != (char *)NULL) strcpy(fn, s+j+1);
|
|
if (p != (char *)NULL) {
|
|
strncpy(p, s, j+1);
|
|
p[j+1] = 0;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (fn != (char *)NULL) strcpy(fn, s); /* no path */
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
uint16 adr1;
|
|
uint16 adr2;
|
|
char reserve[6];
|
|
uint32 ladrs[3];
|
|
uint16 father_psp_seg;
|
|
char handles[20];
|
|
uint16 environ_seg;
|
|
} PROG_PSP;
|
|
|
|
typedef struct {
|
|
uint8 kennung;
|
|
uint16 prozess_seg;
|
|
uint16 blocks;
|
|
} SPEICH_BLOCK;
|
|
|
|
/*
|
|
* getglobenvironment
|
|
*
|
|
* Purpose:
|
|
* Locates the parent DOS environment block through the PSP and reports its
|
|
* allocated and currently used size.
|
|
*/
|
|
static char *getglobenvironment(uint16 *maxsize, uint16 *aktsize)
|
|
{
|
|
static uint16 globmaxenvsize=0;
|
|
static char *globenviron=NULL;
|
|
if (globenviron == (char *) NULL) {
|
|
PROG_PSP *mypsp = MK_FP(_psp, 0);
|
|
PROG_PSP *fatherpsp = MK_FP(mypsp->father_psp_seg, 0);
|
|
SPEICH_BLOCK *spb = MK_FP(fatherpsp->environ_seg-1, 0);
|
|
globenviron = (char *)MK_FP(fatherpsp->environ_seg, 0);
|
|
globmaxenvsize = spb->blocks * 16;
|
|
}
|
|
if (globmaxenvsize){
|
|
char *search = globenviron;
|
|
char *maxsearch = search+globmaxenvsize;
|
|
while (*search && search < maxsearch) {
|
|
int slen=strlen(search);
|
|
search+=(slen+1);
|
|
}
|
|
*aktsize = max(2, (uint16)(search+1 - globenviron));
|
|
} else *aktsize=0;
|
|
*maxsize = globmaxenvsize;
|
|
/*
|
|
printf("globenv=%p maxsize=%d, aktsize=%d\n", globenviron, globmaxenvsize, *aktsize);
|
|
*/
|
|
return(globenviron);
|
|
}
|
|
|
|
/*
|
|
* getglobenv
|
|
*
|
|
* Purpose:
|
|
* Reads one variable from the parent DOS environment block.
|
|
*/
|
|
char *getglobenv(char *option)
|
|
{
|
|
uint16 maxenvsize;
|
|
uint16 aktenvsize;
|
|
char *search = getglobenvironment(&maxenvsize, &aktenvsize);
|
|
int length = (option == NULL) ? 0 : strlen(option);
|
|
if (aktenvsize && length){
|
|
char *maxsearch=search+aktenvsize;
|
|
while (*search && search < maxsearch) {
|
|
int slen=strlen(search);
|
|
if (slen > length && (*(search + length) == '=')
|
|
&& (strncmp(search, option, length) == 0)) {
|
|
/*
|
|
printf("GET GLOB %s=%s\n", option, search+length+1);
|
|
*/
|
|
return(search + length + 1);
|
|
}
|
|
search+=(slen+1);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* putglobenv
|
|
*
|
|
* Purpose:
|
|
* Updates or removes one variable in the parent DOS environment block.
|
|
*/
|
|
int putglobenv(char *option)
|
|
{
|
|
uint16 maxenvsize;
|
|
uint16 aktenvsize;
|
|
char *search = getglobenvironment(&maxenvsize, &aktenvsize);
|
|
int optionlen = (option == NULL) ? 0 : strlen(option);
|
|
/*
|
|
printf("PUT GLOB option=%s\n", option);
|
|
*/
|
|
if (optionlen && maxenvsize){
|
|
int length;
|
|
char *equal;
|
|
for (equal = option; *equal && *equal != '='; equal++);;
|
|
length = (int) (equal - option);
|
|
if (length > 0 && *equal == '='){
|
|
char *maxsearch=search+aktenvsize;
|
|
while (*search && search < maxsearch) {
|
|
int slen = strlen(search);
|
|
char *nextp = search+slen+1;
|
|
if (slen > length && (*(search + length) == '=')
|
|
&& (strncmp(search, option, length) == 0)) { /* gefunden */
|
|
int diffsize = optionlen-slen;
|
|
if (diffsize){
|
|
int movesize = (int)(maxsearch - nextp);
|
|
if (diffsize > (int)(maxenvsize - aktenvsize))
|
|
return(-1); /* Kein Platz mehr */
|
|
if (!*(equal+1)) diffsize -= (length+2);
|
|
xmemmove(nextp+diffsize, nextp, movesize);
|
|
}
|
|
if (*(equal+1)) strcpy(search, option);
|
|
return(0);
|
|
}
|
|
search=nextp;
|
|
}
|
|
/* nicht gefunden , nun eintragen, falls m�glich */
|
|
if (*(equal+1) && optionlen < maxenvsize - aktenvsize) {
|
|
strcpy(search, option);
|
|
*(search+optionlen+1) = '\0'; /* letzter Eintrag '\0' nicht vergessen */
|
|
return(0);
|
|
} else return(-1);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* tool_page_line
|
|
*
|
|
* Purpose:
|
|
* Implements Novell-style paged output prompting after 24 printed lines.
|
|
*/
|
|
int tool_page_line(int *line_count, int *continuous)
|
|
{
|
|
int ch;
|
|
|
|
if (!line_count || !continuous)
|
|
return(0);
|
|
|
|
if (*continuous)
|
|
return(0);
|
|
|
|
(*line_count)++;
|
|
if (*line_count < 24)
|
|
return(0);
|
|
|
|
fprintf(stdout, "Press any key to continue ('C' for continue)");
|
|
fflush(stdout);
|
|
ch = getch();
|
|
fprintf(stdout, "\n");
|
|
|
|
if (ch == 'c' || ch == 'C')
|
|
*continuous = 1;
|
|
|
|
*line_count = 0;
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
* Shared helpers for the newer DOS utility frontends.
|
|
*
|
|
* These deliberately stay in tools.c instead of in grant/revoke/remove/
|
|
* rights/flagdir so the current multicall binary and possible future
|
|
* grouped multicall binaries can reuse the same code without dragging in
|
|
* command-specific modules.
|
|
****************************************************************/
|
|
|
|
/*
|
|
* tool_strsame
|
|
*
|
|
* Purpose:
|
|
* Compares two command-line strings case-insensitively for ASCII letters.
|
|
*/
|
|
int tool_strsame(char *a, char *b)
|
|
{
|
|
if (!a) a = "";
|
|
if (!b) b = "";
|
|
|
|
while (*a || *b) {
|
|
int ca = *a++;
|
|
int cb = *b++;
|
|
if (ca >= 'a' && ca <= 'z') ca -= 32;
|
|
if (cb >= 'a' && cb <= 'z') cb -= 32;
|
|
if (ca != cb) return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* tool_is_help_arg
|
|
*
|
|
* Purpose:
|
|
* Recognizes the common help arguments used by the DOS utilities.
|
|
*/
|
|
int tool_is_help_arg(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(tool_strsame(s, "/?") || tool_strsame(s, "-?") ||
|
|
tool_strsame(s, "?"));
|
|
}
|
|
|
|
/*
|
|
* tool_is_option
|
|
*
|
|
* Purpose:
|
|
* Checks whether an argument is a DOS-style option beginning with / or -.
|
|
*/
|
|
int tool_is_option(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(s[0] == '/' || s[0] == '-');
|
|
}
|
|
|
|
/*
|
|
* tool_is_files_option
|
|
*
|
|
* Purpose:
|
|
* Recognizes the /FILES option and its short form.
|
|
*/
|
|
int tool_is_files_option(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(tool_strsame(s, "/FILES") || tool_strsame(s, "-FILES") ||
|
|
tool_strsame(s, "/F") || tool_strsame(s, "-F"));
|
|
}
|
|
|
|
/*
|
|
* tool_is_subdirs_option
|
|
*
|
|
* Purpose:
|
|
* Recognizes the /SUBDIRS, /SUBDIRECTORIES and /S recursive options.
|
|
*/
|
|
int tool_is_subdirs_option(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(tool_strsame(s, "/SUBDIRS") || tool_strsame(s, "-SUBDIRS") ||
|
|
tool_strsame(s, "/SUBDIRECTORIES") ||
|
|
tool_strsame(s, "-SUBDIRECTORIES") ||
|
|
tool_strsame(s, "/S") || tool_strsame(s, "-S"));
|
|
}
|
|
|
|
/*
|
|
* tool_get_current_drive
|
|
*
|
|
* Purpose:
|
|
* Returns the DOS default drive using INT 21h AH=19h.
|
|
*/
|
|
int tool_get_current_drive(void)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return((int)regs.h.al);
|
|
}
|
|
|
|
/*
|
|
* tool_current_dhandle
|
|
*
|
|
* Purpose:
|
|
* Resolves the current DOS drive to the active NetWare connection and
|
|
* directory handle.
|
|
*/
|
|
int tool_current_dhandle(uint8 *connid, uint8 *dhandle)
|
|
{
|
|
uint8 flags = 0;
|
|
int drive = tool_get_current_drive();
|
|
|
|
if (get_drive_info((uint8)drive, connid, dhandle, &flags))
|
|
return(-1);
|
|
|
|
if (!*connid || (flags & 0x80))
|
|
return(-1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* tool_current_dhandle_only
|
|
*
|
|
* Purpose:
|
|
* Convenience wrapper for callers that only need the current directory
|
|
* handle and do not use the connection id.
|
|
*/
|
|
int tool_current_dhandle_only(uint8 *dhandle)
|
|
{
|
|
uint8 connid = 0;
|
|
|
|
return(tool_current_dhandle(&connid, dhandle));
|
|
}
|
|
|
|
/*
|
|
* tool_current_prefix
|
|
*
|
|
* Purpose:
|
|
* Builds the current SERVER\VOLUME:PATH display prefix used by Novell-like
|
|
* command output.
|
|
*/
|
|
int tool_current_prefix(char *out, int max)
|
|
{
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
uint8 flags = 0;
|
|
int drive;
|
|
char server[52];
|
|
char dpath[260];
|
|
|
|
if (!out || max < 8)
|
|
return(-1);
|
|
|
|
out[0] = '\0';
|
|
|
|
drive = tool_get_current_drive();
|
|
if (get_drive_info((uint8)drive, &connid, &dhandle, &flags))
|
|
return(-1);
|
|
|
|
if (!connid || (flags & 0x80))
|
|
return(-1);
|
|
|
|
server[0] = '\0';
|
|
if (get_fs_name(connid, server))
|
|
server[0] = '\0';
|
|
|
|
dpath[0] = '\0';
|
|
if (get_dir_path(dhandle, dpath) || !dpath[0])
|
|
return(-1);
|
|
|
|
tool_upcopy(dpath, dpath, sizeof(dpath));
|
|
|
|
if (server[0])
|
|
sprintf(out, "%s\\%s", server, dpath);
|
|
else
|
|
sprintf(out, "%s", dpath);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* tool_is_current_path
|
|
*
|
|
* Purpose:
|
|
* Checks whether a path argument refers to the current directory.
|
|
*/
|
|
int tool_is_current_path(char *path)
|
|
{
|
|
if (!path || !*path) return(1);
|
|
if (tool_strsame(path, ".")) return(1);
|
|
if (tool_strsame(path, ".\\")) return(1);
|
|
if (tool_strsame(path, "./")) return(1);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* tool_upcopy
|
|
*
|
|
* Purpose:
|
|
* Copies a path/string while uppercasing ASCII letters and converting / to \.
|
|
*/
|
|
void tool_upcopy(char *dst, char *src, int max)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!src) src = "";
|
|
|
|
while (*src && i < max - 1) {
|
|
char c = *src++;
|
|
if (c == '/') c = '\\';
|
|
if (c >= 'a' && c <= 'z') c -= 32;
|
|
dst[i++] = c;
|
|
}
|
|
dst[i] = 0;
|
|
}
|
|
|
|
/*
|
|
* tool_basename
|
|
*
|
|
* Purpose:
|
|
* Extracts the final DOS/NetWare path component after uppercasing and path
|
|
* separator normalization.
|
|
*/
|
|
void tool_basename(char *dst, char *src, int max)
|
|
{
|
|
char up[260];
|
|
char *p;
|
|
|
|
tool_upcopy(up, src, sizeof(up));
|
|
p = strrchr(up, '\\');
|
|
if (!p) p = strrchr(up, ':');
|
|
|
|
if (p)
|
|
strmaxcpy(dst, p + 1, max - 1);
|
|
else
|
|
strmaxcpy(dst, up, max - 1);
|
|
}
|
|
|
|
/*
|
|
* tool_header_path
|
|
*
|
|
* Purpose:
|
|
* Formats a path for Novell-style command headers relative to the current
|
|
* mapped NetWare prefix.
|
|
*/
|
|
void tool_header_path(char *out, char *path, int max)
|
|
{
|
|
char prefix[260];
|
|
char up[260];
|
|
char *p;
|
|
int len;
|
|
|
|
if (tool_current_prefix(prefix, sizeof(prefix)))
|
|
prefix[0] = '\0';
|
|
|
|
if (tool_is_current_path(path)) {
|
|
strmaxcpy(out, prefix, max - 1);
|
|
return;
|
|
}
|
|
|
|
tool_upcopy(up, path, sizeof(up));
|
|
|
|
/* Convert DOS drive-qualified paths like F:\DIR to a NetWare path
|
|
* relative to the mapped volume, matching Novell's display. */
|
|
p = up;
|
|
if (p[0] && p[1] == ':') {
|
|
p += 2;
|
|
if (*p == '\\') p++;
|
|
}
|
|
while (*p == '\\') p++;
|
|
|
|
strmaxcpy(out, prefix, max - 1);
|
|
len = strlen(out);
|
|
if (*p && len > 0 && out[len - 1] != ':' && out[len - 1] != '\\') {
|
|
if (len < max - 1) {
|
|
out[len++] = '\\';
|
|
out[len] = '\0';
|
|
}
|
|
}
|
|
if ((int)(strlen(out) + strlen(p)) < max - 1)
|
|
strcat(out, p);
|
|
}
|
|
|
|
/*
|
|
* tool_is_dot_dir
|
|
*
|
|
* Purpose:
|
|
* Checks for the special . and .. directory entries.
|
|
*/
|
|
int tool_is_dot_dir(char *name)
|
|
{
|
|
if (!name) return(0);
|
|
if (name[0] == '.' && name[1] == '\0') return(1);
|
|
if (name[0] == '.' && name[1] == '.' && name[2] == '\0') return(1);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* tool_join_path
|
|
*
|
|
* Purpose:
|
|
* Joins a base path and child name with a DOS/NetWare separator when needed.
|
|
*/
|
|
void tool_join_path(char *out, char *base, char *name, int max)
|
|
{
|
|
int len;
|
|
|
|
out[0] = '\0';
|
|
strmaxcpy(out, base, max - 1);
|
|
len = strlen(out);
|
|
|
|
if (len > 0 && out[len - 1] != '\\' && out[len - 1] != '/' &&
|
|
out[len - 1] != ':') {
|
|
if (len < max - 1) {
|
|
out[len++] = '\\';
|
|
out[len] = '\0';
|
|
}
|
|
}
|
|
|
|
if ((int)(strlen(out) + strlen(name)) < max - 1)
|
|
strcat(out, name);
|
|
}
|
|
|
|
/*
|
|
* tool_has_wildcards
|
|
*
|
|
* Purpose:
|
|
* Checks whether a path contains DOS wildcard characters.
|
|
*/
|
|
int tool_has_wildcards(char *path)
|
|
{
|
|
if (!path) return(0);
|
|
while (*path) {
|
|
if (*path == '*' || *path == '?') return(1);
|
|
path++;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* tool_parent_path
|
|
*
|
|
* Purpose:
|
|
* Copies the parent directory part of a DOS/NetWare path. The result is
|
|
* uppercased and keeps a trailing volume colon for paths like SYS:FILE.TXT.
|
|
*/
|
|
void tool_parent_path(char *dst, char *src, int max)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
|
|
tool_upcopy(tmp, src, sizeof(tmp));
|
|
p = strrchr(tmp, '\\');
|
|
if (!p) p = strrchr(tmp, ':');
|
|
|
|
if (!p) {
|
|
dst[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
if (*p == ':') {
|
|
p++;
|
|
*p = '\0';
|
|
} else {
|
|
*p = '\0';
|
|
}
|
|
|
|
strmaxcpy(dst, tmp, max - 1);
|
|
}
|
|
|
|
/*
|
|
* tool_parent_pattern
|
|
*
|
|
* Purpose:
|
|
* Splits a wildcard path into parent directory and search pattern. Missing
|
|
* patterns default to *.* for Novell-style directory scans.
|
|
*/
|
|
void tool_parent_pattern(char *dir, char *pattern, char *path,
|
|
int maxdir, int maxpat)
|
|
{
|
|
char tmp[260];
|
|
char *p;
|
|
|
|
tool_upcopy(tmp, path, sizeof(tmp));
|
|
p = strrchr(tmp, '\\');
|
|
if (!p) p = strrchr(tmp, ':');
|
|
|
|
if (p) {
|
|
if (*p == ':') {
|
|
p++;
|
|
strmaxcpy(pattern, p, maxpat - 1);
|
|
*p = '\0';
|
|
strmaxcpy(dir, tmp, maxdir - 1);
|
|
} else {
|
|
strmaxcpy(pattern, p + 1, maxpat - 1);
|
|
*p = '\0';
|
|
strmaxcpy(dir, tmp, maxdir - 1);
|
|
}
|
|
} else {
|
|
strmaxcpy(dir, ".", maxdir - 1);
|
|
strmaxcpy(pattern, tmp, maxpat - 1);
|
|
}
|
|
|
|
if (!pattern[0])
|
|
strmaxcpy(pattern, "*.*", maxpat - 1);
|
|
}
|