#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <exec/types.h>

#include <exec/memory.h>
#include <proto/exec.h>

#include <dos/dos.h>
#include <dos/rdargs.h>
#include <proto/dos.h>

///» "Program-History"
/*
** Programm-History:
**
** 1.0: initial release
**
** 1.1: needs 3 runs through directories!!!
**
** 1.2: now only 1 run :)
**      added possibility to sort entries (using fix arrays of pointers; needs BIIIIG stack)
**
** 2.0: changed sorting; is now performed by linked list/heap (normal stack is enough)
**
** 2.1: new switch: PATZ/S -> using Patz-Font-Output-Strings ( ¬ , ¯ ) instead of " + , ` "
**      use prefs-file (ENV:TDir.prefs)
**      new switch: NOPREFS/S -> do not read prefs-file
**
** 2.2: use ReadArgs() instead of selfmade parsing; multiple dirs possible now
**      tests if dirs exist or not before calling ExamineDir() for the first time
**      if no dirs are given, get name of currentdir and display it instead of ""
**
** 2.3: new switch: STAT/S -> print statistic (#dirs,#files) after dir-name
**
** 2.4: if file-/dir-name is too long, end it with "..." before size : file OK, dir i see no chance
**      new switch: H=HELP/S prints programname and version
**      new switch: N=NOTE/S prints filenote for dirs/files; NOTE: base directory gets no filenote!
**
** 2.5: if file-/dir-name is too long: place "..." in the middle of name (after 5 characters)
**
** 2.6: OutputName() procedure added
**
** 2.7: PutNode()&GetNode() procedures added
**
** 2.8: changed mode lists are ancored: now only one pointer is needed (instead of two/three)
**
** 2.9: replaced printrf() by Printf(); changed 'char tree[]' to 'char *tree[]'
**      place "..." in names longer than 24 chars after 'offset' characters now (instead of fixed 5)
**      changed default length of filesize from 7 to 8 (filesizes from 10 to 99 MB is reasonable i think)
**      restored Printf() to printf() (is needed for OutputName anyway and saved only 2 KB)
**
** 2.10: some code-cleanup
**
*/
///«


///» "typedef"
typedef struct node {
		struct node *up,*left,*right;
		struct node *prev,*next;
		char name[108];
		char note[80];
		int size; /* size<0->dir, size>=0->file */
	} NODE;
///«

///» "global variables"
char *program_name="TDir 2.10";
char *program_template="DIR/M,ALL/S,DIRS/S,PLAIN/S,SP=SPACE/S,S=SORT/S,NS=NOSORT/S,PATZ/S,NP=NOPREFS/S,STAT/S,H=HELP/S,N=NOTE/S";
char *programmers_name="PATZ";
char *version_information="\0$VER: TDir 2.10 by PATZ (20.03.95)";

short break_flag=0;
int space=0,plain=0,dirs=0,all=0,sort=0,nosort=0,patz=1,stat=0,note=0;
char string[256]="";
char *tree[3]={"|","+","`"};
///«

///» "prototypes"
int Nope(void);
int PutNode(NODE **,struct FileInfoBlock *,int);
NODE * GetNode(NODE **,int);
void OutputName(NODE *,int);
void Swap(NODE *, NODE *);
void ExamineDir(char *,char *);
///«

/* procedures */
///» "Nope"
int Nope(void)
{
	break_flag=5;
	return(0);
}
///«

