/*
 *	DF - report disk space		Ver. 1.1
 *
 *		COPYRIGHT (C) 1989 by Urs Zurbuchen
 *			ALL RIGHTS RESERVED
 *
 *		You are granted to the right to copy and
 *		distribute this program via any means
 *		as long as you don't remove this notice.
 *
 *		However, you are specifically prohibited
 *		from charging, requesting any donations,
 *		and from distributing it with commercial
 *		products without prior written permission.
 *
 *	------------------------------------------------------------
 *
 *	Displays a space usage statistic for all known drives in the
 *	systems. For join'ed drives their real free space is reported.
 *	Floppy sizes are only reported on request and if there is really
 *	a floppy in the drive. If there is none, the program continues
 *	without an error message.
 *
 *	Example:
 *		C\:> df -f
 *		Total space     Free space            Drive
 *		    362'496        131'072     36%      a:
 *		  2'060'288      1'155'072     56%      c:
 *		 40'607'744      8'216'576     20%      d:
 *		 67'051'520      5'554'176      8%      e:
 *		 12'976'128      7'417'856     57%      f:
 *		    518'912        518'912    100%      g:
 *
 *
 *	Date:	16 January 1988
 *	Author:	Urs Zurbuchen
 *
 *	Compile:
 *		cl df.c		(for Microsoft C)
 *
 *	Revision history
 *	When		Who		What
 *	30. 4.1989	zu		Support for options -d and -f
 *					Prepared for submission to Usenet.
 *
 *	Last released:
 *	30. 4.1989	Ver. 1.1	Usenet, comp.binaries.ibm.pc
 *
 *---------------------------------------------------------------------------
 */

#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <process.h>
#include <stdlib.h>
#include <setjmp.h>


long		zehner[3] = { 1000000, 1000, 1 };
jmp_buf		jb;
char		error_handler[4] = { 0xb8, 0, 0, 0xcf };

#ifdef	LINT_ARGS
char	*basename( char * );
int	main( int, char ** );
void	print_number( long );
void	print_statistics( unsigned char );
void	setvector( unsigned int );
void	Usage( char * );
#endif


int main( argc, argv )
int	argc;
char	**argv;
{
	union REGS	inregs, outregs;
	struct SREGS	segregs;
	int		drives, floppies;
	int		NoFloppies = 1, NoDisks = 0;
	unsigned int	olderr_offset, olderr_segment;
	unsigned char	i;
	void		print_statistics(), setvector();
	
	/* initialize for error handling */
	inregs.x.ax = 0x3524;		/* get error handler vector	*/
	intdosx( &inregs, &outregs, &segregs );
	olderr_offset = outregs.x.bx;
	olderr_segment= segregs.es;
	setvector( (unsigned int)error_handler );

	/* parse command line arguments */
	if( argc > 1 ) {
		for( i = (unsigned char) 1; (int)i < argc; i++ ) {
			if( argv[i][0] == '-' ) {
				switch( argv[i][1] ) {
				    case '?':
				    case 'h':
					Usage( argv[0] );
					exit( -1 );
					break;
				    case 'f':
					NoFloppies = 0;
					break;
				    case 'd':
					NoDisks = 1;
					break;
				    default:
					fprintf( stderr, "Unknown option %c\n",
						argv[i][1] );
					Usage( argv[0] );
					break;
				}
			} else if( strlen( argv[i] ) == 2 && argv[i][1] == ':' ) {
				print_statistics( (unsigned char)tolower( (int)argv[i][0] ) -
						  (int)'a' );
				exit( 0 );
			} else {
				fprintf( stderr, "df: invalid argument %s\n",
					 argv[i] );
				Usage( argv[0] );
				exit( 1 );
			}
		}
	}
	
	/* determine number of logical drives in system */
	inregs.h.ah = 0x19;		/* get current disk		*/
	intdos( &inregs, &outregs );
	inregs.h.dl = outregs.h.al;
	inregs.h.ah = 0x0e;		/* select disk			*/
	intdos( &inregs, &outregs );
	drives = outregs.h.al;
	/* we now have the number of logical drives. But, this number	*
	 * might be wrong. It is just the value of the 'lastdrive='	*
	 * command in config.sys which defaults to five if not specified*
	 * We will have to test for the drives presence anyway.		*/
	 
	/* determine number of physical floppy drives in system */
	int86( 0x11, &inregs, &outregs );	/* equipment determination */
	floppies = ( outregs.h.al & 0x01 ) ? ( outregs.h.al >> 6 ) +1 : 0;
	
	/* now print statistics */
	/* - for floppies	*/
	if( NoFloppies == 0 )
		for( i = (unsigned char) 0; (int)i < floppies; i++ )
			print_statistics( i );
	
	/* - for harddisks	*/
	if( NoDisks == 0 )
		for( i = (unsigned char) 2; (int)i < drives; i++)
			print_statistics( i );

	/* restore vector for error handling */
	inregs.x.ax = 0x2524;		/* get error handler vector	*/
	outregs.x.dx = olderr_offset;
	segregs.ds   = olderr_segment;
	intdosx( &inregs, &outregs, &segregs );

	return( 0 );
}

