
/********************************************************************\
*								     *
*			Dave's DU version 1.01                       *
*								     *
* ©1991 by Dave Schreiber.  All rights reserved.  This program may   *
* be freely distributed.  The only charges allowed in relation to    *
* distributing this program is in relation to media, shipping, and   *
* handling costs.						     *
*								     *
* Version list: 						     *
*   1.02 (05/24/91) - Improved CTRL-C handling                       *
*   1.00 (04/29/91) - Original release version                       *
*								     *
* Compiled with SAS/C V5.10.  To compile, do the following:	     *
*   1> lc DU							     *
*   1> blink with DU.lnk					     *
*								     *
\********************************************************************/


#include <exec/types.h>
#include <exec/exec.h>
#include <exec/tasks.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <stdio.h>
#include <signal.h>

ULONG *DOSBase;

/*Function prototypes*/

ULONG __regargs getDirSize(BPTR lock,char *dirName);
void interpretArgs(int argc,char *argv[],WORD *countDirs,WORD *countFiles,
		    char *dirName);
void printSize(char *dirName,ULONG size);
void printHelp(void);
void ctrlC_Handler(void);

/*-------------------*/

/*Pragmas*/

#pragma syscall AllocMem C6 1002
#pragma syscall FreeMem D2 0902
#pragma syscall FindTask 126 901

#pragma libcall DOSBase CurrentDir 7E 101
#pragma libcall DOSBase Examine 66 2102
#pragma libcall DOSBase ExNext 6C 2102
#pragma libcall DOSBase Lock 54 2102
#pragma libcall DOSBase UnLock 5A 101

/*-------*/

/*Global variables*/

WORD countFiles,countDirs;
struct Task *task;
BYTE quitNow=FALSE;

/*----------------*/

/*Defines*/

#define ERROR 0xFFFFFFFF
#define ABS(x) ((x) < 0 ? -(x) : (x))

/*-------*/

/*Main.  Initializes, find the directory size, and then cleans up*/
main(int argc,char *argv[])
{
   BPTR lock;
   char dirName[256];
   LONG totalSize;

      /*Interpret the command line arguments*/
   interpretArgs(argc,argv,&countDirs,&countFiles,dirName);

      /*Replace the CTRL-C handler*/
   signal(SIGINT,ctrlC_Handler);

      /*Get a lock on the provided directory name*/
   lock=Lock(dirName,SHARED_LOCK);
   if(lock == NULL)  /*Abort if the directory isn't there*/
   {
      fputs("Couldn't access the directory you requested;  aborting.\n",
	    stderr);
      exit(200);
   }

      /*Get the size of the contents of the directory*/
   totalSize=getDirSize(lock,dirName);

      /*Abort if something went wrong, otherwise print the directory size*/
   if(totalSize == ERROR)
      fputs("Error.  Aborting!\n",stderr);
   else
      if(!countDirs && !quitNow)
	 printSize(dirName,totalSize);

   UnLock(lock);  /*Free the lock on the top directory*/
   exit(0);       /*And exit*/
}