///» "PutNode"
int PutNode(NODE **base,struct FileInfoBlock *fib,int do_sorting)
{
NODE *temp=(NODE *) AllocMem(sizeof(NODE),MEMF_FAST);

	if(!temp) {
		printf("%s\n","Error: No memory for entry!");
		break_flag=20;
		return(20);
	}
	temp->up=temp->left=temp->right=temp->prev=temp->next=NULL;
	strcpy(temp->name,fib->fib_FileName);
	strcpy(temp->note,fib->fib_Comment);
	if(fib->fib_DirEntryType<0) { /* file! */
		temp->size=fib->fib_Size; }
	else { /* dir! */
		temp->size=-1;
	}
	/* put new entry at right place, according to switches */
	if(do_sorting) { /* Heap */
		if(!(*base)) { /* CreateHeap */
			(*base)=temp->up=temp->next=temp->prev=temp; }
		else { /* InsertHeap */
			temp->up=(*base)->up; temp->prev=(*base)->prev; temp->next=(*base);
			((*base)->prev)->next=temp; (*base)->prev=temp;
			if(!((*base)->up)->left) {
				((*base)->up)->left=temp; }
			else {
				((*base)->up)->right=temp; (*base)->up=((*base)->up)->next;
			}
		}
		/* EstablishHeap */
		while(temp!=(*base)&&stricmp((temp->up)->name,temp->name)>0) {
			 Swap(temp,temp->up); temp=temp->up;
		} }
	else { /* List */
		if(!(*base)) { /* a[1] */
			(*base)=temp->next=temp->prev=temp; }
		else {
			temp->next=(*base); temp->prev=(*base)->prev;
			(temp->prev)->next=temp; (*base)->prev=temp;
		}
	}
	return(0);
}
///«

///» "GetNode"
NODE * GetNode(NODE **base,int sorted)
{
NODE *temp;

	if(sorted) { /* Heap */
		if((*base)->next==(*base)) { /* last entry */
			temp=(*base); (*base)=NULL; }
		else {
			Swap((*base),(*base)->prev);
			temp=((*base)->prev)->up;
			if(temp->right) {
				temp->right=NULL; }
			else {
				temp->left=NULL;
			}
			/* Establish Heap */      
			temp=(*base);
			while(temp->left) {
				temp=temp->left;
				if((temp->up)->right&&stricmp(temp->name,(temp->next)->name)>0) {
					temp=temp->next;
				}
				if(stricmp((temp->up)->name,temp->name)<0) {
					break; }
				else {
					Swap(temp,temp->up);
				}
			}
			temp=(*base)->prev; (temp->prev)->next=(*base); (*base)->prev=temp->prev;
		} }
	else { /* Liste */
		temp=(*base);
		if((*base)->next==(*base)) { /* last entry */
			(*base)=NULL; }
		else {
			(*base)=(*base)->next; (temp->prev)->next=(*base); (*base)->prev=temp->prev;
		}
	}
	temp->up=temp->next=temp->prev=NULL;
	return(temp);
}
///«

///» "OutputName"
void OutputName(NODE *t, int offset)
{
int i;

	i=strlen(t->name);
	if(i>24) {
		Flush(Output());
		printf("%.*s...%s",offset,t->name,t->name+(i-24+offset+3)); }
	else {
		printf("%-24s",t->name);
	}
}
///«

///» "Swap"
void Swap(NODE *a, NODE *b)
{
char name[108]="";
char note[80]="";
int size;

	strcpy(name,a->name); strcpy(note,a->note); size=a->size;
	strcpy(a->name,b->name); strcpy(a->note,b->note); a->size=b->size;
	strcpy(b->name,name); strcpy(b->note,note); b->size=size;
}
///«

