From c50202e93bd05bd01fb5c540c5ac5bbb063886ab Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Sun, 24 May 2026 20:45:56 +0200 Subject: [PATCH] dosutils: factor shared helpers and update README Move common DOS utility helper code into tools.c and expose it through net.h. This removes duplicated command-local helpers from GRANT, RIGHTS, FLAG, FLAGDIR and the trustee helper layer. The shared helpers cover case-insensitive argument comparison, help and option detection, /FILES and /SUBDIRS parsing, current network directory handle lookup, current volume prefix formatting, uppercase DOS path copying, basename/header-path handling, wildcard detection and simple path joining/splitting. Keep the command frontends smaller and less coupled so the current multicall utility can later be split into smaller grouped multicall binaries, such as trustee tools, login/session tools and file/flag tools. Update the DOS utilities README for the newer Client32 and trustee commands. Document RIGHTS, GRANT, REVOKE and REMOVE in the status, feature, command and install sections. Add command reference entries for the trustee tools, including Novell-style syntax, supported rights, recursive/file options and missing-trustee behavior. Also mention the shared trustee helper layer and common tools.c helpers used by the newer command frontends. --- README.md | 124 ++++++++++++++++++++++++-- flag.c | 91 +++++-------------- flagdir.c | 241 +++++--------------------------------------------- grant.c | 237 +++++++------------------------------------------- net.exe | Bin 96920 -> 70486 bytes net.h | 20 +++++ rights.c | 153 ++------------------------------ tools.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ trustee.c | 226 ++++++----------------------------------------- 9 files changed, 502 insertions(+), 846 deletions(-) diff --git a/README.md b/README.md index 0b37425..1085da6 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,25 @@ DOS client-side utilities for **mars_nwe** and compatible NetWare-style NCP envi This repository contains the source for a small DOS utility suite built around a **single multi-call executable**, `net.exe`. The program can be used either as: - `net [args...]`, or -- a renamed executable such as `login.exe`, `map.exe`, `flag.exe`, `flagdir.exe`, `capture.exe`, or `logout.exe`. +- a renamed executable such as `login.exe`, `map.exe`, `flag.exe`, `flagdir.exe`, `rights.exe`, `grant.exe`, `revoke.exe`, `remove.exe`, `capture.exe`, or `logout.exe`. The command dispatcher lives in `net.c`, and the install rules deploy the same binary under multiple command names in `SYS:PUBLIC` and selected names in `SYS:LOGIN`. ## Current status -The tree is a modernization of the historical mars_nwe DOS utilities. It still keeps the original Borland-era style and APIs where useful, but now also has an Open Watcom/CMake build path and working DOS Client32 support for the FLAG-family tools. +The tree is a modernization of the historical mars_nwe DOS utilities. It still keeps the original Borland-era style and APIs where useful, but now also has an Open Watcom/CMake build path and working DOS Client32 support for the FLAG-family and trustee/right tools. Validated recently: - `FLAG` file attribute read/modify through DOS Client32 - `FLAGDIR` directory attribute read/modify through DOS Client32 +- `RIGHTS` effective-rights display through Client32 NCP87 +- `GRANT` trustee assignment for users and groups +- `REVOKE` trustee-right removal for users and groups +- `REMOVE` trustee deletion for users and groups - Novell-tool comparison for `FLAG`, including `ALL`, `N`, `RO`, `RW`, high bits, and display layout - Novell-tool comparison for `FLAGDIR`, including `Normal`, `System`, `Hidden`, `DeleteInhibit`, `Purge`, `RenameInhibit`, and combined attributes +- Novell-tool comparison for `RIGHTS`, `GRANT`, `REVOKE`, and `REMOVE` - CMake/Open Watcom build using binary-directory object files, so `.obj`/`.o` files are no longer written into the source tree Still to validate or continue: @@ -26,7 +31,7 @@ Still to validate or continue: - DOSX/VLM/NETX fallback behavior for `FLAG` and `FLAGDIR` - More complex `FLAGDIR` paths beyond the simple mapped-directory cases already tested - OS/2 requester/tool behavior -- Additional Novell-like utilities such as `RIGHTS`, `GRANT`, `REVOKE`, `NDIR`, `PURGE`, and `SALVAGE` +- Additional Novell-like utilities such as `NDIR`, `PURGE`, and `SALVAGE` ## Features @@ -41,6 +46,9 @@ Still to validate or continue: - File attribute management through `FLAG` - Directory attribute management through `FLAGDIR` - Effective rights display through `RIGHTS` +- Trustee rights assignment through `GRANT` +- Trustee rights removal through `REVOKE` +- Trustee assignment deletion through `REMOVE` - Optional mars_nwe debug control hooks - Developer diagnostics through `TESTS` @@ -65,6 +73,9 @@ The current command dispatcher includes these built-ins: - `FLAG` - `FLAGDIR` - `RIGHTS` +- `GRANT` +- `REVOKE` +- `REMOVE` - `DEBUG` - `ECHO` - `CD` @@ -117,8 +128,11 @@ This path is currently used by: - `FLAG` - `FLAGDIR` - `RIGHTS` +- `GRANT` +- `REVOKE` +- `REMOVE` -The old `Net_Call` / INT 21h requester path is kept as a fallback where appropriate, but Client32 is now preferred for the validated FLAG-family operations. +The old `Net_Call` / INT 21h requester path is kept as a fallback where appropriate, but Client32 is now preferred for the validated FLAG-family and trustee operations. ## Command reference @@ -377,10 +391,10 @@ Typical usage: RIGHTS [path] ``` -Supported in this first version: +Supported: - directory paths -- file paths, using the parent directory rights for the first read-only implementation +- file paths - Novell-like display of the effective rights mask Rights are shown in the traditional order: @@ -390,6 +404,94 @@ S R W C E M F A Supervisor, Read, Write, Create, Erase, Modify, File scan, Access Control ``` + +### `GRANT` + +Assign explicit trustee rights to a user or group. + +Typical usage: + +```text +GRANT rightslist* [FOR path] TO [USER | GROUP] name [options] +Options: /SubDirectories | /Files +``` + +Examples: + +```text +GRANT R W C FOR UDIR TO USER MARIO +GRANT ALL FOR UDIR TO GROUP EVERYONE /SUBDIRECTORIES +GRANT R F FOR UDIR\*.TST TO USER MARIO /FILES +``` + +Supported 386-style rights: + +- `ALL` +- `N` / `NONE` +- `S` / `SUPERVISOR` +- `R` / `READ` +- `W` / `WRITE` +- `C` / `CREATE` +- `E` / `ERASE` +- `M` / `MODIFY` +- `F` / `FILESCAN` +- `A` / `ACCESS CONTROL` + +For Novell compatibility, `ALL` grants the normal trustee rights (`RWCEMFA`) and does not imply Supervisor; use `S` explicitly when Supervisor rights are intended. + +### `REVOKE` + +Remove selected rights from an explicit trustee assignment. + +Typical usage: + +```text +REVOKE rightslist* [FOR path] FROM [USER|GROUP] name [options] +Options: /SubDirectories | /Files +``` + +Examples: + +```text +REVOKE W M FOR UDIR FROM USER MARIO +REVOKE R W FOR UDIR\*.TST FROM GROUP EVERYONE /FILES +REVOKE W C FOR UDIR FROM USER MARIO /SUBDIRECTORIES +``` + +`REVOKE` scans the explicit trustee assignment first, subtracts the requested rights, and deletes the trustee entry when no rights remain. Missing trustee entries are reported in Novell style: + +```text +No trustee for the specified directory. +No trustee for the specified file. +``` + +### `REMOVE` + +Delete an explicit trustee assignment for a user or group. + +Typical usage: + +```text +REMOVE [USER | GROUP] name [FROM path] [option] +Options: /Subdirs | /Files +``` + +Examples: + +```text +REMOVE USER MARIO FROM UDIR +REMOVE GROUP EVERYONE FROM UDIR /SUBDIRS +REMOVE USER MARIO FROM UDIR\*.TST /FILES +``` + +If `USER` or `GROUP` is omitted, the tool tries to resolve the name as a user first and then as a group. Successful multi-object operations print Novell-style summaries such as: + +```text +Trustee "MARIO" removed from 4 directories. +Trustee "MARIO" removed from 2 files. +``` + + ### `DEBUG` Set mars_nwe debug levels for selected server-side modules. @@ -504,6 +606,10 @@ The install rules deploy the same binary multiple times into `SYS/public`, inclu - `slist.exe` - `flag.exe` - `flagdir.exe` +- `rights.exe` +- `grant.exe` +- `revoke.exe` +- `remove.exe` - `capture.exe` - `endcap.exe` @@ -514,7 +620,9 @@ They also install selected copies such as `login.exe`, `map.exe`, and `slist.exe - `kern_wasm.asm` is the 16-bit Open Watcom assembly implementation used by the modern build. - `kern.c` was an experimental C-side test wrapper and is no longer required by the current Client32 FLAG/FLAGDIR path. - `c32ncp.c` and `c32ncp.h` contain reusable Client32 NCP helper functions for DOS tools. -- The verified Client32 path uses NCP 87 subfunction 6 for obtaining DOS information and subfunction 7 for modifying DOS information. +- `trustee.c` and `trustee.h` contain shared code for `GRANT`, `REVOKE`, and `REMOVE`. +- `tools.c` contains shared command/frontend helpers so future smaller multicall binaries can reuse common parsing and path code. +- The verified Client32 path uses NCP 87 subfunction 6 for obtaining DOS information, subfunction 7 for modifying DOS information, subfunction 29 for effective rights, and trustee scan/add/delete calls for the trustee tools. - For modify operations, use the modify information mask `DM_ATTRIBUTES` (`0x00000002`) rather than the read-side `RIM_ATTRIBUTES` mask. - High NetWare attributes must be stored and displayed as 32-bit values even in 16-bit DOS builds. @@ -523,7 +631,7 @@ They also install selected copies such as `login.exe`, `map.exe`, and `slist.exe This is legacy DOS networking code from the mid-1990s, and a few caveats are worth keeping in mind: - The code is tightly coupled to DOS, IPX/NCP behavior, and mars_nwe/NetWare requester semantics. -- Client32 support has been validated for the FLAG-family tools, but not yet generalized to every command. +- Client32 support has been validated for the FLAG-family and trustee/right tools, but not yet generalized to every command. - DOSX/VLM/NETX fallback testing is still pending. - `FLAGDIR` currently focuses on the NetWare 386-style attributes and simple mapped directory paths. - OS/2 requester behavior is still future work. diff --git a/flag.c b/flag.c index 36dba7a..7e697ac 100644 --- a/flag.c +++ b/flag.c @@ -54,32 +54,6 @@ static uint32 flag_get_dword_lh(uint8 *p) ((uint32)p[3] << 24)); } -static int flag_get_current_drive(void) -{ - REGS regs; - - regs.h.ah = 0x19; /* DOS get current default drive */ - int86(0x21, ®s, ®s); /* AL = 0 for A:, 1 for B:, ... */ - - return((int)regs.h.al); -} - -static int flag_current_dhandle(uint8 *dhandle) -{ - uint8 connid = 0; - uint8 flags = 0; - int drive; - - drive = flag_get_current_drive(); - if (get_drive_info((uint8)drive, &connid, dhandle, &flags)) - return(-1); - - if (!connid || (flags & 0x80)) - return(-1); - - return(0); -} - static int flag_add_handle_path(uint8 *p, uint8 dhandle, char *name) { int nlen; @@ -115,11 +89,12 @@ static int flag_ncp87_obtain_attrs(char *name, uint32 *attrs) uint16 len; uint8 data[128]; } repl; + uint8 connid = 0; uint8 dhandle = 0; uint8 *p; int hlen; - if (flag_current_dhandle(&dhandle)) + if (tool_current_dhandle(&connid, &dhandle)) return(-1); /* @@ -169,11 +144,12 @@ static int flag_ncp87_modify_attrs(char *name, uint32 attrs) uint16 len; uint8 data[8]; } repl; + uint8 connid = 0; uint8 dhandle = 0; uint8 *p; int hlen; - if (flag_current_dhandle(&dhandle)) + if (tool_current_dhandle(&connid, &dhandle)) return(-1); /* @@ -241,18 +217,6 @@ static int flag_ncp87_modify_attrs(char *name, uint32 attrs) #define _A_ARCH 0x20 #endif -static int flag_same(char *a, char *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); -} - static void flag_help(void) { fprintf(stdout, "USAGE: FLAG [path [ option | [+|-] attribute(s) ] [SUB]]\n"); @@ -294,52 +258,52 @@ static int flag_attr_mask(char *s, uint32 *setbits, uint32 *clearbits) if (!*p) return(-1); - if (flag_same(p, "RO")) { + if (tool_strsame(p, "RO")) { if (set) { *setbits |= (NWFA_RO | NWFA_DI | NWFA_RI); } else { *clearbits |= (NWFA_RO | NWFA_DI | NWFA_RI); } - } else if (flag_same(p, "RW")) { + } else if (tool_strsame(p, "RW")) { *clearbits |= (NWFA_RO | NWFA_DI | NWFA_RI); - } else if (flag_same(p, "S")) { + } else if (tool_strsame(p, "S")) { if (set) *setbits |= NWFA_S; else *clearbits |= NWFA_S; - } else if (flag_same(p, "H")) { + } else if (tool_strsame(p, "H")) { if (set) *setbits |= NWFA_H; else *clearbits |= NWFA_H; - } else if (flag_same(p, "SY") || flag_same(p, "SYS") || flag_same(p, "SYSTEM")) { + } else if (tool_strsame(p, "SY") || tool_strsame(p, "SYS") || tool_strsame(p, "SYSTEM")) { if (set) *setbits |= NWFA_SY; else *clearbits |= NWFA_SY; - } else if (flag_same(p, "T")) { + } else if (tool_strsame(p, "T")) { if (set) *setbits |= NWFA_T; else *clearbits |= NWFA_T; - } else if (flag_same(p, "P")) { + } else if (tool_strsame(p, "P")) { if (set) *setbits |= NWFA_P; else *clearbits |= NWFA_P; - } else if (flag_same(p, "A")) { + } else if (tool_strsame(p, "A")) { if (set) *setbits |= NWFA_A; else *clearbits |= NWFA_A; - } else if (flag_same(p, "RA")) { + } else if (tool_strsame(p, "RA")) { if (set) *setbits |= NWFA_RA; else *clearbits |= NWFA_RA; - } else if (flag_same(p, "WA")) { + } else if (tool_strsame(p, "WA")) { if (set) *setbits |= NWFA_WA; else *clearbits |= NWFA_WA; - } else if (flag_same(p, "CI")) { + } else if (tool_strsame(p, "CI")) { if (set) *setbits |= NWFA_CI; else *clearbits |= NWFA_CI; - } else if (flag_same(p, "DI")) { + } else if (tool_strsame(p, "DI")) { if (set) *setbits |= NWFA_DI; else *clearbits |= NWFA_DI; - } else if (flag_same(p, "RI")) { + } else if (tool_strsame(p, "RI")) { if (set) *setbits |= NWFA_RI; else *clearbits |= NWFA_RI; - } else if (flag_same(p, "N") || flag_same(p, "NORMAL")) { + } else if (tool_strsame(p, "N") || tool_strsame(p, "NORMAL")) { *clearbits |= (NWFA_RO | NWFA_H | NWFA_SY | NWFA_A | NWFA_S | NWFA_T | NWFA_P | NWFA_RA | NWFA_WA | NWFA_CI | NWFA_DI | NWFA_RI); - } else if (flag_same(p, "ALL")) { + } else if (tool_strsame(p, "ALL")) { *setbits |= (NWFA_RO | NWFA_H | NWFA_SY | NWFA_A | NWFA_S | NWFA_T | NWFA_P | NWFA_RA | NWFA_WA | NWFA_CI | NWFA_DI | NWFA_RI); @@ -389,15 +353,6 @@ static void flag_display_one_paged(char *name, uint32 attr, } -static int flag_has_wildcards(char *s) -{ - while (*s) { - if (*s == '*' || *s == '?') return(1); - s++; - } - return(0); -} - static int flag_list(char *pattern) { struct find_t ff; @@ -479,19 +434,19 @@ int func_flag(int argc, char *argv[], int mode) (void)mode; - if (argc > 1 && (flag_same(argv[1], "/?") || flag_same(argv[1], "-?") || - flag_same(argv[1], "?"))) { + if (argc > 1 && (tool_strsame(argv[1], "/?") || tool_strsame(argv[1], "-?") || + tool_strsame(argv[1], "?"))) { flag_help(); return(0); } if (argc > 1) { path = argv[1]; - if (flag_same(path, "SUB")) path = "*.*"; + if (tool_strsame(path, "SUB")) path = "*.*"; } for (i = 2; i < argc; i++) { - if (flag_same(argv[i], "SUB")) continue; + if (tool_strsame(argv[i], "SUB")) continue; rc = flag_attr_mask(argv[i], &setbits, &clearbits); if (rc < 0) return(1); diff --git a/flagdir.c b/flagdir.c index 79c40fc..2e5436f 100644 --- a/flagdir.c +++ b/flagdir.c @@ -28,110 +28,6 @@ #define _A_ARCH 0x20 #endif -static int fd_same(char *a, char *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); -} - -static void fd_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; -} - -static int fd_get_current_drive(void) -{ - REGS regs; - regs.h.ah = 0x19; - int86(0x21, ®s, ®s); - return((int)regs.h.al); -} - -static int fd_current_dhandle(uint8 *dhandle) -{ - uint8 connid = 0; - uint8 flags = 0; - int drive; - - drive = fd_get_current_drive(); - if (get_drive_info((uint8)drive, &connid, dhandle, &flags)) - return(-1); - - if (!connid || (flags & 0x80)) - return(-1); - - return(0); -} - -static int fd_current_prefix(char *out, int max) -{ - uint8 connid = 0; - uint8 dhandle = 0; - uint8 flags = 0; - int drive; - char server[52]; - char path[260]; - char volume[32]; - char *p; - int i = 0; - - if (!out || max < 8) - return(-1); - - out[0] = '\0'; - - drive = fd_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'; - - path[0] = '\0'; - if (get_dir_path(dhandle, path) || !path[0]) - return(-1); - - p = strchr(path, ':'); - if (!p) - return(-1); - - while (path + i < p && i < (int)sizeof(volume) - 1) { - volume[i] = path[i]; - i++; - } - volume[i] = '\0'; - - if (!volume[0]) - return(-1); - - if (server[0]) - sprintf(out, "%s/%s:", server, volume); - else - sprintf(out, "%s:", volume); - - return(0); -} - static int fd_current_display_path(uint8 dhandle, char *out, int max) { char path[260]; @@ -160,98 +56,6 @@ static int fd_current_display_path(uint8 dhandle, char *out, int max) return(0); } -static int fd_is_current_path(char *path) -{ - if (!path || !*path) return(1); - if (fd_same(path, ".")) return(1); - if (fd_same(path, ".\\")) return(1); - if (fd_same(path, "./")) return(1); - return(0); -} - -static int fd_has_wildcards(char *s) -{ - if (!s) return(0); - while (*s) { - if (*s == '*' || *s == '?') - return(1); - s++; - } - return(0); -} - -static void fd_split_pattern(char *spec, char *dir, char *pat) -{ - char tmp[260]; - char *p; - - if (!spec || !*spec) { - strcpy(dir, "."); - strcpy(pat, "*.*"); - return; - } - - strmaxcpy(tmp, spec, sizeof(tmp) - 1); - p = strrchr(tmp, '\\'); - if (!p) p = strrchr(tmp, '/'); - - if (p) { - *p++ = '\0'; - if (tmp[0]) - strmaxcpy(dir, tmp, 259); - else - strcpy(dir, "."); - strmaxcpy(pat, p, 259); - } else { - strcpy(dir, "."); - strmaxcpy(pat, tmp, 259); - } - - if (!pat[0]) - strcpy(pat, "*.*"); -} - -static void fd_join_path(char *out, char *dir, char *name, int max) -{ - int len; - - out[0] = '\0'; - - if (!dir || !dir[0] || fd_same(dir, ".")) { - strmaxcpy(out, name, max - 1); - return; - } - - strmaxcpy(out, dir, 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); -} - -static void fd_basename(char *out, char *path, int max) -{ - char up[260]; - char *p; - - fd_upcopy(up, path, sizeof(up)); - p = strrchr(up, '\\'); - if (!p) p = strrchr(up, ':'); - - if (p) - strmaxcpy(out, p + 1, max - 1); - else - strmaxcpy(out, up, max - 1); -} - static void fd_help(void) { fprintf(stdout, "386 Usage: Flagdir [path [option...]]\n"); @@ -265,20 +69,20 @@ static void fd_help(void) static int fd_attr_mask(char *s, uint32 *setbits, uint32 *clearbits) { - if (fd_same(s, "N") || fd_same(s, "NORMAL")) { + if (tool_strsame(s, "N") || tool_strsame(s, "NORMAL")) { *clearbits |= FD_DIR_BITS; - } else if (fd_same(s, "S") || fd_same(s, "SY") || - fd_same(s, "SYS") || fd_same(s, "SYSTEM")) { + } else if (tool_strsame(s, "S") || tool_strsame(s, "SY") || + tool_strsame(s, "SYS") || tool_strsame(s, "SYSTEM")) { *setbits |= FD_NWFA_SY; - } else if (fd_same(s, "H") || fd_same(s, "HIDDEN")) { + } else if (tool_strsame(s, "H") || tool_strsame(s, "HIDDEN")) { *setbits |= FD_NWFA_H; - } else if (fd_same(s, "DI") || fd_same(s, "DELETEINHIBIT")) { + } else if (tool_strsame(s, "DI") || tool_strsame(s, "DELETEINHIBIT")) { *setbits |= FD_NWFA_DI; - } else if (fd_same(s, "P") || fd_same(s, "PURGE")) { + } else if (tool_strsame(s, "P") || tool_strsame(s, "PURGE")) { *setbits |= FD_NWFA_P; - } else if (fd_same(s, "RI") || fd_same(s, "RENAMEINHIBIT")) { + } else if (tool_strsame(s, "RI") || tool_strsame(s, "RENAMEINHIBIT")) { *setbits |= FD_NWFA_RI; - } else if (fd_same(s, "PRIVATE") || fd_same(s, "PR")) { + } else if (tool_strsame(s, "PRIVATE") || tool_strsame(s, "PR")) { fprintf(stderr, "Private is valid on NetWare 2.15 and above, except NetWare 386.\n"); return(-1); } else { @@ -326,9 +130,9 @@ static void fd_display_header(char *path) char up[260]; char prefix[90]; - fd_upcopy(up, path, sizeof(up)); + tool_upcopy(up, path, sizeof(up)); - if (fd_current_prefix(prefix, sizeof(prefix))) + if (tool_current_prefix(prefix, sizeof(prefix))) prefix[0] = '\0'; fprintf(stdout, "%s%s \n", prefix, up); @@ -338,7 +142,7 @@ static void fd_display_row(char *name, uint32 attrs) { char base[260]; - fd_basename(base, name, sizeof(base)); + tool_basename(base, name, sizeof(base)); fprintf(stdout, " %-12.12s ", base); fd_print_attrs(attrs); } @@ -354,7 +158,7 @@ static int fd_is_directory(char *path) struct find_t ff; unsigned attr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR | _A_ARCH; - if (fd_is_current_path(path)) + if (tool_is_current_path(path)) return(1); if (_dos_findfirst(path, attr, &ff)) @@ -392,10 +196,10 @@ static int fd_process_one(char *path, char *display_path, uint8 dhandle, return(1); } - ncp_path = fd_is_current_path(path) ? "" : path; + ncp_path = tool_is_current_path(path) ? "" : path; if (fd_obtain(ncp_path, dhandle, &attrs)) { - if (fd_is_current_path(path) && display_path && !display_path[0] && + if (tool_is_current_path(path) && display_path && !display_path[0] && !have_change) { attrs = 0; } else { @@ -439,8 +243,8 @@ static int fd_process_wild(char *spec, uint8 dhandle, int continuous = 0; unsigned attr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR | _A_ARCH; - fd_split_pattern(spec, dir, pat); - fd_join_path(search, dir, pat, sizeof(search)); + tool_parent_pattern(dir, pat, spec, sizeof(dir), sizeof(pat)); + tool_join_path(search, dir, pat, sizeof(search)); if (_dos_findfirst(search, attr, &ff) != 0) { fprintf(stderr, "Directory %s not found.\n", spec); @@ -452,7 +256,7 @@ static int fd_process_wild(char *spec, uint8 dhandle, do { if ((ff.attrib & _A_SUBDIR) && strcmp(ff.name, ".") && strcmp(ff.name, "..")) { - fd_join_path(full, dir, ff.name, sizeof(full)); + tool_join_path(full, dir, ff.name, sizeof(full)); if (fd_process_one(full, ff.name, dhandle, setbits, clearbits, have_change, 0)) rc = 1; @@ -473,6 +277,7 @@ int func_flagdir(int argc, char *argv[], int mode) { char *path = "."; char display_path[260]; + uint8 connid = 0; uint8 dhandle = 0; uint32 setbits = 0; uint32 clearbits = 0; @@ -481,8 +286,8 @@ int func_flagdir(int argc, char *argv[], int mode) (void)mode; - if (argc > 1 && (fd_same(argv[1], "/?") || fd_same(argv[1], "-?") || - fd_same(argv[1], "?"))) { + if (argc > 1 && (tool_strsame(argv[1], "/?") || tool_strsame(argv[1], "-?") || + tool_strsame(argv[1], "?"))) { fd_help(); return(0); } @@ -490,7 +295,7 @@ int func_flagdir(int argc, char *argv[], int mode) if (argc > 1) path = argv[1]; - if (fd_current_dhandle(&dhandle)) { + if (tool_current_dhandle(&connid, &dhandle)) { fprintf(stderr, "FlagDir only works on network directories.\n"); return(1); } @@ -501,10 +306,10 @@ int func_flagdir(int argc, char *argv[], int mode) have_change = 1; } - if (fd_has_wildcards(path)) + if (tool_has_wildcards(path)) return(fd_process_wild(path, dhandle, setbits, clearbits, have_change)); - if (fd_is_current_path(path)) { + if (tool_is_current_path(path)) { if (fd_current_display_path(dhandle, display_path, sizeof(display_path))) strcpy(display_path, "."); } else { diff --git a/grant.c b/grant.c index b8830c5..c5ddbc1 100644 --- a/grant.c +++ b/grant.c @@ -22,24 +22,6 @@ NCP_RIGHT_MODIFY | NCP_RIGHT_SEARCH | \ NCP_RIGHT_OWNER) -static int grant_same(char *a, char *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); -} - -static int grant_is_help(char *s) -{ - if (!s) return(0); - return(grant_same(s, "/?") || grant_same(s, "-?") || grant_same(s, "?")); -} - static void grant_usage_error(void) { fprintf(stdout, "Command line arguments violate grammar defined for GRANT.\n\n"); @@ -63,128 +45,6 @@ static void grant_usage(void) fprintf(stdout, "A = Access Control\n"); } -static int grant_get_current_drive(void) -{ - REGS regs; - - regs.h.ah = 0x19; - int86(0x21, ®s, ®s); - return((int)regs.h.al); -} - -static int grant_current_dhandle(uint8 *connid, uint8 *dhandle) -{ - uint8 flags = 0; - int drive = grant_get_current_drive(); - - if (get_drive_info((uint8)drive, connid, dhandle, &flags)) - return(-1); - - if (!*connid || (flags & 0x80)) - return(-1); - - return(0); -} - - -static int grant_current_prefix(char *out, int max) -{ - uint8 connid = 0; - uint8 dhandle = 0; - uint8 flags = 0; - int drive; - char server[52]; - char dpath[260]; - char volume[32]; - char *p; - int i = 0; - - if (!out || max < 8) - return(-1); - - out[0] = '\0'; - - drive = grant_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); - - p = strchr(dpath, ':'); - if (!p) - return(-1); - - while (dpath + i < p && i < (int)sizeof(volume) - 1) { - volume[i] = dpath[i]; - i++; - } - volume[i] = '\0'; - - if (!volume[0]) - return(-1); - - if (server[0]) - sprintf(out, "%s/%s:", server, volume); - else - sprintf(out, "%s:", volume); - - return(0); -} - -static void grant_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; -} - -static void grant_basename(char *dst, char *src, int max) -{ - char up[260]; - char *p; - - grant_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); -} - -static void grant_header_path(char *out, char *path, int max) -{ - char prefix[90]; - char up[260]; - - if (grant_current_prefix(prefix, sizeof(prefix))) - prefix[0] = '\0'; - - grant_upcopy(up, path, sizeof(up)); - - strmaxcpy(out, prefix, max - 1); - if ((int)(strlen(out) + strlen(up)) < max - 1) - strcat(out, up); -} - static void grant_rights_bracket(uint16 rights, char *out) { out[0] = (rights & NCP_RIGHT_SUPER) ? 'S' : ' '; @@ -226,54 +86,54 @@ static void grant_rights_string(uint16 rights, char *out) static int grant_add_right_word(char *s, uint16 *rights) { - if (grant_same(s, "ALL")) { + if (tool_strsame(s, "ALL")) { *rights = NCP_RIGHT_ALL_386; return(0); } - if (grant_same(s, "N") || grant_same(s, "NONE")) { + if (tool_strsame(s, "N") || tool_strsame(s, "NONE")) { *rights = 0; return(0); } - if (grant_same(s, "S") || grant_same(s, "SUPERVISOR")) { + if (tool_strsame(s, "S") || tool_strsame(s, "SUPERVISOR")) { *rights |= NCP_RIGHT_SUPER; return(0); } - if (grant_same(s, "R") || grant_same(s, "READ")) { + if (tool_strsame(s, "R") || tool_strsame(s, "READ")) { *rights |= NCP_RIGHT_READ; return(0); } - if (grant_same(s, "W") || grant_same(s, "WRITE")) { + if (tool_strsame(s, "W") || tool_strsame(s, "WRITE")) { *rights |= NCP_RIGHT_WRITE; return(0); } - if (grant_same(s, "C") || grant_same(s, "CREATE")) { + if (tool_strsame(s, "C") || tool_strsame(s, "CREATE")) { *rights |= NCP_RIGHT_CREATE; return(0); } - if (grant_same(s, "E") || grant_same(s, "ERASE")) { + if (tool_strsame(s, "E") || tool_strsame(s, "ERASE")) { *rights |= NCP_RIGHT_DELETE; return(0); } - if (grant_same(s, "M") || grant_same(s, "MODIFY")) { + if (tool_strsame(s, "M") || tool_strsame(s, "MODIFY")) { *rights |= NCP_RIGHT_MODIFY; return(0); } - if (grant_same(s, "F") || grant_same(s, "FILESCAN") || - grant_same(s, "FILE") || grant_same(s, "SCAN")) { + if (tool_strsame(s, "F") || tool_strsame(s, "FILESCAN") || + tool_strsame(s, "FILE") || tool_strsame(s, "SCAN")) { *rights |= NCP_RIGHT_SEARCH; return(0); } - if (grant_same(s, "A") || grant_same(s, "ACCESS") || - grant_same(s, "CONTROL") || grant_same(s, "ACCESSCONTROL")) { + if (tool_strsame(s, "A") || tool_strsame(s, "ACCESS") || + tool_strsame(s, "CONTROL") || tool_strsame(s, "ACCESSCONTROL")) { *rights |= NCP_RIGHT_OWNER; return(0); } @@ -281,13 +141,6 @@ static int grant_add_right_word(char *s, uint16 *rights) return(-1); } -static int grant_is_option(char *s) -{ - if (!s) return(0); - return(s[0] == '/' || s[0] == '-'); -} - - static int grant_last_rc = 0; static int grant_set_one(char *path, uint16 dhandle, @@ -305,34 +158,6 @@ static int grant_set_one(char *path, uint16 dhandle, return(rc); } -static int grant_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); -} - -static void grant_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); -} - /* * Apply the grant to PATH and every directory below it. * @@ -351,12 +176,12 @@ static int grant_set_subdirs(char *path, uint16 dhandle, if (grant_set_one(path, dhandle, object_id, rights)) rc = 1; - grant_join_path(pattern, path, "*.*", sizeof(pattern)); + tool_join_path(pattern, path, "*.*", sizeof(pattern)); if (_dos_findfirst(pattern, _A_SUBDIR, &ff) == 0) { do { - if ((ff.attrib & _A_SUBDIR) && !grant_is_dot_dir(ff.name)) { - grant_join_path(child, path, ff.name, sizeof(child)); + if ((ff.attrib & _A_SUBDIR) && !tool_is_dot_dir(ff.name)) { + tool_join_path(child, path, ff.name, sizeof(child)); if (grant_set_subdirs(child, dhandle, object_id, rights)) rc = 1; } @@ -384,7 +209,7 @@ int func_grant(int argc, char *argv[], int mode) (void)mode; - if (argc < 2 || grant_is_help(argv[1])) { + if (argc < 2 || tool_is_help_arg(argv[1])) { if (argc < 2) grant_usage_error(); grant_usage(); @@ -395,10 +220,10 @@ int func_grant(int argc, char *argv[], int mode) * GRANT rightslist* [FOR path] TO [USER|GROUP] name [options] */ while (i < argc) { - if (grant_same(argv[i], "FOR") || grant_same(argv[i], "TO")) + if (tool_strsame(argv[i], "FOR") || tool_strsame(argv[i], "TO")) break; - if (grant_is_option(argv[i])) + if (tool_is_option(argv[i])) break; if (grant_add_right_word(argv[i], &rights)) { @@ -416,7 +241,7 @@ int func_grant(int argc, char *argv[], int mode) return(1); } - if (grant_same(argv[i], "FOR")) { + if (tool_strsame(argv[i], "FOR")) { i++; if (i >= argc) { grant_usage_error(); @@ -426,17 +251,17 @@ int func_grant(int argc, char *argv[], int mode) path = argv[i++]; } - if (i >= argc || !grant_same(argv[i], "TO")) { + if (i >= argc || !tool_strsame(argv[i], "TO")) { grant_usage_error(); grant_usage(); return(1); } i++; - if (i < argc && grant_same(argv[i], "USER")) { + if (i < argc && tool_strsame(argv[i], "USER")) { objtype = GRANT_BINDERY_USER; i++; - } else if (i < argc && grant_same(argv[i], "GROUP")) { + } else if (i < argc && tool_strsame(argv[i], "GROUP")) { objtype = GRANT_BINDERY_GROUP; i++; } @@ -450,7 +275,7 @@ int func_grant(int argc, char *argv[], int mode) objname = argv[i++]; while (i < argc) { - if (!grant_is_option(argv[i])) { + if (!tool_is_option(argv[i])) { grant_usage_error(); grant_usage(); return(1); @@ -461,15 +286,15 @@ int func_grant(int argc, char *argv[], int mode) * NCP87 trustee-add call. /SUBDIRECTORIES recursively applies the * same grant to all subdirectories below the given path. */ - if (grant_same(argv[i], "/FILES") || grant_same(argv[i], "-FILES") || - grant_same(argv[i], "/F") || grant_same(argv[i], "-F")) { + if (tool_strsame(argv[i], "/FILES") || tool_strsame(argv[i], "-FILES") || + tool_strsame(argv[i], "/F") || tool_strsame(argv[i], "-F")) { i++; continue; } - if (grant_same(argv[i], "/SUBDIRECTORIES") || - grant_same(argv[i], "-SUBDIRECTORIES") || - grant_same(argv[i], "/S") || grant_same(argv[i], "-S")) { + if (tool_strsame(argv[i], "/SUBDIRECTORIES") || + tool_strsame(argv[i], "-SUBDIRECTORIES") || + tool_strsame(argv[i], "/S") || tool_strsame(argv[i], "-S")) { recurse_subdirs = 1; i++; continue; @@ -480,7 +305,7 @@ int func_grant(int argc, char *argv[], int mode) return(1); } - if (grant_current_dhandle(&connid, &dhandle)) { + if (tool_current_dhandle(&connid, &dhandle)) { fprintf(stdout, "Specified path not locatable.\n"); return(1); } @@ -508,8 +333,8 @@ int func_grant(int argc, char *argv[], int mode) char base[80]; char bracket[10]; - grant_header_path(header, path, sizeof(header)); - grant_basename(base, path, sizeof(base)); + tool_header_path(header, path, sizeof(header)); + tool_basename(base, path, sizeof(base)); grant_rights_bracket(rights, bracket); fprintf(stdout, "%s\n", header); diff --git a/net.exe b/net.exe index 431c3abb8415331f2c8146547d74cc9ff2c5370b..e7db07f5a2633e6b04352e824352e3c87e12be85 100755 GIT binary patch delta 28627 zcmagH30zZ0_dk4Z*bxJ=ge4&?BB;24RuqAVTeL2SpmE2wYOPwU0c{@(C`!2m(zLfV zt?gq=>Aq^UH5#i33htt|7SYyLt-Ud7tqWBI^8cQj1)t~lyr0+hz`bYAoH;Xd=FFKh zbBDJcc%1*(y=dZ04O`9>u=AK$Z=3C(0H&B>`mJM_XvY4py`Ev3jxfvvK<-h7SqHeG zXqWmNW0-J2Dqu075b!SGTflz6dBER*c0kB+hA9Pn0*F4rFo}Sbfa()W5wjZ)jeuVO z9e}`-46_{YBcS>e!}LGRFdD!+fF6wu(+e;bPy#Rjz6BftJOFr|VVIWyg@Eq>Cjk1h z409SF0J@%Im}Ec}UxDOcJ#4yt?Fw6nKuYgAYMKi+;1`Gpy1n7Q|VLk*L1N;T>yM&4WCKlnxM!<)F zy@1eP7^Vnt18@hB@GHZN0Q9@eFmM0HFuw!t0)nnH%pAaKzI0jvO~oMC1IUItv!F-)2si~)ZF z{sQ>Eihc*o18{(+UT2tD0G4N%0%UvzB?Vmn8r|^?!?XaBw?kOKe83_=HQ*8;X$QlM0enz|A1l6P zm^T6JPSgfa54a1++r=C4Y`EEn-Eiv)vp<-R2pZxc6_%|v@Y90&C7udN(Y@Lj!kw!) zTd6LTs)Veyn``oa^wn@nV|5E|KJt*br^C6u2VT$gRCj4&2?`HgN z!*AkaYz#@MJ{FR~$xein6t*(?o+X738EuACSm$E^SLp^Inf&k#g%5ui&i4Z1CS#P;$uI>UMy&aSyqhbLQsFJf3Y@MJ0z9@XLdHN0vrboFO6K1?svxNJQD9 zQO$Gt27YJoXf~LC6x^feUE=w1C*xo9OAh%}ZfWa~<;YAeo%njf?{Ld1>9A=>are$# z<(kf2?Nx_roJUNW*s-V6T|1VCYFAGu}02oi$!LPW3>Jm z%NVV1#*2*BBVz`WA1X5qb&{%t)mo75Wf`k7BZ~-DGWqo~lgddT!e?he;#X#}M=GZ=_eBIWA1 zGx}zomu{K_<+($M0I6VGXV710@M4&TF%pJBOJ|#;#N2BQUlji#GSifkg@WTt}d=2D2=a6QQzPUJ^(e?w;(Y5@wuq0$$@q&R54kw}QL%qcAe|n0!f#{#=gXol>H@Eo1TnQ*55MWfhJ3CaG}e z8AD3-9kj+BnPnpyIvzrsmmB!;%0v`=4dVYxsU&S#vYg+hRLG=4zg32$YffX(lXwqW z&NnK9K^}>W7Nw|Mb*uT{sG(6(Ve@JOSGbBRSYD}FCCee3vVd&L0;zBpbzTt_LCjx# zj<1O78H|zmf?-cFlYEg#P9VYa1^!4>Kd@c|;>S_cXrrKVG0~#V$G*Uih=!ahfnUb2 zj$SMo`G9YZo=k|Jp5>EbX2jrW?Xxu0?V9OJBQK@uE-B4@)MaQZ|9Q;!EYHu6?Mb=T zF6H&Hp*>ycFRwIk4GrqH00wG8SWNK{t@I%p{4GdyGIpasvIVT<9#SW$gwredyzUWF z>lrPbsqXMrTV6fcZ*9!y$&zN7>(;6 zy<#49G9^Hv-7J}{u+)~Jt(f_UTmE9+5QjDsiqO{I#ED(AYB_&4Zm9Ci+|GZGhoJviLEXTxLB@ZBNwv*WxJtO3VSTNnLMH_DJV@htV z$7JzyN|iE{|E*`pG^7t)n$e#r@G$jftUj0>U^!xz8p_m%(PrFn?h%dBm!v}MQbRf6 z*m_-Q8Kc`$j+S_|#E?-;S&JEKR~H(UTf$H1)e{cOu_cDeymhkN%Dfs`F3BejU6n9t zmP1DL5zXh7i`T``PY{3TlH?l8mmS%r z=`^DMn&-5%nWCjVol+G|4c4HpNoVTSwPBcp$`&6lQ=hQULw_zdWF%1)lBo7HHK~Mi zi_6p}shF9%y#2bYeKe%&q{7+7Xn~W|D)+cYggYe__AfS6YTy!8YHI8q;Hcopng3Zq z`+OjQI&4Dq#?LeG=H9W3P}caxPZUoT5>gkV@lQWdOe01+74te3bL>BgIWVKM7|-OI zsx%~RXc|NQ&2meQ6410_B>Jwi?RnBwiNIHv25T+)U z)UERSV;vQmj{4I2j?A+C4IO3Mj&yJjrNZ$=h9{Kfq1bG@7nPMB?I6J)J1qO&MTT+_ z1*K8@=`>Q5^x7hq%5gxy#}-9*1UXtt>EAXxi%4UydiW(dB*0i9YsM zs88>OqQ-q!rvBb;Y6Z82ohs++!2N}xKCE9Db=36*{Oo?Pj=~|}iu#4bJgw>MWS6** zHruoX!cLxT;J@qFYb>;CK_}lX;Jc+%oZ=R6>H@1rgSvoabm{_5t=%)!F-;AX3a0sn zJ?V~Dngu?c&&T&)NMh`o&+Gg5^i7!RY%IQ}|7=V=vGWc3ZRJe;s1$~Kh-Ov^QVTyI zDHZ~MHPgV)NQ&+W+&A-{P;1fQ`}aHp1ksglgzca)P>C^6l@#0u#B1hp>eV{+YEHd; z&sc{ralp6D-LuK@`o2`SJ=ehhPUT?8&*Qz5F{7%4;CXz%2bcSd)82N`>Zm1~}qeqp(?AS2)*d!cWr;eDZ+t zV6qyrZWth=0-v4EzdInA*bSVXQtufb>QzT>iqHeZzYmCJRlH;%BoKa{X5jk|jKtG- zc$z$L44$6F)4vBs!b6-t*FXjY%aa&tmWQ0C6@H+XIBo900OOF7_=mwf#h?@TOmiaoP!1s%46 zHmUHEILlf*JItG1^gn7yL~qdB)_PhS&73P`ub2`ba`N>Ux!eoP^q$sl&Af}`Es-R` zR?s4f>38O;DCIPgaz?0Sy3PEai*~sN9@SIfMIU-vX4=f3xbT@G4X0xZ&&AcBZjH(Tfu#G>ZESO0MD_{9>F=P1%mWWnVdva+dE}VNzuBveRG0mnz zh|Qi?bmec%76+HOj@9LMI=Ak~@pE#EiIIQ{|JvZ;@u|PlBm$duNgOE9xL+>Y7gZvM z05=T4uDSff!SO{uT_(S_uw5!#{mo!*L*1S4*4YNfvQTdJ^yQpo zPlJZIki9OSX3DhB_Z=ci_)PRn(6Ql{4dn6ecTz5xEUI~q%`ymntT+1WuWSP!I&@}t zuz5Ax*%=#d#`~IWY%bN81Zn;U*6J7jjiFgZy?)iHJ6QNA!Vof8XeUjZInds&YAD-+ zY`d0*xljm$WkX53kPfy9(QTIP5_9AesY#TYA~SU-gCHuLIvWb-jb|~uVwfov+@~01 zrbh^7WQ!CS$=RT=ERmQ;x;P2KBov+RWAb8z87-Fa67!hOv=d^Qn`LyHd15E_`$_OP z(n5k8f3x^W%#&RSDxqQ$-<&q8NY!lF#hRx*k?_Ndqz9%QySTB7VTq`2a*DFtObHC-4JA=A#_+{O$C(?3cZ= z!T7PrxCwrRo$+I9sYq1zMCS8TAeq+AwDJl3e!}OEcF^++GUAHf6q!B{nJ#iMePHQk zGrw=oN$O#mNEzKM$*ty3Iv9L3@`+≻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

