#define MADE_VIA_MY_SHELL /*
local f
strrhead f . $_srcfile
cc $f; ln -t $f -Lc
return $_maxerr
*
*	$Id: GDu.c 1.2 1994/10/26 22:35:31 RK Exp RK $
*	$Log: GDu.c $
*	Revision 1.2  1994/10/26  22:35:31  RK
*	FreeVec(Fib)-Bug fixed / New Options: -num -links
*	Dir-request / can be called from WB
*
*	Revision 1.1  1994/10/16  16:43:53  RK
*	Initial revision
*
*	GDu - graphically display disk usage
*	Copyright (C) 1994  Rainer Köhler
*
*	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 1, 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, write to the Free Software
*	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/asl.h>
#include <functions.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static char Version[]= "$VER: GDu 1.1 (26.10.94)";

#define WIDTH	(12*8)

#define Fib_Type  Fib->fib_DirEntryType		/** Fasse Dich kurz! **/

struct Library *IntuitionBase, *GfxBase, *AslBase;
struct Window *Win;
struct RastPort *RPort;

char *MyName= "GDu";
long ForeGround=1, BackGround=0;
BOOL Quiet= FALSE, SortSize= FALSE, ShowLinks= FALSE;

struct Dir
{	struct Dir *Next;
	struct Dir *Prev;
	struct Dir *Parent;
	struct Dir *Subdir;
	char	Name[30];
	ULONG	FileSize;
	ULONG	Total;
	USHORT	Top;
} RootNode, *BaseNode;


void _wb_parse() {}

char Indentation[100];


static long AddObj( struct Dir *Parent, BPTR lock)	/** recursive! **/
{
	struct FileInfoBlock *Fib;
	struct Dir *Node, *Next, *Last;
	BPTR cwd, sublock;
	long objsz, totsz=1;	/** 1 for UserDir-Block **/
	long indent;

  if( !(Fib= AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC)))
	return 0;

  cwd= CurrentDir(lock);
  if( Examine(lock,Fib))
  { if( !Quiet)
	Printf("%s %s...\233K\r",(long)Indentation,Fib->fib_FileName);

    if( Fib->fib_DirEntryType < 0)	/** oops, a file **/
    {	objsz  = Fib->fib_NumBlocks;
	objsz += (objsz+71)/72;		/** File Header bzw. List Blocks **/
	Parent->FileSize += objsz;
    }
    else
    {	if( !(Node= malloc(sizeof(struct Dir))))
	{	PutStr("Out of Memory!\n");	goto abort;	}

	strcpy( Node->Name, Fib->fib_FileName);
	Node->Parent= Parent;
	Node->Subdir= NULL;
	Node->FileSize= 0;
	Node->Total = 1;	/** 1 für Dir-Block **/

	indent= strlen(Indentation);	strcpy( &Indentation[indent],"  ");

	while( ExNext(lock,Fib) && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
	{
	  if( ShowLinks ||
		Fib_Type==ST_ROOT || Fib_Type==ST_USERDIR || Fib_Type==ST_FILE)
	  {
	    if( sublock= Lock(Fib->fib_FileName,ACCESS_READ))
	    {	AddObj( Node,sublock);
		UnLock( sublock);
	    }
	  }
	}
	objsz= Node->Total;	Indentation[indent]= 0;

	if( Next= Parent->Subdir)
	{	Last= NULL;
		while( Next && (SortSize? Next->Total > objsz
					: strcmp(Next->Name,Node->Name)<0))
		{	Last= Next;	Next= Next->Next;	}

		Node->Prev= Last;
		Node->Next= Next;
		if(Next) Next->Prev= Node;
		if(Last) Last->Next= Node;
		else	 Parent->Subdir= Node;
	}
	else
	{	Node->Next= Node->Prev= NULL;	Parent->Subdir= Node;	}
    }
    Parent->Total += objsz;
  }
abort:	CurrentDir(cwd); FreeVec(Fib);	return 1;
}


