Add function-level comments to the shared helper routines in tools.c. Document the purpose and behavior of the common DOS compatibility, keyboard, paging, memory, string, environment, current directory handle, path formatting, wildcard and endian/NCP22 helper functions. No behavior change.
1037 lines
22 KiB
C
1037 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_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);
|
|
}
|