/************************************************/
/* Config.c                                     */
/* Freeware                                     */
/* © 1999 Christian Hattemer                    */
/* email: chris@mail.riednet.wh.tu-darmstadt.de */
/* Based on Code by Adam Dawes, ©1996           */
/* 09.03.99 21:05                               */
/************************************************/

/* stormamiga.lib */
#ifdef USESTORMAMIGA
   #define STORMAMIGA
   #include <stormamiga.h>
   #define sprintf SPRINTF
//   #define fprintf fprintf_
#endif

/* Ansi */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Protos */
#include <proto/exec.h>
#include <exec/memory.h>

/*Config */
#include "Config.h"

#define CFGID "MLS1 - ML-Support Config File\n"

/* Macros */
#define EMPTYSTR(Str) (!(*(Str)))
#define STRCMP3(Str, c1, c2, c3) ( (*(Str) == (c1)) && ((Str)[1] == (c2)) && ((Str)[2] == (c3)) )

/* Global Variables */
static APTR Pool;

/* Functions */
static struct List *InitList   ();
static int          AddListItem(struct List *Lst, STRPTR Item);
static void         DeleteList (struct List *Lst);


void ModifyConfig(struct ConfigData *Config, STRPTR Section, STRPTR Item, STRPTR Data)
{
   BOOL inSection = FALSE, SectionFound = FALSE, ItemFound = FALSE;
   TEXT Buf[BUFFERSIZE];
   LONG SectionLen, ItemLen;

   struct List *newList;
   struct Node *Worknode;

   SectionLen = strlen(Section);
   ItemLen    = strlen(Item);

   newList = InitList();
   if (newList == NULL)
   {
      Config->Failed = TRUE;
      return;
   }

   /* Copy the List to the new List, adding new Entry at required Position */
   Worknode = Config->Cfglist->lh_Head;

   while (Worknode->ln_Succ)
   {
      if (ISSECTION(Worknode->ln_Name))
      {
         if (inSection == TRUE && ItemFound == FALSE)
         {
            /* We were in the Section, but now we've found another.         */
            /* Therefore the Data needs to be added to the previous Section */
            sprintf(Buf, "%s = %s", Item, Data);
            if (AddListItem(newList, Buf) == FALSE)
            {
               DeleteList(newList);
               Config->Failed = TRUE;
               return;
            }
         }

         if (AddListItem(newList, Worknode->ln_Name) == FALSE)
         {
            DeleteList(newList);
            Config->Failed = TRUE;
            return;
         }

         if (strncmp(Worknode->ln_Name + 1, Section, SectionLen) == 0)
         {
            inSection    = TRUE;
            SectionFound = TRUE;
         }
         else
         {
         inSection = FALSE;
         }
      }
      else
      {
         if (inSection == TRUE &&
             strncmp(Worknode->ln_Name, Item, ItemLen) == 0 &&
             STRCMP3(Worknode->ln_Name + ItemLen, ' ', '=', ' ')
            )
         {
            sprintf(Buf, "%s = %s", Item, Data);
            if (AddListItem(newList, Buf) == FALSE)
            {
               DeleteList(newList);
               Config->Failed = TRUE;
               return;
            }

            ItemFound = TRUE;
         }
         else
         {
            if (AddListItem(newList, Worknode->ln_Name) == FALSE)
            {
               DeleteList(newList);
               Config->Failed = TRUE;
               return;
            }
         }
      }

      Worknode = Worknode->ln_Succ;
   }

   if (inSection == TRUE && ItemFound == FALSE)
   {
      /* We were in the Section, but now we've reached the End.       */
      /* Therefore the Data needs to be added to the previous Section */
      sprintf(Buf, "%s = %s", Item, Data);
      if (AddListItem(newList, Buf) == FALSE)
      {
         DeleteList(newList);
         Config->Failed = TRUE;
         return;
      }
   }

   if (SectionFound == FALSE)
   {
      /* Couldn't find the Section, so create it now */
      sprintf(Buf, "[%s]", Section);
      if (AddListItem(newList, Buf) == FALSE)
      {
         DeleteList(newList);
         Config->Failed = TRUE;
         return;
      }
      sprintf(Buf, "%s = %s", Item, Data);
      if (AddListItem(newList, Buf) == FALSE)
      {
         DeleteList(newList);
         Config->Failed = TRUE;
         return;
      }
   }

   /* Clean up */
   DeleteList(Config->Cfglist);
   Config->Cfglist = newList;

   return;
}

