/*******************************************************************************

                                   MOUNTINFO

Format:   MOUNTINFO [<dos-device>]
Template: MOUNTINFO ""
Purpose:  To get an idea of the MountList of the dos-device.
Specification:
MOUNTINFO tries to reconstruct a MountList entry for a given dos-device. Note
that the resulting entry may contain improper values.
	If you omit the dos-device argument a list of all dos-devices in the system
will be output. The dos-device name may be given with or without the concluding
colon.
	All output is directed to the standard output channel.
	The entry may contain the following definitions: Surfaces, BlocksPerTrack,
Reserved, PreAlloc, Interleave, LowCyl, HighCyl, Buffers, BufMemType,
MaxTransfer, Mask, BootPri, DosType, Device, Unit, Flags, Handler, StackSize,
Priority, GlobVec, FileSystem, Startup, Mount.

Examples:

	MOUNTINFO

gives a list of currently available dos-devices.

	MOUNTINFO >>DEVS:MountList DH0:

appends an entry to the DEVS:MountList file. Note: the colon of DH0: could be
omitted.

********************************************************************************

Compiling Information:

	This source compiles successfully using MANX Aztec C v3.6a and the following
makefile:

MountInfo: MountInfo.o
	ln MountInfo.o -lc32
MountInfo.o: MountInfo.c
	cc +l MountInfo.c

*******************************************************************************/

#ifdef AZTEC_C
#include <functions.h>
#endif
#ifndef EXEC_TYPES_H
#include <exec/types.h>
#endif
#ifndef EXEC_MEMORY_H
#include <exec/memory.h>
#endif
#ifndef LIBRARIES_DOSEXTENS_H
#include <libraries/dosextens.h>
#endif
#ifndef LIBRARIES_FILEHANDLER_H
#include <libraries/filehandler.h>
#endif

struct DosLibrary *DOSBase;

/*******************************************************************************

	DefaultTable

		This is a table containing the default values used by the v1.3 Mount
		command. Modifiy this table if the defaults are different.

*******************************************************************************/

#define DEFAULT_STACKSIZE      600
#define DEFAULT_PRIORITY       10
#define DEFAULT_GLOBVEC        0
#define DEFAULT_TABLESIZE      11
#define DEFAULT_SIZEBLOCK      128
#define DEFAULT_SECORG         0
#define DEFAULT_NUMHEADS       -1
#define DEFAULT_SECSPERBLK     1
#define DEFAULT_BLKSPERTRACK   -1
#define DEFAULT_RESERVEDBLKS   -1
#define DEFAULT_PREFAC         0
#define DEFAULT_INTERLEAVE     -1
#define DEFAULT_LOWCYL         -1
#define DEFAULT_UPPERCYL       -1
#define DEFAULT_NUMBUFFERS     -1
#define DEFAULT_BUFMEMTYPE     0
#define DEFAULT_MAXTRANSFER    0x7FFFFFFF
#define DEFAULT_MASK           0xFFFFFFFE
#define DEFAULT_BOOTPRI        0
#define DEFAULT_DOSTYPE        0x444F5300

/*******************************************************************************

	IsFileSystem

		SYNOPSIS
			isFileSystem = IsFileSystem(deviceNode);
			BOOL isFileSystem;
			struct DeviceNode *deviceNode;

		FUNCTION
			Examines the given deviceNode name to check whether it belongs to a
			filesystem or a handler.

		RESULT
			isFileSystem: Is TRUE when the name belongs to a filesystem, otherwise
				FALSE is returned.

		IMPLEMENTATION
			A couple of plausibility checks are made on the filesystem startup
			parameters. If these checks fail the deviceNode is considered to be
			a handler and no filesystem.

*******************************************************************************/

BOOL IsFileSystem(deviceNode)
	struct DeviceNode *deviceNode;
	{
	struct FileSysStartupMsg *startup;
	struct DosEnvec *environment;

	startup = (struct FileSysStartupMsg *)(deviceNode->dn_Startup << 2);
	if (!startup) return FALSE;
	environment = (struct DosEnvec *)(startup->fssm_Environ << 2);
	if (!environment) return FALSE;
	if ((environment->de_TableSize < 11) || (environment->de_TableSize > 32))
		return FALSE;
	if (environment->de_SizeBlock != 128) return FALSE;
	if (environment->de_SecOrg) return FALSE;
	if (environment->de_SectorPerBlock != 1) return FALSE;

	/* If all the previos checks were satisfied we consider the deviceNode to be
	a filesystem. */

	return TRUE;
	}


