Metropoli BBS
VIEWER: attrib.c MODE: TEXT (ASCII)
/*
 * ATTRIB command for MS-DOS
 *
 * This is an ATTRIB command that pretty much works exactly like the
 * one from Microsoft with the following enhancements:
 *
 * More flexible parsing: accepts +RS +R-S +RS-HA -H/S etc.
 * Parameters can be in any order.
 *
 * Better error reporting, instead of just saying parameters are wrong,
 * it tells you exactly what it didn't like.
 * 
 * /V option for verbose output
 * /F option for "full" listing. Shows directories & volume labels
 *
 * Walks the directory tree in a more natural manner. Ie, processes
 * the files in a directory FIRST, then moves on to the subdirs.
 *
 * Unambiguous specification of directories. ie: append '\' to a directory
 * name, otherwise it is assumed to be a filename. Microsofts ATTRIB will
 * happily change all files in a directory if you happen to mistype it's
 * name instead of a filename.
 *
 * Copyright 1995 Dave Dunfield
 * Freely Distributable.
 */
#include <stdio.h>
#include <file.h>

#define	DIRS	500		/* Depth of directory stacking */

char
	*pattern = "",		/* Pattern we are searching for */
	path[66],			/* Path we are searching */
	subdir = 0,			/* Indicates we are to search sub-dirs */
	verbose = 0,		/* Be talky */
	full = 0,			/* Full listing */
	badopt = 0,			/* A bad command option was detected */
	set = 0,			/* Indicates we are setting attributes */
	dirstack[DIRS][13];	/* Stack of subdirectory names */

unsigned
	setmask = 0,		/* Attribute bits to set */
	clrmask = -1,		/* Attribute bits to clear */
	dircount = 0,		/* Count of directories processed */
	filecount = 0,		/* Count of files processed */
	dirptr = 0;			/* Current position in directory stack */

static char
	attribs[] = { "RHSVDA" };	/* Letters for attributes */

static char help_text[] = { "\n\
Displays or changes file attributes.\n\n\
ATTRIB [+/-RASH ...] [[drive:][path]filename] [/F][/S][/V]\n\
\n\
  +/- Set/Clear attribute(s).\n\
  R   Read-only file attribute.\n\
  A   Archive file attribute.\n\
  S   System file attribute.\n\
  H   Hidden file attribute.\n\
  /F  Full listing, show dirs and volume labels\n\
  /S  Processes files in all subdirectories.\n\
  /V  Verbose command operation.\n\n\
Copyright 1995 Dave Dunfield - Freely Distributable.\n" };

/*
 * Process all the files in one directory
 */
void process_dir(void)
{
	unsigned plen, i, attrs, dirbase;
	char name[13], *ptr;

	plen = strlen(path);
	strcpy(path+plen, pattern);
	++dircount;

	if(verbose && !set)
		printf("\nListing %s\n\n", path);

	/* Search for files & handle show/set attributes */
	if(!find_first(path, -1, name, &i, &i, &attrs, &i, &i)) do {
		if((attrs & (DIRECTORY|VOLUME)) && !full)
			continue;
		++filecount;
		strcpy(path+plen, name);
		if(set) {	/* Setting attributes */
			if(set_attr(path, (attrs & clrmask) | setmask)) 
				printf("%s: Unable to set attributes\n", path); }
		else {
			i = 0x01;
			for(ptr = attribs; *ptr; ++ptr) {
				putc((attrs & i) ? *ptr : ' ', stdout);
				i <<= 1; }
			printf("       %s\n", path); } }
		while(!find_next(name, &i, &i, &attrs, &i, &i));

	if(subdir) {
		dirbase = dirptr;
		strcpy(path+plen, "*.*");
		if(!find_first(path, -1, name, &i, &i, &attrs, &i, &i)) do {
			if(*name == '.')
				continue;
			if(attrs & DIRECTORY)
				strcpy(dirstack[dirptr++], name); }
		while(!find_next(name, &i, &i, &attrs, &i, &i));

		for(i=dirbase; i < dirptr; ++i) {
			strcpy(path+plen, dirstack[i]);
			strcat(path, "\\");
			process_dir(); }
		dirptr = dirbase; }

	path[plen] = 0;
}

/*
 * Main program - Parse options and call process_dir
 */
main(int argc, char *argv[])
{
	int i, j;
	char *ptr, *ptr1, c, mode;

	/* Parse command options */
	for(i=1; (i < argc) && !badopt; ++i) {
		ptr = argv[i];
		mode = 0;
		while(*ptr) switch(c = toupper(*ptr++)) {
			case '/' :		/* Option switch */
				mode = 0;
				switch(c = toupper(*ptr++)) {
					case 'F' :	/* Full listing */
						full = -1;
						continue;
					case 'S' :	/* Process subdirs */
						subdir = -1;
						continue;
					case 'V' :
						verbose = -1;
						continue;
					case '?' :	/* Help request */
						argc = 0;
						badopt = -1;
						continue; }
				printf("Unknown switch: '/%c'\n", c);
				badopt = -1;
				continue;
			case '+' :			/* Add these options */
				mode = 1;
				continue;
			case '-' :			/* Subtract these options */
				mode = -1;
				continue;
			default:			/* Default */
				if(mode) {
					set = -1;
					ptr1 = attribs;
					for(j=1; *ptr1; j <<= 1)
						if(*ptr1++ == c)
							goto founda;
					printf("Unknown attribute bit: '%c'\n", c);
					badopt = -1;
				founda:
					if(mode > 0)
						setmask |= j;
					else
						clrmask &= ~j;
					continue; }
				if(*pattern) {
					printf("Too many file specifications.\n");
					badopt = -1; }
				else
					pattern = ptr-1;
				ptr = ""; } }

	if(setmask & ~clrmask) {
		printf("Cannot Set & Clear same attribute\n");
		badopt = -1; }

	if((setmask | ~clrmask) & (DIRECTORY|VOLUME)) {
		printf("Cannot alter DIRECTORY and VOLUME attributes\n");
		badopt = -1; }

	if(!argc)
		fputs(help_text, stdout);

	if(badopt)
		exit(-1);

	/* Separate directory and path, establish defaults */
	strcpy(path, pattern);
	for(i=j=0; c = path[i]; ++i) switch(c) {
		case ':' :
		case '\\' :
			j = i+1; }
	if(j) {
		path[j] = 0;
		pattern += j; }
	else {
		*path = get_drive() + 'A';
		strcpy(path+1, ":\\");
		if(getdir(path+3))
			*path = 0;
		else if(path[3])
			strcat(path, "\\"); }

	if(!*pattern)
		pattern = "*.*";

	process_dir();

	/* Display result messages */
	if(!filecount)
		printf("File not found - %s\n", pattern);
	else if(verbose) {
		putc('\n', stdout);
		if(subdir)
			printf("%u directories and ", dircount);
		printf("%u files were processed.\n", filecount); }
}
[ RETURN TO DIRECTORY ]