diff --git a/CMakeLists.txt b/CMakeLists.txt index b6935d5..9111825 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/README.md b/README.md index 1085da6..e5d54cd 100644 --- a/README.md +++ b/README.md @@ -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` diff --git a/ndir.c b/ndir.c new file mode 100644 index 0000000..1ee07c6 --- /dev/null +++ b/ndir.c @@ -0,0 +1,729 @@ +/* ndir.c - first Novell NDIR-like directory listing utility */ + +#include "net.h" +#include "c32ncp.h" +#include +#include +#include + +#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 \n"); + fprintf(stdout, " SIze \n"); + fprintf(stdout, " UPdate \n"); + fprintf(stdout, " CReate \n"); + fprintf(stdout, " ACcess \n"); + fprintf(stdout, " ARchive \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 %-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)); +} diff --git a/net.c b/net.c index cbee6a3..e015d00 100644 --- a/net.c +++ b/net.c @@ -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}, diff --git a/net.exe b/net.exe index e7db07f..03f6716 100755 Binary files a/net.exe and b/net.exe differ diff --git a/net.h b/net.h index d90038a..bd7e9ee 100644 --- a/net.h +++ b/net.h @@ -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);