#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <exec/memory.h>
#include <utility/tagitem.h>

#include <clib/dos_protos.h>
#include <clib/exec_protos.h>

#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>

#include <string.h>

#define TOBADDR(x) (((ULONG)(x)) >> 2)

// Dieses Programm kann resident gemacht werden

long __saveds progstart (void)
{
	struct ExecBase   *SysBase;
	struct DosLibrary *DOSBase;

	const char     VersionString[] = {"\0$VER: DeviceInfo 1.3 " __AMIGADATE__" ©1999 Thomas Krafzik\r\n"};
	UBYTE          template[]      = "DEVICE/A,TYPE/S,NAME/S\n";
	LONG           error           = RETURN_OK;
	struct         {
			       char *dev;
			       LONG type;
			       LONG name;
		       }
		       arg             = {NULL};
	struct RDArgs *rdargs;

	// Zuerst SysBase, sonst geht alles schief
	SysBase = * (struct ExecBase **) 4L;

	if (DOSBase = (struct DosLibrary *) OpenLibrary (DOSNAME, 37L))
	{
		if (rdargs = ReadArgs (template, (LONG *) &arg, 0))
		{
			struct FileSysStartupMsg *fssm;
			struct DosEnvec          *de;
			struct DosList           *dol;
			char                     *temp_devname;
			UBYTE                    *devname;
			ULONG                     dostype;
			ULONG                     dostype_bool = FALSE;
			ULONG                     try_the_other_way = FALSE;

			// ------···--------------------···
			// ---···--- FilesystemID ---···---
			// ···--------------------···------

			// eventuell eingegebenen Doppelpunkt eliminieren
			// in der DosList sind die Geräte nur ohne Doppelpunkt anzutreffen
			if (devname = AllocVec (strlen (arg . dev) + 1, MEMF_CLEAR))
			{
				strcpy (devname, arg . dev);

				temp_devname = devname;

				while (*temp_devname)
				{
					if (*temp_devname == ':')
					{
						// Doppelpunkt gefunden, eliminieren
						*temp_devname = 0;
						break;
					}
					temp_devname++;
				}

				// DosList vorbereiten
				dol = LockDosList (LDF_DEVICES | LDF_READ);

				// Gerät vorhanden ?
				// LDF_READ muß hier nicht mehr angegeben werden
				if (dol = FindDosEntry (dol, devname, LDF_DEVICES))
				{
					// FileSysStartupMsg vorhanden ?
					fssm = BADDR (dol -> dol_misc . dol_handler . dol_Startup);

					// Ist der Speicher an der Stelle gültig ?
					if (TypeOfMem (fssm))
					{
						// DosEnvec vorhanden ?
						de = BADDR (fssm -> fssm_Environ);

						// Ist der Speicher an der Stelle gültig ?
						if (TypeOfMem (de))
						{
							// TableSize muß eine bestimmte Größe haben
							// um überhaupt eine Partition sein zu können
							if (de -> de_TableSize >= 16)
							{
								// DosType für die ID kopieren
								dostype = de -> de_DosType;

								// Flag setzen
								dostype_bool = TRUE;
								goto argh;
							}
						}
					}
					try_the_other_way = TRUE;
				}

				argh:

				// DosList wieder freigeben
				UnLockDosList (LDF_DEVICES | LDF_READ);

				if ((arg . type && dostype_bool) || (arg . type && try_the_other_way))
				{
					char buf [5]; // 4 Zeichen + Nullbyte
					BOOL yes_its_true = TRUE;

					if (try_the_other_way)   // ;-)
					{
						// RAM: etc. ...

						BPTR lock = Lock ((STRPTR) arg . name, ACCESS_READ);
						__aligned struct InfoData info;

						if (Info (lock, &info))
						{
							dostype = info.id_DiskType;
						}
						else
						{
							yes_its_true = FALSE;
						}
						UnLock (lock);
					}


					if (yes_its_true)
					{
						buf [0] = (dostype >> 24) & 0xff;
						buf [1] = (dostype >> 16) & 0xff;
						buf [2] = (dostype >>  8) & 0xff;
						if  ((dostype & 0xff) < 17)
						{
							buf [3] = (dostype & 0xff) + '0';
						}
						else
						{
							buf [3] = (dostype & 0xff);
						}

						buf [4] = 0;

						Printf ("%s", buf);

						if (arg . name)
						{
							Printf (" ");
						}
					}
				}

				FreeVec (devname);
			}

			// ------···------------------···
			// ---···--- Volumename ---···---
			// ···------------------···------

			if (arg . name || (! arg . name && ! arg . type))  // Wenn 'NAME' oder kein Argument
			{
				struct FileLock *lock;
				struct Process  *myproc;
				APTR            *old;

				// die Adresse unseres Prozesses holen
				Forbid();
				myproc = (struct Process *) FindTask (NULL);
				Permit();

				if (myproc)
				{
					// alten Pointer sichern
					old = myproc -> pr_WindowPtr;

					// -1 zum Verhindern von Requestern
					// ("Please insert volume...")
					myproc -> pr_WindowPtr = (APTR) -1;
				}

				if (lock = (struct FileLock *) BADDR (Lock (arg . dev, ACCESS_READ)))
				{
					char              *volume;
					struct DeviceList *devlist;

					devlist = (struct DeviceList *) BADDR (lock    -> fl_Volume);
					volume  = (char *)              BADDR (devlist -> dl_Name);

					Printf ("%s", &volume [1]);

					UnLock (TOBADDR (lock));
				}

				// Wichtig, restaurieren
				if (myproc)
				{
					myproc -> pr_WindowPtr = old;
				}
			}

			// DosList wieder freigeben
			FreeArgs (rdargs);
		}
		else
		{
			Printf ("Falsche Argumente.\n");
			error = RETURN_FAIL;
		}

		// Und die DosLibrary wieder schließen
		CloseLibrary ((struct Library *) DOSBase);
	}
	return (error);
}
