#include <xfbuf.h>

char *strip_path();
#define same(s1,s2) (strcmp(s1,s2)==0)

/*
	Z
	Tom Jennings
	26 Jan 92
*/

#define RO 0x01		/* Read only attribute. */
#define HI 0x02		/* Hidden attribute, */
#define SY 0x04		/* System attribute, */
#define VO 0x08		/* Volume ID */
#define DI 0x10		/* directory */
#define AR 0x20		/* Archive, */
#define TG1 0x40	/* Tag #1 */
#define TG2 0x80	/* Tag #2 */

char arg[80];
char sw[80];
char filename[80];

char work[80];		/* simple workspace */
int level;		/* how deep we are, */
char drive;
int query;		/* 1 == verify deletes */
int list_info;		/* 1 == list all files */
int list_changed;	/* 1 == list only changed files */
int kill;		/* 1 == delete matching files */
int space;		/* 1 == list space consumed */
int crawl;		/* 1 == crawl the tree */
int nomatter;		/* 1 == no matter what */
int pause;		/* 1 == pause every 24 lines */
int tag;		/* 1 == tag files */
int line;		/* current lines output */
long totl_bytes;	/* total file size */
long totl_space;	/* total space consumed */
unsigned blksize;	/* allocation size */

unsigned _stack = 8000;


char _months[13][4] = {
	"Eh?",
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
};

main(argc,argv)
int argc;
char *argv[];
{
char sw[40],*p,buff[80];
int i;

	pause= level= drive= line= 0;
	nomatter= crawl= query= kill= list_changed= 0;
	list_info= 1;

	*arg= '\0';
	*filename= '\0';
	totl_bytes= totl_space= 0L;

	for (i= 1; i < argc; i++) {		/* Process any options, */
		cpyarg(buff,strip_path(buff,argv[i])); /* copy out filename */
		strip_switch(sw,argv[i]);	/* recover switches, */
/*		if (arg[1] == ':') {
			drive= toupper(arg[0]) - 'A' + 1;
			if (_getdpb(drive,&dpb) == -1) {
				printf("No such drive, doo-doo head\r\n");
				exit(1);
			}
		}
*/
		for (p= sw; *p; ++p) {
			switch (*p) {
				case 'D': kill= 1; break;
				case 'N': list_info= 0; break;
				case 'Q': list_info= 0; query= 1; break;
				case 'A': list_changed= 1; break;
				case 'P': pause= 1; break;
				case 'C': crawl= 1; break;
				case 'T': tag= 1; break;
				case 'U': tag= -1; break;
				case 'Z': nomatter= 1; break;
				case '?':
help:;					printf("Z -- a File/Directory Tool\r\n");
					printf("Tom Jennings, Fido Software, Box 77731, San Francisco CA 94107\r\n");
					printf("26 Jan 92, (k) All Rites Revered\r\n\r\n");
					printf("Displays DOS file information, starting with the current disk/directory,\r\n");
					printf("unless otherwise specified. You can use more than one option at a time:\r\n");
					printf("    Z pathname/A/N/D/T/U/D/Q/S/P/C\r\n");
					printf("    PATHNAME is any DOS path or filename\r\n");
					printf("\r\n");
					printf("    /A    List only files that have changed (ARCHIVE)\r\n");
					printf("    /N    Don't list filenames; list file counts and totals only\r\n");
					printf("    /D    Delete files (*.* not allowed; \"????????.???\" is!)\r\n");
					printf("    /T    Tag files with \"changed\" status (ARCHIVE)\r\n");
					printf("    /U    Un-tag files \"changed\" status (ARCHIVE)\r\n");
					printf("    /Q    Use with /D, /T, /U: makes you type \"Y\" or \"N\" for each file\r\n");
					printf("    /S    Display disk space consumed by each file\r\n");
					printf("    /P    Pause every 24 lines\r\n");
					printf("    /C    Use with all above; crawl recursively through sub-directories\r\n");
					printf("    /Z    Ignore StopFile.Z's\r\n");
					printf("\r\n");
					printf("    \"[ASRH]\" after file info are attribute(s); A=Archive (also used\r\n");
					printf("    by BACKUP/RESTORE); S=System; R=ReadOnly; H=Hidden\r\n");
					exit(1);
			}
		}
		if (buff[0]) cpyarg(filename,buff); /* possible filename */
	}
	if (! *filename) goto help;

/*	if (space && (_getdpb(drive,&dpb) == -1)) {
		printf("Huh? There is no drive \"%c:\"?\r\n",drive + 'A');
		exit(1);
	}
	blksize= dpb.secs_clust * dpb.sector_size;
*/

	fixdir(filename);			/* fixup directory names */
	if ((*filename == '.') && (strlen(filename) <= 4)) {
		strcpy(buff,"*");		/* turn extention-only  (.EXE) */
		strcat(buff,filename);		/* into full filename (*.EXE) */
		strcpy(filename,buff);
	}
	for (p= filename; *p; ++p) {		/* if no extention given (FILE) */
		if (*p == '.') break;
	}
	if (*p != '.') strcat(filename,".*");	/* make it wild (FILE.*) */

	if (kill && (!query) && (strcmp(filename,"*.*") == 0)) {
		printf("WRONG! Cannot delete \"*.*\" without /Q (But \"????????.???/D\" works!)\r\n");
		exit(1);
	}
	dir(arg);
	exit(0);
}

