//////////////////////////////////////////////
// NovaWM - Nova Window Manager for X11     //
//////////////////////////////////////////////
// By: Tim Walters                          //
//////////////////////////////////////////////
// Copyright (C) 2001-2002 Tim Walters      //
//////////////////////////////////////////////

////////////////////////////////////////////////////////////////
//This code is released under the terms of the GNU GPL. Refer //
//to the license file included with this source code.         //
////////////////////////////////////////////////////////////////

#include "novawm.h"


using namespace std;

int curLine, domain, curByte;
char *line;
bool delQuotes;

#define DOMAIN_CONFIG	0
#define DOMAIN_MENU		1
#define DOMAIN_STYLE	2

#define NKEYWORDS	15

char *dKeyword[NKEYWORDS] = {
  //Domain 0
  "DisableBar", "ClockFormat", "ShowFocus", "FocusType",
  "BarPosition", "HideClock", "StartupCommand", "MainColor",
  //Domain 1
  "item", "item_splitter", "item_restart", "item_shutdown",
  //Doman 2
  "name", "author", "info"
};

//Number of arguments accepted for each keyword
int knArgs[NKEYWORDS] = {
  1, 1, 1, 1,
  1, 1, 1, 1,
  2, 0, 0, 0,
  1, 1, 1
};

FILE *fIndex[5];		//Tmp index of open files
int lineIndex[5];
int byteIndex[5];

FILE *oFile;

bool shellHack;

void
Config::LoadConfig ()
{
  string FName;
  FName = getenv ("HOME");
  FName += "/.novawm.cfg";


  oFile = fopen (FName.c_str (), "r");
  if (!oFile)
    {
      printf ("NovaWM: Failed to load ~/.novawm.cfg! Please create it!\n");
      return;
    }
  printf ("NovaWM: Opened ~/.novawm.cfg...\n");

  delQuotes = false;

  Search ();
}