/*******************************************************************************

	NextDeviceNode

		SYNOPSIS
			nextDeviceNode = NextDeviceNode(deviceNode);
			struct DeviceNode *nextDeviceNode, deviceNode;

		FUNCTION
			The next DeviceNode on the DOS DevInfo list is returned. You may supply
			a NULL pointer to get the first DeviceNode in the list. If there are no
			more nodes NULL is returned.

		BUGS & CAVEATS
			A little DOS intermezzo dealing with the DevInfo list may crash the
			machine.

*******************************************************************************/

struct DeviceNode *NextDeviceNode(deviceNode)
	struct DeviceNode *deviceNode;
	{
	struct RootNode *rootNode;
	struct DosInfo *dosInfo;

	if (!deviceNode)
		{
		rootNode = (struct RootNode *)DOSBase->dl_Root;
		dosInfo = (struct DosInfo *)(rootNode->rn_Info << 2);
		deviceNode = (struct DeviceNode *)(dosInfo->di_DevInfo << 2);
		}
	else
		deviceNode = (struct DeviceNode *)(deviceNode->dn_Next << 2);

	while (deviceNode)
		{
		if (!deviceNode->dn_Type) return deviceNode;
		deviceNode = (struct DeviceNode *)(deviceNode->dn_Next << 2);
		}

	return NULL;
	}


/*******************************************************************************

	DumpDosNodes

		SYNOPSIS
			DumpDosNodes();

		FUNCTION
			All dos device-nodes are dumped to the standard output.

*******************************************************************************/

DumpDosNodes()
	{
	register struct DeviceNode *deviceNode = NULL;

	while (deviceNode = NextDeviceNode(deviceNode))
		{
		PrintBSTR(deviceNode->dn_Name);
		putchar('\t');
		}

	putchar('\n');
	}


/*******************************************************************************

	FindDeviceNode

		SYNOPSIS
			deviceNode = FindDeviceNode(name)
			struct DeviceNode *deviceNode;
			char *name;

		FUNCTION
			Scans the dos data structures and looks for a DeviceNode which belongs
			to the name.

		RESULTS
			deviceNode: If the requested node couldn't be found NULL is returned.
				Otherwise this points to the node we found.

*******************************************************************************/

struct DeviceNode *FindDeviceNode(name)
	char name[];
	{
	struct DeviceNode *deviceNode = NULL;
	int nameLen, i;

	nameLen = strlen(name);
	if (name[nameLen-1] == ':') --nameLen;
	for (i=0; i<nameLen; i++)
		name[i] = toupper(name[i]);

	while (deviceNode = NextDeviceNode(deviceNode))
		{
		char *candidate;
		int n;

		candidate = (char *)(deviceNode->dn_Name << 2);
		n = *candidate++;
		if ((n == nameLen) && !strncmp(name, candidate, n)) return deviceNode;
		}

	return NULL;
	}


/*******************************************************************************

	PrintBSTR

		SYNOPSIS
			PrintBSTR(bstr);
			BSTR bstr;

		FUNCTION
			The bcpl-string given as the parameter bstr is dumped to the standard
			output.

*******************************************************************************/

PrintBSTR(bstr)
	BSTR bstr;
	{
	int i, n;
	char *s;

	s = (char *)(bstr << 2);
	n = *s++;
	for (i=0; i<n; i++) putchar(*s++);
	}


/*******************************************************************************

	DumpDeviceNode

		SYNOPSIS
			DumpDeviceNode(deviceNode);
			struct DeviceNode *deviceNode;

		FUNCTION
			The device node's contents are dumped in MountList format to the
			standard output.

*******************************************************************************/

