dosutils: add Novell-style NDIR listing tool

Add an initial Novell-style NDIR implementation for the DOS utilities.

The new tool supports basic DOS namespace directory listings using
_dos_findfirst/_dos_findnext, including wildcard paths, current-directory
listing, files-only and directories-only modes, recursive /SUB listings,
continuous output and Novell-style help text.

NDIR separates file and directory output into Novell-like sections,
prints long NetWare-style attribute brackets, and reports both file bytes
and allocated bytes in blocks using a simple per-file 4 KiB block
approximation for the initial implementation.

Add initial /RIGHTS support.  The rights format displays rights columns
for files and directories, obtains effective rights through Client32
NCP87, and falls back to the older directory-handle effective-rights path
when needed.  For now the inherited-rights column uses the effective
rights value until a separate inherited-rights mask is available.

Add initial /DATES support with a Novell-style date column layout showing
Last Updated, Last Archived, Accessed and Created/Copied fields.  The
initial implementation uses the DOS findfirst update timestamp for the
available date values and placeholder archive/access values until full
NetWare namespace date fields are exposed.

Add /SHORT and /BRIEF as compact output modes for recursive listings and
manual testing.  These modes keep the useful values while suppressing the
large Novell-style section headers and empty recursive sections.

Wire NDIR into the multicall dispatcher, CMake build and install list,
and document the supported command forms in the README.

Owner display, true inherited-right masks, full namespace date fields,
sorting and restrictions remain future work.
This commit is contained in:
Mario Fetka
2026-05-25 08:49:47 +02:00
parent c50202e93b
commit 7117bfff68
6 changed files with 782 additions and 0 deletions

View File

@@ -44,6 +44,7 @@ set(MARS_DOSUTILS_NEW_ONLY_TOOLS
grant
revoke
remove
ndir
)
if(MARS_NWE_BUILD_DOSUTILS)
@@ -57,6 +58,7 @@ if(MARS_NWE_BUILD_DOSUTILS)
login.c
map.c
slist.c
ndir.c
flag.c
flagdir.c
rights.c

View File

