#define NAME	 "XpkMasterPrefs"
#define REVISION "13"
//#define DEBUG

/* Programmheader

	Name:		XpkMasterPrefs
	Author:		SDI
	Distribution:	PD
	Description:	xpk prefs loader, like IPrefs
	Compileropts:	-
	Linkeropts:	-gsi (no startup)

 1.0   25.12.96 : first Version
 1.1   26.12.96 : nearly redefined xpkprefs structures - a lot of changes
 1.2   27.12.96 : some fixes
 1.3   30.12.96 : did a bit on Recog function
 1.4   02.01.97 : added correctness check of BufPattern
 1.5   11.01.97 : corrected some errors
 1.6   28.02.97 : changed some semaphore specific stuff
 1.7   01.03.97 : added Version info to MainPrefs
 1.8   07.03.97 : fixed RecogFunc
 1.9   24.03.97 : added DEBUG stuff
 1.10  28.03.97 : added last function and ReadArgs
 1.11  02.04.97 : XpkTypeData changed, ListData now internal structure
 1.12  03.04.97 : added DEBUG info, debuged RecogFunction
 1.13  04.04.97 : buffer now 2 K
*/

/*
How to parse the type list:
- The struct XpkTypeData of the first matching structure is returned.
- If no type matches, the default is returned.
- Scan goes always in linear order from the beginning to end. (and never
  reverse).
- if no file name is given, the xtp_FilePattern field is ignored.
*/

#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/iffparse_lib.h>
#include <prefs/prefhdr.h>
#include <xpk/xpkprefs.h>
#include <exec/memory.h>
#include "SDI_defines.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"


/**************************************************************************
 *
 * Standard prefs specific data structure. xps_PrefsData points to this.
 *
 * xssd_TypeList nodes are of type XpkTypeNode.
 *
 * 
 */

struct XpkStdSemaphoreData {
  struct List	xssd_TypeList;     /* list of file types */
  ULONG		xssd_Buffer1Size;  /* size of following buffer */
  STRPTR	xssd_Buffer1;	   /* pointer to buffer */
};

struct XpkTypeNode {
  struct Node		xtn_Node;	/* standard node structure */
  ULONG			xtn_Size;	/* hold complete size to free */
  struct XpkTypePrefs	xtn_TypePrefs;  /* real data */
};

#define RECOGBUFSIZE	2048

#define PARAM "QUIT/S"

#ifdef __MAXON__
  #define __asm
  struct Library *IFFParseBase;
#else
  #define iffparsebase	IFFParseBase
#endif

struct DosLibrary *DOSBase;

void CorrectAdr(APTR pos, APTR mempos);
void DoPrefsCreation(struct XpkPrefsSemaphore *sem);
void ClearTypeList(struct XpkStdSemaphoreData *sem);
void ClearMainPrefs(struct XpkPrefsSemaphore *sem);
struct XpkTypeData * __asm RecogFunc(register __a0 STRPTR buf,
register __a1 STRPTR name, register __a2 STRPTR printname,
register __d0 ULONG bufsize, register __d1 ULONG fullsize);

UBYTE CheckHex(STRPTR *data, LONG number);
ULONG CheckBufPattern(STRPTR pattern);
UBYTE GetCharacter(STRPTR pattern);
/* these two return zero, when name matches or defined values, when not */
ULONG MatchBufPattern(STRPTR pattern, STRPTR buffer, ULONG size);
ULONG MatchFileName(STRPTR pattern, STRPTR name);

#define MATCHERR_PATTERN_INCORRECT	1
#define MATCHERR_NO_MEMORY		2
#define MATCHERR_BUFFER_TO_SHORT	3
#define MATCHERR_NO_MATCH		4