/* Recursively find each directory, and execute all the weird options. Returns
the number of bytes tallied. */

dir(s) 
char *s;
{
int i,n;
struct _xfbuf xfbuf;
char path[80];
char *p;
long tsize,dsize;
char tally;
char dont;

	++level;
	tsize= dsize= 0L;				/* file size so far */
	strcpy(path,s);					/* build full search path */
	strcat(path,filename);
	i= 0;						/* _find flag, file count */
	n= 0;						/* number of files */
	dont= 0;					/* "dont-chdir" filename not found */
	tally= 0;					/* flag to tally files or not */
	xfbuf.s_attrib= 0xff;
	while (_find(path,i,&xfbuf)) {
		++i;					/* count a file */
		tally= 0;
		strcpy(work,s);				/* build full filename */
		strcat(work,xfbuf.name);		/* to get stats on it */

/* Handle the Volume ID separately */

		if (xfbuf.f_attrib & VO) {
			if (list_info) {
				stoupper(xfbuf.name);
				tabover();
				printf("Volume ID: %s",xfbuf.name);
				newline();
			}
			continue;
		}

/* List directories only if we are not crawling the tree. */

		if (xfbuf.f_attrib & DI) {
			if (*xfbuf.name == '.') continue;
			if (crawl) continue;

			if (list_info ||
			    (list_changed && (xfbuf.f_attrib & AR)) ) {
				stoupper(xfbuf.name);
				dofname(xfbuf.name);
				printf("      <dir>");
				newline();
			}
			continue;			/* do nothing to them */
		}

/* Exclude files when we have tag things specified. */

		if (
		    (list_changed && !(xfbuf.f_attrib & AR)) ||	/* list changed */
		    ((tag > 0) && (xfbuf.f_attrib & AR)) ||	/* tag untagged, */
		    ((tag < 0) && !(xfbuf.f_attrib & AR)) ) 	/* untag tagged, */
			continue;

		if (list_info || query) {
			stolower(xfbuf.name);		/* display filename */
			dofname(xfbuf.name);
			printf("%11,lu",xfbuf.fsize); 
			fileinfo(&xfbuf);
		}

		if (kill) {				/* for killing, */
			tally= query ? ask("delete") : 1;/* ask or just do it */
			if (tally) {
				unlink(work);		/* do it */
				n++;
			}
			if (tally && (list_info || query)) printf(" -- deleted");

		} else if (tag) {
			if (tag < 0) {			/* set text and */
				p= "untag";		/* attribute now */
				xfbuf.f_attrib &= ~AR;
			} else {
				p= "tag";
				xfbuf.f_attrib |= AR;
			}
			tally= query ? ask(p) : 1;
			if (tally) {
				setattribs(work,xfbuf.f_attrib);
				++n;
			}
			if (tally && (list_info || query)) printf(" -- %sged",p);

		} else {
			tally= 1;			/* no special treatment */
			 ++n;				/* for this file */
		}
		if (list_info || query) newline();

		if (tally) {
			tsize += xfbuf.fsize;		/* well then do it */
/*			dsize += ((xfbuf.fsize + blksize - 1) / blksize) * blksize;
*/		}
	}
	totl_bytes += tsize;				/* overall totals */
/*	totl_space += dsize;
*/	if ((list_info || query) && (n != 0)) {		/* skip if no files */
		tabover(); 
		printf("%,u files, %,lu bytes",n,tsize);
		if (tsize != totl_bytes) printf(" (=%,lu)",totl_bytes);
/*		if (space) printf(", %,lu bytes disk space",totl_space);
*/		newline();
	}

	if (! crawl) return(tsize);			/* stop if just current dir */

/* Check for a stop file. */

	strcpy(path,s);					/* build full search path */
	strcat(path,"StopFile.Z");
	xfbuf.s_attrib= 0xff;
	if (_find(path,0,&xfbuf)) {			/* if "StopFile.Z" exists */
		tabover(); 
		printf("(StopFile.Z present)\r\n");	/* dont do it */
		if (!nomatter) return(tsize);
	}
	i= 0;
	strcpy(path,s);					/* build pathname for */
	strcat(path,"*.*");				/* directory search */
	xfbuf.s_attrib= DI;				/* directory attribute, */
	while (_find(path,i++,&xfbuf)) {
		if (*xfbuf.name == '.') continue;	/* skip . and .. */
		if (! (xfbuf.f_attrib & DI)) continue;

		strcpy(path,s);				/* build full pathname */
		strcat(path,xfbuf.name);		/* for next iteration */
		stoupper(path);				/* dirs are upper case */
		tabover(); printf("%s",path);		/* display it */
		newline();
		strcat(path,"\\");
		dir(path);				/* do it, tally total */
		if (level) --level;			/* pop up one level */
	}
	return(tsize);					/* return the total */
}