/*Get the size of the contents of the specified directory (recursive)*/
ULONG __regargs getDirSize(BPTR lock,char *dirName)
{
   register ULONG totalSize=0;
   register struct FileInfoBlock *fib;
   register BPTR dirLock,oldCurDir;
   register UWORD dirNameLen;
   register ULONG size;

   dirNameLen=strlen(dirName);

      /*Allocate (longword aligned) FileInfoBlock structure*/
   fib=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),
	       MEMF_PUBLIC,MEMF_CLEAR);
   if(fib==NULL)
      return(ERROR);

      /*Save the current directory, and change to the directory we need*/
      /*to find the size of.*/
   oldCurDir=CurrentDir(lock);
   Examine(lock,fib);

      /*For each file/directory, get the size...*/
   while(ExNext(lock,fib) && !quitNow)
   {
      totalSize++;	/*Add 1 (for the directory entry)*/
      if(ABS(fib->fib_EntryType) != 4)  /*If the file isn't a link...*/
      {
	 if(fib->fib_EntryType < 0)  /*If the file isn't a directory*/
	 {				     /*get the size directly*/
	       /*Add the size to the total*/
	    totalSize+=fib->fib_NumBlocks;

	    if(countFiles) /*If the user wants the size of each file*/
	    {		   /*printed, print the file size*/
	       if(dirNameLen!=0 && dirName[dirNameLen-1]!=':')
		  strcat(dirName,"/");
	       strcat(dirName,fib->fib_FileName);
	       printSize(dirName,totalSize);
	       dirName[dirNameLen]=NULL;
	    }
	 }
	 else  /*It's a directory*/
	 {
	       /*Get the lock on the directory*/
	    dirLock = Lock(fib->fib_FileName,SHARED_LOCK);

	    if(dirLock == NULL)  /*Abort if there was a problem getting*/
	    {			 /*a lock on the directory*/
	       CurrentDir(oldCurDir);
	       fputs("Couldn't lock directory:",stderr);
	       fputs(fib->fib_FileName,stderr);
	       fputs("\n",stderr);
	       FreeMem(fib,sizeof(struct FileInfoBlock));
	       return(ERROR);
	    }
	       /*Create the full name of the directory we're going to */
	       /*descend into*/
	    if(dirNameLen!=0 && dirName[dirNameLen-1]!=':')
	       strcat(dirName,"/");
	    strcat(dirName,fib->fib_FileName);

	       /*Recursively get the directory size*/
	    size=getDirSize(dirLock,dirName)-1;
	    UnLock(dirLock);

	    if(size==ERROR)   /*Abort if there was an error*/
	    {
	       CurrentDir(oldCurDir);
	       FreeMem(fib,sizeof(struct FileInfoBlock));
	       return(ERROR);
	    }
	    else
	       totalSize+=size+2;  /*Otherwise, add the size to the total*/

	       /*Restore the old directory name*/
	    dirName[dirNameLen]=NULL;
	 }
      }
   }
   CurrentDir(oldCurDir);

   if(quitNow)    /*If the user pressed CTRL-C, abort*/
      fputs("*** Break: du\n",stderr);
   else
      if(countDirs)  /*If CTRL-C wasn't pressed, print the size of the*/
	 printSize(dirName,totalSize); /*directory (if the user wants it)*/

      /*Deallocate the FileInfoBlock*/
   FreeMem(fib,sizeof(struct FileInfoBlock));
      /*Return the total size*/
   return(totalSize);
}

/*Interpret the command line arguments*/
void interpretArgs(int argc,char *argv[],WORD *countDirs,WORD *countFiles,
		    char *dirName)
{
   int i;

   if(argv[1][0]=='?')  /*The user is asking for help*/
   {
      printHelp();
      exit(0);
   }

      /*Set these variables to their default value*/
   *countDirs=TRUE;
   *countFiles=FALSE;
   strcpy(dirName,"");

   for(i=1;i<argc;i++)
   {
      if(argv[i][0]=='-')
	 switch(argv[i][1])
	 {
	    case 's':   /*Don't print the size of each directory*/
	    case 'S':
	       *countDirs=FALSE;
	       break;
	    case 'a':   /*Print the size of each directory*/
	    case 'A':
	       *countFiles=TRUE;
	       break;
	 }
      else  /*If the first character wasn't '-', the argument must be the*/
	 strcpy(dirName,argv[i]);   /*directory name*/
   }
      /*If the user specified that directory sizes wouldn't be printed,*/
      /*don't print out the file sizes either*/
   if(*countDirs==FALSE)
      *countFiles=FALSE;
   return;
}

/*Make the directory/file name, then print out the size*/
void printSize(char *dirName,ULONG size)
{
   char sizeStr[16];

      /*Conver the size (int) to a string*/
   stci_d(sizeStr,size);

      /*Print it out*/
   fputs(sizeStr,stdout);

      /*Print out a TAB*/
   fputs("\t",stdout);

      /*Print out the directory name, or '.' if it's the current directory*/
   if(dirName[0]==NULL)
      puts(".");
   else
      puts(dirName);

      /*Flush the output stream*/
   flushall();
   return;
}

/*Print help*/
void printHelp(void)
{
   puts("\nDave's DU V1.01 ©1991 by Dave Schreiber. All rights reserved.");
   puts("\nUsage:");
   puts(" du [-s][-a][<dir>]");
   puts("-s -- Print only the grand total size");
   puts("-a -- Print the size for each file");
   puts("<dir> -- Starting directory");
   puts("");
   return;
}

/*CTRL-C handler*/
void ctrlC_Handler(void)
{
   signal(SIGINT,ctrlC_Handler);
   quitNow=TRUE;
   return;
}

