Finish the c32ncp.c/c32ncp.h to ncpapi.c/ncpapi.h rename. Update the build files, include directives, dependency tracking, header guard, README references and file-level comments to use the new ncpapi naming. This matches the current split where ncpapi contains the ncpXX_YY_* protocol API wrappers and ncpcall contains the lower-level requester/transport helpers. No behavior change.
585 lines
14 KiB
C
585 lines
14 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: FLAGDIR utility for displaying and changing NetWare directory attributes.
|
|
* Depends on: net.h, ncpapi.h, netcall.c requester helpers, ncpapi.c namespace/NCP helpers, tools.c shared utility routines.
|
|
*/
|
|
/* flagdir.c - Novell FLAGDIR-like DOS utility, Client32 version */
|
|
|
|
#include "net.h"
|
|
#include "ncpapi.h"
|
|
#include <dos.h>
|
|
#include <direct.h>
|
|
|
|
#define FD_NWFA_H 0x00000002UL
|
|
#define FD_NWFA_SY 0x00000004UL
|
|
#define FD_NWFA_P 0x00010000UL
|
|
#define FD_NWFA_RI 0x00020000UL
|
|
#define FD_NWFA_DI 0x00040000UL
|
|
|
|
#define FD_DIR_BITS (FD_NWFA_H | FD_NWFA_SY | FD_NWFA_P | FD_NWFA_RI | FD_NWFA_DI)
|
|
#define FD_DM_ATTRIBUTES 0x00000002UL
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
static int fd_ncp22_1e_obtain_attrs(char *name, 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; /* Scan directory */
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x16; /* hidden/system/subdir */
|
|
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 Scan Directory returns attributes at offset 8. */
|
|
if (attrs)
|
|
*attrs = tool_get_dword_lh(repl.data + 8);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int fd_ncp22_25_modify_attrs(char *name, uint32 attrs)
|
|
{
|
|
struct {
|
|
uint16 len;
|
|
uint8 func;
|
|
uint8 dirhandle;
|
|
uint8 search_attributes;
|
|
uint8 searchsequence[4];
|
|
uint8 change_bits[4];
|
|
uint8 subdir[4];
|
|
uint8 attributes[4];
|
|
uint8 uniqueid;
|
|
uint8 flags;
|
|
uint8 namespace;
|
|
uint8 namlen;
|
|
uint8 name[12];
|
|
uint8 rest[104];
|
|
} req;
|
|
struct {
|
|
uint16 len;
|
|
} repl;
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
|
|
if (tool_current_dhandle(&connid, &dhandle))
|
|
return(-1);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&repl, 0, sizeof(repl));
|
|
|
|
req.func = 0x25; /* Set directory/file information */
|
|
req.dirhandle = dhandle;
|
|
req.search_attributes = 0x16; /* hidden/system/subdir */
|
|
U32_TO_BE32(0xffffffffUL, req.searchsequence);
|
|
U32_TO_32(FD_DM_ATTRIBUTES, req.change_bits);
|
|
tool_put_dword_lh(req.attributes, attrs);
|
|
|
|
if (tool_copy_ncp22_name(req.name, name, &req.namlen))
|
|
return(-1);
|
|
|
|
req.len = sizeof(req) - sizeof(req.len);
|
|
|
|
neterrno = Net_Call(0xE200, &req, &repl);
|
|
if (neterrno)
|
|
return(-1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static char *fd_last_sep(char *s)
|
|
{
|
|
char *last = NULL;
|
|
char *p;
|
|
|
|
for (p = s; *p; p++) {
|
|
if (*p == '\\' || *p == '/' || *p == ':')
|
|
last = p;
|
|
}
|
|
|
|
return(last);
|
|
}
|
|
|
|
static void fd_split_pattern(char *pattern, char *parent, int parent_size,
|
|
char *leaf, int leaf_size)
|
|
{
|
|
char temp[260];
|
|
char *sep;
|
|
int plen;
|
|
|
|
parent[0] = '\0';
|
|
leaf[0] = '\0';
|
|
|
|
strmaxcpy(temp, pattern, sizeof(temp) - 1);
|
|
sep = fd_last_sep(temp);
|
|
|
|
if (!sep) {
|
|
strmaxcpy(leaf, temp, leaf_size - 1);
|
|
return;
|
|
}
|
|
|
|
if (*(sep + 1))
|
|
strmaxcpy(leaf, sep + 1, leaf_size - 1);
|
|
else
|
|
strmaxcpy(leaf, "*.*", leaf_size - 1);
|
|
|
|
plen = (int)(sep - temp);
|
|
if (*sep == ':' && (*(sep + 1) == '\\' || *(sep + 1) == '/'))
|
|
plen += 2;
|
|
else if (*sep == ':' || *sep == '\\' || *sep == '/')
|
|
plen += 1;
|
|
|
|
if (plen >= parent_size)
|
|
plen = parent_size - 1;
|
|
|
|
memcpy(parent, temp, plen);
|
|
parent[plen] = '\0';
|
|
}
|
|
|
|
static int fd_enter_parent(char *pattern, char *leaf, int leaf_size,
|
|
char *oldcwd, int oldcwd_size)
|
|
{
|
|
char parent[260];
|
|
|
|
oldcwd[0] = '\0';
|
|
fd_split_pattern(pattern, parent, sizeof(parent), leaf, leaf_size);
|
|
|
|
if (!leaf[0])
|
|
strmaxcpy(leaf, "*.*", leaf_size - 1);
|
|
|
|
if (!parent[0])
|
|
return(0);
|
|
|
|
if (!getcwd(oldcwd, oldcwd_size))
|
|
oldcwd[0] = '\0';
|
|
|
|
if (chdir(parent)) {
|
|
if (oldcwd[0])
|
|
chdir(oldcwd);
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
static void fd_leave_parent(char *oldcwd)
|
|
{
|
|
if (oldcwd[0])
|
|
chdir(oldcwd);
|
|
}
|
|
|
|
static int fd_current_display_path(uint8 dhandle, char *out, int max)
|
|
{
|
|
char path[260];
|
|
char *p;
|
|
|
|
if (!out || max < 2)
|
|
return(-1);
|
|
|
|
out[0] = '\0';
|
|
path[0] = '\0';
|
|
|
|
if (get_dir_path(dhandle, path) || !path[0])
|
|
return(-1);
|
|
|
|
p = strchr(path, ':');
|
|
if (p)
|
|
p++;
|
|
else
|
|
p = path;
|
|
|
|
if (!*p)
|
|
out[0] = '\0';
|
|
else
|
|
strmaxcpy(out, p, max - 1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static void fd_help(void)
|
|
{
|
|
fprintf(stdout, "386 Usage: Flagdir [path [option...]]\n");
|
|
fprintf(stdout, "Options: Normal\n");
|
|
fprintf(stdout, " System\n");
|
|
fprintf(stdout, " Hidden\n");
|
|
fprintf(stdout, " Deleteinhibit\n");
|
|
fprintf(stdout, " Purge\n");
|
|
fprintf(stdout, " Renameinhibit\n");
|
|
}
|
|
|
|
static int fd_attr_mask(char *s, uint32 *setbits, uint32 *clearbits)
|
|
{
|
|
if (tool_strsame(s, "N") || tool_strsame(s, "NORMAL")) {
|
|
*clearbits |= FD_DIR_BITS;
|
|
} else if (tool_strsame(s, "S") || tool_strsame(s, "SY") ||
|
|
tool_strsame(s, "SYS") || tool_strsame(s, "SYSTEM")) {
|
|
*setbits |= FD_NWFA_SY;
|
|
} else if (tool_strsame(s, "H") || tool_strsame(s, "HIDDEN")) {
|
|
*setbits |= FD_NWFA_H;
|
|
} else if (tool_strsame(s, "DI") || tool_strsame(s, "DELETEINHIBIT")) {
|
|
*setbits |= FD_NWFA_DI;
|
|
} else if (tool_strsame(s, "P") || tool_strsame(s, "PURGE")) {
|
|
*setbits |= FD_NWFA_P;
|
|
} else if (tool_strsame(s, "RI") || tool_strsame(s, "RENAMEINHIBIT")) {
|
|
*setbits |= FD_NWFA_RI;
|
|
} else if (tool_strsame(s, "PRIVATE") || tool_strsame(s, "PR")) {
|
|
fprintf(stderr, "Private is valid on NetWare 2.15 and above, except NetWare 386.\n");
|
|
return(-1);
|
|
} else {
|
|
fprintf(stderr, "Unknown attribute: %s.\n", s);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static void fd_print_attrs(uint32 attrs)
|
|
{
|
|
int any = 0;
|
|
|
|
if (!(attrs & FD_DIR_BITS)) {
|
|
fprintf(stdout, "Normal \n");
|
|
return;
|
|
}
|
|
|
|
if (attrs & FD_NWFA_SY) {
|
|
fprintf(stdout, "System");
|
|
any = 1;
|
|
}
|
|
if (attrs & FD_NWFA_H) {
|
|
fprintf(stdout, "%sHidden", any ? " " : "");
|
|
any = 1;
|
|
}
|
|
if (attrs & FD_NWFA_DI) {
|
|
fprintf(stdout, "%sDeleteInhibit", any ? " " : "");
|
|
any = 1;
|
|
}
|
|
if (attrs & FD_NWFA_P) {
|
|
fprintf(stdout, "%sPurge", any ? " " : "");
|
|
any = 1;
|
|
}
|
|
if (attrs & FD_NWFA_RI) {
|
|
fprintf(stdout, "%sRenameInhibit", any ? " " : "");
|
|
any = 1;
|
|
}
|
|
|
|
fprintf(stdout, " \n");
|
|
}
|
|
|
|
static void fd_display_header(char *path)
|
|
{
|
|
char up[260];
|
|
char prefix[260];
|
|
char header[300];
|
|
int len;
|
|
char *p;
|
|
|
|
if (tool_current_prefix(prefix, sizeof(prefix)))
|
|
prefix[0] = '\0';
|
|
|
|
tool_upcopy(up, path, sizeof(up));
|
|
|
|
/* fd_process_one() enters the parent directory before displaying the
|
|
* entry. Use the current NetWare prefix plus the leaf/relative name,
|
|
* not the original F:\... argument, otherwise the drive-qualified path is
|
|
* appended to the already changed current directory.
|
|
*/
|
|
strmaxcpy(header, prefix, sizeof(header) - 1);
|
|
if (!tool_is_current_path(up) && up[0]) {
|
|
len = strlen(header);
|
|
if (len > 0 && header[len - 1] != ':' &&
|
|
header[len - 1] != '\\' && header[len - 1] != '/') {
|
|
if (len < (int)sizeof(header) - 1) {
|
|
header[len++] = '\\';
|
|
header[len] = '\0';
|
|
}
|
|
}
|
|
if ((int)(strlen(header) + strlen(up)) < (int)sizeof(header) - 1)
|
|
strcat(header, up);
|
|
}
|
|
|
|
/* Novell FLAGDIR displays SERVER/SYS: and uses / as path separator in
|
|
* the header line.
|
|
*/
|
|
for (p = header; *p; p++) {
|
|
if (*p == '\\')
|
|
*p = '/';
|
|
}
|
|
|
|
fprintf(stdout, "%s \n", header);
|
|
}
|
|
|
|
static void fd_display_row(char *name, uint32 attrs)
|
|
{
|
|
char base[260];
|
|
|
|
tool_basename(base, name, sizeof(base));
|
|
fprintf(stdout, " %-12.12s ", base);
|
|
fd_print_attrs(attrs);
|
|
}
|
|
|
|
static void fd_display(char *path, uint32 attrs)
|
|
{
|
|
fd_display_header(path);
|
|
fd_display_row(path, attrs);
|
|
}
|
|
|
|
static int fd_is_directory(char *path)
|
|
{
|
|
struct find_t ff;
|
|
unsigned attr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR | _A_ARCH;
|
|
|
|
if (tool_is_current_path(path))
|
|
return(1);
|
|
|
|
if (_dos_findfirst(path, attr, &ff))
|
|
return(0);
|
|
|
|
return((ff.attrib & _A_SUBDIR) != 0);
|
|
}
|
|
|
|
static int fd_obtain(char *path, uint8 dhandle, uint32 *attrs)
|
|
{
|
|
if (!fd_ncp22_1e_obtain_attrs(path, attrs))
|
|
return(0);
|
|
|
|
if (ncp87_06_obtain_rim_attributes(path, (uint16)dhandle,
|
|
attrs, NULL, NULL, NULL) == 0)
|
|
return(0);
|
|
return(-1);
|
|
}
|
|
|
|
static int fd_modify(char *path, uint8 dhandle, uint32 attrs)
|
|
{
|
|
if (!fd_ncp22_25_modify_attrs(path, attrs))
|
|
return(0);
|
|
|
|
if (ncp87_07_modify_dos_attributes(path, (uint16)dhandle, attrs,
|
|
NULL, NULL, NULL) == 0)
|
|
return(0);
|
|
return(-1);
|
|
}
|
|
|
|
static int fd_process_one(char *path, char *display_path, uint8 dhandle,
|
|
uint32 setbits, uint32 clearbits,
|
|
int have_change, int show_header)
|
|
{
|
|
char leaf[260];
|
|
char oldcwd[260];
|
|
char *ncp_path;
|
|
char *check_path;
|
|
uint32 attrs;
|
|
uint32 newattrs;
|
|
int rc = 0;
|
|
|
|
oldcwd[0] = '\0';
|
|
leaf[0] = '\0';
|
|
|
|
if (!tool_is_current_path(path)) {
|
|
if (fd_enter_parent(path, leaf, sizeof(leaf), oldcwd, sizeof(oldcwd))) {
|
|
fprintf(stderr, "Directory %s not found.\n", path);
|
|
return(1);
|
|
}
|
|
check_path = leaf;
|
|
ncp_path = leaf;
|
|
} else {
|
|
check_path = path;
|
|
ncp_path = "";
|
|
}
|
|
|
|
if (!fd_is_directory(check_path)) {
|
|
fd_display_header(ncp_path[0] ? ncp_path : path);
|
|
fprintf(stdout, "Directory %s not found.", ncp_path[0] ? ncp_path : path);
|
|
fd_leave_parent(oldcwd);
|
|
return(1);
|
|
}
|
|
|
|
if (fd_obtain(ncp_path, dhandle, &attrs)) {
|
|
if (tool_is_current_path(path) && display_path && !display_path[0] &&
|
|
!have_change) {
|
|
attrs = 0;
|
|
} else {
|
|
fprintf(stderr, "Unable to get directory attributes.\n");
|
|
fd_leave_parent(oldcwd);
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
if (have_change) {
|
|
newattrs = (attrs | setbits) & ~clearbits;
|
|
if (newattrs != attrs) {
|
|
if (fd_modify(ncp_path, dhandle, newattrs)) {
|
|
fprintf(stderr, "Unable to change attributes.\n");
|
|
fd_leave_parent(oldcwd);
|
|
return(1);
|
|
}
|
|
attrs = newattrs;
|
|
fd_obtain(ncp_path, dhandle, &attrs);
|
|
}
|
|
}
|
|
|
|
if (show_header)
|
|
fd_display(ncp_path[0] ? ncp_path : display_path, attrs);
|
|
else
|
|
fd_display_row(ncp_path[0] ? ncp_path : display_path, attrs);
|
|
|
|
fd_leave_parent(oldcwd);
|
|
return(rc);
|
|
}
|
|
|
|
static int fd_process_wild(char *spec, uint8 dhandle,
|
|
uint32 setbits, uint32 clearbits,
|
|
int have_change)
|
|
{
|
|
struct find_t ff;
|
|
char dir[260];
|
|
char pat[260];
|
|
char search[260];
|
|
char full[260];
|
|
int found = 0;
|
|
int rc = 0;
|
|
int lines = 0;
|
|
int continuous = 0;
|
|
unsigned attr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR | _A_ARCH;
|
|
|
|
tool_parent_pattern(dir, pat, spec, sizeof(dir), sizeof(pat));
|
|
tool_join_path(search, dir, pat, sizeof(search));
|
|
|
|
if (_dos_findfirst(search, attr, &ff) != 0) {
|
|
fprintf(stderr, "Directory %s not found.\n", spec);
|
|
return(1);
|
|
}
|
|
|
|
fd_display_header(spec);
|
|
|
|
do {
|
|
if ((ff.attrib & _A_SUBDIR) && strcmp(ff.name, ".") &&
|
|
strcmp(ff.name, "..")) {
|
|
tool_join_path(full, dir, ff.name, sizeof(full));
|
|
if (fd_process_one(full, ff.name, dhandle, setbits, clearbits,
|
|
have_change, 0))
|
|
rc = 1;
|
|
found++;
|
|
tool_page_line(&lines, &continuous);
|
|
}
|
|
} while (_dos_findnext(&ff) == 0);
|
|
|
|
if (!found) {
|
|
fprintf(stderr, "Directory %s not found.\n", spec);
|
|
return(1);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
int func_flagdir(int argc, char *argv[], int mode)
|
|
{
|
|
char *path = ".";
|
|
char display_path[260];
|
|
uint8 connid = 0;
|
|
uint8 dhandle = 0;
|
|
uint32 setbits = 0;
|
|
uint32 clearbits = 0;
|
|
int have_change = 0;
|
|
int i;
|
|
|
|
(void)mode;
|
|
|
|
if (argc > 1 && (tool_strsame(argv[1], "/?") || tool_strsame(argv[1], "-?") ||
|
|
tool_strsame(argv[1], "?"))) {
|
|
fd_help();
|
|
return(0);
|
|
}
|
|
|
|
if (argc > 1)
|
|
path = argv[1];
|
|
|
|
if (tool_current_dhandle(&connid, &dhandle)) {
|
|
fprintf(stderr, "FlagDir only works on network directories.\n");
|
|
return(1);
|
|
}
|
|
|
|
for (i = 2; i < argc; i++) {
|
|
if (fd_attr_mask(argv[i], &setbits, &clearbits))
|
|
return(1);
|
|
have_change = 1;
|
|
}
|
|
|
|
if (tool_has_wildcards(path))
|
|
return(fd_process_wild(path, dhandle, setbits, clearbits, have_change));
|
|
|
|
if (tool_is_current_path(path)) {
|
|
if (fd_current_display_path(dhandle, display_path, sizeof(display_path)))
|
|
strcpy(display_path, ".");
|
|
} else {
|
|
strmaxcpy(display_path, path, sizeof(display_path) - 1);
|
|
}
|
|
|
|
return(fd_process_one(path, display_path, dhandle,
|
|
setbits, clearbits, have_change, 1));
|
|
}
|