///» "ExamineDir"
void ExamineDir(char *dirname,char *dirfilenote)
{
BPTR currentdir;
NODE *file=NULL;
NODE *dir=NULL;
NODE *temp=NULL;
int i;
int filecount=0,dircount=0,tmpfc=1,tmpdc=1;
BPTR MyLock;
struct FileInfoBlock *infoBlock=(struct FileInfoBlock *) AllocMem(sizeof(struct FileInfoBlock),MEMF_FAST);

	if(!infoBlock) {
		printf("%s\n","Error: Can't allocate memory for FileInfoBlock!");
		break_flag=20; }
	else {
		MyLock=Lock(dirname,ACCESS_READ);
		if(!MyLock) {
			printf("Error: Can't lock \"%s\"!\n",dirname);
			FreeMem(infoBlock,sizeof(struct FileInfoBlock));
			break_flag=20; }
		else {
			if(!Examine(MyLock,infoBlock)) {
				printf("%s\n","Error: Can't get infoBlock!");
				FreeMem(infoBlock,sizeof(struct FileInfoBlock));
				UnLock(MyLock);
				break_flag=20;
			}
		}
	}

	/* read directory into arrays */
	while(!break_flag&&ExNext(MyLock,infoBlock)) {
		if(infoBlock->fib_DirEntryType<0) { /* file! */
			filecount++;
			if(!dirs) {
				PutNode(&file,infoBlock,!nosort);
			} }
		else { /* dir! */
			dircount++;
			PutNode(&dir,infoBlock,sort);
		}
	}

	/* print filenote of currently examined dir */
	if(!break_flag&&note) {
		if(strcmp(dirfilenote,"")) {
			printf("%s",string);
			printf("%s",(all&&(filecount||dircount)&&!dirs)||(dirs&&dircount)?tree[0]:" ");
			printf("%s",!stat?tree[2]:tree[1]);
			printf("(%s)\n",dirfilenote);
		}
	}

	/* print statistics */
	if(!break_flag&&stat) {
		if(!dircount&&!filecount) {
			printf("%s %s(",string,tree[2]);
			if(!plain) { printf("\x9b%s","32m"); }
			printf("%s","empty");
			if(!plain) { printf("\x9b%s","31m"); }
			printf("%s\n",")"); }
		else {
			printf("%s",string);
			printf("%s%s(",!dirs||(dirs&&dircount)?tree[0]:" ",tree[2]);
			if(dircount) {
				if(!plain) { printf("\x9b%s","32m"); }
				printf("%ld dir%s",(long) dircount,dircount>1?"s":"");
				if(!plain) { printf("\x9b%s","31m"); }
			}
			if(dircount&&filecount) {
				printf("%s","/");
			}
			if(filecount) {
				if(!plain) { printf("\x9b%s","32m"); }
				printf("%ld file%s",(long) filecount,filecount>1?"s":"");
				if(!plain) { printf("\x9b%s","31m"); }
			}
			printf("%s\n",")");
			if(space&&dirs&&dircount) {
				printf("%s%s\n",string,tree[0]);
			}
		}
	}

	/* process the dir-array according to switches */
	while(!break_flag&&tmpdc<=dircount) {
		if(space&&tmpdc!=1) {
			printf("%s%s\n",string,tree[0]);
		}
		/* get temp from dir-array */
		temp=GetNode(&dir,sort);
		/* output */
		printf("%s%s­­",string,(dircount==tmpdc&&(!filecount||dirs))?tree[2]:tree[1]);
		if(!plain) { printf("\x9b%s","33m"); }
		OutputName(temp,8);
		printf("%s","        Dir");
		if(!plain) { printf("\x9b%s","31m"); }
		printf("%s\n","");

		if(!break_flag&&all) {
			currentdir=CurrentDir(MyLock);
			i=strlen(string);
			string[i]=tmpdc<dircount||(filecount&&!dirs)?*tree[0]:' '; string[i+1]=' '; string[i+2]=' '; string[i+3]=0;

			ExamineDir(temp->name,temp->note);

			string[i]=0;
			CurrentDir(currentdir);
		}
		/* free temp */
		FreeMem(temp,sizeof(NODE)); temp=NULL;
		tmpdc++;
	}

	/* process the file-array according to switches */
	if(!break_flag&&!dirs&&tmpfc<=filecount) {
		if(space&&dircount&&filecount) {
			printf("%s%s\n",string,tree[0]);
		}
		while(!break_flag&&tmpfc<=filecount) {
			/* get temp from file-array */
			temp=GetNode(&file,!nosort);
			/* output */
			printf("%s%s­",string,filecount==tmpfc?tree[2]:tree[1]);
			OutputName(temp,8);
			printf(" %8ld\n",(long) temp->size);

			/* print filenote of file */
			if(!break_flag&&note) {
				if(strcmp(temp->note,"")) {
					printf("%s",string);
					printf("%s  %s",tmpfc!=filecount?tree[0]:" ",tree[2]);
					printf("(%s)\n",temp->note);
				}
			}

			/* free temp */
			FreeMem(temp,sizeof(NODE)); temp=NULL;
			tmpfc++;
		}
	}

	/* release memory in case of ctrl-c or other strange happenings */
	if(temp) { FreeMem(temp,sizeof(NODE)); }
	if(file) {
		file->prev->next = NULL;
		while(file) { temp=file; file=file->next; FreeMem(temp,sizeof(NODE)); }
	}
	if(dir) {
		dir->prev->next = NULL;
		while(dir) { temp=dir; dir=dir->next; FreeMem(temp,sizeof(NODE)); }
	}
	if(infoBlock) { FreeMem(infoBlock,sizeof(struct FileInfoBlock)); }
	if(MyLock) { UnLock(MyLock); }
}
///«