void print_statistics( drive )
unsigned char	drive;
{
	union REGS	iregs, oregs;
	long		total, free;
	static int	firsttime = 0;
	void		print_number();
	int		checkdrive();

	if( firsttime++ == 0 )
		printf( "Total space     Free space            Drive\n" );

	/* Try to print the statistics. If we try to read an inexistent	*
	 * drive the operating system calls its 'fatal error' handler	*
	 * (via INT 24h). Because we redirected this vector we will get	*
	 * control again in case of an error. In such a case the program*
	 * just continues with the next drive.				*/
	if( setjmp( jb ) != 0 )
		return;

	iregs.h.ah = 0x36;		/* get disk space		*/
	iregs.h.dl = drive +1;
	intdos( &iregs, &oregs );
	if( oregs.x.ax != 0xffff ) {
		total = (long)oregs.x.ax * (long)oregs.x.cx * (long)oregs.x.dx;
		free  = (long)oregs.x.ax * (long)oregs.x.cx * (long)oregs.x.bx;
		print_number( total );
		print_number( free  );
		printf( "%3.1ld%%    ", free / (total / 100L) );
		printf( "  %c:\n", drive + 'a' );
	}
}

void print_number( n )
long	n;
{
	int	i = 0, has_printed = 0;
	long	c;

	/* if the number is greater than 999'999'999 we output it as it is */
	if( n > 999999999 )
		printf( "%ld", n );
	else
		while( i < 3 ) {
			if( has_printed )
				printf( "'" );
			if( (c = n / zehner[i] ) >= 1L ) {
				printf( (has_printed ? "%.3ld" : "%3.1ld"), c );
				has_printed = 1;
			} else
				printf( has_printed ? "000" : "    " );
			n %= zehner[i++];
		}
	printf( "    " );
}


void setvector( func )
unsigned int func;
{
	union REGS	inregs, outregs;
	struct SREGS	segregs;

	segread( &segregs );		/* pick up segment registers	*/
	inregs.x.ax = 0x2524;		/* set our error handler vector	*/
	inregs.x.dx = func;
	intdosx( &inregs, &outregs, &segregs );
}


void Usage( prog )
char *prog;
{
	fprintf( stderr, "Usage is:\n" );
	fprintf( stderr, "%s: [-? | -h] [-f | -d] [d:]\n", strlwr( basename( prog )) );
	fprintf( stderr, "        -?, -h    display this message\n" );
	fprintf( stderr, "        -f        check floppies, too\n" );
	fprintf( stderr, "        -d        do not check harddisks\n" );
	fprintf( stderr, "        d:        check only drive d:\n" );
	return;
}

char *basename( name )
char *name;
{
	char	*pos;

	if( (pos = strrchr( name, '/' )) == NULL &&
	    (pos = strrchr( name, '\\' )) == NULL )
	    	pos = name;
	else
		pos++;

	return( pos );
}



