/* myfsinfo.c - program to display disk types for volumes currently in
 *               system.
 *
 * Doesn't check all allocations!
 *
 * ToDo: ReadArgs() interface to specify a particular device
 *       Pooled memory allocation ? nah, no real point.
 */

#include <exec/types.h>
#include <exec/alerts.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <exec/tasks.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/dosextens.h>

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

struct MyVolumeNode {
	struct MinNode minnode;
	STRPTR name;
	ULONG DiskType;
};

#define IsMinListEmpty(x) \
	( ((x)->mlh_TailPred) == (struct MinNode *)(x) )

static const TEXT verstag[] = "$VER: disktype 42.1 (7.8.95)\0";
static const TEXT HexFmt[] = "$%08lx";
static const UWORD putChProc[2] = { 0x16c0, 0x4e75 };
/*static const TEXT cmdTemplate[] = "DEVICE";*/

static __inline void InlineNewList( struct List *list );

ULONG main( void )
{
	struct ExecBase		*SysBase = (*((struct ExecBase **)4L));
	struct DosLibrary	*DOSBase;		/* obvious ;) */
	struct DosList		*dosList;		/* pointer to system doslist */
	struct MinList		myVolList;		/* pointer to private copy */
	struct MyVolumeNode	*thisVol;		/* pointer to current volume node */
	struct Process		*myProcess = (struct Process *) SysBase->ThisTask;
	ULONG				rc = 0L;		/* our return code */

	/* open dos */
	if (!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library",36L)))
	{
		Alert( AG_OpenLib | AO_DOSLib );
		myProcess->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;
		return( 20L );
	}

	/* init volume list */
	InlineNewList((struct List *)&myVolList);

	/* arbitrate for system doslist */
	dosList = LockDosList(LDF_VOLUMES|LDF_READ);

	while (dosList = NextDosEntry(dosList,LDF_VOLUMES))
	{
		/* add node to vollist */
		STRPTR newName;
		STRPTR oldName;
		ULONG nameSize;

		/* get memory for node */
		thisVol = AllocMem( sizeof(struct MyVolumeNode), MEMF_CLEAR);

		/* copy type */
		thisVol->DiskType = dosList->dol_misc.dol_volume.dol_DiskType;

		/* copy name */
		oldName = BADDR(dosList->dol_Name);		/* BCPL cabbage */
		nameSize = (ULONG)(*oldName);

		newName = AllocVec((nameSize+1),0L);		/* get RAM for string */
		CopyMem((oldName+1), newName, nameSize);	/* copy it */
		newName[nameSize] = ':';					/* append terminator */
		newName[nameSize+1] = '\0';					/* append terminator */
		thisVol->name = newName;					/* put pointer in node */

		AddTail((struct List *)&myVolList,(struct Node *)thisVol);
	}
	UnLockDosList(LDF_VOLUMES|LDF_READ);	/* finished with system doslist */

	PutStr("Volume              DosType\n");		/* make localisable? */

	if ( !IsMinListEmpty( (struct MinList *) &myVolList))
		for (	thisVol = (struct MyVolumeNode *) myVolList.mlh_Head;
				thisVol->minnode.mln_Succ;
				thisVol = (struct MyVolumeNode *) thisVol->minnode.mln_Succ )
	{
		TEXT hexFmtBuf[64];
		ULONG disktype = thisVol->DiskType;
		STRPTR verboseType;

		if (!disktype)
		{
			/* we need to do it the hard way */
			struct InfoData info;
			BPTR rootLock;

			/* I can't be bothered checking ;) */
			/* and i'd rather have used packets instead... */

			rootLock = Lock( thisVol->name, SHARED_LOCK );
			(void) Info( rootLock, &info );
			UnLock(rootLock);

			disktype = info.id_DiskType;
		}

		switch( disktype )
		{
			case ID_UNREADABLE_DISK:
				verboseType = "Unreadable disk";	/* make localisable */
			break;
			case ID_DOS_DISK:
				verboseType = "OFS [DOS\\0]";
			break;
			case ID_FFS_DISK:
				verboseType = "FFS [DOS\\1]";
			break;
			case ID_INTER_DOS_DISK:
				verboseType = "OFS INTL [DOS\\2]";
			break;
			case ID_INTER_FFS_DISK:
				verboseType = "FFS INTL [DOS\\3]";
			break;
			case ID_FASTDIR_DOS_DISK:
				verboseType = "DCFS OFS [DOS\\4]";
			break;
			case ID_FASTDIR_FFS_DISK:
				verboseType = "DCFS FFS [DOS\\5]";
			break;
			case ID_MSDOS_DISK:
				verboseType = "MS-DOS [MSD\\0]";
			break;
			case ID_KICKSTART_DISK:
				verboseType = "Kickstart [KICK]";
			break;
			case ID_NOT_REALLY_DOS:
				Fault( ERROR_NOT_A_DOS_DISK, NULL, hexFmtBuf, 64L );
				verboseType = hexFmtBuf; /* localised. */
			break;
			default:
				/* do substitution for unprintable chars */
				RawDoFmt(HexFmt, &HexFmt+1, (void (*)) putChProc, hexFmtBuf);
				verboseType = hexFmtBuf;
			break;
		}

		/* spill the beans */
		Printf("%-20s%s\n",thisVol->name,verboseType);
	}

cleanup:

	/* free our volume list */
	if ( !IsMinListEmpty( (struct MinList *) &myVolList))
	{
		/* List header is not updated while each node is freed (!) */
		struct MyVolumeNode *worknode = (struct MyVolumeNode *) myVolList.mlh_Head;
		struct MyVolumeNode *nextnode;

		while (nextnode = (struct MyVolumeNode *)(worknode->minnode.mln_Succ))
		{
			if (worknode->name) FreeVec(worknode->name);	/* free string */
			FreeMem(worknode, sizeof(struct MyVolumeNode));
			worknode = nextnode;
		}
	} /* until done */

	CloseLibrary((struct Library *)DOSBase);
	return(rc);
}

static __inline void InlineNewList( struct List *list )
{
	list->lh_TailPred = (struct Node *) list;	/* get lh_tail */
	list->lh_Tail = NULL;						/* clear lh_Tail */
	list->lh_Head = (struct Node *) &(list->lh_Tail);	/* address of lh_tail to lh_Head */
}

/* disktype.c */