void DrawObj( struct Dir *Node, long left, long top, long height)
{
	long total= Node->Total, subheight;

  Node->Top= top;

  if( height > RPort->TxHeight<<1)
  {	Move(RPort,left+2,top+(height>>1));
	Text(RPort,Node->Name,strlen(Node->Name));

	sprintf( Indentation,"(%ld)",total);
	Move(RPort,left+2,top+(height>>1)+RPort->TxHeight);
	Text(RPort,Indentation,strlen(Indentation));
  }

  Move(RPort,left,top+height);	left += WIDTH;
  Draw(RPort,left,top+height); Draw(RPort,left,top); Draw(RPort,left-WIDTH,top);

  if(left > Win->Width-Win->BorderRight)  return;

  for( Node= Node->Subdir;  Node;  Node= Node->Next)
  {	subheight= height * Node->Total / total;
	DrawObj(Node,left,top,subheight);
	top += subheight;
  }
}

void DrawAll(void)
{
	struct Window *W= Win;

  SetAPen( RPort,BackGround);
  RectFill(RPort,W->BorderLeft,W->BorderTop,
		W->Width-W->BorderRight-1, W->Height-W->BorderBottom-1);

  SetAPen(RPort,ForeGround);	SetBPen(RPort,BackGround);
  DrawObj( BaseNode, W->BorderLeft,W->BorderTop,
		W->Height - W->BorderTop - W->BorderBottom -1);
}


struct Dir *FindNode(long x, long y)
{
	struct Dir *Node= BaseNode;

  x /= WIDTH;
  if(x<=0)  return Node->Parent;

  while( Node->Subdir && x)
    for( Node= Node->Subdir, --x;
	 Node->Next && Node->Next->Top<y;  Node= Node->Next) ;

  return Node;
}


struct
{	Tag ti_Tag;
	void *ti_Data;
}
AslTags[]=
{	ASL_Hail, "Select root of directory tree",
	ASL_OKText, "Display",
	ASL_ExtFlags1, (void *)FIL1F_NOFILES,
	TAG_DONE
};

char *DirRequest(void)
{	struct FileRequester *FReq;
	char *DirName= NULL;

  if( !(AslBase= OpenLibrary("asl.library",37L)))
  {	Printf("%s: Can't open asl.library!\n",(long)MyName);	}
  else
  {
    if( !(FReq= (void *)AllocAslRequest(ASL_FileRequest,(void *)AslTags)))
    {	Printf("%s: Can't create file requester!\n",(long)MyName);	}
    else
    {	if( AslRequest(FReq,NULL))
	{	if( DirName= malloc( strlen(FReq->rf_Dir)+1))
			strcpy( DirName,FReq->rf_Dir);
	}
	FreeAslRequest(FReq);
    }
    CloseLibrary(AslBase);
  }
  return DirName;
}

ULONG WinTags[]=
{	WA_Title,NULL,	WA_PubScreenName,NULL,
	TAG_IGNORE,0, TAG_IGNORE,0, TAG_IGNORE,-1, TAG_IGNORE,-1,  /* l,t,w,h */
	WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|
		IDCMP_REFRESHWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_RAWKEY,
	WA_SizeGadget,TRUE, WA_DragBar,TRUE, WA_DepthGadget,TRUE,
	WA_CloseGadget,TRUE, WA_SimpleRefresh,TRUE,
	WA_MinWidth,42, WA_MinHeight,42,	TAG_DONE
};

