/*
**	avail.c - report memory statistics
**	$VER: avail.c 42.1 (10.2.96)
**	Copyright © 1996 Michal Letowski
**
**	42.1 (10.2.96) - initial version
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <support/types.h>
#include <support/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "avail.rev.h"


/*
**	Constants
*/
#define DOS_NAME		"dos.library"
#define DOS_VERN		37L

#define TEMPLATE		"CHIP/S,FAST/S,VIRTUAL/S,TOTAL/S,"\
										"FREE/S,INUSE/S,MAXIMUM/S,LARGEST/S,FLUSH/S"

#define BIG_SIZE		0x7FFFFFF0									/* Memory allocation size */
#define ALLOC_CNT		16													/* Number of attempts */

/* Memory types */
#define MT_CHIP			0
#define MT_FAST			1
#define MT_VIRT			2
#define MT_TOTAL		3
#define MT_COUNT		4

/* Amount types */
#define AT_FREE			0
#define AT_INUSE		1
#define AT_MAXIMUM	2
#define AT_LARGEST	3
#define AT_COUNT		4

/* Find maximum of three numbers */
#define Max3(a,b,c)	((a)>(b) ? ((a)>(c) ? (a) : (c)) : ((b)>(c) ? (b) : (c)))


/*
**	Private structures
*/
struct Options
{
	LBOOL  opt_Chip;															/* HARD/S */
	LBOOL  opt_Fast;															/* SOFT/S */
	LBOOL  opt_Virtual;														/* FORCE/S */
	LBOOL  opt_Total;															/* TOTAL/S */
	LBOOL  opt_Free;															/* FREE/S */
	LBOOL  opt_InUse;															/* INUSE/S */
	LBOOL  opt_Maximum;														/* MAXIMUM/S */
	LBOOL  opt_Largest;														/* LARGEST/S */
	LBOOL  opt_Flush;															/* FLUSH/S */
};	/* Options */


/*
**	Global data
*/
STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);


/*
**	Private functions prototypes
*/
STATIC VOID GetMemoryAmount(ULONG *amnt, ULONG memType);


