void do_cdfs(char *);

/*
 * df.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "kernel.h"

#define OS_FSControl 0x29
#define OS_SWINumberFromString 0x39


int header = 0;		/* 'header line printed' flag */


/*
 * convert the (integer) number 'bytes' into a character string,
 * all values are expressed in Kbytes.
 */
char *k(int bytes)
{
	const int one_kilo = 1024;
	static char ret[64];
	char  unit = 'K';
	int   divisor = one_kilo;

	sprintf(ret,"%d%c",bytes/divisor,unit);
	return ret;
}


/*
 * Return the name of filing system number 'fsnum'.  If there is no
 * filing system with that number, an empty string is resturned
 */
char *fsname(int fsnum)
{
	static char buffer[64];		/* static so we can return a pointer to it */
	_kernel_swi_regs r;
	_kernel_oserror *e;

	r.r[0] = 33;				/* identify filing system */
	r.r[1] = fsnum;
	r.r[2] = (int) buffer;
	r.r[3] = 64;				/* size of buffer */
	e = _kernel_swi( OS_FSControl, &r, &r );
	if (e)
	{
		*buffer = 0;		/* treat as no FS for that number */
	}
	return buffer;
}


/*
 * find the SWI number corresponding to the name 'name'.
 * returns -1 if no matching SWI.
 */
int swinumfromname(char *name)
{
	_kernel_swi_regs r;
	_kernel_oserror *e;

	r.r[1] = (int) name;
	e = _kernel_swi( OS_SWINumberFromString, &r, &r );

	return e ? -1 : r.r[0];
}


/*
 * finds the SWI number of the filing system SWI 'fs'_'swiname'.
 * if this can't be found, 'fs'FS_'swiname' is tried. This is necessary
 * for filing systems such as SCSIFS that have the name SCSI, but use the
 * SWI prefix SCSIFS, etc.
 * Returns -1 if both attempts fail.
 */
int fs_swi(char *fs, char *swiname)
{
	char buffer[128];
	int  swi;
	/* Try for fs_swi first */
	sprintf(buffer,"%s_%s",fs,swiname);
	swi = swinumfromname(buffer);
	/* if that failed, try fsFS_swi */
	if (swi<0)
	{
		sprintf(buffer,"%sFS_%s",fs,swiname);
		swi = swinumfromname(buffer);
	}

	return swi;
}


/*
 * returns in *f and *h the number of floppies * harddiscs respectively
 * that filing system 'fs' admits to having.
 * NB: f & h should be integers PASSED BY REFERENCE
 */
void drives(char *fs, int *f, int *h)
{
	int swi = fs_swi(fs,"Drives");
	_kernel_swi_regs r;
	_kernel_oserror *e;

	if ( swi<0 ) { *f = 0; *h = 0; return; }	/* no _Drives SWI */
	e = _kernel_swi( swi, &r, &r );
	if (e)
	{
		*f = 0; *h = 0;							/* if _Drives SWi failed */
	}
	else
	{
		*f = r.r[1];
		*h = r.r[2];
	}
}

/*
 * Return the total free space on disc 'd' on FS 'fs' (eg. ':4' on 'SCSI').
 */
unsigned int freespace(char *fs, char *d)
{
	int swi = fs_swi(fs,"FreeSpace");
	_kernel_swi_regs r;
	_kernel_oserror *e;

	if ( swi<0 ) { return 0; }			/* no _FreeSpace SWI */
	r.r[0] = (int) d;
	e = _kernel_swi( swi, &r, &r );
	return e ? 0 : (unsigned int) r.r[0];	/* could be *huge* => unsigned */
}


/*
 * Return the total size of disc 'd' on filing system 'fs'
 */
unsigned int size(char *fs, char *d)
{
	int swi = fs_swi(fs,"DescribeDisc");
	_kernel_swi_regs r;
	_kernel_oserror *e;
	char block[68];			/* only really need 64 */
	unsigned int *discsize = (unsigned int*) (block+16);

	if ( swi<0 ) { return 0; }		/* no _DescribeDisc SWI */
	r.r[0] = (int) d;
	r.r[1] = (int) block;
	e = _kernel_swi( swi, &r, &r );

	return e ? 0 : *discsize;
}


/*
 * Return the name of disc 'd' on filing system 'fs'
 */
char *name(char *fs, char *d)
{
	int swi = fs_swi(fs,"DescribeDisc");
	_kernel_swi_regs r;
	_kernel_oserror *e;
	static char block[68];			/* only really need 64 */
	char *discname = block+22;
	char *i;

	if ( swi<0 ) { return NULL; }	/* no _DescribeDisc SWI */
	r.r[0] = (int) d;
	r.r[1] = (int) block;
	e = _kernel_swi( swi, &r, &r );
	if (e) { return NULL; }			/* SWI failed */
	for (i=discname; *i>32; i++)
		;							/* find first ctrl or space char */
	*i = 0;							/* and terminate there */
	return discname;
}


void printheader(void)
{
	printf("Typ %-12s %-10s ","Disc","On");
	printf("%9s %9s %9s  %s\n","Size","Used","Avail","Capacity");
}


/*
 * print information for drive 'drive' on filing system 'fs'
 */