/* Indent to indicate sub-directory level. */

tabover() {
int n;

	for (n= level * 4; n--;) lconout(' ');
}

/* Display a filename, at least 14 characters wide, aligned at the 
extention dot. */

dofname(p)
char *p;
{
char *cp;
int n;
	tabover();
	for (cp= p; *cp; ++cp) if (*cp == '.') break;	/* find the extention */
	for (n= 8 - (cp - p); n-- > 0;) lconout(' ');	/* make names same width */
	while (*p) {
		if (*p == '.') break;			/* stop if a dot */
		lconout(*p++);				/* else output the name */
	}
	for (n= 4; n--;) {				/* display the extention */
		if (*p) lconout(*p++);			/* make it 4 chars (.EXT) */
		else lconout(' ');
	}
}

/* Display stuff for this file. */

fileinfo(xfbuf)
struct _xfbuf *xfbuf;
{
	printf("    %2u ",xfbuf-> date & 0x1f);			/* day */
	printf("%s ",_months[(xfbuf-> date >> 5) & 0x0f]);	/* month */
	printf("%2u",((xfbuf-> date >> 9) & 0x3f) + 80);	/* year */
	printf("  %2u:%02u",xfbuf-> time >> 11,(xfbuf-> time >> 5) & 0x3f);

	if (xfbuf-> f_attrib & (AR | SY | RO | HI)) {
		printf("    [");
		if (xfbuf-> f_attrib & AR) lconout('A');
		if (xfbuf-> f_attrib & SY) lconout('S');
		if (xfbuf-> f_attrib & RO) lconout('R');
		if (xfbuf-> f_attrib & HI) lconout('H');
		lconout(']');
	}
}

/* Do a newline, plus a pause every 24 lines. */

newline() {

	printf("\r\n");
	if (pause && !query) {
		if (++line >= 24) {
			printf("More[Y,n,c] ");
			switch (lconin()) {
				case 'N': case 'n': case 3: case 27: exit(1);
				case 'C': case 'c': pause= 0; break;
			}
			printf("\r");
			line= 0;
		}
	}
}

/* Set file attributes. */

setattribs(p,a)
char *p;
unsigned a;
{
	a &= ~(DI | VO);		/* clear dir and volume, */
	_chmod(p,a,1);
}

/* Ask a question, return 1 if Yes. */

ask(q)
char *q;
{
char c;
	printf(" %s? (y,n) ",q);
	c= lconin();
	if (tolower(c) == 'y') return(1);
	if ((c == 27) || (c == 3)) {
		printf("-- ABORTED\r\n");
		exit(1);
	}
	return(0);
}

