/*
 * du.c
 * (C) 1986 Software Solution, all rights reserved
 *----------------------------------------------------------------------------
 *
 *    Original taken from Fish Disk #48
 *
 *   - modified on May 89, emitted Mar 90 (since no original enhancements) by:-
 *
 *	 	Gary Duncan
 *		24 Inkster St
 *		Kambah ACT 2902
 *		Australia
 *              ~~~~~~~~~
 *
 *
 *    reasons as follows :-
 *
 *	1.   print all files in dir at once ( original algorithm printed
 *	     files and sub-dirs in given,  unordered way ).
 *	     - this is the major functional  change.
 *
 *	2.   allow ^C abort - every text-cruncher should have it.
 *
 *      3.   print dirs in a redpen if not deselected ( looks much better )
 *
 *      4.   changed code to be compiled under AZTEC 16-bit ints 
 *	     ( it would be helpful if  people flagged their code with 
 *	       the compilation environment - hint  ).
 *
 *	Limitations ( the excession of will cause a GURU most likely )
 *
 *	a.   50 levels of dir nesting ( unreasonable ???!!!! )
 *      b.   file name path length < 200 (   "         "     )
 *
 *
 *----------------------------------------------------------------------------
 *
 * du is patterned after the Unix(tm) du command. Its default action is to
 * give the total number of disk blocks used by a directory and recursively,
 * its subdirectories. 
 *
 * options:
 *
 *       -a  ;  prints blocks use by  files and Directories
 *
 *       -s  ; sum of all blocks used by a directory and its subdirectories.
 *
 *	 -n  ; no redpen for dir'd		--GMD--
 *
 * Bug reports or suggestions should be sent to: <<< original >>>
 *    The Software Solution
 *    16850 S.W. Timberland Dr.
 *    Aloha, Oregon 97007
 */


#include <stdio.h>
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include "exec/memory.h"
#include "gd_functions.h"	

extern char *indent() ;
extern long Recursive_Sum () ;
extern char *MakeDate[] ;

char *version = "V1.1" ; /* every PD program should have one */
/*               Vers1.0 is the presumed version GMD modified  */


char banner[100] ;

int t_flag = 0;   /* summary only flag */
int a_flag = 0;   /* list disk usage for both files and directories */

struct FileLock  *flockstt = NULL  ;

char *mostbug = "\n\n" ; 	/* extra \n for 'most' - see intro 	*/

char *myname;     /* name by which this command has been called i.e."du" */

char *redpen  =  "    \x1b[33m";  	/* red pen     */
char *witepen =  "\x1b[0m ";  	/* white  pen  */

#define	NEST_LVL	50		/* arbitrary dir nesting level */
char *dirs[NEST_LVL] = NULL ;
int level = 0 ;
int Xblksz  ;


main(argc, argv)

int argc;
char **argv;

{
int j  , ch ;
char *cur_dir , *ptr ,*rtp ; 
long total = 0 ;

   ptr = MakeDate[0] ;
  /* 
   * fiddle around to extract only the ddmmyy from date string
   */

   while ( *ptr++ != ' ') ;	
   rtp=ptr;while(*rtp++!=' ');*--rtp='\0'; /* hackers practise, avoid */

   sprintf ( banner ,  
"DU : %s ; (original by Joe Mueller) , (v1.1 by Gary Duncan)\n" , 
			ptr , version ) ;

   printf ( "%s" , banner ) ;	/* announce program	*/

   myname = *argv++;

   for ( j=0 ; j < 50 ; ++j )	/* clear array		*/
	dirs[j] = NULL ;

  while ( --argc )
   {
   if ( *(ptr = *argv ) == '-' )
     {   			/* handle options ( - ) */
	++argv ;
      	ch = *++ptr ;
	++ptr ;
        switch ( ch ) 
          {
            case 'n':		/* no redpen for dirs */
            case 'N':
		redpen = " <d>" ;
		witepen = "" ;
                break;

            case 't':
            case 'T':
               if (a_flag)
                   usage();

               t_flag++;   /* sum only */
               break;

            case 's':		/* block size filter	*/
            case 'S':
	       if ( *ptr )	/* check for block size filter	*/
		 {
		  if ( sscanf (ptr , "%d" , &Xblksz ) != 1 )
			usage () ;
		 } 	       
	       else
			usage() ;

               break;

            case 'a':
            case 'A':
               if (t_flag)
                   usage();

               a_flag++;   /* all files reported */
               break;

            default:	
               usage();
          }
     }
   }

   if (*argv) 
     {
      while (cur_dir = *argv++) 
	{
	  total = Recursive_Sum(cur_dir)  ;
          printf("%5ld %s%s%s%s", 
            total, redpen, dirs[0] ,witepen , mostbug  );
	}
     }
   else 
     {
      total = Recursive_Sum(argv);
      printf("%5ld %s%s%s%s", 
  	   total , redpen , dirs[0] , witepen , mostbug  );
     }

   exit (0);
}