void do_disc(char *fs, int drive)
{
	char temp[64];
	char discspec[12];
	char *discname;
	unsigned int discfree;
	unsigned int discsize;
	unsigned int used;
	double pcnt;

	sprintf(discspec,":%d",drive);		/* disc specifier */
	discname = name( fs, discspec );	/* get disc name */
	if ( !discname )
	{
		return;							/* no name, no printout */
	}

	if ( !header )			/* if header not printed yet */
	{
		printheader();
		header = 1;
	}

	sprintf(temp,"%s:%s",fs,discspec);
	printf("[%c] %-12s %-10s ",drive>3 ? 'H' : 'F',discname,temp);

	discsize = size( fs, discspec );		/* get disc size */
	discfree = freespace( fs, discspec );	/* and free space */
	used = discsize - discfree;				/* calc. used space */
	pcnt = (used * (100.0/discsize));		/* & capacity */

	printf("%9s ",k(discsize)); 	/* total */
	printf("%9s ",k(used));      	/* used */
	printf("%9s  ",k(discfree));	/* free */
	printf("%4.1f%%\n",pcnt);		/* capacity */
}







int main(void)
{
	int i,f,h;
	char *fsn;
	int floppies,harddiscs;

	for ( i=1; i<0x100; i++)	/* FS's have #s 1..256 */
	{
		fsn = fsname(i);		/* get FS number 'i' name */
		if (*fsn)
		{
			if ( strcmp("CDFS",fsn)==0 )		/* Special case CDFS */
			{
				/*do_cdfs(fsn)*/;
			}
			else
			{
				drives(fsn,&floppies,&harddiscs);		/* how many drives? */
				for ( h=0; h<harddiscs; h++ )
				{
					do_disc(fsn,h+4);
				}
				for ( f=0; f<floppies; f++ )
				{
					do_disc(fsn,f);
				}
			}
		}
	}
}

/*
 * Return the type of the CD refered to in *ccb (CDFS_ControlBlock)
 * PASS ccb BY REFERENCE!
 * Types are:
 *     0=>Empty 1=>Audio 2=>CDROM 3=>Audio&CDROM
 */
int cdtype(int *ccb)
{
	_kernel_swi_regs r;
	_kernel_oserror *e;
	int enquiretrack = fs_swi("CD","EnquireTrack");
	char block[5];
	int firsttrack;
	int lasttrack;
	int track;
	int type = 0;

	r.r[7] = (int) ccb;
	r.r[1] = (int) block;
	r.r[0] = 0;
	e = _kernel_swi( enquiretrack, &r, &r );
	if ( e )
	{
		fprintf(stderr,"Ooops/1: %s\n",e->errmess);
		return -1;
	}
	firsttrack = block[0];
	lasttrack = block[1];
	for ( track = firsttrack; track <= lasttrack; track++ )
	{
		r.r[0] = track;
		e = _kernel_swi( enquiretrack, &r, &r );
		if (e)
		{
			return 0;
		}
		if (block[4] & 1) { type |= 2; } else { type |= 1; }
	}

	return type;
}



void do_cdfs(char *fs)
{
	int getnumberofdrives = fs_swi(fs,"GetNumberOfDrives");
	int convertdrivetodevice = fs_swi("CD","ConvertDriveToDevice");
	int discused = fs_swi("CD","DiscUsed");
	int cdfsdrives;
	char *discname;
	unsigned int discsize;
	int ccb[5];			/* CDFS control block */
	int cdsize[2];
	char temp[64];
	int i;
	int cdt;
	_kernel_swi_regs r;
	_kernel_oserror *e;

	if (getnumberofdrives<0) { return; }
	e = _kernel_swi( getnumberofdrives, &r, &r );
	if (e) { return; }
	cdfsdrives = r.r[0];

	for ( i = 1; i<=cdfsdrives; i++)
	{
		r.r[0] = i - 1;		/* Logical drive number */
		e = _kernel_swi( convertdrivetodevice, &r, &r );	/* get device# */
		if (e) { continue; }
		/* Set up CDFS control block */
		ccb[0] = (r.r[1] & 0x00000007) /* '>> 0' removed due to bug in EasyC */;
		ccb[1] = (r.r[1] & 0x00000018) >> 3;
		ccb[2] = (r.r[1] & 0x000000e0) >> 5;
		ccb[3] = (r.r[1] & 0x0000ff00) >> 8;
		ccb[4] = (r.r[1] & 0xffff0000) >> 16;
		/* Get type of CD */
		cdt = cdtype(ccb);
		switch( cdt )
		{
			case -1: discname = "*Error*"; break;
			case 0 : discname = "*Empty*"; break;
			case 1 : discname = "*Audio*"; break;
			case 2 : discname = "*Data*"; break;
			case 3 : discname = "*Audio/Data*"; break;
		}

		if ( cdt )
		{
			r.r[0] = 2;		/* Physical block addressing */
			r.r[1] = (int) cdsize;
			r.r[7] = (int) ccb;
			e = _kernel_swi( discused, &r, &r );
			if (e) { continue; }
			discsize = cdsize[1] * cdsize[0];
		}
		else
		{
			discsize = 0;
		}

		if ( !header )			/* if header not printed yet */
		{
			printheader();
			header = 1;
		}
	
		sprintf(temp,"%s::%d",fs,i-1);
		printf("[%c] %-12s %-10s ",'C',discname,temp);

		if (discsize)
		{
			printf("%9s ",k(discsize)); 	/* total */
			printf("%9s ",k(discsize)); 	/* used */
			printf("%9s  ","-");			/* free */
			printf("%4.1f%%\n",100.0);		/* capacity */
		}
		else
		{
			printf("%9s ","-"); 	/* total */
			printf("%9s ","-"); 	/* used */
			printf("%9s  ","-");	/* free */
			printf("%4s\n","-");	/* capacity */
		} 
	}
}