/*
**	Public functions
*/
/****** C/Avail **********************************************************
*
*   NAME
*       Avail -- report memory statistics. (V42)
*
*   SYNOPSIS
*       Avail [CHIP] [FAST] [VIRTUAL] [TOTAL]
*             [FREE] [INUSE] [MAXIMUM] [LARGEST] [FLUSH]
*
*   TEMPLATE
*       Avail "CHIP/S,FAST/S,VIRTUAL/S,TOTAL/S,
*              FREE/S,INUSE/S,MAXIMUM/S,LARGEST/S,FLUSH/S"
*
*   FUNCTION
*       Avail examines memory usage and reports statistics back to the
*       user. Avail can print full (default) or just selected statistics.
*       Avail can report amount of free memory, amount of memory in use,
*       amount of total memory or size of largest contiguous memory block with
*       respect to the memory types. If FLUSH option is used, Avail will try
*       to free as much memory taken by system as possible. You can't use
*       neither CHIP, FAST, VIRTUAL, TOTAL nor FREE, INUSE, MAXIMUM, LARGEST
*       options together. All values are in bytes.
*
*   INPUTS
*       CHIP    - amount of chip memory will be printed.
*       FAST    - amount of fast memory will be printed.
*       VIRTUAL - amount of virtual memory will be printed.
*       TOTAL   - amount of total (i.e. fast, chip and virtual) memory will
*                 be printed (default).
*       FREE    - amount of free memory will be printed (default).
*       INUSE   - amount of memory in use will be printed.
*       MAXIMUM - amount of free and in use memory will be printed.
*       LARGEST - size of largest contiguous memory block will be printed.
*       FLUSH   - if this option is used, Avail will try to free as much
*                 memory taken by system as possible, before printing
*                 statistics.
*
*   RESULT
*       RETURN_FAIL  - if 'dos.library' couldn't be opened.
*       RETURN_ERROR - if command line arguments couldn't be processed or
*                      unallowed combination of options appeared.
*       RETURN_OK    - if statistics were printed.
*
*   EXAMPLE
*       Avail
*           ; Prints all memory statistics
*       Avail CHIP
*           ; Prints amount of free chip memory.
*       Avail TOTAL FLUSH
*           ; Frees some system memory and prints total amount of free memory.
*       Avail VIRTUAL LARGEST
*           ; Prints size of largest contiguous virtual memory block
*
*   NOTES
*       Memory is flushed through several attempts to allocate 2GB of memory.
*       Avail is pure and can be made resident.
*
*   BUGS
*       Virtual memory statistics were not tested.
*
*   SEE ALSO
*       exec.library/AvailMem().
*
*****************************************************************************
*
*/
LONG Avail(VOID)
{
	struct ExecBase *SysBase=INITSYSBASE;
	struct Library *DOSBase;

	ULONG Amnt[MT_COUNT][AT_COUNT];
	struct Options Opts;
	struct RDArgs *Args;
	LONG *OptPtr;
	LONG I,MT=0,AT=0,RC=RETURN_FAIL;
	
	/* Open DOS */
	unless(DOSBase=OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_DOS);

	RC=RETURN_ERROR;															/* Working a bit... */

	/* Read arguments */
	clear(&Opts);																	/* Clear out buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),PROG_NAME),								NO_ARGS);

	/* Check options correctness */
	for(OptPtr=&Opts.opt_Chip; OptPtr<&Opts.opt_Free; OptPtr++)
		if(*OptPtr)	MT++;
	for(OptPtr=&Opts.opt_Free; OptPtr<&Opts.opt_Flush; OptPtr++)
		if(*OptPtr)	AT++;
	if(MT>1 || AT>1)
		throw2(CauseIoErr(ERROR_TOO_MANY_ARGS,PROG_NAME),		BAD_ARGS);

	RC=RETURN_OK;																	/* Success */

	/* First: flush memory if needed */
	if(Opts.opt_Flush)														/* Flushing requested? */
		for(I=0; I<ALLOC_CNT; I++)									/* Try several times to... */
			FreeVec(AllocVec(BIG_SIZE,MEMF_PUBLIC));	/* ... allocate/free 2 GB */

	/* Process amounts */
	Forbid();
	GetMemoryAmount(Amnt[MT_CHIP],MEMF_CHIP);
	GetMemoryAmount(Amnt[MT_FAST],MEMF_FAST);
	GetMemoryAmount(Amnt[MT_VIRT],MEMF_PUBLIC);
	GetMemoryAmount(Amnt[MT_TOTAL],MEMF_ANY);
	Permit();
	for(I=0; I<AT_COUNT; I++)											/* Calculate amount of VMEM */
		Amnt[MT_VIRT][I]=Amnt[MT_TOTAL][I]-Amnt[MT_VIRT][I];
	Amnt[MT_TOTAL][AT_LARGEST]=Max3(Amnt[MT_CHIP][AT_LARGEST],
																	Amnt[MT_FAST][AT_LARGEST],
																	Amnt[MT_VIRT][AT_LARGEST]);

	if(Opts.opt_Chip)				MT=MT_CHIP;
	elif(Opts.opt_Fast)			MT=MT_FAST;
	elif(Opts.opt_Virtual)	MT=MT_VIRT;
	elif(Opts.opt_Total)		MT=MT_TOTAL;
	else										MT=-1;

	if(Opts.opt_Free)				AT=AT_FREE;
	elif(Opts.opt_InUse)		AT=AT_INUSE;
	elif(Opts.opt_Maximum)	AT=AT_MAXIMUM;
	elif(Opts.opt_Largest)	AT=AT_LARGEST;
	else										AT=-1;

	if(MT<0 && !(AT<0))	MT=MT_TOTAL;
	elif(AT<0)					AT=AT_FREE;
	if(MT<0)																			/* No memory type specified */
	{																							/* Do full report */
		VPrintf("Type  Available    In-Use   Maximum   Largest\n",NULL);
		VPrintf("CHIP %10lu %9lu %9lu %9lu\n",Amnt[MT_CHIP]);
		VPrintf("FAST %10lu %9lu %9lu %9lu\n",Amnt[MT_FAST]);
		VPrintf("VIRT %10lu %9lu %9lu %9lu\n",Amnt[MT_VIRT]);
		VPrintf("Total %9lu %9lu %9lu %9lu\n",Amnt[MT_TOTAL]);
	}
	else																					/* Type specified */
		VPrintf("%lu\n",&Amnt[MT][AT]);							/* Print amount */
			
	/* Exceptions */
	catch(BAD_ARGS,	);
	catch(NO_ARGS,	FreeArgs(Args));
	catch(NO_DOS,		CloseLibrary(DOSBase));
	return(RC);
}	/* Avail */


/*
**	Private functions
*/
STATIC VOID GetMemoryAmount(ULONG *amnt, ULONG memType)
{
	struct ExecBase *SysBase=INITSYSBASE;

	amnt[AT_MAXIMUM]=	AvailMem(memType | MEMF_TOTAL);
	amnt[AT_LARGEST]=	AvailMem(memType | MEMF_LARGEST);
	amnt[AT_FREE]=		AvailMem(memType);
	amnt[AT_INUSE]=		amnt[AT_MAXIMUM]-amnt[AT_FREE];
}	/* GetMemoryAmount */