void
Config::Search ()
{
  domain = -1;
  bool srch4strt = false;
  bool inArgs = false;

  string kw;

  vector < string > tmpVec;

  line = (char *) malloc (1027);
  curLine = -1;
  curByte = 0;
  shellHack = false;

  while (1)
    {
      while (fgets (line, 1027, oFile) != NULL)
	{
	  curLine++;
	  curByte += strlen (line);
	  if (strlen (line) < 0)	//Empty Line
	    continue;
	  if (line[0] == '!')	//This line is just a comment
	    continue;

	  //Check line for a }
//	  for (int i = 0; i < strlen (line); i++)
//	    {

	      if (line[0] == '}')
		{
		  if (domain < 0)
		    {
		      printf
			("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: Found unexpected '}'!\n",
			 curLine);
		      return;
		    }
		  else		//domain > 0
		    {
		      domain = -1;
		      break;
		    }
		}


	      if ((domain >= 0) && (srch4strt == false))
		{
		  tmpVec = GetKeywordInfo (&kw);
		  AnalyzeKeyword (kw, tmpVec, tmpVec.size ());
		  //  continue
		  continue;	//Found one { in the line, probably a keyword so no extra parsing should be done
		}
	      
		//if (line[i] != ' ')
		//break;
	    //}

	  if (srch4strt)
	    {
	      //Search for {
	      for (int i = 0; i < strlen (line); i++)
		{
		  if (line[i] == '{')
		    {
		      srch4strt = false;
		      break;
		    }

		  if (line[i] != ' ')
		    {
		      printf
			("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: Unexpected character found when searching for beginning of definition block.\n",
			 curLine);
		      return;
		    }
		}
	    }

	  //Check of a "special" keyword
	  tmpVec = SplitStr (line, ' ');
	  if (strncmp (tmpVec[0].c_str (), "include", 7) == 0)
	    {
	      if (tmpVec.size () < 2)
		{
		  printf
		    ("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: include must be followed by a file name!\n",
		     curLine);
		  return;
		}

	      for (int i = 0; i < 4; i++)
		{
		  if (fIndex[i] == 0)
		    {
		      fIndex[i] = oFile;
		      lineIndex[i] = curLine;
		      byteIndex[i] = curByte;
		      curLine = 0;
		      curByte = 0;
		      string FName;
		      FName = getenv ("HOME");
		      FName += "/";
		      FName += tmpVec[1].c_str ();
		      vector < string > tmpVec2;
		      //Remove the newline
		      tmpVec2 = SplitStr (FName, '\n');
		      FName = tmpVec2[0];
		      oFile = fopen (FName.c_str (), "r");
		      if (!oFile)
			{
			  printf
			    ("NovaWM Error: ~/.novawm.cfg: Failed to open included file(%s)!\n",
			     FName.c_str ());
			  return;
			}
		      printf ("NovaWM: Opened %s\n", FName.c_str ());
		      break;
		    }
		}
	      continue;
	    }

	  if (strncmp (tmpVec[0].c_str (), "define", 6) == 0)
	    {
	      srch4strt = true;
	      if (tmpVec.size () < 2)
		{
		  printf
		    ("NovaWM Error: ~/.novawm.cfg:%d: Parse error: define must be followed by definition block name!\n",
		     curLine);
		  return;
		}

	      if (strncmp (tmpVec[1].c_str (), "NovaWM_Config", 13) == 0)
		{
		  domain = DOMAIN_CONFIG;
		  continue;
		}
	      if (strncmp (tmpVec[1].c_str (), "NovaWM_Menu", 11) == 0)
		{
		  domain = DOMAIN_MENU;
		  continue;
		}
	      if (strncmp (tmpVec[1].c_str (), "NovaWM_Style", 12) == 0)
		{
		  domain = DOMAIN_STYLE;
		  continue;
		}

	      if ((domain == -1) && (srch4strt == true))
		{
		  printf
		    ("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: Invalid name of definition block!\n",
		     curLine);
		  return;
		}

	    }
	}

      fclose (oFile);
      if (fIndex[0] == 0)
	return;

      for (int i = 4; i > -1; i--)
	{
	  if (fIndex[i] != 0)
	    {
	      oFile = fIndex[i];
	      fIndex[i] = 0;
	      curLine = lineIndex[i] + 2;
	      curByte = byteIndex[i];
	      lineIndex[i] = 0;
	      byteIndex[i] = 0;
	      fseek (oFile, curByte, SEEK_SET);
	      break;
	    }
	}
    }
}

vector < string > Config::SplitStr (string str, char tok)
{
  vector < string > splitString;
  string newString;
  bool inQuotes = false;

  for (unsigned int i = 0; i < str.size (); i++)
    {

      if (str[i] == tok)
	{
	  if (newString.size () > 0)	//Each string must have a greater than 0 length
	    {
	      splitString.push_back (newString);
	      newString = "";
	    }
	}
      else
	{
	  if (str[i] == '"')
	    {
	      inQuotes = !inQuotes;
	      if (delQuotes == true)
		continue;	//In this paticular use of this function we want to ignore double quotes
	    }

	  if ((str[i] == ' ') || (str[i] == '\t'))	//Ignore whitespaces
	    {
	      if (inQuotes == false)
		continue;
	    }

	  newString += str[i];
	}
    }

  splitString.push_back (newString);

  return splitString;
}