/*************************************************************************** 
 
 
  Name :      	Recursive_Sum

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 


long Recursive_Sum (fn)

char *fn;  /* directory file name */
{
    long total = 0 ;
    int  val;
    long temp ;
    struct FileLock *flock, *flockold ;
    struct FileInfoBlock *fib ;
    struct FileInfoBlock *fiblast;
    char  *gfib ;
    int foxy = 0 ;

      
   level++ ;		/* dir indentation level */

   fib = AllocMem((long)sizeof(*fib), MEMF_CLEAR);
   if (fib == NULL) 
   {
      fprintf(stderr,"%s: unable to allocate space for fileinfo block\n",
	myname);
      return 0;
   }
   if ( (flock = Lock(fn, ACCESS_READ)) == NULL )
   {
      fprintf(stderr,"%s: unable to lock %s\n", myname, fn);
      FreeMem(fib, (long)sizeof(*fib));
      return 0;
   }
   flockold = NULL;

  if (Examine(flock,fib)) 	/* process all under this dir */
   { 				
      if (fib->fib_DirEntryType > 0) 
	{ 				  /* it's a directory */
         flockold = CurrentDir(flock);
         if ( flockstt == NULL )
		flockstt = flockold ;	/* hold initial dir ptr for a ^C */

 	 gfib =  fib->fib_FileName ;		/* last is current */

         temp = strlen(gfib) + 1 ;		/* 1 for \0   */

         dirs[level-1] = AllocMem( temp , MEMF_CLEAR); /* mem for dir string */ 
         if (dirs[level-1] == NULL) 
          {
             fprintf(stderr,"%s: unable to allocate space for dir ptr\n",
		myname);
             return 0;
          }
         strcpy ( dirs[level-1] , gfib ) ;  /* copy dir name */

        }
      total += fib->fib_NumBlocks;


     /* --- now scan for files --------------------*/

      val = 0 ;
      Examine(flock,fib)  ; 	/* now look for dirs */
      while (ExNext(flock, fib))       
       { 
           if ( breakcheck() )
		break ;
          
     	   if ( fib->fib_DirEntryType < 0 )  /* file ? */
	       {
		    ++foxy  ;
	            val = fib->fib_NumBlocks;	/* yes	*/
		    ++val ;		/* add 1 for info block -
					   ?? correct		*/
        	    if (a_flag && (val > Xblksz) )
               		printf("%5d     %s%s\n", 
                            val , indent(level), fib->fib_FileName );
		    total += val ;
                }
	   else
		foxy = -1 ;
       }
     if ( (foxy < 0) && (a_flag) )
	{
  		foxy = 147 ;		/* mysterious magic # 	*/
		printf ( "\n" ) ;
	}

      Examine(flock,fib)  ; 	/* now look for dirs */
      while (ExNext(flock, fib)) 
	{
           if ( breakcheck() )
		break ;

	     if ( fib->fib_DirEntryType > 0)   /* dir ? */
	  	{				 /* found a subdirectory */
               	    val = Recursive_Sum(fib->fib_FileName); /* recursive call */
		    ++val ;		/* add 1 for info block -
					   ?? correct		*/
              	    if ( (t_flag==0)  && (val > Xblksz) )
                  	printf("%5d %s%s%s%s\n", val,redpen,indent(level),
                              fib->fib_FileName, witepen );
   		    total += val ;
           	} 
	 }


   }    
   FreeMem(fib, (long)sizeof(*fib));
   if (flockold != NULL)
         CurrentDir(flockold);   /* change directories back */

   UnLock(flock);

   FreeMem ( dirs[level] , (long)strlen(dirs[level]) ) ;
   dirs[level--]  = NULL ;

   if ( breakcheck() )	/* ^C abort check	*/
	{
	      fprintf ( stderr , "\n^C\n" ) ;
	      hexit () ;
	}
   return total;

}

/*************************************************************************** 
 
 
  Name :      	indent

  Purpose:   	generates a dir string , given level req'd.
		- uses external list of ptrs to dir names.

  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 

char *indent ( no )

int no ;
{
static char citats[200]    ; 	/* indentation space buffer - nom. size */
int j ;

    citats[0] = 0 ;
    for ( j=0  ; j < no  ; ++j )
	{
	  strcat (citats , dirs[j]);
	  strcat (citats , "/" ) ;
	}
    
    return ( citats ) ;
}

/*************************************************************************** 
 
 
  Name :      	breakcheck

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 

 


breakcheck()
{

   return ((SetSignal(0L,0L) & SIGBREAKF_CTRL_C) );

}

/*************************************************************************** 
 
 
  Name :      hexit

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 




int hexit ()

{
 int j ;

         fflush ( stdout ) ;
         if (flockstt != NULL)
         	CurrentDir(flockstt);   /* change to orig dir	*/

 	 while ( level ) 
	   {
		 FreeMem ( dirs[level] , (long)strlen(dirs[level]) ) ;
	   	 --level ;
	   }
	 exit (0) ;

}

Chk_Abort()
{
return(0);
}

/*************************************************************************** 
 
 
  Name :      usage 

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 



int usage ()

{

static char *rats[] =  {

"Usage:  du   [-a] [-t] [-n] [-sNNN] [vol: / dir list]...\n" ,
"                a = print # of blocks used in  all files and dirs \n" ,
"                n = deselect pretty red-pen DIR hiliting\n",
"                s = don't print those with lesser than NNN blocks\n",
"                t = grand total of blocks only  \n"  ,
"\n",
"               no a,t options  = directories only\n",
""
	} ;
 
int j = 0 ; 
char *ptr ; 
 
 
  while ( *(ptr = rats[j++])  ) 
      fprintf ( stderr, ptr ) ;

  hexit () ;
}
