/* 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_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 \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_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 uint32 ndir_get_dword_lh(uint8 *p) { return((uint32)p[0] | ((uint32)p[1] << 8) | ((uint32)p[2] << 16) | ((uint32)p[3] << 24)); } static int ndir_copy_ncp22_name(uint8 *dst, char *src, uint8 *len_out) { char tmp[260]; int len; if (!dst || !len_out) return(-1); if (!src) src = ""; tool_upcopy(tmp, src, sizeof(tmp)); if (strchr(tmp, '\\') || strchr(tmp, '/') || strchr(tmp, ':')) return(-1); len = strlen(tmp); if (len < 1 || len > 12) return(-1); memcpy(dst, tmp, len); *len_out = (uint8)len; return(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 (ndir_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 = ndir_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 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, 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 || !ndir_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 (!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 (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)); }