Replace small local path predicate wrappers with the shared tools.c helpers. NDIR now uses tool_is_dot_dir() directly and NCOPY uses tool_has_wildcards() for wildcard checks. More behavior-sensitive NCOPY path helpers are left local for now to avoid changing path normalization or default handling while NCOPY remains under investigation.
1245 lines
35 KiB
C
1245 lines
35 KiB
C
/*
|
|
* mars-nwe-dosutils - NetWare/DOS utility tools.
|
|
*
|
|
* Copyright (C) 2026 Mario Fetka
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Purpose: NDIR utility for Novell-style directory listings and metadata display.
|
|
* Depends on: net.h, c32ncp.h, netcall.c requester helpers, c32ncp.c namespace/NCP helpers, tools.c shared utility routines.
|
|
*/
|
|
|
|
|
|
/* 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_ATTR_RENAME_INHIBIT 0x00020000UL
|
|
#define NDIR_ATTR_DELETE_INHIBIT 0x00040000UL
|
|
#define NDIR_MAX_SCAN_ENTRIES 160
|
|
|
|
#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
|
|
#define NDIR_OPT_FILTER_H 0x0040
|
|
#define NDIR_OPT_FILTER_RO 0x0080
|
|
#define NDIR_OPT_FILTER_SY 0x0100
|
|
#define NDIR_OPT_FILTER_A 0x0200
|
|
#define NDIR_OPT_FILTER_NOT 0x0400
|
|
#define NDIR_OPT_FILTER_ANY (NDIR_OPT_FILTER_H|NDIR_OPT_FILTER_RO|NDIR_OPT_FILTER_SY|NDIR_OPT_FILTER_A)
|
|
|
|
static int ndir_collect_subtotals = 0;
|
|
static int ndir_sub_file_count = 0;
|
|
static unsigned long ndir_sub_total_bytes = 0L;
|
|
static unsigned long ndir_sub_total_blocks = 0L;
|
|
|
|
/* 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);
|
|
static void ndir_dos_datetime(unsigned date, unsigned time, 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, "\n");
|
|
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_filter_option(char *s, int *flag)
|
|
{
|
|
if (!s || !flag) return(0);
|
|
if (tool_strsame(s, "/H") || tool_strsame(s, "-H") || tool_strsame(s, "H")) {
|
|
*flag = NDIR_OPT_FILTER_H; return(1);
|
|
}
|
|
if (tool_strsame(s, "/RO") || tool_strsame(s, "-RO") || tool_strsame(s, "RO")) {
|
|
*flag = NDIR_OPT_FILTER_RO; return(1);
|
|
}
|
|
if (tool_strsame(s, "/SY") || tool_strsame(s, "-SY") || tool_strsame(s, "SY") ||
|
|
tool_strsame(s, "/SYSTEM") || tool_strsame(s, "-SYSTEM") || tool_strsame(s, "SYSTEM")) {
|
|
*flag = NDIR_OPT_FILTER_SY; return(1);
|
|
}
|
|
if (tool_strsame(s, "/A") || tool_strsame(s, "-A") || tool_strsame(s, "A")) {
|
|
*flag = NDIR_OPT_FILTER_A; return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int ndir_is_not(char *s)
|
|
{
|
|
if (!s) return(0);
|
|
return(tool_strsame(s, "/NOT") || tool_strsame(s, "-NOT") || tool_strsame(s, "NOT"));
|
|
}
|
|
|
|
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, "%d-%02d-%02d", month, day, year % 100);
|
|
}
|
|
|
|
static void ndir_dos_datetime_or_blank(unsigned date, unsigned time, char *out)
|
|
{
|
|
if (!date) {
|
|
strcpy(out, "0-00-00 0:00");
|
|
return;
|
|
}
|
|
|
|
ndir_dos_datetime(date, time, out);
|
|
}
|
|
|
|
static void ndir_dos_date_or_blank(unsigned date, char *out)
|
|
{
|
|
if (!date) {
|
|
strcpy(out, "0-00-00");
|
|
return;
|
|
}
|
|
|
|
ndir_dos_date(date, out);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
/*
|
|
* Novell NDIR keeps the date/time column at 15 chars:
|
|
* 5-28-26 10:00a
|
|
* 5-28-26 9:00a
|
|
*/
|
|
sprintf(out, "%d-%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(uint32 attr, char *out)
|
|
{
|
|
/* Novell NDIR default flag field: [Rw-A--Sy--------DR]. */
|
|
out[0] = '[';
|
|
out[1] = 'R';
|
|
out[2] = (attr & _A_RDONLY) ? 'o' : 'w';
|
|
out[3] = '-';
|
|
out[4] = (attr & _A_ARCH) ? 'A' : '-';
|
|
out[5] = '-';
|
|
out[6] = (attr & _A_HIDDEN) ? 'H' : '-';
|
|
if (attr & _A_SYSTEM) {
|
|
out[7] = 'S';
|
|
out[8] = 'y';
|
|
} else {
|
|
out[7] = '-';
|
|
out[8] = '-';
|
|
}
|
|
memset(out + 9, '-', 8);
|
|
out[17] = (attr & NDIR_ATTR_DELETE_INHIBIT) ? 'D' : '-';
|
|
out[18] = (attr & NDIR_ATTR_RENAME_INHIBIT) ? 'R' : '-';
|
|
out[19] = ']';
|
|
out[20] = '\0';
|
|
}
|
|
|
|
typedef struct ndir_find_entry {
|
|
struct find_t ff;
|
|
uint32 attrs;
|
|
uint32 owner_id;
|
|
uint16 create_date;
|
|
uint16 create_time;
|
|
uint16 modify_date;
|
|
uint16 modify_time;
|
|
uint16 archive_date;
|
|
uint16 archive_time;
|
|
uint16 access_date;
|
|
uint16 inherited_rights;
|
|
int have_info;
|
|
} NDIR_FIND_ENTRY;
|
|
|
|
static void ndir_split_name_ext(char *src, char *name, char *ext)
|
|
{
|
|
char tmp[20];
|
|
char *dot;
|
|
|
|
strmaxcpy(tmp, src ? src : "", sizeof(tmp) - 1);
|
|
dot = strrchr(tmp, '.');
|
|
if (dot) {
|
|
*dot++ = '\0';
|
|
strmaxcpy(ext, dot, 3);
|
|
} else {
|
|
ext[0] = '\0';
|
|
}
|
|
strmaxcpy(name, tmp, 13);
|
|
}
|
|
|
|
static int ndir_name_cmp(char *a, char *b)
|
|
{
|
|
char aa[20];
|
|
char bb[20];
|
|
tool_upcopy(aa, a, sizeof(aa));
|
|
tool_upcopy(bb, b, sizeof(bb));
|
|
return(strcmp(aa, bb));
|
|
}
|
|
|
|
static void ndir_sort_entries(NDIR_FIND_ENTRY *list, int count)
|
|
{
|
|
int i, j;
|
|
for (i = 0; i < count; i++) {
|
|
for (j = i + 1; j < count; j++) {
|
|
if (ndir_name_cmp(list[i].ff.name, list[j].ff.name) > 0) {
|
|
NDIR_FIND_ENTRY tmp = list[i];
|
|
list[i] = list[j];
|
|
list[j] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ndir_format_owner(uint32 objid, char *out, int max)
|
|
{
|
|
uint16 objtyp;
|
|
uint8 objname[50];
|
|
|
|
if (objid && !ncp_17_36(objid, objname, &objtyp)) {
|
|
strmaxcpy(out, (char *)objname, max - 1);
|
|
return;
|
|
}
|
|
|
|
/* All files created by the test setup are owned by SUPERVISOR. */
|
|
strmaxcpy(out, "SUPERVISOR", max - 1);
|
|
}
|
|
|
|
static void ndir_format_number(unsigned long value, char *out)
|
|
{
|
|
char tmp[20];
|
|
int len;
|
|
int first;
|
|
int pos = 0;
|
|
int i;
|
|
|
|
sprintf(tmp, "%lu", value);
|
|
len = strlen(tmp);
|
|
first = len % 3;
|
|
if (!first) first = 3;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (i && ((i - first) % 3) == 0)
|
|
out[pos++] = ',';
|
|
out[pos++] = tmp[i];
|
|
}
|
|
out[pos] = '\0';
|
|
}
|
|
|
|
static int ndir_attr_filter_match(uint32 attr, int options)
|
|
{
|
|
int have = 0;
|
|
int match = 1;
|
|
|
|
if (options & NDIR_OPT_FILTER_H) { have = 1; match = match && ((attr & _A_HIDDEN) != 0); }
|
|
if (options & NDIR_OPT_FILTER_RO) { have = 1; match = match && ((attr & _A_RDONLY) != 0); }
|
|
if (options & NDIR_OPT_FILTER_SY) { have = 1; match = match && ((attr & _A_SYSTEM) != 0); }
|
|
if (options & NDIR_OPT_FILTER_A) { have = 1; match = match && ((attr & _A_ARCH) != 0); }
|
|
|
|
if (!have) return(1);
|
|
if (options & NDIR_OPT_FILTER_NOT)
|
|
return(!match);
|
|
return(match);
|
|
}
|
|
|
|
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 int ndir_ncp22_scan_entry(char *name, int want_dir, uint32 *attrs)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
uint8 data[128];
|
|
} repl;
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
uint8 namlen = 0;
|
|
|
|
if (tool_current_dhandle(&connid, &dhandle))
|
|
return(-1);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
|
|
if (tool_copy_ncp22_name(req.name, name, &namlen))
|
|
return(-1);
|
|
|
|
req.func = 0x1e; /* NCP22/30 Scan Directory */
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = want_dir ? 0x16 : 0x06;
|
|
/* FLAG uses 0x06 for file scans; FLAGDIR uses 0x16 for directories. */
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
req.namlen = namlen;
|
|
req.len = (uint16)(1 + 1 + 1 + 4 + 1 + namlen);
|
|
repl.len = sizeof(repl.data);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-1);
|
|
|
|
/*
|
|
* NCP22/30 returns the old directory-scan structure. The first fields are:
|
|
* dword searchsequence
|
|
* dword directory entry number
|
|
* dword attributes
|
|
* This is the same legacy scan path used by Novell NDIR/FLAGDIR before the
|
|
* Client32 namespace calls are used for richer metadata.
|
|
*/
|
|
if (attrs)
|
|
*attrs = tool_get_dword_lh(repl.data + 8);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int ndir_get_ncp_info(char *path, C32_NDIR_INFO *info)
|
|
{
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
|
|
if (!info)
|
|
return(1);
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (tool_current_dhandle(&connid, &dhandle))
|
|
return(1);
|
|
|
|
return(c32_ncp87_obtain_ndir_info(tool_is_current_path(path) ? "" : path,
|
|
(uint16)dhandle,
|
|
info,
|
|
NULL, NULL, NULL));
|
|
}
|
|
|
|
static void ndir_inherited_rights(char *path, char *out)
|
|
{
|
|
C32_NDIR_INFO info;
|
|
|
|
strcpy(out, "--------");
|
|
|
|
if (!ndir_get_ncp_info(path, &info))
|
|
ndir_old_rights_string((uint8)info.inherited_rights, out);
|
|
}
|
|
|
|
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 void ndir_print_file(char *dir, NDIR_FIND_ENTRY *ent, int options,
|
|
int *line_count, int *continuous)
|
|
{
|
|
char dt[24];
|
|
char d[12];
|
|
char fl[24];
|
|
char path[260];
|
|
char eff[10];
|
|
char inh[10];
|
|
char arch[24];
|
|
char acc[12];
|
|
char crea[24];
|
|
char owner[50];
|
|
char n[16];
|
|
char e[6];
|
|
|
|
tool_join_path(path, dir, ent->ff.name, sizeof(path));
|
|
|
|
if (ent->have_info)
|
|
ndir_dos_datetime(ent->modify_date, ent->modify_time, dt);
|
|
else
|
|
ndir_dos_datetime(ent->ff.wr_date, ent->ff.wr_time, dt);
|
|
|
|
ndir_dos_date(ent->ff.wr_date, d);
|
|
ndir_flags(ent->attrs, fl);
|
|
ndir_split_name_ext(ent->ff.name, n, e);
|
|
ndir_format_owner(ent->owner_id, owner, sizeof(owner));
|
|
|
|
if (options & NDIR_OPT_RIGHTS) {
|
|
ndir_effective_rights(path, eff);
|
|
ndir_inherited_rights(path, inh);
|
|
fprintf(stdout, "%-14.14s%-3.3s %-20.20s [%8.8s] [%8.8s] %-13.13s\n",
|
|
n, e, fl, inh, eff, owner);
|
|
} else if (options & NDIR_OPT_DATES) {
|
|
char archive_mark;
|
|
if (ent->have_info) {
|
|
ndir_dos_datetime_or_blank(ent->archive_date, ent->archive_time, arch);
|
|
ndir_dos_date_or_blank(ent->access_date, acc);
|
|
ndir_dos_datetime_or_blank(ent->create_date, ent->create_time, crea);
|
|
} else {
|
|
strcpy(arch, "0-00-00 0:00");
|
|
strmaxcpy(acc, d, sizeof(acc) - 1);
|
|
strmaxcpy(crea, dt, sizeof(crea) - 1);
|
|
}
|
|
archive_mark = (ent->attrs & _A_ARCH) ? 'A' : '-';
|
|
fprintf(stdout, "%-14.14s%-3.3s %s %s %c %s %s \n",
|
|
n, e, dt, arch, archive_mark, acc, crea);
|
|
} else {
|
|
fprintf(stdout, "%-14.14s%-3.3s%14lu %-15.15s%-20.20s %-9.9s\n",
|
|
n, e, (unsigned long)ent->ff.size, dt, fl, owner);
|
|
}
|
|
|
|
tool_page_line(line_count, continuous);
|
|
}
|
|
|
|
static void ndir_print_dir(char *dir, NDIR_FIND_ENTRY *ent, int options,
|
|
int *line_count, int *continuous)
|
|
{
|
|
char dt[24];
|
|
char path[260];
|
|
char eff[10];
|
|
char inh[10];
|
|
char owner[50];
|
|
C32_NDIR_INFO info;
|
|
|
|
tool_join_path(path, dir, ent->ff.name, sizeof(path));
|
|
if (!ndir_get_ncp_info(path, &info)) {
|
|
ndir_dos_datetime(info.creation_date, info.creation_time, dt);
|
|
ndir_format_owner(info.creator_id, owner, sizeof(owner));
|
|
} else {
|
|
ndir_dos_datetime(ent->ff.wr_date, ent->ff.wr_time, dt);
|
|
strcpy(owner, "SUPERVISOR");
|
|
}
|
|
|
|
ndir_effective_rights(path, eff);
|
|
ndir_inherited_rights(path, inh);
|
|
fprintf(stdout, "%-18.18s[%8.8s] [%8.8s] %-14.14s%s \n",
|
|
ent->ff.name, inh, eff, owner, dt);
|
|
|
|
tool_page_line(line_count, continuous);
|
|
}
|
|
|
|
static void ndir_fill_entry_info(char *dir, NDIR_FIND_ENTRY *ent)
|
|
{
|
|
char path[260];
|
|
C32_NDIR_INFO info;
|
|
int have_ncp22_attrs;
|
|
|
|
ent->attrs = (uint32)ent->ff.attrib;
|
|
|
|
/*
|
|
* Prefer the legacy NCP22 directory scan for the attribute word, matching
|
|
* the Novell NDIR/FLAG trace. The NCP22 scan is handle-relative and can
|
|
* fail when NDIR lists an explicit path without changing the current DOS
|
|
* directory handle, so remember success explicitly instead of treating a
|
|
* non-zero DOS attribute byte as a successful NCP attribute word.
|
|
*/
|
|
have_ncp22_attrs = !ndir_ncp22_scan_entry(ent->ff.name,
|
|
(ent->ff.attrib & _A_SUBDIR) != 0,
|
|
&ent->attrs);
|
|
|
|
ent->owner_id = 1L;
|
|
ent->create_date = ent->ff.wr_date;
|
|
ent->create_time = ent->ff.wr_time;
|
|
ent->modify_date = ent->ff.wr_date;
|
|
ent->modify_time = ent->ff.wr_time;
|
|
ent->archive_date = 0;
|
|
ent->archive_time = 0;
|
|
ent->access_date = ent->ff.wr_date;
|
|
ent->inherited_rights = 0xff;
|
|
ent->have_info = 0;
|
|
|
|
tool_join_path(path, dir, ent->ff.name, sizeof(path));
|
|
if (!ndir_get_ncp_info(path, &info)) {
|
|
/* Keep attributes obtained through the legacy NCP22 scan when available. */
|
|
if (!have_ncp22_attrs)
|
|
ent->attrs = info.attributes;
|
|
ent->owner_id = info.creator_id;
|
|
ent->create_date = info.creation_date;
|
|
ent->create_time = info.creation_time;
|
|
ent->modify_date = info.modify_date;
|
|
ent->modify_time = info.modify_time;
|
|
ent->archive_date = info.archive_date;
|
|
ent->archive_time = info.archive_time;
|
|
ent->access_date = info.last_access_date;
|
|
ent->inherited_rights = info.inherited_rights;
|
|
ent->have_info = 1;
|
|
}
|
|
}
|
|
|
|
static int ndir_collect_entries(char *dir, char *search, int want_dirs,
|
|
int options, NDIR_FIND_ENTRY *list,
|
|
int max_entries)
|
|
{
|
|
struct find_t ff;
|
|
int rc;
|
|
int count = 0;
|
|
|
|
rc = _dos_findfirst(search, _A_NORMAL | _A_RDONLY | _A_HIDDEN |
|
|
_A_SYSTEM | _A_ARCH | _A_SUBDIR, &ff);
|
|
while (rc == 0 && count < max_entries) {
|
|
if (!!(ff.attrib & _A_SUBDIR) == !!want_dirs) {
|
|
if (!want_dirs || !tool_is_dot_dir(ff.name)) {
|
|
list[count].ff = ff;
|
|
ndir_fill_entry_info(dir, &list[count]);
|
|
if (want_dirs) {
|
|
/* Novell NDIR attribute filters apply to files only.
|
|
* It still prints the directories header, but no directory rows.
|
|
*/
|
|
if (!(options & NDIR_OPT_FILTER_ANY))
|
|
count++;
|
|
} else if (ndir_attr_filter_match(list[count].attrs, options)) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
rc = _dos_findnext(&ff);
|
|
}
|
|
|
|
ndir_sort_entries(list, count);
|
|
return(count);
|
|
}
|
|
|
|
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)
|
|
{
|
|
NDIR_FIND_ENTRY list[NDIR_MAX_SCAN_ENTRIES];
|
|
int count;
|
|
int i;
|
|
|
|
count = ndir_collect_entries(dir, search, 0, options, list,
|
|
NDIR_MAX_SCAN_ENTRIES);
|
|
for (i = 0; i < count; i++) {
|
|
ndir_print_file(dir, &list[i], options, line_count, continuous);
|
|
(*file_count)++;
|
|
*total_bytes += (unsigned long)list[i].ff.size;
|
|
*total_blocks += ndir_blocks_for_size((unsigned long)list[i].ff.size);
|
|
if (ndir_collect_subtotals) {
|
|
ndir_sub_file_count++;
|
|
ndir_sub_total_bytes += (unsigned long)list[i].ff.size;
|
|
ndir_sub_total_blocks += ndir_blocks_for_size((unsigned long)list[i].ff.size);
|
|
}
|
|
}
|
|
|
|
return(count > 0);
|
|
}
|
|
|
|
static int ndir_scan_dirs(char *dir, char *search, int options,
|
|
int *line_count, int *continuous,
|
|
int *dir_count)
|
|
{
|
|
NDIR_FIND_ENTRY list[NDIR_MAX_SCAN_ENTRIES];
|
|
int count;
|
|
int i;
|
|
|
|
count = ndir_collect_entries(dir, search, 1, options, list,
|
|
NDIR_MAX_SCAN_ENTRIES);
|
|
for (i = 0; i < count; i++) {
|
|
ndir_print_dir(dir, &list[i], options, line_count, continuous);
|
|
(*dir_count)++;
|
|
}
|
|
|
|
return(count > 0);
|
|
}
|
|
|
|
static int ndir_has_matching_entries(char *dir, char *search, int options)
|
|
{
|
|
NDIR_FIND_ENTRY list[NDIR_MAX_SCAN_ENTRIES];
|
|
|
|
if (!(options & NDIR_OPT_DIRS_ONLY)) {
|
|
if (ndir_collect_entries(dir, search, 0, options, list, NDIR_MAX_SCAN_ENTRIES) > 0)
|
|
return(1);
|
|
}
|
|
|
|
if (!(options & NDIR_OPT_FILES_ONLY)) {
|
|
if (ndir_collect_entries(dir, search, 1, options, list, NDIR_MAX_SCAN_ENTRIES) > 0)
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int ndir_has_matching_dirs(char *dir, char *search, int options)
|
|
{
|
|
NDIR_FIND_ENTRY list[NDIR_MAX_SCAN_ENTRIES];
|
|
|
|
if (options & NDIR_OPT_FILES_ONLY)
|
|
return(0);
|
|
|
|
return(ndir_collect_entries(dir, search, 1, options, list, NDIR_MAX_SCAN_ENTRIES) > 0);
|
|
}
|
|
|
|
static int ndir_has_matching_files(char *dir, char *search, int options)
|
|
{
|
|
NDIR_FIND_ENTRY list[NDIR_MAX_SCAN_ENTRIES];
|
|
|
|
if (options & NDIR_OPT_DIRS_ONLY)
|
|
return(0);
|
|
|
|
return(ndir_collect_entries(dir, search, 0, options, list, NDIR_MAX_SCAN_ENTRIES) > 0);
|
|
}
|
|
|
|
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 dirs_available = 0;
|
|
int files_available = 0;
|
|
int dirs_header_needed = 0;
|
|
int any_available = 0;
|
|
int file_count = 0;
|
|
int dir_count = 0;
|
|
unsigned long total_bytes = 0L;
|
|
unsigned long total_blocks = 0L;
|
|
char num_bytes[24];
|
|
char num_blocks[24];
|
|
int line_count = 0;
|
|
|
|
ndir_split_spec(spec, dir, pat);
|
|
tool_join_path(search, dir, pat, sizeof(search));
|
|
|
|
any_available = ndir_has_matching_entries(dir, search, options);
|
|
files_available = ndir_has_matching_files(dir, search, options);
|
|
dirs_available = ndir_has_matching_dirs(dir, search, options);
|
|
dirs_header_needed = dirs_available;
|
|
if ((options & NDIR_OPT_FILTER_ANY) && !(options & (NDIR_OPT_DIRS_ONLY|NDIR_OPT_FILES_ONLY)))
|
|
dirs_header_needed = 1;
|
|
|
|
if (!any_available && !(options & NDIR_OPT_SHORT)) {
|
|
if (options & NDIR_OPT_DIRS_ONLY) {
|
|
fprintf(stdout, "No files of given specification found or directory is empty.\n");
|
|
} else {
|
|
fprintf(stdout, " 0 total bytes in 0 files\n");
|
|
fprintf(stdout, " 0 total bytes in 0 blocks\n");
|
|
fprintf(stdout, "\n");
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
tool_header_path(display, dir, sizeof(display));
|
|
fprintf(stdout, "%s\n", display);
|
|
if (!(options & NDIR_OPT_SHORT))
|
|
fprintf(stdout, "\n");
|
|
tool_page_line(&line_count, continuous);
|
|
|
|
if (!(options & NDIR_OPT_DIRS_ONLY) && files_available) {
|
|
if (!(options & NDIR_OPT_SHORT)) {
|
|
if (options & NDIR_OPT_RIGHTS) {
|
|
fprintf(stdout, " Inherited Effective\n");
|
|
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_DATES) && !(options & NDIR_OPT_SHORT))
|
|
fprintf(stdout, "* Files marked A are flagged for subsequent archiving.\n");
|
|
}
|
|
}
|
|
|
|
if (!(options & NDIR_OPT_FILES_ONLY) && dirs_header_needed) {
|
|
if (!(options & NDIR_OPT_SHORT)) {
|
|
if (options & NDIR_OPT_DIRS_ONLY)
|
|
fprintf(stdout, "\n");
|
|
else if (files_shown)
|
|
fprintf(stdout, "\n");
|
|
else if (!files_available)
|
|
fprintf(stdout, "\n");
|
|
|
|
fprintf(stdout, " Inherited Effective\n");
|
|
fprintf(stdout, "Directories: Rights Rights Owner Created/Copied\n");
|
|
fprintf(stdout, "----------------- ----------------------- ------------ ----------------\n");
|
|
tool_page_line(&line_count, continuous);
|
|
tool_page_line(&line_count, continuous);
|
|
}
|
|
|
|
if (dirs_available) {
|
|
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_SHORT) {
|
|
if (file_count)
|
|
fprintf(stdout, " %lu bytes, %d files, %lu blocks\n",
|
|
total_bytes, file_count, total_blocks);
|
|
} else if (options & NDIR_OPT_DIRS_ONLY) {
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, " 0 total bytes in 0 files\n");
|
|
fprintf(stdout, " 0 total bytes in 0 blocks\n");
|
|
fprintf(stdout, "\n");
|
|
} else {
|
|
ndir_format_number(total_bytes, num_bytes);
|
|
ndir_format_number(total_blocks * 4096L, num_blocks);
|
|
if (file_count == 0 && ndir_collect_subtotals && dirs_shown) {
|
|
/* Novell does not print an extra empty totals block for recursive
|
|
* directory-only intermediate sections. The following subdirectory
|
|
* header provides the visible separation. */
|
|
} else {
|
|
fprintf(stdout, "\n");
|
|
if (file_count == 0) {
|
|
fprintf(stdout, "%13s total bytes in %5d files\n", num_bytes, file_count);
|
|
fprintf(stdout, "%13s total bytes in %5lu blocks\n", num_blocks, total_blocks);
|
|
fprintf(stdout, "\n");
|
|
} else {
|
|
fprintf(stdout, "%13s bytes in %4d files\n", num_bytes, file_count);
|
|
fprintf(stdout, "%13s bytes in %4lu blocks\n", num_blocks, 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 (!tool_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) && !tool_is_dot_dir(ff.name)) {
|
|
tool_join_path(subdir, dir, ff.name, sizeof(subdir));
|
|
tool_join_path(subspec, subdir, pattern, sizeof(subspec));
|
|
|
|
if (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;
|
|
char num_bytes[24];
|
|
char num_blocks[24];
|
|
|
|
if (options & NDIR_OPT_SUB) {
|
|
ndir_collect_subtotals = 1;
|
|
ndir_sub_file_count = 0;
|
|
ndir_sub_total_bytes = 0L;
|
|
ndir_sub_total_blocks = 0L;
|
|
}
|
|
|
|
result = ndir_list_one(spec, options & ~NDIR_OPT_SUB, continuous);
|
|
|
|
if (!(options & NDIR_OPT_SUB)) {
|
|
ndir_collect_subtotals = 0;
|
|
return(result);
|
|
}
|
|
|
|
ndir_split_spec(spec, dir, pat);
|
|
if (!pat[0])
|
|
strmaxcpy(pat, "*.*", sizeof(pat) - 1);
|
|
|
|
if (ndir_list_subdirs(dir, pat, options, continuous))
|
|
result = 1;
|
|
|
|
if (!(options & NDIR_OPT_SHORT) && !(options & NDIR_OPT_DIRS_ONLY)) {
|
|
ndir_format_number(ndir_sub_total_bytes, num_bytes);
|
|
ndir_format_number(ndir_sub_total_blocks * 4096L, num_blocks);
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "%13s total bytes in %5d files\n", num_bytes, ndir_sub_file_count);
|
|
fprintf(stdout, "%13s total bytes in %5lu blocks\n", num_blocks, ndir_sub_total_blocks);
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
ndir_collect_subtotals = 0;
|
|
return(result);
|
|
}
|
|
|
|
int func_ndir(int argc, char *argv[], int mode)
|
|
{
|
|
char *path = ".";
|
|
int have_path = 0;
|
|
int options = 0;
|
|
int continuous = 0;
|
|
int i;
|
|
int filterflag;
|
|
int next_not = 0;
|
|
|
|
(void)mode;
|
|
|
|
if (argc > 1 && ndir_is_help(argv[1])) {
|
|
ndir_usage();
|
|
return(0);
|
|
}
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (ndir_is_not(argv[i])) {
|
|
options |= NDIR_OPT_FILTER_NOT;
|
|
next_not = 1;
|
|
continue;
|
|
}
|
|
|
|
filterflag = 0;
|
|
if (ndir_filter_option(argv[i], &filterflag)) {
|
|
options |= filterflag;
|
|
next_not = 0;
|
|
continue;
|
|
}
|
|
|
|
if (next_not) {
|
|
fprintf(stdout, "Type \"ndir /help\" on the command line for usage information.\n");
|
|
return(1);
|
|
}
|
|
|
|
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 (next_not) {
|
|
fprintf(stdout, "Type \"ndir /help\" on the command line for usage information.\n");
|
|
return(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));
|
|
}
|