///» "main"
int main(int argc, char *argv[])
{
int i;
int noprefs=0;

FILE *prefsfile;
char line[108]="";
char **try;

struct RDArgs *rdargs;
long arg[12]={NULL,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE,DOSFALSE};

BPTR MyLock;

onbreak(Nope);

	/* do the argument parsing */
	if(rdargs=ReadArgs(program_template,arg,NULL)) {
		all    =(arg[1]==DOSTRUE);
		dirs   =(arg[2]==DOSTRUE);
		plain  =(arg[3]==DOSTRUE);
		space  =(arg[4]==DOSTRUE);
		sort   =(arg[5]==DOSTRUE);
		nosort =(arg[6]==DOSTRUE);
		patz   =(arg[7]==DOSTRUE);
		noprefs=(arg[8]==DOSTRUE);
		stat   =(arg[9]==DOSTRUE);
/*  HELP   =(arg[10]==DOSTRUE); */
		note   =(arg[11]==DOSTRUE);
	}

	/* get the defaults from ENV:TDir.prefs (if file exists) */
	if(!noprefs&&(prefsfile=fopen("ENV:TDir.prefs","r"))) {
		i=0;while((line[i]=getc(prefsfile))!=EOF) {line[i]=toupper(line[i]); i++;}
		fclose(prefsfile);
		if(strstr(line,"PLAIN"))  { plain=1;  }
		if(strstr(line,"SPACE"))  { space=1;  }
		if(strstr(line,"DIRS"))   { dirs=1;   }
		if(strstr(line,"ALL"))    { all=1;    }
		if(strstr(line,"SORT"))   { sort=1;   }
		if(strstr(line,"NOSORT")) { nosort=1; }
		if(strstr(line,"PATZ"))   { patz=1;   }
		if(strstr(line,"STAT"))   { stat=1;   }
		if(strstr(line,"NOTE"))   { note=1;   }
	}

	if(patz) { tree[0]="|"; tree[1]="¬"; tree[2]="¯"; }

	if(arg[10]==DOSTRUE) {
		printf("%s by %s\n",program_name,programmers_name);
		printf("%s\n","For details read the nonexisting docs :)\n"); }
	else {
		if(arg[0]) {
			try=(char **)arg[0];
			i=0;

			while(!break_flag&&try[i]) {
				MyLock=Lock((char *)try[i],ACCESS_READ);
				if(!MyLock) {
					printf("Can't find directory \"%s\"!\n\n",(char *)try[i]); i++;
				}
				else {
					UnLock(MyLock);
	
					printf("Directory \"%s\"\n",(char *)try[i]);
					ExamineDir((char *)try[i],"");
					if(!break_flag) {
						printf("%s\n","");
					}
					i++;
				}
			} }
		else {
			MyLock=Lock("",ACCESS_READ);
			if(!MyLock) {
				printf("%s\n\n","Can't lock current directory!");
				break_flag=20; }
			else {
				NameFromLock(MyLock,line,100);
				UnLock(MyLock);
	
				printf("Directory \"%s\"\n",line);
				ExamineDir("","");
				if(!break_flag) {
					printf("%s\n","");
				}
			}
		}
		if(break_flag==5) {
			printf("%s\n","***Break");
		}
	}
	
	if(rdargs) { FreeArgs(rdargs); }
	exit(break_flag);
}
///«

