/*-
 * MAGIC.C
 *
 * The xpkdisk.device code that looks into DOS data structures.
 *
 * $Id: magic.c,v 1.3 1993/11/08 13:11:15 Rhialto Rel $
 * $Log: magic.c,v $
 * Revision 1.3  1993/11/08  13:11:15  Rhialto
 * Add RCS tags.
 *
 *
 * This code is (C) Copyright 1993 by Olaf Seibert. All rights reserved.
 * May not be used or copied without a licence.
-*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "xpkdisk.h"
#ifndef LIBRARIES_FILEHANDLER_H
#include <libraries/filehandler.h>
#endif

/*#undef DEBUG	  /**/
#ifdef DEBUG
#   include "syslog.h"
#else
#   define	debug(x)
#endif

extern struct DosLibrary *DOSBase;

int
FileNamesMatch(char *path, char *name)
{
    char	   *s;

    if (s = strchr(path, ':'))
	path = s + 1;
    if (s = strrchr(path, '/'))
	path = s + 1;
    return !strcmp(path, name);
}

/*
 * Try to get the DosEnvec values from the mountlist. This is quite a
 * work!
 * We must be reasonable sure that the dn_Startup fields both refer
 * to a FileSysStarupMsg. They must both be pointers (and not some
 * small integral values, as used to distinguish CON and RAW, for
 * example).
 */

struct DosEnvec *
IsOurDevicenode(UNIT *unit, struct DeviceNode *dn)
{
    struct FileSysStartupMsg *dnmsg;

    if (dn->dn_Type != DLT_DEVICE)
	return NULL;

#ifdef DEBUG
    {
	char *s = BADDR(dn->dn_Name);
	debug(("%.*s:\n", *s, s + 1));
    }
#endif
    dnmsg = BADDR(dn->dn_Startup);
    debug(("Startup: %x, '%.20s' %d\n", dnmsg,
	(char *)BADDR(dnmsg->fssm_Device) + 1,
	dnmsg->fssm_Unit ));
    if (dnmsg &&
	(long)dnmsg > 4*100 &&
	dnmsg->fssm_Device &&
	FileNamesMatch((char *)BADDR(dnmsg->fssm_Device) + 1, DevName) &&
	dnmsg->fssm_Unit == unit->xu_UnitNr
    ) {
	long	       *dnenv = BADDR(dnmsg->fssm_Environ);

	/* actually struct DosEnvec */
	debug(("Environ: %x\n", dnenv));

	if (dnenv && dnenv[0] >= DE_DOSTYPE) {
	    debug(("Found our mount information at %lx, %lx!\n", dn, dnenv));
	    return (struct DosEnvec *)dnenv;
	}
    }
    return NULL;
}

void
MagicInitDevicenode(UNIT *unit)
{
    struct RootNode *rn = (struct RootNode *) DOSBase->dl_Root;
    struct DosInfo *DosInfo = (struct DosInfo *) BADDR(rn->rn_Info);
    struct DeviceNode *dn = BADDR(DosInfo->di_DevInfo);

    debug(("traverse_bcpl: %x\n", dn));
    while (dn) {
	struct DosEnvec *de;

	if (de = IsOurDevicenode(unit, dn)) {
	    unit->xu_NumTracks = de->de_Surfaces * (de->de_HighCyl + 1);
	    unit->xu_TrackLen = 4 * de->de_SizeBlock * de->de_BlocksPerTrack;
	    debug(("numtracks %d tracklen %d\n",
		   unit->xu_NumTracks, unit->xu_TrackLen));
#if 0
	    /* Hackery: */
	    debug(("tablesize %d CONTROL=%d\n", de->de_TableSize, DE_CONTROL));
	    if (de->de_TableSize >= DE_CONTROL) {
		debug(("string %x: %.20s\n", de->de_Control, de->de_Control));
	    }
#endif
	    return;
	}

	dn = BADDR(dn->dn_Next);
	debug(("traverse_bcpl: next is %x\n", dn));
    }
}

#define ON(d,s)     ((d) |= (s))
#define OFF(d,s)    ((d) &= ~(s))

ulong
Flag(char *opt, char *name, ulong mask, ulong previous)
{
    char *match;

    if (match = strstr(opt, name)) {
	int o = 1;
	int l = strlen(name);

	debug(("Flag: %s\n", match));
	if (match[l] == '=')
	    o = strtol(match + l + 1, NULL, 0);
	if (o)
	    previous |= mask;
	else
	    previous &= ~mask;
    }
    return previous;
}

void
ParseOptions(UNIT *unit, char *opt)
{
    ulong	    f;
    char	   *value;

    debug(("ParseOptions: %s\n", opt));

    if (value = strstr(opt, S_XPKPackMethod "=")) {
#define SZ sizeof(unit->xu_XPKPackMethod)
	int		len;

	value += sizeof(S_XPKPackMethod);
	len = strcspn(value, " \t\n,;=");
	len = min(len, SZ-1);
	debug(("Match "S_XPKPackMethod"='%s', len %d\n", value, len));
	strncpy(unit->xu_XPKPackMethod, value, len);
	unit->xu_XPKPackMethod[len] = '\0';
	debug(("PackMethod now %s\n", unit->xu_XPKPackMethod));
    } else
	debug(("Don't match "S_XPKPackMethod"=\n"));
    if (value = strstr(opt, S_MaxCache "=")) {
	unit->xu_MaxCache = strtol(value + sizeof(S_MaxCache), NULL, 0);
	debug(("Match "S_MaxCache "='%d'\n", unit->xu_MaxCache));
    }
    if (value = strstr(opt, S_CacheTimeout "=")) {
	unit->xu_CacheTimeout = strtol(value + sizeof(S_CacheTimeout), NULL, 0);
	debug(("Match "S_CacheTimeout "='%d'\n", unit->xu_CacheTimeout));
    }
    f = unit->xu_CacheFlags;
    f = Flag(opt, S_CMDUPDATE, CACHEF_CMDUPDATE, f);
    f = Flag(opt, S_DELAY, CACHEF_DELAY, f);
    f = Flag(opt, S_SAFEWRITE, CACHEF_SAFEWRITE, f);
    f = Flag(opt, S_LICENSED, CACHEF_LICENSED, f);
    unit->xu_CacheFlags = f;
}

int
ReadConfig(UNIT *unit, char *buf, int sz)
{
    BPTR	    fh;

    fh = Open(buf, MODE_OLDFILE);
    if (fh) {
	int		len;

	len = Read(fh, buf, sz-1);
	Close(fh);
	buf[len] = 0;
	ParseOptions(unit, buf);

	return 1;
    }
    return 0;
}

/*
 * First get the permanent settings, then overwrite with the
 * temporary ones.
 */

void
MagicInitFile(UNIT *unit)
{
    char	    buf[128];

    sprintf(buf, CONFIGFILE_ARC, unit->xu_UnitNr);
    ReadConfig(unit, buf, sizeof(buf));
    sprintf(buf, CONFIGFILE, unit->xu_UnitNr);
    ReadConfig(unit, buf, sizeof(buf));
}

Prototype void MagicInit(UNIT *unit);
void
MagicInit(UNIT *unit)
{
    MagicInitDevicenode(unit);
    MagicInitFile(unit);
}