$X5^N&LQ4k=YP(M>Bh~>*pHRiPhfPZXrur? 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 oBe{DksV?>Ih4dT0jDEd~>9= 'a' && ca <= 'z') ca -= 32; - if (cb >= 'a' && cb <= 'z') cb -= 32; - if (ca != cb) return(0); - } - return(1); -} - -static int rights_is_help(char *s) -{ - if (!s) return(0); - return(rights_same(s, "/?") || rights_same(s, "-?") || rights_same(s, "?")); -} - static void rights_usage(void) { fprintf(stdout, "Usage: RIGHTS [path]\n\n"); @@ -58,59 +40,12 @@ static void rights_usage(void) fprintf(stdout, " Modify | File scan | Access Control\n"); } -static int rights_get_current_drive(void) -{ - REGS regs; - - regs.h.ah = 0x19; - int86(0x21, ®s, ®s); - return((int)regs.h.al); -} - -static int rights_current_dhandle(uint8 *connid, uint8 *dhandle) -{ - uint8 flags = 0; - int drive = rights_get_current_drive(); - - if (get_drive_info((uint8)drive, connid, dhandle, &flags)) - return(-1); - - if (!*connid || (flags & 0x80)) - return(-1); - - return(0); -} - -static int rights_is_current_path(char *path) -{ - if (!path || !*path) return(1); - if (rights_same(path, ".")) return(1); - if (rights_same(path, ".\\")) return(1); - if (rights_same(path, "./")) return(1); - return(0); -} - -static void rights_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; -} - static void rights_parent_path(char *dst, char *src, int max) { char tmp[260]; char *p; - rights_upcopy(tmp, src, sizeof(tmp)); + tool_upcopy(tmp, src, sizeof(tmp)); p = strrchr(tmp, '\\'); if (!p) p = strrchr(tmp, ':'); @@ -134,7 +69,7 @@ static int rights_path_is_dir(char *path) { struct stat st; - if (rights_is_current_path(path)) + if (tool_is_current_path(path)) return(1); if (stat(path, &st) == 0) { @@ -146,78 +81,6 @@ static int rights_path_is_dir(char *path) } -static int rights_current_prefix(char *out, int max) -{ - uint8 connid = 0; - uint8 dhandle = 0; - uint8 flags = 0; - int drive; - char server[52]; - char dpath[260]; - char volume[32]; - char *p; - int i = 0; - - if (!out || max < 8) - return(-1); - - out[0] = '\0'; - - drive = rights_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); - - p = strchr(dpath, ':'); - if (!p) - return(-1); - - while (dpath + i < p && i < (int)sizeof(volume) - 1) { - volume[i] = dpath[i]; - i++; - } - volume[i] = '\0'; - - if (!volume[0]) - return(-1); - - if (server[0]) - sprintf(out, "%s/%s:", server, volume); - else - sprintf(out, "%s:", volume); - - return(0); -} - - -static void rights_make_header_path(char *out, char *path) -{ - char up[260]; - char prefix[90]; - - if (rights_current_prefix(prefix, sizeof(prefix))) - prefix[0] = '\0'; - - if (rights_is_current_path(path)) { - strcpy(out, prefix); - return; - } - - rights_upcopy(up, path, sizeof(up)); - strcpy(out, prefix); - strcat(out, up); -} - static int rights_effective_mask(char *path, int is_dir, uint8 *mask) { uint8 connid = 0; @@ -229,7 +92,7 @@ static int rights_effective_mask(char *path, int is_dir, uint8 *mask) if (mask) *mask = 0; - if (rights_current_dhandle(&connid, &dhandle)) + if (tool_current_dhandle(&connid, &dhandle)) return(-1); /* @@ -238,7 +101,7 @@ static int rights_effective_mask(char *path, int is_dir, uint8 *mask) * It works for both files and directories, so pass the requested path * itself instead of mapping files to their parent directory. */ - if (!c32_ncp87_get_effective_rights(rights_is_current_path(path) ? "" : path, + if (!c32_ncp87_get_effective_rights(tool_is_current_path(path) ? "" : path, (uint16)dhandle, &ncp_rights, NULL, NULL, NULL)) { @@ -251,10 +114,10 @@ static int rights_effective_mask(char *path, int is_dir, uint8 *mask) * handle and use the returned old-style effective-rights byte. * This cannot directly target a file, so files are mapped to their parent. */ - if (rights_is_current_path(path)) { + if (tool_is_current_path(path)) { usepath[0] = '\0'; } else if (is_dir) { - rights_upcopy(usepath, path, sizeof(usepath)); + tool_upcopy(usepath, path, sizeof(usepath)); } else { rights_parent_path(usepath, path, sizeof(usepath)); } @@ -333,7 +196,7 @@ static void rights_display(char *path, int is_dir, uint8 mask) char hdr[300]; char mstr[10]; - rights_make_header_path(hdr, path); + tool_header_path(hdr, path, sizeof(hdr)); rights_mask_string(mask, mstr); fprintf(stdout, "%s\n", hdr); @@ -389,7 +252,7 @@ int func_rights(int argc, char *argv[], int mode) } if (argc == 2) { - if (rights_is_help(argv[1])) { + if (tool_is_help_arg(argv[1])) { rights_usage(); return(0); } diff --git a/tools.c b/tools.c index d3bf7cf..427edd4 100644 --- a/tools.c +++ b/tools.c @@ -366,3 +366,259 @@ int tool_page_line(int *line_count, int *continuous) 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]; + char volume[32]; + char *p; + int i = 0; + + 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); + + p = strchr(dpath, ':'); + if (!p) + return(-1); + + while (dpath + i < p && i < (int)sizeof(volume) - 1) { + volume[i] = dpath[i]; + i++; + } + volume[i] = '\0'; + + if (!volume[0]) + return(-1); + + if (server[0]) + sprintf(out, "%s/%s:", server, volume); + else + sprintf(out, "%s:", volume); + + 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[90]; + char up[260]; + + 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)); + strmaxcpy(out, prefix, max - 1); + if ((int)(strlen(out) + strlen(up)) < max - 1) + strcat(out, up); +} + +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); +} diff --git a/trustee.c b/trustee.c index 2adc2cd..44b1ace 100644 --- a/trustee.c +++ b/trustee.c @@ -9,28 +9,34 @@ #define S_IFDIR 0040000 #endif -int trustee_same(char *a, char *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 trustee_is_help(char *s) -{ - if (!s) return(0); - return(trustee_same(s, "/?") || trustee_same(s, "-?") || trustee_same(s, "?")); -} -int trustee_is_option(char *s) +int trustee_same(char *a, char *b) { return(tool_strsame(a, b)); } +int trustee_is_help(char *s) { return(tool_is_help_arg(s)); } +int trustee_is_option(char *s) { return(tool_is_option(s)); } +int trustee_current_dhandle(uint8 *connid, uint8 *dhandle) { - if (!s) return(0); - return(s[0] == '/' || s[0] == '-'); + return(tool_current_dhandle(connid, dhandle)); +} +void trustee_upcopy(char *dst, char *src, int max) { tool_upcopy(dst, src, max); } +void trustee_basename(char *dst, char *src, int max) +{ + tool_basename(dst, src, max); +} +void trustee_header_path(char *out, char *path, int max) +{ + tool_header_path(out, path, max); +} +void trustee_join_path(char *out, char *base, char *name, int max) +{ + tool_join_path(out, base, name, max); +} +int trustee_is_dot_dir(char *name) { return(tool_is_dot_dir(name)); } +int trustee_path_has_wildcards(char *path) { return(tool_has_wildcards(path)); } +void trustee_parent_pattern(char *dir, char *pattern, char *path, + int maxdir, int maxpat) +{ + tool_parent_pattern(dir, pattern, path, maxdir, maxpat); } int trustee_is_subdirs_option(char *s) @@ -48,155 +54,6 @@ int trustee_is_files_option(char *s) trustee_same(s, "/F") || trustee_same(s, "-F")); } -static int trustee_get_current_drive(void) -{ - REGS regs; - - regs.h.ah = 0x19; - int86(0x21, ®s, ®s); - return((int)regs.h.al); -} - -int trustee_current_dhandle(uint8 *connid, uint8 *dhandle) -{ - uint8 flags = 0; - int drive = trustee_get_current_drive(); - - if (get_drive_info((uint8)drive, connid, dhandle, &flags)) - return(-1); - - if (!*connid || (flags & 0x80)) - return(-1); - - return(0); -} - -static int trustee_current_prefix(char *out, int max) -{ - uint8 connid = 0; - uint8 dhandle = 0; - uint8 flags = 0; - int drive; - char server[52]; - char dpath[260]; - char volume[32]; - char *p; - int i = 0; - - if (!out || max < 8) - return(-1); - - out[0] = '\0'; - - drive = trustee_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); - - p = strchr(dpath, ':'); - if (!p) - return(-1); - - while (dpath + i < p && i < (int)sizeof(volume) - 1) { - volume[i] = dpath[i]; - i++; - } - volume[i] = '\0'; - - if (!volume[0]) - return(-1); - - if (server[0]) - sprintf(out, "%s/%s:", server, volume); - else - sprintf(out, "%s:", volume); - - return(0); -} - -void trustee_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 trustee_basename(char *dst, char *src, int max) -{ - char up[260]; - char *p; - - trustee_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 trustee_header_path(char *out, char *path, int max) -{ - char prefix[90]; - char up[260]; - - if (trustee_current_prefix(prefix, sizeof(prefix))) - prefix[0] = '\0'; - - trustee_upcopy(up, path, sizeof(up)); - - strmaxcpy(out, prefix, max - 1); - if ((int)(strlen(out) + strlen(up)) < max - 1) - strcat(out, up); -} - -void trustee_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 trustee_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); -} - int trustee_parse_right_word(char *s, uint16 *rights) { if (trustee_same(s, "ALL")) { @@ -306,36 +163,3 @@ int trustee_path_is_dir(char *path) return(0); } -int trustee_path_has_wildcards(char *path) -{ - if (!path) return(0); - while (*path) { - if (*path == '*' || *path == '?') return(1); - path++; - } - return(0); -} - -void trustee_parent_pattern(char *dir, char *pattern, char *path, int maxdir, int maxpat) -{ - char tmp[260]; - char *p; - - trustee_upcopy(tmp, path, sizeof(tmp)); - p = strrchr(tmp, '\\'); - if (!p) p = strrchr(tmp, ':'); - - if (p) { - char save = *p; - *p = '\0'; - strmaxcpy(pattern, p + 1, maxpat - 1); - if (save == ':') { - *p = ':'; - *(p + 1) = '\0'; - } - strmaxcpy(dir, tmp, maxdir - 1); - } else { - strmaxcpy(dir, ".", maxdir - 1); - strmaxcpy(pattern, tmp, maxpat - 1); - } -}