void ModifyConfigNumber(struct ConfigData *Config, STRPTR Section, STRPTR Item, LONG Data)
{
   TEXT Numbuf[20];

   sprintf(Numbuf, "%ld", Data);
   ModifyConfig(Config, Section, Item, Numbuf);

   return;
}

int ReadConfig(struct ConfigData *Config, STRPTR Section, STRPTR Item, STRPTR Buffer, LONG Bufsize, STRPTR Def)
{
   BOOL inSection = FALSE;
   LONG SectionLen, ItemLen;

   struct Node *Worknode;

   SectionLen = strlen(Section);
   ItemLen    = strlen(Item);

   /* Scan the List looking the the desired Section and Item */
   Worknode = Config->Cfglist->lh_Head;

   while (Worknode->ln_Succ)
   {
      if (ISSECTION(Worknode->ln_Name))
      {
         if (strncmp(Worknode->ln_Name + 1, Section, SectionLen) == 0)
         {
            inSection = TRUE;
         }
         else
         {
            inSection = FALSE;
         }
      }
      else
      {
         if (inSection == TRUE &&
             strncmp(Worknode->ln_Name, Item, ItemLen) == 0 &&
             STRCMP3(Worknode->ln_Name + ItemLen, ' ', '=', ' ')
            )
         {
            if (strlen(Worknode->ln_Name + ItemLen + 3) >= Bufsize)
            {
               if (strlen(Def) >= Bufsize)
               {
                  return CFG_READ_FAIL;
               }
               else
               {
                  strcpy(Buffer, Def);
                  return CFG_READ_SUCCESS;
               }
            }
            else
            {
               strcpy(Buffer, Worknode->ln_Name + ItemLen + 3);
               return CFG_READ_SUCCESS;
            }
         }
      }

      Worknode = Worknode->ln_Succ;
   }

   /* We've reached the end of the List without finding the requested Item */
   /* in the requested Section, so we'll return the default Value instead. */

   if (strlen(Def) >= Bufsize)
   {
      return CFG_READ_FAIL;
   }
   else
   {
      strcpy(Buffer, Def);
      return CFG_READ_SUCCESS;
   }
}

LONG ReadConfigNumber(struct ConfigData *Config, STRPTR Section, STRPTR Item, LONG Def)
{
   TEXT Numbuf[20], Retbuf[20];

   sprintf(Numbuf, "%ld", Def);

   ReadConfig(Config, Section, Item, Retbuf, 20, Numbuf);

   return (strtol(Retbuf, NULL, 10));
}

void DeleteSection(struct ConfigData *Config, STRPTR Section)
{
   LONG SectionLen;

   struct List *newList;
   struct Node *Worknode;

   SectionLen = strlen(Section);

   newList = InitList();
   if (newList == NULL)
   {
      Config->Failed = TRUE;
      return;
   }

   /* Copy List to new List, given Section is left out */
   Worknode = Config->Cfglist->lh_Head;

   while (Worknode->ln_Succ)
   {
      if (ISSECTION(Worknode->ln_Name))
      {
         /* If this is the Section to delete then skip it */
         if (strncmp(Worknode->ln_Name + 1, Section, SectionLen) == 0)
         {
            Worknode = Worknode->ln_Succ;

            while (Worknode->ln_Succ && !(ISSECTION(Worknode->ln_Name)))
            {
               Worknode = Worknode->ln_Succ;
            }

            Worknode = Worknode->ln_Pred;
         }
         else
         {
            /* Else copy the Data */
            if (AddListItem(newList, Worknode->ln_Name) == FALSE)
            {
               DeleteList(newList);
               Config->Failed = TRUE;
               return;
            }
         }
      }
      else
      {
         if (AddListItem(newList, Worknode->ln_Name) == FALSE)
         {
            DeleteList(newList);
            Config->Failed = TRUE;
            return;
         }
      }

      Worknode = Worknode->ln_Succ;
   }

   /* Clean up */
   DeleteList(Config->Cfglist);
   Config->Cfglist = newList;

   return;
}

struct ConfigData *OpenConfig(STRPTR Filename)
{
   FILE *Cfgfile;
   TEXT  Readbuffer[BUFFERSIZE];