vector < string > Config::GetKeywordInfo (string * keyword)
{
  vector < string > tmpVec;
  vector < string > arguments;

  //First get the keyword
  //NOTE: It is assumed there there is ONE keyword per line!
  tmpVec = SplitStr (line, '{');	//Get rid of arguments

  *keyword = tmpVec[0];

  if (tmpVec.size () == 1)	//zero arguments
    {
      tmpVec.clear ();
      return arguments;
    }

  if ((tmpVec[tmpVec.size () - 1].
       c_str ()[tmpVec[tmpVec.size () - 1].size () - 2] != '}')
      && (tmpVec[tmpVec.size () - 1].
	  c_str ()[tmpVec[tmpVec.size () - 1].size () - 2] != ','))
    {
      printf
	("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: Line did not end with a } or ,\n",
	 curLine);
      return arguments;
    }

  //Remove the }
  tmpVec = SplitStr (tmpVec[tmpVec.size () - 1], '}');

  delQuotes = true;
  //Get the final list of arguments
  tmpVec = SplitStr (tmpVec[0], ',');
  delQuotes = false;

  return tmpVec;
}

void
Config::AnalyzeKeyword (string keyword, vector < string > argVec, int nArgs)
{
  int keyValue = -1;
  //Search for keyword
  for (int i = 0; i < NKEYWORDS; i++)
    {
      if (strcmp (keyword.c_str (), dKeyword[i]) == 0)
	{
	  keyValue = i;
	  break;
	}
    }

  if (knArgs[keyValue] != nArgs)
    {
      printf
	("NovaWM Error: ~/.novawm.cfg:%d: Parse Error: invalid number of arguments for keyword! %s %d\n",
	 curLine, keyword.c_str (), nArgs);
      return;
    }

  switch (keyValue)
    {
    case 0:			//DisableBar
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}

      if (strncmp (argVec[0].c_str (), "true", 4) == 0)
	disableBar = true;
      else
	disableBar = false;

      break;

    case 1:			//ClockFormat
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!(%d)\n",
	     curLine, domain);
	  return;
	}
      clockFormat = atoi (argVec[0].c_str ());
      break;

    case 2:			//ShowFocus
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}
      if (strncmp (argVec[0].c_str (), "true", 4) == 0)
	shwFocusMode = true;
      else
	shwFocusMode = false;
      break;

    case 3:			//FocusType
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}
      focusType = atoi (argVec[0].c_str ());
      break;

    case 4:			//BarPosition
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}
      barPosition = atoi (argVec[0].c_str ());
      break;

    case 5:			//HideClock
      if (domain != 0)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}
      if (strncmp (argVec[0].c_str (), "true", 4) == 0)
	hideClock = true;
      else
	hideClock = false;
      break;

    case 6:			//StartupCommand
      if (shellHack == true)
	break;			//Prevent running 4 times(Possibly causing SEVERE performance problems)
      novawm.Run ((char *) argVec[0].c_str ());
      shellHack = true;
      break;

    case 7:			//MainColor
      mainColor = argVec[0].c_str ();
      break;

    case 8:			//item
      if (domain != 1)
	{
	  printf
	    ("NovaWM Error: ~/.novawm.cfg:%d: Keyword in wrong definition block!\n",
	     curLine);
	  return;
	}

      for (int i = 0; i < itemExec.size (); i++)	//Ignore duplicates
	{
	  if (strcmp (itemExec[i].c_str (), argVec[1].c_str ()) == 0)
	    return;
	}

      novaMenu.AddItem ((char *) argVec[0].c_str ());
      itemExec.push_back (argVec[1].c_str ());
      break;
    case 9:			//item_splitter
      novaMenu.AddItem ("?+MENU_SPLIT+?");
      break;
    case 10:			//item_restart
      novaMenu.AddItem ("?+MENU_RESTART+?");
      break;
    case 11:			//item_shutdown
      novaMenu.AddItem ("?+MENU_SHUTDOWN+?");
      break;
    }
}

Config::Config ()
{
  fIndex[0] = 0;
  fIndex[1] = 0;
  fIndex[2] = 0;
  fIndex[3] = 0;
  fIndex[4] = 0;
  lineIndex[0] = 0;
  lineIndex[1] = 0;
  lineIndex[2] = 0;
  lineIndex[3] = 0;
  lineIndex[4] = 0;
}

Config::~Config ()
{
}


syntax highlighted by Code2HTML, v. 0.9.1