; /* To make, just execute me
lc -v -j73 -O -cusf -M du
blink du.o lib lib:lc.lib nd sc sd batch quiet
protect du +p
quit
*/
/************************************************************************
 * du.c -- calculate disk usage for files in a directory tree		*
 *									*
 *		Copyright 1989,1990 Peter Chubb				*
 *		   All rights reserved					*
 *									*
 ************************************************************************/

static char RCSid[] = "$Id: du.c,v 1.2 90/06/06 20:07:37 peterc Exp $";

/*
 * Written:
 *	10 July 1989 Peter Chubb
 * $Log:	du.c,v $
 * Revision 1.2  90/06/06  20:07:37  peterc
 * Added options to allow selection of blocksize;
 * added allowance for file extension blocks.
 * 
 *
 */

#include <libraries/dos.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>
#include <string.h>
#include <stdlib.h>
#include <dos.h>

#ifndef min
#    define min(a, b) ((a) < (b) ? (a) : (b))
#endif

#define ABSEXECBASE ((struct ExecBase **)4L)

void RawDoFmt(char *fmt, void *arglist, void (*prbuf)(), char *obuf);
#pragma syscall RawDoFmt 20a ba9804

void __asm prbuf(register __d0 char c);
#define R_A3 (8+3)

struct DosLibrary *DOSBase;

void outval(char *fmt,...);

long __regargs du(BPTR dir, long slnt, unsigned long level, unsigned long bsize);
long __asm __saveds dumain(register __a0 char *cmd);
unsigned long numBlocks(LONG size, unsigned long bsize);


/* main routine -- parse arguments, then call du() to do the work */
long __asm __saveds
dumain(register __a0 char *cmd)
{
    long total;
    register char *p;
    long 	  c;
    long	  slnt = 0;
    BPTR	  dir;
    struct Library *foo;
    unsigned long bsize = 0;
    struct InfoData infodata;

    if (!(foo = OpenLibrary("dos.library", 0)))
        return RETURN_ERROR;
    DOSBase = (struct DosLibrary *) foo;

    while ((c = *cmd++) == ' ')
	;

    while (c == '-')
    {
    	c = *cmd++;
	switch(c)
	{
	case 's':    
		slnt = 1;
		break;

	case 'F':
		if (bsize)
		{
		    outval ("File system blocksize specified twice!\n");
		    return RETURN_ERROR;
		}
		bsize = 512;
		break;

	case 'S':
		if (bsize)
		{
		    outval ("File system blocksize specified twice!\n");
		    return RETURN_ERROR;
		}
		bsize = 488;
		break;

	default:
	    outval("Usage: du [-s] [-F | -S] [dirname]\n");
	    return RETURN_ERROR;
	}
	while ((c = *cmd++) == ' ')
	    ;
    } 
    p = --cmd;
    while (*p != ' ' && *p != '\n' && *p)
	p++;
    *p = '\0';

    if (p != cmd) 
    {
	c = 1;
	dir = Lock(cmd, ACCESS_READ);
    }
    else 
    {
	c = 0;
	dir = CurrentDir(0L);
	(void) CurrentDir(dir);
    }
    if (!dir) 
    {
    	outval("Can't open %ls\n", cmd);
	return RETURN_WARN;
    }

    if (!bsize)
    {
	Info(dir, &infodata);
	bsize = infodata.id_BytesPerBlock;
    }

    total = du(dir, slnt, 0, bsize);

    if (c)
	UnLock(dir);

    if (total >= 0)
        outval("Total: %ld\n", total);
    else
	outval("**BREAK**\n\n");

    CloseLibrary(foo);
    return RETURN_OK;
}


/* outval -- our equivalent of printf */
void
outval(char *fmt, ...)
{
    char obuf[200];
   
    RawDoFmt(fmt, (&fmt) + 1, prbuf, obuf);
    Write(Output(), obuf, strlen(obuf));
}

/* du -- calculate and print disc usage */
long __regargs
du(dir, slnt, level, bsize)
BPTR dir; /* lock on top of directory tree */
long slnt; /* whether to print or not */
unsigned long level; /* how deep in the tree */
unsigned long bsize;
{
    /* note all these are longword aligned */

    struct FileInfoBlock fib;
    BPTR   	lck;
    long	total = 0;
    long	extra;
    BPTR   	curdir = CurrentDir(dir);
    long	abort = 0;

    if (Examine(dir, &fib)) {
	if (fib.fib_DirEntryType < 0) {
	    if (!slnt) {
		Write(Output(), "                                  ", 
				(long)min(level, 32));
	    	outval("%ls	%ld\n", fib.fib_FileName, 
				total = numBlocks(fib.fib_Size, bsize));
	    }
	} else 
	    while (ExNext(dir, &fib) & abort >= 0) {
	    	if (SetSignal(0, 0) & SIGBREAKF_CTRL_C)
		    abort = -1;
	    	else {
		    extra = numBlocks(fib.fib_Size, bsize);
	    	    if (fib.fib_DirEntryType > 0) {
			lck = Lock(fib.fib_FileName, ACCESS_READ);
			extra += (abort = du(lck, slnt, level+1, bsize));
		    	UnLock(lck);
		    } 
		    total += extra;
	    	    if (!slnt && abort >= 0) {
			Write(Output(), "                                  ",
					min(level, 32));
	    	        outval("%-32ls %6ld\n", fib.fib_FileName, extra);
	    	    }
            	}
            }
    }
    (void) CurrentDir(curdir);
    return abort < 0 ? -1 : total;
}



/*----------------------------------------------------------------*/
/* This stub routine is called from the RawDoFmt routine for each */
/* character in the string.  At invocation, we have:              */
/*   D0 - next character to be formatted                          */
/*   A3 - pointer to data buffer                                  */
/* Stolen from Lattice-supplied Avail utility			  */
/*----------------------------------------------------------------*/
void __asm prbuf(register __d0 char c)
{
    char *p = (char *)__builtin_getreg(R_A3);
    *p++ = c;
    __builtin_putreg(R_A3, (long)p);

    /* It's a pity this doesn't generate
     * 	 move.b d0,(a3)+
     *   rts
     */
}

/* numBlocks -- work out how many blocks a file takes */
unsigned long
numBlocks(LONG size, unsigned long bsize)
{
	register unsigned long	blocks;
	
	blocks = (size + bsize - 1) / bsize;
	blocks += (blocks / ((bsize/4) - 56)) + 1;
	
	return blocks;
}