   struct ConfigData *Config;

   Pool = CreatePool(MEMF_PUBLIC, 2048, 1024);
   if (Pool == NULL)
   {
      return NULL;
   }

   Config = AllocPooled(Pool, sizeof(struct ConfigData));
   if (Config == NULL)
   {
      DeletePool(Pool);
      return NULL;
   }

   Config->Cfglist = InitList();
   if (Config->Cfglist == NULL)
   {
      DeletePool(Pool);  /* Delete Pool frees everything we have */
      return NULL;
   }

   strcpy(Config->Filename, Filename);
   Config->Failed = FALSE;

   /* Read Cfg File into Memory, return empty List if it doesn't exist */
   Cfgfile = fopen(Config->Filename, "r");
   if (Cfgfile == NULL)
   {
      return Config;
   }

   fgets(Readbuffer, BUFFERSIZE, Cfgfile);
   if (strcmp(Readbuffer, CFGID) != 0)
   {
      fclose(Cfgfile);
      return Config;
   }

   while (fgets(Readbuffer, BUFFERSIZE, Cfgfile) != NULL)
   {
      Readbuffer[strlen(Readbuffer) - 1] = '\0';  /* Kill the trailing CR */
      if (!EMPTYSTR(Readbuffer))
      {
         if (AddListItem(Config->Cfglist, Readbuffer) == FALSE)  /* Error allocating node? */
         {
            fclose(Cfgfile);              /* Close the File */
            DeleteList(Config->Cfglist);  /* Free everything we've allocated so far */
            DeletePool(Pool);             /* Delete Pool frees everything we have */
            return NULL;                  /* Return an Error */
         }
      }
   }

   fclose(Cfgfile);

   return Config;
}

int WriteConfig(struct ConfigData *Config)
{
   FILE *Cfgfile;

   struct Node *Worknode;

   Cfgfile = fopen(Config->Filename, "w");
   if (Cfgfile == NULL)
   {
      return CFG_WRITE_FAIL;
   }

   fprintf(Cfgfile, "%s", CFGID);

   Worknode = Config->Cfglist->lh_Head;

   while (Worknode->ln_Succ)
   {
      fprintf(Cfgfile, "%s%s\n",
              ISSECTION(Worknode->ln_Name) ? "\n" : "",
              Worknode->ln_Name
             );

      Worknode = Worknode->ln_Succ;
   }

   fclose(Cfgfile);

   return CFG_WRITE_SUCCESS;
}

void CloseConfig(struct ConfigData *Config)
{
   DeleteList(Config->Cfglist);
   DeletePool(Pool);  /* Delete Pool frees everything we have */
}


static struct List *InitList()
{
   struct List *Lst;

   Lst = AllocPooled(Pool, sizeof(struct List));
   if (Lst == NULL)
   {
      return NULL;
   }

   Lst->lh_Head = (struct Node *)&Lst->lh_Tail;
   Lst->lh_Tail = 0;
   Lst->lh_TailPred = (struct Node *)&Lst->lh_Head;
   Lst->lh_Type = NT_TASK;

   return Lst;
}

static int AddListItem(struct List *Lst, STRPTR Item)
{
   STRPTR Str;

   struct Node *Worknode;

   Worknode = AllocPooled(Pool, sizeof(struct Node));
   if (Worknode == NULL)  /* Allocation Error */
   {
      return FALSE;
   }

   Str = AllocPooled(Pool, strlen(Item) + 1);
   if (Str == NULL)
   {
      FreePooled(Pool, Worknode, sizeof(struct Node));
      return FALSE;
   }

   strcpy(Str, Item);

   Worknode->ln_Name = Str;
   Worknode->ln_Pri = 0;
   Worknode->ln_Type = NT_TASK;

   AddTail(Lst, Worknode);

   return TRUE;
}

static void DeleteList(struct List *Lst)
{
   struct Node *Worknode;

   while (! IsListEmpty(Lst))
   {
      Worknode = RemHead(Lst);                          /* Remove from the List */

      FreePooled(Pool, Worknode->ln_Name, strlen(Worknode->ln_Name) + 1);  /* Free the Name Memory */
      FreePooled(Pool, Worknode, sizeof(struct Node));  /* Free the Node        */
   }

   FreePooled(Pool, Lst, sizeof(struct List));
}