ULONG start(void) /* not named main, to get error with startup code ! */
{
  struct DosLibrary *dosbase;
  struct XpkPrefsSemaphore *semaphore;
  struct Process *task;
  struct Message *msg = 0;
  struct RDArgs *rda;
  LONG quit = 0;

  if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  {
    WaitPort(&task->pr_MsgPort);
    msg = GetMsg(&task->pr_MsgPort);
    /* no tooltype check now, so means always start semaphore */
  }
  else
  {
    if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
    {
      DOSBase = dosbase;
      if((rda = ReadArgs(PARAM, &quit, 0)))
        FreeArgs(rda);
      CloseLibrary((struct Library *) dosbase);
    }
    /* does not recognize failed ReadArgs - normal start in this case */
  }

  if(quit)
  {
    Forbid();
    if((semaphore = (struct XpkPrefsSemaphore *) FindSemaphore(XPKPREFSSEMNAME)))
    {
      ObtainSemaphoreShared((struct SignalSemaphore *) semaphore);
        Signal(semaphore->xps_MasterTask, SIGBREAKF_CTRL_C);
      ReleaseSemaphore((struct SignalSemaphore *) semaphore);
    }
    Permit();
  }
  else if(!FindSemaphore(XPKPREFSSEMNAME) &&
  (dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  {
    DOSBase = dosbase;
    if((semaphore = (struct XpkPrefsSemaphore *) AllocMem(sizeof(struct
    XpkPrefsSemaphore) + sizeof(struct XpkStdSemaphoreData), MEMF_PUBLIC|MEMF_CLEAR)))
    {
      struct NotifyRequest *notifyrequest;

      struct XpkStdSemaphoreData *sd = (struct XpkStdSemaphoreData *)
       (((STRPTR) semaphore) + sizeof(struct XpkPrefsSemaphore));
        // both structures are allocated with one AllocMem

      semaphore->xps_Semaphore.ss_Link.ln_Type = NT_SEMAPHORE;
      semaphore->xps_Semaphore.ss_Link.ln_Name = XPKPREFSSEMNAME;
      semaphore->xps_PrefsData = sd;
//    semaphore->xps_Version = 0;
      semaphore->xps_PrefsType = XPREFSTYPE_STANDARD;
//    semaphore->xps_ProgressFunc = 0;
      semaphore->xps_RecogSize = RECOGBUFSIZE;
      semaphore->xps_RecogFunc = (struct XpkTypeData *(*)()) RecogFunc;
      semaphore->xps_MasterTask = (struct Task *) task;

      sd->xssd_TypeList.lh_Type = NT_USER;
      sd->xssd_TypeList.lh_Head = (struct Node *) &sd->xssd_TypeList.lh_Tail;
      sd->xssd_TypeList.lh_TailPred = (struct Node *) &sd->xssd_TypeList.lh_Head;

      InitSemaphore((struct SignalSemaphore *) semaphore);
      AddSemaphore((struct SignalSemaphore *) semaphore);

      if((notifyrequest = (struct NotifyRequest *)
      AllocMem(sizeof(struct NotifyRequest), MEMF_CLEAR)))
      {
        ULONG signr;

        if((signr = AllocSignal(-1L)) != -1)
        {
          notifyrequest->nr_Name = "ENV:xpkmaster.prefs";
          notifyrequest->nr_Flags = NRF_SEND_SIGNAL | NRF_NOTIFY_INITIAL;
          notifyrequest->nr_stuff.nr_Signal.nr_Task = FindTask(0);
          notifyrequest->nr_stuff.nr_Signal.nr_SignalNum = signr;
          if((StartNotify(notifyrequest)) == DOSTRUE)
          {
	    ULONG signal = 0;
            while(!(signal & SIGBREAKF_CTRL_C))
              if((signal = Wait(1 << signr | SIGBREAKF_CTRL_C)) & (1 << signr))
	        DoPrefsCreation(semaphore);
            EndNotify(notifyrequest);
          }
          FreeSignal(signr);
        }
        FreeMem(notifyrequest, sizeof(struct NotifyRequest));
      }
      ObtainSemaphore((struct SignalSemaphore *) semaphore);
      RemSemaphore((struct SignalSemaphore *) semaphore);
      ClearTypeList((struct XpkStdSemaphoreData *) semaphore->xps_PrefsData);
      ClearMainPrefs(semaphore);

      FreeMem(semaphore, sizeof(struct XpkPrefsSemaphore) +
        sizeof(struct XpkStdSemaphoreData));
    }
    CloseLibrary((struct Library *) dosbase);
  }

  if(msg)
  {
    Forbid();
    ReplyMsg(msg);
  }

  return 0;
}

/* changes relative addresses into normal ones */
void CorrectAdr(APTR pos, APTR mempos)
{
  if(*(ULONG *)pos)
    *(ULONG *)pos += (ULONG) mempos;
}

void ClearTypeList(struct XpkStdSemaphoreData *sd)
{
  struct Node *n;

  while((n = RemHead(&sd->xssd_TypeList)))
    FreeMem(n, ((struct XpkTypeNode *) n)->xtn_Size);
}

void ClearMainPrefs(struct XpkPrefsSemaphore *sem)
{
  struct XpkStdSemaphoreData *sd = (struct XpkStdSemaphoreData *) sem->xps_PrefsData;
  if(sd->xssd_Buffer1)
    FreeMem(sd->xssd_Buffer1, sd->xssd_Buffer1Size);
  sem->xps_MainPrefs = 0;
  sd->xssd_Buffer1Size = 0;
  sd->xssd_Buffer1 = 0;
}

void DoPrefsCreation(struct XpkPrefsSemaphore *sem)
{
  struct Library *iffparsebase;
  struct ContextNode  *cn;
  struct IFFHandle *iff;
  ULONG prefsversion = 0;

  ObtainSemaphore(&sem->xps_Semaphore);

  ClearTypeList((struct XpkStdSemaphoreData *) sem->xps_PrefsData);
  ClearMainPrefs(sem);

  if(!(iffparsebase = OpenLibrary("iffparse.library", 37)))
    return;

#ifdef __MAXON__
  IFFParseBase = iffparsebase;
#endif

  if((iff = AllocIFF()))
  {
    if((iff->iff_Stream = Open("ENV:xpkmaster.prefs", MODE_OLDFILE)))
    {
      InitIFFasDOS(iff);
      if(!OpenIFF(iff, IFFF_READ))
      {
	LONG a[] = { ID_PREF, ID_PRHD, ID_PREF, ID_XPKT, ID_PREF, ID_XPKM};
	if(!StopChunks(iff, a, 3))
	{
	  ULONG error = 0;

	  while(!error)
          {
            if(ParseIFF(iff,IFFPARSE_SCAN))
              break;

            cn = CurrentChunk(iff);

	    switch(cn->cn_ID)
	    {
	    case ID_PRHD:
	      {
	        struct PrefHeader prh;
	        ReadChunkBytes(iff, &prh, sizeof(struct PrefHeader));
	        prefsversion = prh.ph_Version;
	/* use this, when internal version is greater than one. In this
	   case the value is version of XPKT chunks */
	      } break;
	    case ID_XPKT:
	      {
	        struct XpkTypeNode *buf;
		ULONG size;
	        size = cn->cn_Size + sizeof(struct Node) + sizeof(ULONG);
	        if((buf = (struct XpkTypeNode *) AllocMem(size, MEMF_PUBLIC|MEMF_CLEAR)))
	        {
		  struct XpkTypePrefs *pref = &buf->xtn_TypePrefs;

	          ReadChunkBytes(iff, pref, cn->cn_Size);
		  CorrectAdr(&pref->xtp_NamePattern, pref);
		  CorrectAdr(&pref->xtp_FilePattern, pref);
		  CorrectAdr(&pref->xtp_TypeName, pref);
		  CorrectAdr(&pref->xtp_PackerData, pref);
		  buf->xtn_Node.ln_Name = pref->xtp_TypeName;
		  buf->xtn_Node.ln_Type = NT_USER;
		  buf->xtn_Size = size;
		  pref->xtp_PackerData->xtd_Memory = 0;
		  pref->xtp_PackerData->xtd_MemorySize = 0;
		  AddTail(&((struct XpkStdSemaphoreData *)sem->xps_PrefsData)->xssd_TypeList, (struct Node *) buf);
	        }
	        else error = 1;
	      } break;
	    case ID_XPKM:
	      {
		struct XpkMainPrefs *pref;
		struct XpkStdSemaphoreData *sd =
		  (struct XpkStdSemaphoreData *) sem->xps_PrefsData;
		struct XpkTypeData *p;

		ClearMainPrefs(sem);

	        if((pref = (struct XpkMainPrefs *) AllocMem(cn->cn_Size, MEMF_PUBLIC|MEMF_CLEAR)))
	        {
	          ReadChunkBytes(iff, pref, cn->cn_Size);
		  CorrectAdr(&pref->xmp_DefaultType, pref);
		  p = pref->xmp_DefaultType;
	/* XpkTypeData newer than version 0 may be incorrect, so set to 0 */
		  p->xtd_Version = 0;
		  p->xtd_Memory = 0;
		  p->xtd_MemorySize = 0;
		  sem->xps_MainPrefs = pref;
                  sd->xssd_Buffer1Size = cn->cn_Size;
                  sd->xssd_Buffer1 = (STRPTR) pref;
		}
		else error = 1;
	      } break;
	    }
	  }
	}
        CloseIFF(iff);
      }
      Close(iff->iff_Stream);
    }
    FreeIFF(iff);
  }
  CloseLibrary(IFFParseBase);
  ReleaseSemaphore(&sem->xps_Semaphore);
}

UBYTE CheckHex(STRPTR *data, LONG number)
{
  LONG num;
  do
  {
    num = number;
    while(num-- && isxdigit(*((*data)++)))
      ;
    if(++num)
      return MATCHERR_PATTERN_INCORRECT;
  } while(isxdigit(**data));
  return 0;
}

/* return 0, when pattern is valid */
ULONG CheckBufPattern(STRPTR pattern)
{
  while(*pattern)
  {
    ULONG a;
    STRPTR s;
    
    s = pattern;

    switch(*(pattern++))
    {
    case 'm': a = 1; break;
    case 'v': a = 2; break;
    case 'r': a = 4; break;
    case 'g': a = 4; break;
    case 's': a = 2; break;
    default: return MATCHERR_PATTERN_INCORRECT; break;
    }
    if(CheckHex(&pattern, a))
      return MATCHERR_PATTERN_INCORRECT;

    if(*s == 'm' && (pattern-s) > 5) /* only word jumps allowed + 1 for 'm' */
      return MATCHERR_PATTERN_INCORRECT;
  }

  return 0;
}

UBYTE GetCharacter(STRPTR pattern)
{
  UBYTE i = 0, c;
  ULONG size = 2;
  while(size--)
  {
    i <<= 4;
    if((c = toupper(*(pattern++))) >= 'A')
      i += c + 10 - 'A';
    else
      i += c - '0';
  }
  return i;
}

ULONG MatchBufPattern(STRPTR pattern, STRPTR buffer, ULONG size)
{
  STRPTR bufpos = buffer;
  ULONG i;
  UBYTE j, k;
  STRPTR errpos, str;

  if(CheckBufPattern(pattern))
  {
#ifdef DEBUG
    Printf("MatchBufPattern: CheckBufPattern(%s) failed\n", pattern);
#endif
    return MATCHERR_PATTERN_INCORRECT;
  }

  while(*pattern)
  {
    switch(*(pattern++))
    {
    case 'm':
      i = strtoul(pattern, &errpos, 16);
      pattern = errpos; /* first non hex char is next command */
      if(i >= size)
        return MATCHERR_BUFFER_TO_SHORT;
      else
        bufpos = buffer + i;
      break;
    case 'v':
      while(isxdigit(*pattern))
      {
	j = GetCharacter(pattern); pattern += 2;
        if(bufpos >= buffer + size)
          return MATCHERR_BUFFER_TO_SHORT;
	if(j != *(bufpos++))
	  return MATCHERR_NO_MATCH;
      }
      break;
    case 'r':
      while(isxdigit(*pattern))
      {
	errpos = buffer;
	k = GetCharacter(pattern); pattern += 2;
	j = GetCharacter(pattern); pattern += 2;
        while(errpos < buffer + size)
        {
	  if(*errpos >= k && *errpos <= j)
	    return MATCHERR_NO_MATCH;
	  ++errpos;
	}
      }
      break;
    case 'g':
      while(isxdigit(*pattern))
      {
	k = GetCharacter(pattern); pattern += 2;
	j = GetCharacter(pattern); pattern += 2;
        if(bufpos >= buffer + size)
          return MATCHERR_BUFFER_TO_SHORT;
	if(*bufpos < k && *bufpos > j)
	  return MATCHERR_NO_MATCH;
	++bufpos;
      }
      break;
    case 's':
      bufpos = buffer;
      while(isxdigit(*pattern))
      {
        str = pattern;
	j = GetCharacter(str); str += 2;
	while(bufpos < buffer + size && *bufpos != j)
	  ++bufpos;
	if(*bufpos == j)
	{
	  errpos = ++bufpos;
          while(isxdigit(*str) && bufpos < buffer + size && 
          GetCharacter(str) == *bufpos)
          {
	    str += 2; ++bufpos;
	  }
	  if(!isxdigit(*str))
	    pattern = str;	/* exits while loop */
	  else
	    bufpos = errpos; /* reset buffer */
        }
        else
	  return MATCHERR_NO_MATCH;
      }
      break;
    default: return MATCHERR_NO_MATCH; break;
    }
  }

  return 0;
}

ULONG MatchFileName(STRPTR pattern, STRPTR name)
{
  ULONG a, b = MATCHERR_NO_MATCH;
  STRPTR buf;
  
  a = (strlen(pattern)<<1) + 10; // pattern match buffer

  if(!(buf = (STRPTR) AllocMem(a, MEMF_ANY)))
    return MATCHERR_NO_MEMORY;

  if(ParsePattern(pattern, buf, a) >= 0 && MatchPattern(buf, name))
    b = 0;

  FreeMem(buf, a);

  return b;
}

struct XpkTypeData * __asm RecogFunc(register __a0 STRPTR buf,
register __a1 STRPTR name, register __a2 STRPTR printname,
register __d0 ULONG bufsize, register __d1 ULONG fullsize)
{
  struct XpkPrefsSemaphore *sem;
  struct XpkTypePrefs *xp;
  struct Node *n;

  if(!buf || !bufsize)
    return 0;

  sem = (struct XpkPrefsSemaphore *) FindSemaphore(XPKPREFSSEMNAME);
  /* I can do without ObtainSemaphore, because when I'm called here the
  semaphore is owned already and surely exists */

  if(sem->xps_PrefsData)
  {
    for(n = ((struct XpkStdSemaphoreData *)sem->xps_PrefsData)->
      xssd_TypeList.lh_Head; n->ln_Succ; n = n->ln_Succ)
    {
      xp = &((struct XpkTypeNode *) n)->xtn_TypePrefs;

      if(!(xp->xtp_Flags & XPKT_FilePattern) || !MatchBufPattern(
      xp->xtp_FilePattern, buf, bufsize))
      {
        if(!name || !(xp->xtp_Flags & XPKT_NamePattern) || !MatchFileName(
        xp->xtp_NamePattern, name))
	{
#ifdef DEBUG
          Printf("RecogFunc: matched '%s'\n", xp->xtp_TypeName);
#endif
          return xp->xtp_PackerData;
	}
#ifdef DEBUG
        else
          Printf("RecogFunc: no namematch '%s'\n", xp->xtp_TypeName);
#endif
      }
#ifdef DEBUG
      else
        Printf("RecogFunc: no bufmatch '%s'\n", xp->xtp_TypeName);
#endif
    }
  }

  if(sem->xps_MainPrefs)
    return sem->xps_MainPrefs->xmp_DefaultType;

  return 0;
}

