Files
mars-dosutils/flag.c
Mario Fetka f48f62dd91 flag
2026-05-23 11:19:55 +02:00

466 lines
11 KiB
C

/* flag.c - Novell FLAG-like DOS utility, stage 1 */
#include "net.h"
#include <dos.h>
/*
* NetWare old file-info attributes.
* NCP 23/15 returns FileAttributes byte and ExtendedFileAttributes byte.
* NCP 23/16 writes them back while preserving the rest of the scanned info.
*
* This gives us real NetWare flags for:
* S Shareable byte0 bit7
* T Transactional byte1 bit4
* RA Read Audit byte1 bit6
* WA Write Audit byte1 bit7
*
* Full 386 byte2 flags P/CI/DI/RI need NCP 87/35 later.
*/
#define NWFA_RO 0x0001UL
#define NWFA_H 0x0002UL
#define NWFA_SY 0x0004UL
#define NWFA_A 0x0020UL
#define NWFA_S 0x0080UL
#define NWFA_T 0x1000UL
#define NWFA_RA 0x4000UL
#define NWFA_WA 0x8000UL
typedef struct {
uint16 next_index;
char name[15];
uint8 attr;
uint8 extattr;
uint8 raw[76]; /* file size + dates + owner + archive + reserved */
} FLAG_NWINFO;
static int flag_get_current_drive(void)
{
REGS regs;
regs.h.ah = 0x19; /* DOS get current default drive */
int86(0x21, &regs, &regs); /* AL = 0 for A:, 1 for B:, ... */
return((int)regs.h.al);
}
static int flag_current_dhandle(uint8 *dhandle)
{
uint8 connid = 0;
uint8 flags = 0;
int drive;
drive = flag_get_current_drive(); /* 0=A */
if (get_drive_info((uint8)drive, &connid, dhandle, &flags))
return(-1);
if (!connid || (flags & 0x80))
return(-1);
return(0);
}
static int flag_ncp23_scan(uint8 dhandle, char *name, FLAG_NWINFO *info)
{
struct {
uint16 len;
uint8 func;
uint8 last[2];
uint8 dhandle;
uint8 searchattr;
uint8 namelen;
uint8 name[255];
} req;
struct {
uint16 len;
uint8 next[2];
uint8 fname[14];
uint8 attr;
uint8 extattr;
uint8 raw[76];
} repl;
int nlen;
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
nlen = strlen(name); if (nlen > 255) nlen = 255;
req.func = 0x0f;
req.last[0] = 0xff;
req.last[1] = 0xff;
req.dhandle = dhandle;
req.searchattr = 0x06; /* include hidden/system */
req.namelen = (uint8)nlen;
memcpy(req.name, name, nlen);
req.len = 6 + nlen;
repl.len = sizeof(repl) - sizeof(uint16);
neterrno = Net_Call(0xE300, &req, &repl);
if (neterrno) return(-1);
if (info) {
info->next_index = GET_16(repl.next);
memcpy(info->name, repl.fname, 14);
info->name[14] = '\0';
info->attr = repl.attr;
info->extattr = repl.extattr;
memcpy(info->raw, repl.raw, sizeof(info->raw));
}
return(0);
}
static int flag_ncp23_set(uint8 dhandle, char *name, FLAG_NWINFO *info, uint32 attrs)
{
struct {
uint16 len;
uint8 func;
uint8 attr;
uint8 extattr;
uint8 raw[76];
uint8 dhandle;
uint8 searchattr;
uint8 namelen;
uint8 name[255];
} req;
struct {
uint16 len;
} repl;
int nlen;
memset(&req, 0, sizeof(req));
memset(&repl, 0, sizeof(repl));
nlen = strlen(name); if (nlen > 255) nlen = 255;
req.func = 0x10;
req.attr = (uint8)(attrs & 0xff);
req.extattr = (uint8)((attrs >> 8) & 0xff);
memcpy(req.raw, info->raw, sizeof(req.raw));
req.dhandle = dhandle;
req.searchattr = 0x06;
req.namelen = (uint8)nlen;
memcpy(req.name, name, nlen);
req.len = 82 + nlen;
repl.len = 0;
neterrno = Net_Call(0xE300, &req, &repl);
if (neterrno) return(-1);
return(0);
}
static int flag_get_nw_attrs(char *name, uint32 *attrs, FLAG_NWINFO *info)
{
uint8 dhandle = 0;
FLAG_NWINFO local;
if (flag_current_dhandle(&dhandle))
return(-1);
if (!info) info = &local;
if (flag_ncp23_scan(dhandle, name, info))
return(-1);
if (attrs)
*attrs = ((uint32)info->attr) | (((uint32)info->extattr) << 8);
return(0);
}
static int flag_set_nw_attrs(char *name, uint32 attrs)
{
uint8 dhandle = 0;
FLAG_NWINFO info;
if (flag_current_dhandle(&dhandle))
return(-1);
if (flag_ncp23_scan(dhandle, name, &info))
return(-1);
return(flag_ncp23_set(dhandle, name, &info, attrs));
}
#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
static int flag_same(char *a, char *b)
{
while (*a || *b) {
int ca = *a++;
int cb = *b++;
if (ca >= 'a' && ca <= 'z') ca -= 32;
if (cb >= 'a' && cb <= 'z') cb -= 32;
if (ca != cb) return(0);
}
return(1);
}
static void flag_help(void)
{
fprintf(stdout, "USAGE: FLAG [path [ option | [+|-] attribute(s) ] [SUB]]\n");
fprintf(stdout, "\n");
fprintf(stdout, "386 Attributes:\n");
fprintf(stdout, "--------------\n");
fprintf(stdout, "\n");
fprintf(stdout, "RO Read Only\n");
fprintf(stdout, "RW Read Write\n");
fprintf(stdout, "S Sharable\n");
fprintf(stdout, "H Hidden\n");
fprintf(stdout, "Sy System\n");
fprintf(stdout, "T Transactional\n");
fprintf(stdout, "P Purge\n");
fprintf(stdout, "A Archive Needed\n");
fprintf(stdout, "RA Read Audit\n");
fprintf(stdout, "WA Write Audit\n");
fprintf(stdout, "CI Copy Inhibit\n");
fprintf(stdout, "DI Delete Inhibit\n");
fprintf(stdout, "RI Rename Inhibit\n");
fprintf(stdout, "\n");
fprintf(stdout, "All All\n");
fprintf(stdout, "N Normal\n");
fprintf(stdout, "SUB\n");
}
static int flag_attr_mask(char *s, unsigned *setbits, unsigned *clearbits)
{
int set = 1;
char *p = s;
if (*p == '+') {
set = 1;
p++;
} else if (*p == '-') {
set = 0;
p++;
}
if (!*p) return(-1);
if (flag_same(p, "RO")) {
if (set) {
*setbits |= NWFA_RO;
/*
* Novell FLAG also shows DI/RI for +RO on real NetWare.
* Those are byte2 flags and need NCP87/35 later.
*/
} else *clearbits |= NWFA_RO;
} else if (flag_same(p, "RW")) {
*clearbits |= NWFA_RO;
} else if (flag_same(p, "S")) {
if (set) *setbits |= NWFA_S;
else *clearbits |= NWFA_S;
} else if (flag_same(p, "H")) {
if (set) *setbits |= NWFA_H;
else *clearbits |= NWFA_H;
} else if (flag_same(p, "SY") || flag_same(p, "SYS") || flag_same(p, "SYSTEM")) {
if (set) *setbits |= NWFA_SY;
else *clearbits |= NWFA_SY;
} else if (flag_same(p, "T")) {
if (set) *setbits |= NWFA_T;
else *clearbits |= NWFA_T;
} else if (flag_same(p, "A")) {
if (set) *setbits |= NWFA_A;
else *clearbits |= NWFA_A;
} else if (flag_same(p, "RA")) {
if (set) *setbits |= NWFA_RA;
else *clearbits |= NWFA_RA;
} else if (flag_same(p, "WA")) {
if (set) *setbits |= NWFA_WA;
else *clearbits |= NWFA_WA;
} else if (flag_same(p, "N") || flag_same(p, "NORMAL")) {
*clearbits |= (NWFA_RO | NWFA_H | NWFA_SY | NWFA_A |
NWFA_S | NWFA_T | NWFA_RA | NWFA_WA);
} else if (flag_same(p, "ALL")) {
*setbits |= (NWFA_RO | NWFA_H | NWFA_SY | NWFA_A |
NWFA_S | NWFA_T | NWFA_RA | NWFA_WA);
} else if (flag_same(p, "P") || flag_same(p, "CI") ||
flag_same(p, "DI") || flag_same(p, "RI")) {
fprintf(stderr, "Attribute not supported on this version of NetWare\n");
return(1);
} else {
fprintf(stderr, "Unknown attribute encountered in command line.\n");
return(-1);
}
return(0);
}
static void flag_print_attrs(unsigned attr)
{
/*
* Novell order:
* RO/RW S A H Sy T P RA WA CI DI RI
*/
fprintf(stdout, "[ ");
fprintf(stdout, "%s ", (attr & NWFA_RO) ? "RO" : "Rw");
fprintf(stdout, "%c ", (attr & NWFA_S) ? 'S' : '-');
fprintf(stdout, "%c ", (attr & NWFA_A) ? 'A' : '-');
fprintf(stdout, "%c ", (attr & NWFA_H) ? 'H' : '-');
fprintf(stdout, "%s ", (attr & NWFA_SY) ? "Sy" : "-");
fprintf(stdout, "%c ", (attr & NWFA_T) ? 'T' : '-');
fprintf(stdout, "-- "); /* P needs NCP87 */
fprintf(stdout, "%s ", (attr & NWFA_RA) ? "Ra" : "--");
fprintf(stdout, "%s ", (attr & NWFA_WA) ? "Wa" : "--");
fprintf(stdout, "-- "); /* CI needs NCP87 */
fprintf(stdout, "-- "); /* DI needs NCP87 */
fprintf(stdout, "-- "); /* RI needs NCP87 */
fprintf(stdout, "]");
}
static void flag_display_one(char *name, unsigned attr)
{
fprintf(stdout, " %-23s ", name);
flag_print_attrs(attr);
fprintf(stdout, "\n");
}
static int flag_has_wildcards(char *s)
{
while (*s) {
if (*s == '*' || *s == '?') return(1);
s++;
}
return(0);
}
static int flag_list(char *pattern)
{
struct find_t ff;
unsigned findattr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH;
int found = 0;
if (_dos_findfirst(pattern, findattr, &ff))
return(-1);
do {
if (!(ff.attrib & _A_SUBDIR)) {
uint32 nwattrs;
if (flag_get_nw_attrs(ff.name, &nwattrs, NULL))
nwattrs = (uint32)ff.attrib;
flag_display_one(ff.name, (unsigned)nwattrs);
found++;
}
} while (!_dos_findnext(&ff));
return(found);
}
static int flag_apply(char *pattern, unsigned setbits, unsigned clearbits)
{
struct find_t ff;
unsigned findattr = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH;
int shown = 0;
if (_dos_findfirst(pattern, findattr, &ff))
return(-1);
do {
uint32 attrs;
uint32 newattrs;
char fname[260];
if (ff.attrib & _A_SUBDIR) continue;
strmaxcpy(fname, ff.name, sizeof(fname) - 1);
if (flag_get_nw_attrs(fname, &attrs, NULL))
attrs = (uint32)ff.attrib;
newattrs = (attrs | setbits) & ~clearbits;
if (newattrs != attrs) {
if (flag_set_nw_attrs(fname, newattrs)) {
/*
* Fallback for local/DOS-visible bits. This keeps old behavior alive.
*/
unsigned dosattr = (unsigned)(newattrs & (_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH));
if (_dos_setfileattr(fname, dosattr)) {
fprintf(stderr, "You don't have rights to change : %s\n", fname);
continue;
}
}
}
if (flag_get_nw_attrs(fname, &attrs, NULL))
attrs = newattrs;
flag_display_one(fname, (unsigned)attrs);
shown++;
} while (!_dos_findnext(&ff));
return(shown);
}
int func_flag(int argc, char *argv[], int mode)
{
char *path = "*.*";
int i;
unsigned setbits = 0;
unsigned clearbits = 0;
int have_change = 0;
int rc;
(void)mode;
if (argc > 1 && (flag_same(argv[1], "/?") || flag_same(argv[1], "-?") ||
flag_same(argv[1], "?"))) {
flag_help();
return(0);
}
if (argc > 1) {
path = argv[1];
if (flag_same(path, "SUB")) path = "*.*";
}
for (i = 2; i < argc; i++) {
if (flag_same(argv[i], "SUB")) continue;
rc = flag_attr_mask(argv[i], &setbits, &clearbits);
if (rc < 0) return(1);
if (rc > 0) continue;
have_change = 1;
}
if (have_change) {
rc = flag_apply(path, setbits, clearbits);
if (rc < 0) {
fprintf(stderr, "Files could not be found with pattern \"%s\"\n", path);
return(1);
}
return(0);
}
rc = flag_list(path);
if (rc < 0) {
fprintf(stderr, "Files could not be found with pattern \"%s\"\n", path);
return(1);
}
return(0);
}