DumpDeviceNode(deviceNode)
	struct DeviceNode *deviceNode;
	{
	int i;

	puts("/* This MountList was automatically created by 'MountInfo'   */");
	puts("/* MountInfo v1.0 is public domain as long as you don't make */");
	puts("/* financial profit of it. Bug reports and suggestions are   */");
	puts("/* welcome. Contact me at:                                   */");
	puts("/* Andreas Hofbauer; Kurfuerstenplatz 7; D 8000 Muenchen 40. */");
	putchar('\n');

	PrintBSTR(deviceNode->dn_Name);
	putchar(':');
	putchar('\n');

	if (IsFileSystem(deviceNode))
		{
		struct FileSysStartupMsg *startup;
		struct DosEnvec *environment;

		if (deviceNode->dn_Handler)
			{
			printf("        FileSystem = ");
			PrintBSTR(deviceNode->dn_Handler);
			putchar('\n');
			}
		if (deviceNode->dn_StackSize != DEFAULT_STACKSIZE)
			printf("        StackSize = %ld\n", deviceNode->dn_StackSize);
		if (deviceNode->dn_Priority != DEFAULT_PRIORITY)
			printf("        Priority = %ld\n", deviceNode->dn_Priority);
		if (deviceNode->dn_GlobalVec == -1)
			puts("        GlobVec = -1");

		startup = (struct FileSysStartupMsg *)(deviceNode->dn_Startup << 2);
		printf("        Unit = %ld\n", startup->fssm_Unit);
		printf("        Device = ");
		PrintBSTR(startup->fssm_Device);
		putchar('\n');
		printf("        Flags = %ld\n", startup->fssm_Flags);

		environment = (struct DosEnvec *)(startup->fssm_Environ << 2);

		for (i=1; i<=environment->de_TableSize; i++)
			{
			switch (i)
				{
				case DE_NUMHEADS:
					printf("        Surfaces = %ld\n", environment->de_Surfaces);
					break;
				case DE_BLKSPERTRACK:
					printf("        BlocksPerTrack = %ld\n",
						environment->de_BlocksPerTrack);
					break;
				case DE_RESERVEDBLKS:
					printf("        Reserved = %ld\n", environment->de_Reserved);
					break;
				case DE_PREFAC:
					if (environment->de_PreAlloc != DEFAULT_PREFAC)
						printf("        PreAlloc = %ld\n", environment->de_PreAlloc);
					break;
				case DE_INTERLEAVE:
					printf("        Interleave = %ld\n", environment->de_Interleave);
					break;
				case DE_LOWCYL:
					printf("        LowCyl = %ld\n", environment->de_LowCyl);
					break;
				case DE_UPPERCYL:
					printf("        HighCyl = %ld\n", environment->de_HighCyl);
					break;
				case DE_NUMBUFFERS:
					printf("        Buffers = %ld\n", environment->de_NumBuffers);
					break;
				case DE_BUFMEMTYPE:
					printf("        BufMemType = %ld", environment->de_BufMemType);
					if (environment->de_BufMemType)
						{
						printf(" /* ");
						if (environment->de_BufMemType & MEMF_PUBLIC)
							printf("MEMF_PUBLIC ");
						if (environment->de_BufMemType & MEMF_CHIP)
							printf("MEMF_CHIP ");
						if (environment->de_BufMemType & MEMF_FAST)
							printf("MEMF_FAST ");
						if (environment->de_BufMemType & MEMF_CLEAR)
							printf("MEMF_CLEAR ");
						printf("*/");
						}
					putchar('\n');
					break;
				case DE_MAXTRANSFER:
					if (environment->de_MaxTransfer != DEFAULT_MAXTRANSFER)
						printf("        MaxTransfer = 0x%lx\n",
							environment->de_MaxTransfer);
					break;
				case DE_MASK:
					if (environment->de_Mask != DEFAULT_MASK)
						printf("        Mask = 0x%lx\n", environment->de_Mask);
					break;
				case DE_BOOTPRI:
					if (environment->de_BootPri != DEFAULT_BOOTPRI)
						printf("        BootPri = %ld\n", environment->de_BootPri);
					break;
				case DE_DOSTYPE:
					if (environment->de_DosType != DEFAULT_DOSTYPE)
						printf("        DosType = 0x%lx\n", environment->de_DosType);
					break;
				}
			}
		}
	else
		{
		if (deviceNode->dn_Handler)
			{
			printf("        Handler = ");
			PrintBSTR(deviceNode->dn_Handler);
			putchar('\n');
			}
		if (deviceNode->dn_StackSize != DEFAULT_STACKSIZE)
			printf("        StackSize = %ld\n", deviceNode->dn_StackSize);
		if (deviceNode->dn_Priority != DEFAULT_PRIORITY)
			printf("        Priority = %ld\n", deviceNode->dn_Priority);
		if (deviceNode->dn_GlobalVec == -1)
			puts("        GlobVec = -1");
		if (deviceNode->dn_Startup)
			printf("        Startup = 0x%lx\n", deviceNode->dn_Startup);
		}
	puts("#");
	}


/*******************************************************************************

	main

*******************************************************************************/

main(argc, argv)
	int argc;
	char *argv[];
	{
	struct DeviceNode *deviceNode;

	if ((argc != 2) && (argc != 1))
		{
		puts("USAGE: MountInfo [<drive>]");
		exit(5);
		}

	DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);

	if (argc == 1)
		{
		puts("Available DOS-Devices:");
		DumpDosNodes();
		}

	if (argc == 2)
		{
		deviceNode = FindDeviceNode(argv[1]);
		if (!deviceNode)
			{
			puts("Couldn't find requested device!");
			exit(10);
			}
		DumpDeviceNode(deviceNode);
		}

	CloseLibrary(DOSBase);
	}