@@ -43,6 +43,7 @@ Still to validate or continue:
- Scripted session setup through command files
- External program execution via `SPAWN` and `EXEC`
- Server listing through `SLIST`
- Directory listing through `NDIR`
- File attribute management through `FLAG`
- Directory attribute management through `FLAGDIR`
- Effective rights display through `RIGHTS`
@@ -70,6 +71,7 @@ The current command dispatcher includes these built-ins:
- `CAPTURE`
- `ENDCAP`
- `SLIST`
- `NDIR`
- `FLAG`
- `FLAGDIR`
- `RIGHTS`
@@ -492,6 +494,52 @@ Trustee "MARIO" removed from 2 files.
```
### `NDIR`
List files and directories in a NetWare-style format.
Typical usage:
```text
NDIR [path] [/option...]
```
The implementation supports basic DOS namespace listings, wildcards,
paging, first rights and dates displays, and these options:
- `/FO` or `FO` for files only
- `/DO` or `DO` for directories only
- `/CONTINUOUS`, `/CONTINUE`, or `/C` for continuous output
- `/RIGHTS` for a first effective-rights display
- `/DATES` for Novell-style date columns with current DOS timestamp data
- `/SUB` or `/SUBDIRECTORIES` for recursive directory listings
- `/SHORT` or `/BRIEF` for compact output, useful with `/SUB`
- `/HELP` for Novell-style usage text
Examples:
```text
NDIR
NDIR *.EXE
NDIR PUBLIC\*.EXE
NDIR PUBLIC /DO
NDIR PUBLIC\*.EXE /FO /CONTINUOUS
NDIR PUBLIC /RIGHTS
NDIR PUBLIC /DATES
NDIR PUBLIC /SUB
NDIR PUBLIC /SUB /SHORT
NDIR PUBLIC /DO
```
The first version intentionally focuses on the common listing path. More
advanced Novell NDIR features such as sorting and restrictions are accepted
only as future compatibility points or will be implemented in later revisions. The `/RIGHTS` display currently uses effective rights for both
rights columns until inherited-right masks are exposed. The `/DATES` display
uses the DOS update timestamp and placeholder values for archive/access fields
until full NetWare namespace date fields are available.
### `DEBUG`
Set mars_nwe debug levels for selected server-side modules.
@@ -604,6 +652,7 @@ The install rules deploy the same binary multiple times into `SYS/public`, inclu
- `mapdel.exe`
- `logout.exe`
- `slist.exe`
- `ndir.exe`
- `flag.exe`
- `flagdir.exe`
- `rights.exe`

729
ndir.c Normal file
View File

@@ -0,0 +1,729 @@
/* ndir.c - first Novell NDIR-like directory listing utility */
#include "net.h"
#include "c32ncp.h"
#include <dos.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef S_IFDIR
#define S_IFDIR 0040000
#endif
#ifndef _A_NORMAL
#define _A_NORMAL 0x00
#endif
#ifndef _A_RDONLY
#define _A_RDONLY 0x01
#endif
#ifndef _A_HIDDEN
#define _A_HIDDEN 0x02
#endif
#ifndef _A_SYSTEM
#define _A_SYSTEM 0x04
#endif
#ifndef _A_SUBDIR
#define _A_SUBDIR 0x10
#endif
#ifndef _A_ARCH
#define _A_ARCH 0x20
#endif
#define NDIR_OPT_FILES_ONLY 0x0001
#define NDIR_OPT_DIRS_ONLY 0x0002
#define NDIR_OPT_RIGHTS 0x0004
#define NDIR_OPT_DATES 0x0008
#define NDIR_OPT_SUB 0x0010
#define NDIR_OPT_SHORT 0x0020
/* NCP effective-rights bits returned by NCP87 subfunction 29. */
#define NDIR_NCP_RIGHT_READ 0x0001
#define NDIR_NCP_RIGHT_WRITE 0x0002
#define NDIR_NCP_RIGHT_CREATE 0x0008
#define NDIR_NCP_RIGHT_DELETE 0x0010
#define NDIR_NCP_RIGHT_OWNER 0x0020
#define NDIR_NCP_RIGHT_SEARCH 0x0040
#define NDIR_NCP_RIGHT_MODIFY 0x0080
#define NDIR_NCP_RIGHT_SUPER 0x0100
static void ndir_dos_date(unsigned date, char *out);
#define NDIR_OLD_RIGHT_S 0x01
#define NDIR_OLD_RIGHT_R 0x02
#define NDIR_OLD_RIGHT_W 0x04
#define NDIR_OLD_RIGHT_C 0x08
#define NDIR_OLD_RIGHT_E 0x10
#define NDIR_OLD_RIGHT_M 0x20
#define NDIR_OLD_RIGHT_F 0x40
#define NDIR_OLD_RIGHT_A 0x80
static void ndir_usage(void)
{
fprintf(stdout, "usage: NDIR [path] [/option...]\n");
fprintf(stdout, "path: [path] [filename] [,filename, ...] (up to 16 in chain)\n");
fprintf(stdout, "options: [format], [flag], [sortspec], [restriction], [FO] (files only),\n");
fprintf(stdout, " [DO] (directories only), [SUBdirectories], [Continuous], [HELP]\n");
fprintf(stdout, "\n");
fprintf(stdout, "format: DATES, RIGHTS, MACintosh, LONGnames\n");
fprintf(stdout, "\n");
fprintf(stdout, "flag: [NOT] RO, S, A, X, H, SY, T, I, P, RA, WA, CI, DI, RI\n");
fprintf(stdout, "\n");
fprintf(stdout, "sortspec: [REVerse] SORT [OWner], [SIze], [UPdate], [CReate],\n");
fprintf(stdout, " [ACcess], [ARchive], [UNsorted]\n");
fprintf(stdout, "\n");
fprintf(stdout, "restriction: OWner <operator> <name>\n");
fprintf(stdout, " SIze <operator> <number>\n");
fprintf(stdout, " UPdate <operator> <date>\n");
fprintf(stdout, " CReate <operator> <date>\n");
fprintf(stdout, " ACcess <operator> <date>\n");
fprintf(stdout, " ARchive <operator> <date>\n");
fprintf(stdout, "\n");
fprintf(stdout, "operator: [NOT] Less than, GReater than,\n");
fprintf(stdout, " EQual to, BEfore, AFter\n");
fprintf(stdout, "\n");
fprintf(stdout, "To search filenames equivalent to any of the capitalized KEYWORD options\n");
fprintf(stdout, "shown above, the filename must be preceded by a drive letter or path.\n");
}
static int ndir_is_help(char *s)
{
if (!s) return(0);
return(tool_is_help_arg(s) || tool_strsame(s, "/HELP") ||
tool_strsame(s, "-HELP") || tool_strsame(s, "HELP"));
}
static int ndir_is_files_only(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/FO") || tool_strsame(s, "-FO") ||
tool_strsame(s, "/FILESONLY") || tool_strsame(s, "-FILESONLY") ||
tool_strsame(s, "FO") || tool_strsame(s, "FILESONLY"));
}
static int ndir_is_dirs_only(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/DO") || tool_strsame(s, "-DO") ||
tool_strsame(s, "/DIRSONLY") || tool_strsame(s, "-DIRSONLY") ||
tool_strsame(s, "/DIRECTORIES") || tool_strsame(s, "-DIRECTORIES") ||
tool_strsame(s, "DO") || tool_strsame(s, "DIRSONLY") ||
tool_strsame(s, "DIRECTORIES"));
}
static int ndir_is_continuous(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/CONTINUOUS") || tool_strsame(s, "-CONTINUOUS") ||
tool_strsame(s, "/CONTINUE") || tool_strsame(s, "-CONTINUE") ||
tool_strsame(s, "/C") || tool_strsame(s, "-C") ||
tool_strsame(s, "CONTINUOUS") || tool_strsame(s, "CONTINUE"));
}
static int ndir_is_rights(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/RIGHTS") || tool_strsame(s, "-RIGHTS") ||
tool_strsame(s, "RIGHTS"));
}
static int ndir_is_dates(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/DATES") || tool_strsame(s, "-DATES") ||
tool_strsame(s, "DATES"));
}
static int ndir_is_subdirs(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/SUB") || tool_strsame(s, "-SUB") ||
tool_strsame(s, "/SUBDIRECTORIES") || tool_strsame(s, "-SUBDIRECTORIES") ||
tool_strsame(s, "SUB") || tool_strsame(s, "SUBDIRECTORIES"));
}
static int ndir_is_short(char *s)
{
if (!s) return(0);
return(tool_strsame(s, "/SHORT") || tool_strsame(s, "-SHORT") ||
tool_strsame(s, "/BRIEF") || tool_strsame(s, "-BRIEF") ||
tool_strsame(s, "SHORT") || tool_strsame(s, "BRIEF"));
}
static int ndir_is_accepted_stub(char *s)
{
if (!s) return(0);
/*
* Keep this version tolerant of common NDIR format/options so users
* can compare command lines while unimplemented formats remain harmless.
*/
return(0);
}
static int ndir_path_is_dir(char *path)
{
struct stat st;
if (!path || !*path || tool_is_current_path(path))
return(1);
if (stat(path, &st) == 0) {
if (st.st_mode & S_IFDIR)
return(1);
}
return(0);
}
static void ndir_split_spec(char *spec, char *dir, char *pat)
{
if (!spec || !*spec || tool_is_current_path(spec)) {
strmaxcpy(dir, ".", 259);
strmaxcpy(pat, "*.*", 259);
return;
}
if (!tool_has_wildcards(spec) && ndir_path_is_dir(spec)) {
strmaxcpy(dir, spec, 259);
strmaxcpy(pat, "*.*", 259);
return;
}
tool_parent_pattern(dir, pat, spec, 260, 260);
}
static void ndir_dos_date(unsigned date, char *out)
{
int year;
int month;
int day;
year = ((date >> 9) & 0x7f) + 1980;
month = (date >> 5) & 0x0f;
day = date & 0x1f;
sprintf(out, "%02d-%02d-%02d", month, day, year % 100);
}
static void ndir_dos_datetime(unsigned date, unsigned time, char *out)
{
int year;
int month;
int day;
int hour;
int minute;
char ap;
year = ((date >> 9) & 0x7f) + 1980;
month = (date >> 5) & 0x0f;
day = date & 0x1f;
hour = (time >> 11) & 0x1f;
minute = (time >> 5) & 0x3f;
ap = (hour >= 12) ? 'p' : 'a';
if (hour == 0)
hour = 12;
else if (hour > 12)
hour -= 12;
sprintf(out, "%02d-%02d-%02d %2d:%02d%c",
month, day, year % 100, hour, minute, ap);
}
static unsigned long ndir_blocks_for_size(unsigned long size)
{
/*
* Novell NDIR reports allocated bytes/blocks. For this first DOS
* findfirst/findnext implementation, approximate with 4 KiB allocation
* units so small test files match the observed Novell output more closely.
*/
if (size == 0L)
return(0L);
return((size + 4095L) / 4096L);
}
static void ndir_flags(unsigned attr, char *out)
{
/*
* Keep the first positions close to Novell's default NDIR display,
* for example [RW-A------------].
*/
out[0] = '[';
out[1] = 'R';
out[2] = (attr & _A_RDONLY) ? 'O' : 'W';
out[3] = (attr & _A_SYSTEM) ? 'S' : '-';
out[4] = (attr & _A_ARCH) ? 'A' : '-';
out[5] = (attr & _A_HIDDEN) ? 'H' : '-';
memset(out + 6, '-', 11);
out[17] = ']';
out[18] = '\0';
}
static void ndir_parent_path(char *dst, char *src, int max)
{
char tmp[260];
char *p;
tool_upcopy(tmp, src, sizeof(tmp));
p = strrchr(tmp, '\\');
if (!p) p = strrchr(tmp, ':');
if (p) {
if (*p == ':') {
p[1] = '\0';
} else {
*p = '\0';
}
strmaxcpy(dst, tmp, max - 1);
} else {
dst[0] = '\0';
}
}
static void ndir_old_rights_string(uint8 old_rights, char *out)
{
out[0] = (old_rights & NDIR_OLD_RIGHT_S) ? 'S' : '-';
out[1] = (old_rights & NDIR_OLD_RIGHT_R) ? 'R' : '-';
out[2] = (old_rights & NDIR_OLD_RIGHT_W) ? 'W' : '-';
out[3] = (old_rights & NDIR_OLD_RIGHT_C) ? 'C' : '-';
out[4] = (old_rights & NDIR_OLD_RIGHT_E) ? 'E' : '-';
out[5] = (old_rights & NDIR_OLD_RIGHT_M) ? 'M' : '-';
out[6] = (old_rights & NDIR_OLD_RIGHT_F) ? 'F' : '-';
out[7] = (old_rights & NDIR_OLD_RIGHT_A) ? 'A' : '-';
out[8] = '\0';
}
static void ndir_rights_string(uint16 ncp_rights, char *out)
{
out[0] = (ncp_rights & NDIR_NCP_RIGHT_SUPER) ? 'S' : '-';
out[1] = (ncp_rights & NDIR_NCP_RIGHT_READ) ? 'R' : '-';
out[2] = (ncp_rights & NDIR_NCP_RIGHT_WRITE) ? 'W' : '-';
out[3] = (ncp_rights & NDIR_NCP_RIGHT_CREATE) ? 'C' : '-';
out[4] = (ncp_rights & NDIR_NCP_RIGHT_DELETE) ? 'E' : '-';
out[5] = (ncp_rights & NDIR_NCP_RIGHT_MODIFY) ? 'M' : '-';
out[6] = (ncp_rights & NDIR_NCP_RIGHT_SEARCH) ? 'F' : '-';
out[7] = (ncp_rights & NDIR_NCP_RIGHT_OWNER) ? 'A' : '-';
out[8] = '\0';
}
static void ndir_effective_rights(char *path, char *out)
{
uint8 connid = 0;
uint8 dhandle = 0;
uint8 eff_old = 0;
uint16 ncp_rights = 0;
char usepath[260];
int newhandle;
strcpy(out, "--------");
if (tool_current_dhandle(&connid, &dhandle))
return;
/*
* Prefer Client32 NCP87 effective-rights. If that fails for a listed
* entry, fall back to the older directory-handle effective-rights path
* that RIGHTS also uses.
*/
if (!c32_ncp87_get_effective_rights(tool_is_current_path(path) ? "" : path,
(uint16)dhandle,
&ncp_rights,
NULL, NULL, NULL)) {
ndir_rights_string(ncp_rights, out);
return;
}
ndir_parent_path(usepath, path, sizeof(usepath));
newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff_old);
if (newhandle >= 0) {
dealloc_dir_handle(newhandle);
ndir_old_rights_string(eff_old, out);
return;
}
if (usepath[0]) {
int subdir = 1;
int r = ncp_16_02(dhandle, (uint8 *)usepath, &subdir,
NULL, NULL, NULL);
if (r >= 0)
ndir_old_rights_string((uint8)r, out);
} else {
ndir_old_rights_string(0xff, out);
}
}
static int ndir_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 ndir_print_file(char *dir, struct find_t *ff, int options,
int *line_count, int *continuous)
{
char dt[24];
char d[12];
char fl[20]; /* [RW-A------------] plus NUL */
char path[260];
char eff[10];
ndir_dos_datetime(ff->wr_date, ff->wr_time, dt);
ndir_dos_date(ff->wr_date, d);
ndir_flags(ff->attrib, fl);
if (options & NDIR_OPT_RIGHTS) {
tool_join_path(path, dir, ff->name, sizeof(path));
ndir_effective_rights(path, eff);
/*
* We do not have the inherited-rights mask yet. Use the effective
* rights in both columns for this first /RIGHTS implementation.
*/
fprintf(stdout, "%-16.16s %-18.18s [%8.8s] [%8.8s]\n",
ff->name, fl, eff, eff);
} else if (options & NDIR_OPT_DATES) {
/*
* DOS findfirst gives us the update timestamp only. Keep the Novell
* /DATES layout and use placeholders for archive/access data until
* full NetWare namespace date fields are available.
*/
fprintf(stdout, "%-16.16s %-17.17s 0-00-00 0:00 A %-8.8s %-17.17s\n",
ff->name, dt, d, dt);
} else {
fprintf(stdout, "%-16.16s %12lu %-17.17s %-18.18s\n",
ff->name, (unsigned long)ff->size, dt, fl);
}
tool_page_line(line_count, continuous);
}
static void ndir_print_dir(char *dir, struct find_t *ff, int options,
int *line_count, int *continuous)
{
char dt[24];
char path[260];
char eff[10];
ndir_dos_datetime(ff->wr_date, ff->wr_time, dt);
if (options & (NDIR_OPT_RIGHTS | NDIR_OPT_DIRS_ONLY)) {
tool_join_path(path, dir, ff->name, sizeof(path));
ndir_effective_rights(path, eff);
fprintf(stdout, "%-16.16s [%8.8s] [%8.8s] %-17.17s\n",
ff->name, eff, eff, dt);
} else {
fprintf(stdout, "%-16.16s <DIR> %-17.17s\n", ff->name, dt);
}
tool_page_line(line_count, continuous);
}
static int ndir_scan_files(char *dir, char *search, int options,
int *line_count, int *continuous,
int *file_count, unsigned long *total_bytes,
unsigned long *total_blocks)
{
struct find_t ff;
int rc;
int shown = 0;
rc = _dos_findfirst(search, _A_NORMAL | _A_RDONLY | _A_HIDDEN |
_A_SYSTEM | _A_ARCH | _A_SUBDIR, &ff);
while (rc == 0) {
if (!(ff.attrib & _A_SUBDIR)) {
ndir_print_file(dir, &ff, options, line_count, continuous);
(*file_count)++;
*total_bytes += (unsigned long)ff.size;
*total_blocks += ndir_blocks_for_size((unsigned long)ff.size);
shown = 1;
}
rc = _dos_findnext(&ff);
}
return(shown);
}
static int ndir_scan_dirs(char *dir, char *search, int options,
int *line_count, int *continuous,
int *dir_count)
{
struct find_t ff;
int rc;
int shown = 0;
rc = _dos_findfirst(search, _A_NORMAL | _A_RDONLY | _A_HIDDEN |
_A_SYSTEM | _A_ARCH | _A_SUBDIR, &ff);
while (rc == 0) {
if ((ff.attrib & _A_SUBDIR) && !ndir_is_dot_dir(ff.name)) {
ndir_print_dir(dir, &ff, options, line_count, continuous);
(*dir_count)++;
shown = 1;
}
rc = _dos_findnext(&ff);
}
return(shown);
}
static int ndir_list_one(char *spec, int options, int *continuous)
{
char dir[260];
char pat[260];
char search[260];
char display[300];
int got = 0;
int files_shown = 0;
int dirs_shown = 0;
int file_count = 0;
int dir_count = 0;
unsigned long total_bytes = 0L;
unsigned long total_blocks = 0L;
int line_count = 0;
ndir_split_spec(spec, dir, pat);
tool_join_path(search, dir, pat, sizeof(search));
tool_header_path(display, dir, sizeof(display));
fprintf(stdout, "%s\n", display);
tool_page_line(&line_count, continuous);
if (!(options & NDIR_OPT_DIRS_ONLY)) {
if (!(options & NDIR_OPT_SHORT)) {
if (options & NDIR_OPT_RIGHTS) {
fprintf(stdout, "Files: Flags Rights Rights Owner\n");
fprintf(stdout, "---------------- ------------------ ----------- ----------- --------\n");
} else if (options & NDIR_OPT_DATES) {
fprintf(stdout, "Files: Last Updated Last Archived * Accessed Created/Copied\n");
fprintf(stdout, "---------------- ----------------- ------------- -- -------- -----------------\n");
} else {
fprintf(stdout, "Files: Size Last Updated Flags Owner\n");
fprintf(stdout, "---------------- ---------------- ----------------- ------------------ --------\n");
}
tool_page_line(&line_count, continuous);
tool_page_line(&line_count, continuous);
}
files_shown = ndir_scan_files(dir, search, options, &line_count,
continuous, &file_count,
&total_bytes, &total_blocks);
if (files_shown)
got = 1;
}
if (!(options & NDIR_OPT_FILES_ONLY)) {
if (!(options & NDIR_OPT_DIRS_ONLY) && !(options & NDIR_OPT_SHORT))
fprintf(stdout, "\n");
if (!(options & NDIR_OPT_SHORT)) {
if (options & (NDIR_OPT_RIGHTS | NDIR_OPT_DIRS_ONLY)) {
fprintf(stdout, "Directories: Rights Rights Owner Created/Copied\n");
fprintf(stdout, "---------------- ----------- ----------- ----------- -----------------\n");
} else {
fprintf(stdout, "Directories:\n");
fprintf(stdout, "----------------\n");
}
tool_page_line(&line_count, continuous);
tool_page_line(&line_count, continuous);
}
dirs_shown = ndir_scan_dirs(dir, search, options, &line_count,
continuous, &dir_count);
if (dirs_shown)
got = 1;
}
if (!got && (options & NDIR_OPT_DIRS_ONLY) && !(options & NDIR_OPT_SHORT))
fprintf(stdout, "No files of given specification found or directory is empty.\n");
if (!(options & NDIR_OPT_DIRS_ONLY)) {
if (options & NDIR_OPT_SHORT) {
if (file_count)
fprintf(stdout, " %lu bytes, %d files, %lu blocks\n",
total_bytes, file_count, total_blocks);
} else {
fprintf(stdout, "\n");
fprintf(stdout, "%12lu bytes in %5d files\n", total_bytes, file_count);
fprintf(stdout, "%12lu bytes in %5lu blocks\n", total_blocks * 4096L,
total_blocks);
}
}
return(got ? 0 : 1);
}
static int ndir_spec_has_output(char *spec, int options)
{
struct find_t ff;
char dir[260];
char pat[260];
char search[260];
int rc;
ndir_split_spec(spec, dir, pat);
tool_join_path(search, dir, pat, sizeof(search));
rc = _dos_findfirst(search, _A_NORMAL | _A_RDONLY | _A_HIDDEN |
_A_SYSTEM | _A_ARCH | _A_SUBDIR, &ff);
while (rc == 0) {
if (ff.attrib & _A_SUBDIR) {
if (!ndir_is_dot_dir(ff.name) && !(options & NDIR_OPT_FILES_ONLY))
return(1);
} else {
if (!(options & NDIR_OPT_DIRS_ONLY))
return(1);
}
rc = _dos_findnext(&ff);
}
return(0);
}
static int ndir_list_subdirs(char *dir, char *pattern, int options,
int *continuous)
{
struct find_t ff;
char search[260];
char subdir[260];
char subspec[260];
int rc;
int result = 0;
int had_any = 0;
tool_join_path(search, dir, "*.*", sizeof(search));
rc = _dos_findfirst(search, _A_NORMAL | _A_RDONLY | _A_HIDDEN |
_A_SYSTEM | _A_ARCH | _A_SUBDIR, &ff);
while (rc == 0) {
if ((ff.attrib & _A_SUBDIR) && !ndir_is_dot_dir(ff.name)) {
tool_join_path(subdir, dir, ff.name, sizeof(subdir));
tool_join_path(subspec, subdir, pattern, sizeof(subspec));
if (!(options & NDIR_OPT_SHORT) ||
ndir_spec_has_output(subspec, options & ~NDIR_OPT_SUB)) {
fprintf(stdout, "\n");
if (ndir_list_one(subspec, options & ~NDIR_OPT_SUB, continuous))
result = 1;
}
had_any = 1;
if (ndir_list_subdirs(subdir, pattern, options, continuous))
result = 1;
}
rc = _dos_findnext(&ff);
}
if (!had_any)
return(result);
return(result);
}
static int ndir_list(char *spec, int options, int *continuous)
{
char dir[260];
char pat[260];
int result;
result = ndir_list_one(spec, options & ~NDIR_OPT_SUB, continuous);
if (!(options & NDIR_OPT_SUB))
return(result);
ndir_split_spec(spec, dir, pat);
if (!pat[0])
strmaxcpy(pat, "*.*", sizeof(pat) - 1);
return(ndir_list_subdirs(dir, pat, options, continuous) ? 1 : result);
}
int func_ndir(int argc, char *argv[], int mode)
{
char *path = ".";
int have_path = 0;
int options = 0;
int continuous = 0;
int i;
(void)mode;
if (argc > 1 && ndir_is_help(argv[1])) {
ndir_usage();
return(0);
}
for (i = 1; i < argc; i++) {
if (ndir_is_help(argv[i])) {
ndir_usage();
return(0);
}
if (ndir_is_files_only(argv[i])) {
options |= NDIR_OPT_FILES_ONLY;
continue;
}
if (ndir_is_dirs_only(argv[i])) {
options |= NDIR_OPT_DIRS_ONLY;
continue;
}
if (ndir_is_continuous(argv[i])) {
continuous = 1;
continue;
}
if (ndir_is_rights(argv[i])) {
options |= NDIR_OPT_RIGHTS;
continue;
}
if (ndir_is_dates(argv[i])) {
options |= NDIR_OPT_DATES;
continue;
}
if (ndir_is_subdirs(argv[i])) {
options |= NDIR_OPT_SUB;
continue;
}
if (ndir_is_short(argv[i])) {
options |= NDIR_OPT_SHORT;
continue;
}
if (ndir_is_accepted_stub(argv[i])) {
continue;
}
if (tool_is_option(argv[i])) {
fprintf(stdout, "Type \"ndir /help\" on the command line for usage information.\n");
return(1);
}
if (have_path) {
fprintf(stdout, "Too many filenames in chain.\n");
return(1);
}
path = argv[i];
have_path = 1;
}
if ((options & NDIR_OPT_FILES_ONLY) && (options & NDIR_OPT_DIRS_ONLY)) {
fprintf(stdout, "No files of given specification found or directory is empty.\n");
return(1);
}
return(ndir_list(path, options, &continuous));
}

1
net.c
View File

@@ -39,6 +39,7 @@ static struct s_net_functions {
{"GRANT", "grant trustee rights", func_grant , 0},
{"REVOKE", "revoke trustee rights", func_revoke , 0},
{"REMOVE", "remove trustee", func_remove , 0},
{"NDIR", "list directory contents", func_ndir , 0},
{"RIGHTS", "display effective file/directory rights",func_rights, 0},
{"SLIST", "list servers", func_slist , 0},
{"PASSWD", "change password", func_passwd , 0},

BIN
net.exe

Binary file not shown.

1
net.h
View File

@@ -263,6 +263,7 @@ extern int read_command_file(char *fstr);
/* slist.c */
extern int func_slist (int argc, char *argv[], int mode);
extern int func_ndir (int argc, char *argv[], int mode);
/* nwdebug.c */
extern int func_debug (int argc, char *argv[], int mode);