Add maintainer-only support used by the automated DOS compatibility tests. This introduces the MAINTAINER_BUILD option for the DOS tools. In maintainer builds, LOGIN accepts the hidden /PWD: and /PASSWORD: arguments for automated test relogin, and the DLYSTRT helper is built to delay-start DOS batch files after the invoking batch has returned to the prompt. Add the WHOAMI utility and wire it into the NET command dispatch. Also adjust SLIST and RIGHTS output to match Novell behavior more closely, including server-not-found handling, path formatting, Supervisor rights, missing-path errors and usage text. Extend the test scripts to compare NPUBLIC Novell baselines against the PUBLIC implementations. LOGIN/LOGOUT can now run automatically via DLYSTRT and the maintainer LOGIN password option. RIGHTS gains an additional NOPASSUSER effective-rights matrix that covers single rights, mixed rights, Supervisor rights, ALL/N and file trustee cases. Normal builds remain free of maintainer-only helpers and hidden password handling.
630 lines
14 KiB
C
630 lines
14 KiB
C
/* tools.c: 12-Jan-96 */
|
|
#include "net.h"
|
|
|
|
/****************************************************************
|
|
* (C)opyright (C) 1993,1996 Martin Stover, Marburg, Germany *
|
|
****************************************************************/
|
|
|
|
|
|
#ifdef __WATCOMC__
|
|
/*
|
|
* Borland C compatibility wrappers used by the historical mars-nwe DOS tools.
|
|
* Open Watcom does not provide getcurdir()/setdisk() under these Borland names.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
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
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
char *xcmalloc(uint size)
|
|
{
|
|
char *p = xmalloc(size);
|
|
if (size) memset(p, 0, size);
|
|
return(p);
|
|
}
|
|
|
|
void x_x_xfree(char **p)
|
|
{
|
|
if (*p != (char *)NULL){
|
|
free(*p);
|
|
*p = (char*)NULL;
|
|
}
|
|
}
|
|
|
|
int strmaxcpy(char *dest, char *source, int len)
|
|
/* copied max. len chars + '\0' Byte */
|
|
{
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
uint8 *upstr(uint8 *s)
|
|
{
|
|
if (!s) return((uint8*)NULL);
|
|
for (;*s;s++) *s=up_char(*s);
|
|
return(s);
|
|
}
|
|
|
|
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';
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void korrpath(char *s)
|
|
{
|
|
if (!s) return;
|
|
for (;*s;s++) {
|
|
if (*s=='\\') *s='/';
|
|
else *s=down_char(*s);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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.
|
|
****************************************************************/
|
|
|
|
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);
|
|
}
|
|
|
|
int tool_is_help_arg(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(tool_strsame(s, "/?") || tool_strsame(s, "-?") ||
|
|
tool_strsame(s, "?"));
|
|
}
|
|
|
|
int tool_is_option(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(s[0] == '/' || s[0] == '-');
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
int tool_get_current_drive(void)
|
|
{
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return((int)regs.h.al);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
int tool_has_wildcards(char *path)
|
|
{
|
|
if (!path) return(0);
|
|
while (*path) {
|
|
if (*path == '*' || *path == '?') return(1);
|
|
path++;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
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);
|
|
}
|