main(int argc, char **argv)
{
	char *RootName;
	BPTR RootLock;
	BOOL ende= FALSE;
	long argi, res=20;

  MyName= argv[0];

  if( argc>=2 && argv[1][0]=='?')
  {	Printf("Usage: %s [-quiet] [-links] [-num] [-window x y w h] [-fg num] [-bg num] [dir]\n",
			(long)MyName);
	return 0;
  }
  if( !(IntuitionBase= OpenLibrary("intuition.library",36))
	||  !(GfxBase= OpenLibrary("graphics.library",36)))	exit(36);

  for( argi=1;  argi<argc;  ++argi)
  {		char *arg= argv[argi];

	if( !strcmp( arg,"-fg"))
	{	if( 0 >= StrToLong( argv[++argi], &ForeGround))
			ForeGround=1;
	}
	else
	if( !strcmp( arg,"-bg"))
	{	StrToLong( argv[++argi], &BackGround);	}
	else
	if( !strncmp( arg,"-w",2))		/** "-window" **/
	{ if( 0<StrToLong( argv[++argi], (LONG *)&WinTags[2*2+1]))  /* left */
			WinTags[2*2]= WA_Left;
	  if( 0<StrToLong( argv[++argi], (LONG *)&WinTags[3*2+1]))  /* top */
			WinTags[3*2]= WA_Top;
	  if( 0<StrToLong( argv[++argi], (LONG *)&WinTags[4*2+1]))  /* width */
			WinTags[4*2]= WA_Width;
	  if( 0<StrToLong( argv[++argi], (LONG *)&WinTags[5*2+1]))  /* height */
			WinTags[5*2]= WA_Height;
	}
	else if( !strncmp( arg,"-n",2))  SortSize= TRUE;
	else if( !strncmp( arg,"-l",2))  ShowLinks= TRUE;
	else if( !strncmp( arg,"-q",2))  Quiet= TRUE;
	else break;
  }
  if( argi>=argc)
  {	if( !(RootName= DirRequest()))	goto clean1;	}
  else	RootName= argv[argi];

  if( !(RootLock= Lock(RootName,ACCESS_READ)))
  {	Printf("%s not found\n",(long)RootName);  goto clean1;  }

  RootNode.Total= 1;	RootNode.FileSize= 0;
  strncpy( RootNode.Name,RootName,30);

  Indentation[0]= 0;	AddObj( &RootNode,RootLock);	UnLock(RootLock);

  if( SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
  {	Printf("\233K%s: User Abort\n",(long)MyName);  goto clean1;	}

  if( !Quiet)  PutStr("\233K\n");

  WinTags[1]= (ULONG) RootName;

  if( !(Win= OpenWindowTagList(NULL,(void *)WinTags)))
  {	PutStr("GDu: Can't open Window!\n");	goto clean1;	}

  RPort= Win->RPort;	BaseNode= RootNode.Subdir;
  DrawAll();

  do{	struct IntuiMessage *Msg;
	struct Dir *Node;
	USHORT m_code;
	ULONG m_class;
	SHORT m_X,m_Y;

	while( Msg= (void *) GetMsg(Win->UserPort))
	{ m_class= Msg->Class;	m_code= Msg->Code;
	  m_X = Msg->MouseX;	m_Y = Msg->MouseY;
	  ReplyMsg((void *)Msg);

	  switch(m_class)
	  { case IDCMP_MOUSEBUTTONS:
		m_code ^= 128;		/** up in down **/
	    case IDCMP_RAWKEY:
		if(m_code>128)	break;	/** don't redraw on up-event **/
		switch(m_code)
		{ case SELECTDOWN:
			Node= FindNode(m_X-Win->BorderLeft,m_Y);  break;
		  case CURSORUP:
			Node= BaseNode->Prev;	break;
		  case CURSORDOWN:
			Node= BaseNode->Next;	break;
		  case CURSORLEFT:
			Node= BaseNode->Parent;	break;
		  case CURSORRIGHT:
			Node= BaseNode->Subdir;	break;
		  case 0x41:		/** BS	**/
		  case 0x3A:		/** '/'(usa) bzw. '-'(d) **/
			Node= RootNode.Subdir;
			break;
		  case 0x45:		/** ESC **/
		  case KEYCODE_Q:
			ende= TRUE;
		  default:
			Node= NULL;	break;
		}
		if( Node==NULL || Node== &RootNode)	break;
		BaseNode= Node;
	    case IDCMP_NEWSIZE:
		DrawAll();
		break;
	    case IDCMP_REFRESHWINDOW:
		BeginRefresh(Win);	DrawAll();	EndRefresh(Win,TRUE);
		break;
	    case IDCMP_CLOSEWINDOW:
		ende= TRUE;
	  }
	}
    }while( !ende);

	res= 0;
	CloseWindow(Win);
clean1:	CloseLibrary(GfxBase);	CloseLibrary(IntuitionBase);
	return res;
}
